diff --git a/cmd/mailing-list-api/design/mailing_list.go b/cmd/mailing-list-api/design/mailing_list.go index 7273502..92db9ab 100644 --- a/cmd/mailing-list-api/design/mailing_list.go +++ b/cmd/mailing-list-api/design/mailing_list.go @@ -130,13 +130,12 @@ var _ = dsl.Service("mailing-list", func() { IfMatchAttribute() GrpsIOServiceUIDAttribute() - GrpsIOServiceUpdateAttributesNoDefaults() + GrpsIOServiceBaseAttributes() WritersAttribute() AuditorsAttribute() - // Only require essential fields for updates - most fields are now optional - dsl.Required("version") + dsl.Required("type", "project_uid", "version") }) dsl.Result(GrpsIOServiceWithReadonlyAttributes) dsl.Error("BadRequest", BadRequestError, "Bad request") @@ -145,7 +144,7 @@ var _ = dsl.Service("mailing-list", func() { dsl.Error("InternalServerError", InternalServerError, "Internal server error") dsl.Error("ServiceUnavailable", ServiceUnavailableError, "Service unavailable") dsl.HTTP(func() { - dsl.PATCH("/groupsio/services/{uid}") + dsl.PUT("/groupsio/services/{uid}") dsl.Param("version:v") dsl.Param("uid") dsl.Header("bearer_token:Authorization") @@ -266,13 +265,12 @@ var _ = dsl.Service("mailing-list", func() { IfMatchAttribute() GrpsIOMailingListUIDAttribute() - GrpsIOMailingListUpdateAttributesNoDefaults() + GrpsIOMailingListBaseAttributes() WritersAttribute() AuditorsAttribute() - // Only require essential fields for updates - most fields are now optional for partial updates - dsl.Required("version") + dsl.Required("group_name", "public", "type", "description", "title", "service_uid", "version") }) dsl.Result(GrpsIOMailingListWithReadonlyAttributes) dsl.Error("BadRequest", BadRequestError, "Bad request") @@ -281,7 +279,7 @@ var _ = dsl.Service("mailing-list", func() { dsl.Error("InternalServerError", InternalServerError, "Internal server error") dsl.Error("ServiceUnavailable", ServiceUnavailableError, "Service unavailable") dsl.HTTP(func() { - dsl.PATCH("/groupsio/mailing-lists/{uid}") + dsl.PUT("/groupsio/mailing-lists/{uid}") dsl.Param("version:v") dsl.Param("uid") dsl.Header("bearer_token:Authorization") @@ -410,7 +408,7 @@ var _ = dsl.Service("mailing-list", func() { GrpsIOMailingListUIDAttribute() GrpsIOMemberUIDAttribute() - GrpsIOMemberUpdateAttributesNoDefaults() + GrpsIOMemberUpdateAttributes() dsl.Required("bearer_token", "version", "uid", "member_uid", "if_match") }) @@ -421,7 +419,7 @@ var _ = dsl.Service("mailing-list", func() { dsl.Error("InternalServerError", InternalServerError, "Internal server error") dsl.Error("ServiceUnavailable", ServiceUnavailableError, "Service unavailable") dsl.HTTP(func() { - dsl.PATCH("/groupsio/mailing-lists/{uid}/members/{member_uid}") + dsl.PUT("/groupsio/mailing-lists/{uid}/members/{member_uid}") dsl.Param("version:v") dsl.Param("uid") dsl.Param("member_uid") diff --git a/cmd/mailing-list-api/design/type.go b/cmd/mailing-list-api/design/type.go index a6becab..529e0b3 100644 --- a/cmd/mailing-list-api/design/type.go +++ b/cmd/mailing-list-api/design/type.go @@ -56,53 +56,6 @@ func GrpsIOServiceBaseAttributes() { dsl.Required("type", "project_uid") } -// GrpsIOServiceUpdateAttributesNoDefaults defines service attributes for UPDATE operations WITHOUT defaults -// This ensures omitted fields preserve existing values instead of being reset to defaults -func GrpsIOServiceUpdateAttributesNoDefaults() { - dsl.Attribute("type", dsl.String, "Service type", func() { - dsl.Enum("primary", "formation", "shared") - dsl.Example("primary") - }) - dsl.Attribute("domain", dsl.String, "Service domain", func() { - dsl.Example("lists.project.org") - }) - dsl.Attribute("group_id", dsl.Int64, "GroupsIO group ID", func() { - dsl.Example(12345) - }) - dsl.Attribute("status", dsl.String, "Service status", func() { - dsl.Example("created") - }) - dsl.Attribute("global_owners", dsl.ArrayOf(dsl.String), "List of global owner email addresses (required for primary, forbidden for shared)", func() { - dsl.Elem(func() { - dsl.Format(dsl.FormatEmail) - }) - dsl.Example([]string{"admin@example.com"}) - }) - dsl.Attribute("prefix", dsl.String, "Email prefix (required for formation and shared, forbidden for primary)", func() { - dsl.Example("formation") - }) - dsl.Attribute("project_slug", dsl.String, "Project slug identifier", func() { - dsl.Format(dsl.FormatRegexp) - dsl.Pattern(`^[a-z][a-z0-9_\-]*[a-z0-9]$`) - dsl.Example("cncf") - }) - dsl.Attribute("project_uid", dsl.String, "LFXv2 Project UID", func() { - dsl.Format(dsl.FormatUUID) - dsl.Example("7cad5a8d-19d0-41a4-81a6-043453daf9ee") - }) - dsl.Attribute("url", dsl.String, "Service URL", func() { - dsl.Format(dsl.FormatURI) - dsl.Example("https://lists.project.org") - }) - dsl.Attribute("group_name", dsl.String, "GroupsIO group name", func() { - dsl.Example("project-name") - }) - // CRITICAL: No default for update operations - omitted field preserves existing value - dsl.Attribute("public", dsl.Boolean, "Whether the service is publicly accessible", func() { - dsl.Example(true) - // NO DEFAULT - this makes it a pointer in generated code - }) -} // GrpsIOServiceWithReadonlyAttributes is the DSL type for a GroupsIO service with readonly attributes. var GrpsIOServiceWithReadonlyAttributes = dsl.Type("grps-io-service-with-readonly-attributes", func() { @@ -342,53 +295,6 @@ func GrpsIOMailingListBaseAttributes() { } -// GrpsIOMailingListUpdateAttributesNoDefaults defines mailing list attributes for UPDATE operations WITHOUT defaults -// This ensures omitted fields preserve existing values instead of being reset to defaults -func GrpsIOMailingListUpdateAttributesNoDefaults() { - dsl.Attribute("group_name", dsl.String, "Mailing list group name", func() { - dsl.Example("technical-steering-committee") - dsl.Pattern(`^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$`) - dsl.MinLength(3) - dsl.MaxLength(34) - }) - // CRITICAL: No default for update operations - omitted field preserves existing value - dsl.Attribute("public", dsl.Boolean, "Whether the mailing list is publicly accessible", func() { - dsl.Example(false) - // NO DEFAULT - this makes it a pointer in generated code - }) - dsl.Attribute("type", dsl.String, "Mailing list type", func() { - dsl.Enum("announcement", "discussion_moderated", "discussion_open") - dsl.Example("discussion_moderated") - }) - dsl.Attribute("committee_uid", dsl.String, "Committee UUID for committee-based mailing lists", func() { - dsl.Format(dsl.FormatUUID) - dsl.Example("7cad5a8d-19d0-41a4-81a6-043453daf9ee") - }) - dsl.Attribute("committee_filters", dsl.ArrayOf(dsl.String), "Committee member filters", func() { - dsl.Elem(func() { - dsl.Enum("Voting Rep", "Alternate Voting Rep", "Observer", "Emeritus", "None") - }) - dsl.Example([]string{"Voting Rep", "Alternate Voting Rep"}) - }) - dsl.Attribute("description", dsl.String, "Mailing list description (11-500 characters)", func() { - dsl.MinLength(11) - dsl.MaxLength(500) - dsl.Example("Technical steering committee discussions") - }) - dsl.Attribute("title", dsl.String, "Mailing list title", func() { - dsl.Example("Technical Steering Committee") - dsl.MinLength(5) - dsl.MaxLength(100) - }) - dsl.Attribute("subject_tag", dsl.String, "Subject tag prefix", func() { - dsl.Example("[TSC]") - dsl.MaxLength(50) - }) - dsl.Attribute("service_uid", dsl.String, "Service UUID", func() { - dsl.Format(dsl.FormatUUID) - dsl.Example("7cad5a8d-19d0-41a4-81a6-043453daf9ee") - }) -} // GrpsIOMailingListUIDAttribute is the DSL attribute for mailing list UID. func GrpsIOMailingListUIDAttribute() { @@ -623,44 +529,3 @@ func GrpsIOMemberUpdateAttributes() { }) } -// GrpsIOMemberUpdateAttributesNoDefaults defines mutable attributes for member updates WITHOUT defaults -// This is used for UPDATE operations where omitted fields should be preserved, not reset to defaults -func GrpsIOMemberUpdateAttributesNoDefaults() { - dsl.Attribute("username", dsl.String, "Member username", func() { - dsl.MaxLength(255) - dsl.Example("jdoe") - }) - - dsl.Attribute("first_name", dsl.String, "Member first name", func() { - dsl.MinLength(1) - dsl.MaxLength(255) - dsl.Example("John") - }) - - dsl.Attribute("last_name", dsl.String, "Member last name", func() { - dsl.MinLength(1) - dsl.MaxLength(255) - dsl.Example("Doe") - }) - - dsl.Attribute("organization", dsl.String, "Member organization", func() { - dsl.MaxLength(255) - dsl.Example("Example Corp") - }) - - dsl.Attribute("job_title", dsl.String, "Member job title", func() { - dsl.MaxLength(255) - dsl.Example("Software Engineer") - }) - - // CRITICAL: No defaults for update operations - omitted fields preserve existing values - dsl.Attribute("delivery_mode", dsl.String, "Email delivery mode", func() { - dsl.Enum("normal", "digest", "none") - // NO DEFAULT - this makes it a pointer in generated code - }) - - dsl.Attribute("mod_status", dsl.String, "Moderation status", func() { - dsl.Enum("none", "moderator", "owner") - // NO DEFAULT - this makes it a pointer in generated code - }) -} diff --git a/cmd/mailing-list-api/service/mailing_list_service.go b/cmd/mailing-list-api/service/mailing_list_service.go index 91bffbb..303c7ea 100644 --- a/cmd/mailing-list-api/service/mailing_list_service.go +++ b/cmd/mailing-list-api/service/mailing_list_service.go @@ -151,6 +151,16 @@ func (s *mailingListService) UpdateGrpsioService(ctx context.Context, payload *m // Convert GOA payload to domain model domainService := s.convertGrpsIOServiceUpdatePayloadToDomain(existingService, payload) + // Enhanced business rule validation (POST-PUT conversion) + // This prevents PUT semantics from violating mandatory business constraints + if err := validateServiceBusinessRules(domainService); err != nil { + slog.WarnContext(ctx, "business rule validation failed after payload conversion", + "error", err, + "service_uid", payload.UID, + "service_type", domainService.Type) + return nil, wrapError(ctx, err) + } + // Execute use case updatedService, revision, err := s.grpsIOWriterOrchestrator.UpdateGrpsIOService(ctx, *payload.UID, domainService, expectedRevision) if err != nil { @@ -575,7 +585,6 @@ func (s *mailingListService) DeleteGrpsioMailingListMember(ctx context.Context, // Helper functions - // payloadStringValue safely extracts string value from payload pointer func payloadStringValue(val *string) string { if val == nil { diff --git a/cmd/mailing-list-api/service/mailing_list_validators_test.go b/cmd/mailing-list-api/service/mailing_list_validators_test.go index 36fc5ee..16ac330 100644 --- a/cmd/mailing-list-api/service/mailing_list_validators_test.go +++ b/cmd/mailing-list-api/service/mailing_list_validators_test.go @@ -37,10 +37,10 @@ func TestValidateMailingListUpdate(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - GroupName: stringPtr("test-group"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Valid description with enough characters"), + GroupName: "test-group", + Public: true, + ServiceUID: "svc-123", + Description: "Valid description with enough characters", }, wantErr: false, }, @@ -53,9 +53,9 @@ func TestValidateMailingListUpdate(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Valid description with enough characters"), + Public: true, + ServiceUID: "svc-123", + Description: "Valid description with enough characters", }, wantErr: false, }, @@ -68,9 +68,9 @@ func TestValidateMailingListUpdate(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Valid description with enough characters"), + Public: true, + ServiceUID: "svc-123", + Description: "Valid description with enough characters", }, wantErr: true, errMsg: "cannot change visibility from private to public", @@ -84,9 +84,9 @@ func TestValidateMailingListUpdate(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-456"), - Description: stringPtr("Valid description with enough characters"), + Public: true, + ServiceUID: "svc-456", + Description: "Valid description with enough characters", }, wantErr: true, errMsg: "cannot change parent service", @@ -101,9 +101,9 @@ func TestValidateMailingListUpdate(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Valid description with enough characters"), + Public: true, + ServiceUID: "svc-123", + Description: "Valid description with enough characters", SubjectTag: stringPtr("VALID-TAG"), }, wantErr: false, @@ -117,9 +117,9 @@ func TestValidateMailingListUpdate(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Valid description with enough characters"), + Public: true, + ServiceUID: "svc-123", + Description: "Valid description with enough characters", SubjectTag: stringPtr("[INVALID]"), }, wantErr: true, @@ -177,11 +177,11 @@ func TestValidateMailingListUpdateNewRules(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - GroupName: stringPtr("changed-group"), - Type: stringPtr("announcement"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Valid description with enough characters"), + GroupName: "changed-group", + Type: "announcement", + Public: true, + ServiceUID: "svc-123", + Description: "Valid description with enough characters", }, wantErr: true, errMsg: "field 'group_name' is immutable", @@ -203,11 +203,11 @@ func TestValidateMailingListUpdateNewRules(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - GroupName: stringPtr("main-group"), - Type: stringPtr("discussion_open"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Valid description with enough characters"), + GroupName: "main-group", + Type: "discussion_open", + Public: true, + ServiceUID: "svc-123", + Description: "Valid description with enough characters", }, wantErr: true, errMsg: "main group must be an announcement list", @@ -229,11 +229,11 @@ func TestValidateMailingListUpdateNewRules(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - GroupName: stringPtr("main-group"), - Type: stringPtr("announcement"), - Public: boolPtr(false), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Valid description with enough characters"), + GroupName: "main-group", + Type: "announcement", + Public: false, + ServiceUID: "svc-123", + Description: "Valid description with enough characters", }, wantErr: true, errMsg: "main group must remain public", @@ -255,11 +255,11 @@ func TestValidateMailingListUpdateNewRules(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - GroupName: stringPtr("test-group"), - Type: stringPtr("custom"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Valid description with enough characters"), + GroupName: "test-group", + Type: "custom", + Public: true, + ServiceUID: "svc-123", + Description: "Valid description with enough characters", }, wantErr: true, errMsg: "cannot set type to \"custom\"", @@ -281,11 +281,11 @@ func TestValidateMailingListUpdateNewRules(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - GroupName: stringPtr("main-group"), - Type: stringPtr("announcement"), - Public: boolPtr(true), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Updated description with enough characters"), + GroupName: "main-group", + Type: "announcement", + Public: true, + ServiceUID: "svc-123", + Description: "Updated description with enough characters", }, wantErr: false, }, @@ -306,11 +306,11 @@ func TestValidateMailingListUpdateNewRules(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ UID: stringPtr("ml-123"), - GroupName: stringPtr("sub-group"), - Type: stringPtr("discussion_moderated"), - Public: boolPtr(false), - ServiceUID: stringPtr("svc-123"), - Description: stringPtr("Updated description with enough characters"), + GroupName: "sub-group", + Type: "discussion_moderated", + Public: false, + ServiceUID: "svc-123", + Description: "Updated description with enough characters", }, wantErr: false, }, diff --git a/cmd/mailing-list-api/service/service_payload_converters.go b/cmd/mailing-list-api/service/service_payload_converters.go index e90187d..1d300ef 100644 --- a/cmd/mailing-list-api/service/service_payload_converters.go +++ b/cmd/mailing-list-api/service/service_payload_converters.go @@ -69,7 +69,6 @@ func (s *mailingListService) convertGrpsIOMailingListPayloadToDomain(p *mailingl } // convertGrpsIOServiceUpdatePayloadToDomain converts GOA update payload to domain model -// convertPayloadToUpdateBase func (s *mailingListService) convertGrpsIOServiceUpdatePayloadToDomain(existing *model.GrpsIOService, p *mailinglistservice.UpdateGrpsioServicePayload) *model.GrpsIOService { // Check for nil payload or existing to avoid panic if p == nil || p.UID == nil || existing == nil { @@ -77,153 +76,59 @@ func (s *mailingListService) convertGrpsIOServiceUpdatePayloadToDomain(existing } now := time.Now() - updated := &model.GrpsIOService{ - // Preserve immutable fields from existing service + return &model.GrpsIOService{ + // Preserve ALL immutable fields from existing service UID: *p.UID, + Type: existing.Type, // Fixed: preserve from existing, not payload Domain: existing.Domain, GroupID: existing.GroupID, Prefix: existing.Prefix, ProjectSlug: existing.ProjectSlug, ProjectName: existing.ProjectName, - ProjectUID: existing.ProjectUID, - URL: existing.URL, - GroupName: existing.GroupName, + URL: existing.URL, // Fixed: add missing field preservation + GroupName: existing.GroupName, // Fixed: add missing field preservation CreatedAt: existing.CreatedAt, LastReviewedAt: existing.LastReviewedAt, LastReviewedBy: existing.LastReviewedBy, - UpdatedAt: now, - } - - // Handle conditionally updateable fields - preserve existing if not provided - if p.Type != nil { - updated.Type = *p.Type - } else { - updated.Type = existing.Type - } - - if p.Status != nil { - updated.Status = *p.Status - } else { - updated.Status = existing.Status - } - - if p.Public != nil { - updated.Public = *p.Public - } else { - updated.Public = existing.Public - } - - if p.ProjectUID != nil { - updated.ProjectUID = *p.ProjectUID - } else { - updated.ProjectUID = existing.ProjectUID - } - - // Handle slice fields - if p.GlobalOwners != nil { - updated.GlobalOwners = p.GlobalOwners - } else { - updated.GlobalOwners = existing.GlobalOwners - } - - if p.Writers != nil { - updated.Writers = p.Writers - } else { - updated.Writers = existing.Writers - } - if p.Auditors != nil { - updated.Auditors = p.Auditors - } else { - updated.Auditors = existing.Auditors + // Update mutable fields (PUT semantics - complete replacement) + Status: payloadStringValue(p.Status), // nil → "" + ProjectUID: existing.ProjectUID, // IMMUTABLE (keep as is) + Public: p.Public, // Direct assignment + GlobalOwners: p.GlobalOwners, // nil → nil + Writers: p.Writers, // nil → nil + Auditors: p.Auditors, // nil → nil + UpdatedAt: now, } - - return updated } // convertGrpsIOMailingListUpdatePayloadToDomain converts an update payload to domain model func (s *mailingListService) convertGrpsIOMailingListUpdatePayloadToDomain(existing *model.GrpsIOMailingList, payload *mailinglistservice.UpdateGrpsioMailingListPayload) *model.GrpsIOMailingList { - // Start from existing to preserve immutable/readonly fields - updated := &model.GrpsIOMailingList{ + // Create updated mailing list from payload data (PUT semantics) + return &model.GrpsIOMailingList{ + // Preserve immutable/readonly fields UID: existing.UID, + GroupName: existing.GroupName, // Fixed: GroupName is immutable, preserve from existing ProjectUID: existing.ProjectUID, ProjectName: existing.ProjectName, ProjectSlug: existing.ProjectSlug, CreatedAt: existing.CreatedAt, - UpdatedAt: time.Now().UTC(), LastReviewedAt: existing.LastReviewedAt, LastReviewedBy: existing.LastReviewedBy, - } - - // Handle conditionally updateable fields - preserve existing if not provided - if payload.GroupName != nil { - updated.GroupName = *payload.GroupName - } else { - updated.GroupName = existing.GroupName - } - - if payload.Public != nil { - updated.Public = *payload.Public - } else { - updated.Public = existing.Public - } - - if payload.Type != nil { - updated.Type = *payload.Type - } else { - updated.Type = existing.Type - } - - if payload.Description != nil { - updated.Description = *payload.Description - } else { - updated.Description = existing.Description - } - - if payload.Title != nil { - updated.Title = *payload.Title - } else { - updated.Title = existing.Title - } - - if payload.ServiceUID != nil { - updated.ServiceUID = *payload.ServiceUID - } else { - updated.ServiceUID = existing.ServiceUID - } - - if payload.CommitteeUID != nil { - updated.CommitteeUID = *payload.CommitteeUID - } else { - updated.CommitteeUID = existing.CommitteeUID - } - - if payload.SubjectTag != nil { - updated.SubjectTag = *payload.SubjectTag - } else { - updated.SubjectTag = existing.SubjectTag - } - - // Handle slice fields - if payload.CommitteeFilters != nil { - updated.CommitteeFilters = payload.CommitteeFilters - } else { - updated.CommitteeFilters = existing.CommitteeFilters - } - - if payload.Writers != nil { - updated.Writers = payload.Writers - } else { - updated.Writers = existing.Writers - } - if payload.Auditors != nil { - updated.Auditors = payload.Auditors - } else { - updated.Auditors = existing.Auditors + // Update all mutable fields (PUT semantics - complete replacement) + Public: payload.Public, // Direct assignment + Type: payload.Type, // Direct assignment + Description: payload.Description, // Direct assignment + Title: payload.Title, // Direct assignment + ServiceUID: payload.ServiceUID, // Direct assignment + CommitteeUID: payloadStringValue(payload.CommitteeUID), // nil → "" + SubjectTag: payloadStringValue(payload.SubjectTag), // nil → "" + CommitteeFilters: payload.CommitteeFilters, // nil → nil + Writers: payload.Writers, // nil → nil + Auditors: payload.Auditors, // nil → nil + UpdatedAt: time.Now().UTC(), } - - return updated } // convertGrpsIOMemberPayloadToDomain converts GOA member payload to domain model @@ -270,8 +175,9 @@ func (s *mailingListService) convertGrpsIOMemberPayloadToDomain(payload *mailing // convertGrpsIOMemberUpdatePayloadToDomain converts update payload to domain member model func (s *mailingListService) convertGrpsIOMemberUpdatePayloadToDomain(payload *mailinglistservice.UpdateGrpsioMailingListMemberPayload, existing *model.GrpsIOMember) *model.GrpsIOMember { - // Start with existing member to preserve immutable fields - updated := &model.GrpsIOMember{ + // Create updated member from payload data (PUT semantics) + return &model.GrpsIOMember{ + // Preserve immutable fields UID: existing.UID, MailingListUID: existing.MailingListUID, Email: existing.Email, // Immutable @@ -280,60 +186,17 @@ func (s *mailingListService) convertGrpsIOMemberUpdatePayloadToDomain(payload *m GroupsIOGroupID: existing.GroupsIOGroupID, CreatedAt: existing.CreatedAt, Status: existing.Status, + LastReviewedAt: existing.LastReviewedAt, + LastReviewedBy: existing.LastReviewedBy, + + // Update all mutable fields (PUT semantics - complete replacement) + Username: payloadStringValue(payload.Username), // nil → "" + FirstName: payloadStringValue(payload.FirstName), // nil → "" + LastName: payloadStringValue(payload.LastName), // nil → "" + Organization: payloadStringValue(payload.Organization), // nil → "" + JobTitle: payloadStringValue(payload.JobTitle), // nil → "" + DeliveryMode: payload.DeliveryMode, // Direct (always has value) + ModStatus: payload.ModStatus, // Direct (always has value) + UpdatedAt: time.Now().UTC(), } - - // Update mutable fields from payload - if payload.Username != nil { - updated.Username = *payload.Username - } else { - updated.Username = existing.Username - } - - if payload.FirstName != nil { - updated.FirstName = *payload.FirstName - } else { - updated.FirstName = existing.FirstName - } - - if payload.LastName != nil { - updated.LastName = *payload.LastName - } else { - updated.LastName = existing.LastName - } - - if payload.Organization != nil { - updated.Organization = *payload.Organization - } else { - updated.Organization = existing.Organization - } - - if payload.JobTitle != nil { - updated.JobTitle = *payload.JobTitle - } else { - updated.JobTitle = existing.JobTitle - } - - // DeliveryMode and ModStatus are now pointers - apply only when provided - if payload.DeliveryMode != nil { - updated.DeliveryMode = *payload.DeliveryMode - } else { - updated.DeliveryMode = existing.DeliveryMode - } - - if payload.ModStatus != nil { - updated.ModStatus = *payload.ModStatus - } else { - updated.ModStatus = existing.ModStatus - } - - // Note: Access control is managed at the mailing list level - - // Set update timestamp - updated.UpdatedAt = time.Now().UTC() - - // Preserve other existing fields - updated.LastReviewedAt = existing.LastReviewedAt - updated.LastReviewedBy = existing.LastReviewedBy - - return updated } diff --git a/cmd/mailing-list-api/service/service_payload_converters_test.go b/cmd/mailing-list-api/service/service_payload_converters_test.go index d0ccce8..ce057d2 100644 --- a/cmd/mailing-list-api/service/service_payload_converters_test.go +++ b/cmd/mailing-list-api/service/service_payload_converters_test.go @@ -233,7 +233,7 @@ func TestConvertUpdatePayloadToDomain(t *testing.T) { UID: stringPtr("service-123"), Status: stringPtr("inactive"), GlobalOwners: []string{"newowner@example.com"}, - Public: boolPtr(true), + Public: true, Writers: []string{"writer1", "writer2"}, Auditors: []string{"auditor1"}, }, @@ -271,7 +271,7 @@ func TestConvertUpdatePayloadToDomain(t *testing.T) { }, payload: &mailinglistservice.UpdateGrpsioServicePayload{ UID: stringPtr("service-456"), - Public: boolPtr(false), + Public: false, }, expected: &model.GrpsIOService{ Type: "formation", @@ -360,49 +360,49 @@ func TestConvertMemberUpdatePayloadToDomain(t *testing.T) { expected *model.GrpsIOMember }{ { - name: "partial update - only name fields, preserve delivery and mod status", + name: "partial update - only name fields, clear other mutable fields (PUT semantics)", existing: existingMember, payload: &mailinglistservice.UpdateGrpsioMailingListMemberPayload{ FirstName: stringPtr("Updated"), LastName: stringPtr("Name"), - // DeliveryMode and ModStatus are nil - should preserve existing values + // DeliveryMode and ModStatus are nil - PUT semantics clears them to "" }, expected: &model.GrpsIOMember{ UID: "member-123", MailingListUID: "ml-456", Email: "existing@example.com", - Username: "existinguser", + Username: "", // CLEARED (PUT semantics) FirstName: "Updated", // Updated - LastName: "Name", // Updated - Organization: "Existing Corp", - JobTitle: "Existing Engineer", - DeliveryMode: "digest", // PRESERVED - this was the bug! - ModStatus: "moderator", // PRESERVED - this was the bug! - MemberType: "direct", - Status: "active", + LastName: "Name", // Updated + Organization: "", // CLEARED (PUT semantics) + JobTitle: "", // CLEARED (PUT semantics) + DeliveryMode: "", // CLEARED (PUT semantics) + ModStatus: "", // CLEARED (PUT semantics) + MemberType: "direct", // IMMUTABLE + Status: "active", // IMMUTABLE CreatedAt: existingMember.CreatedAt, }, }, { - name: "partial update - only delivery mode", + name: "partial update - only delivery mode, clear other mutable fields (PUT semantics)", existing: existingMember, payload: &mailinglistservice.UpdateGrpsioMailingListMemberPayload{ - DeliveryMode: stringPtr("normal"), - // All other fields nil - should preserve existing values + DeliveryMode: "normal", + // All other fields nil - PUT semantics clears them to "" }, expected: &model.GrpsIOMember{ UID: "member-123", MailingListUID: "ml-456", Email: "existing@example.com", - Username: "existinguser", // PRESERVED - FirstName: "Existing", // PRESERVED - LastName: "User", // PRESERVED - Organization: "Existing Corp", // PRESERVED - JobTitle: "Existing Engineer", // PRESERVED + Username: "", // CLEARED (PUT semantics) + FirstName: "", // CLEARED (PUT semantics) + LastName: "", // CLEARED (PUT semantics) + Organization: "", // CLEARED (PUT semantics) + JobTitle: "", // CLEARED (PUT semantics) DeliveryMode: "normal", // Updated - ModStatus: "moderator", // PRESERVED - MemberType: "direct", - Status: "active", + ModStatus: "", // CLEARED (PUT semantics) + MemberType: "direct", // IMMUTABLE + Status: "active", // IMMUTABLE CreatedAt: existingMember.CreatedAt, }, }, @@ -415,8 +415,8 @@ func TestConvertMemberUpdatePayloadToDomain(t *testing.T) { LastName: stringPtr("Person"), Organization: stringPtr("New Corp"), JobTitle: stringPtr("New Role"), - DeliveryMode: stringPtr("none"), - ModStatus: stringPtr("owner"), + DeliveryMode: "none", + ModStatus: "owner", }, expected: &model.GrpsIOMember{ UID: "member-123", @@ -435,24 +435,24 @@ func TestConvertMemberUpdatePayloadToDomain(t *testing.T) { }, }, { - name: "empty update - no fields provided, all preserved", + name: "empty update - no fields provided, all mutable fields cleared (PUT semantics)", existing: existingMember, payload: &mailinglistservice.UpdateGrpsioMailingListMemberPayload{ - // All fields nil + // All fields nil - PUT semantics clears all mutable fields }, expected: &model.GrpsIOMember{ UID: "member-123", MailingListUID: "ml-456", Email: "existing@example.com", - Username: "existinguser", - FirstName: "Existing", - LastName: "User", - Organization: "Existing Corp", - JobTitle: "Existing Engineer", - DeliveryMode: "digest", // PRESERVED - ModStatus: "moderator", // PRESERVED - MemberType: "direct", - Status: "active", + Username: "", // CLEARED (PUT semantics) + FirstName: "", // CLEARED (PUT semantics) + LastName: "", // CLEARED (PUT semantics) + Organization: "", // CLEARED (PUT semantics) + JobTitle: "", // CLEARED (PUT semantics) + DeliveryMode: "", // CLEARED (PUT semantics) + ModStatus: "", // CLEARED (PUT semantics) + MemberType: "direct", // IMMUTABLE + Status: "active", // IMMUTABLE CreatedAt: existingMember.CreatedAt, }, }, @@ -472,8 +472,8 @@ func TestConvertMemberUpdatePayloadToDomain(t *testing.T) { assert.Equal(t, tt.expected.LastName, result.LastName) assert.Equal(t, tt.expected.Organization, result.Organization) assert.Equal(t, tt.expected.JobTitle, result.JobTitle) - assert.Equal(t, tt.expected.DeliveryMode, result.DeliveryMode, "DeliveryMode should be preserved when not provided") - assert.Equal(t, tt.expected.ModStatus, result.ModStatus, "ModStatus should be preserved when not provided") + assert.Equal(t, tt.expected.DeliveryMode, result.DeliveryMode, "DeliveryMode should follow PUT semantics (nil clears to empty)") + assert.Equal(t, tt.expected.ModStatus, result.ModStatus, "ModStatus should follow PUT semantics (nil clears to empty)") assert.Equal(t, tt.expected.MemberType, result.MemberType) assert.Equal(t, tt.expected.Status, result.Status) assert.Equal(t, tt.expected.CreatedAt, result.CreatedAt) @@ -507,43 +507,43 @@ func TestConvertServiceUpdatePayloadToDomain(t *testing.T) { expected *model.GrpsIOService }{ { - name: "partial update - only status, preserve public field", + name: "partial update - only status, clear other mutable fields (PUT semantics)", existing: existingService, payload: &mailinglistservice.UpdateGrpsioServicePayload{ UID: stringPtr("service-123"), Status: stringPtr("updated"), - // Public is nil - should preserve existing value (true) + // Public is nil - PUT semantics clears it to false }, expected: &model.GrpsIOService{ UID: "service-123", - Type: "primary", // PRESERVED - Domain: "existing.domain.com", // PRESERVED (immutable) - GroupID: 12345, // PRESERVED (immutable) + Type: "primary", // IMMUTABLE + Domain: "existing.domain.com", // IMMUTABLE + GroupID: 12345, // IMMUTABLE Status: "updated", // Updated - Public: true, // PRESERVED - this was the bug! - GlobalOwners: []string{"existing@example.com"}, // PRESERVED - ProjectUID: "project-123", // PRESERVED - CreatedAt: existingService.CreatedAt, + Public: false, // CLEARED (PUT semantics) + GlobalOwners: nil, // CLEARED (PUT semantics) + ProjectUID: "project-123", // IMMUTABLE + CreatedAt: existingService.CreatedAt, // IMMUTABLE }, }, { - name: "partial update - only public field", + name: "partial update - only public field, clear other mutable fields (PUT semantics)", existing: existingService, payload: &mailinglistservice.UpdateGrpsioServicePayload{ UID: stringPtr("service-123"), - Public: boolPtr(false), - // All other fields nil - should preserve existing values + Public: false, + // All other fields nil - PUT semantics clears mutable fields }, expected: &model.GrpsIOService{ UID: "service-123", - Type: "primary", // PRESERVED - Domain: "existing.domain.com", // PRESERVED - GroupID: 12345, // PRESERVED - Status: "active", // PRESERVED + Type: "primary", // IMMUTABLE + Domain: "existing.domain.com", // IMMUTABLE + GroupID: 12345, // IMMUTABLE + Status: "", // CLEARED (PUT semantics) Public: false, // Updated - GlobalOwners: []string{"existing@example.com"}, // PRESERVED - ProjectUID: "project-123", // PRESERVED - CreatedAt: existingService.CreatedAt, + GlobalOwners: nil, // CLEARED (PUT semantics) + ProjectUID: "project-123", // IMMUTABLE + CreatedAt: existingService.CreatedAt, // IMMUTABLE }, }, { @@ -551,41 +551,41 @@ func TestConvertServiceUpdatePayloadToDomain(t *testing.T) { existing: existingService, payload: &mailinglistservice.UpdateGrpsioServicePayload{ UID: stringPtr("service-123"), - Type: stringPtr("formation"), + Type: "formation", Status: stringPtr("disabled"), - Public: boolPtr(false), + Public: false, GlobalOwners: []string{"new1@example.com", "new2@example.com"}, - ProjectUID: stringPtr("new-project-456"), + ProjectUID: "new-project-456", }, expected: &model.GrpsIOService{ UID: "service-123", - Type: "formation", // Updated - Domain: "existing.domain.com", // PRESERVED (immutable) - GroupID: 12345, // PRESERVED (immutable) + Type: "primary", // IMMUTABLE (can't change service type) + Domain: "existing.domain.com", // IMMUTABLE + GroupID: 12345, // IMMUTABLE Status: "disabled", // Updated Public: false, // Updated GlobalOwners: []string{"new1@example.com", "new2@example.com"}, // Updated - ProjectUID: "new-project-456", // Updated - CreatedAt: existingService.CreatedAt, // PRESERVED (immutable) + ProjectUID: "project-123", // IMMUTABLE (can't change project) + CreatedAt: existingService.CreatedAt, // IMMUTABLE }, }, { - name: "empty update - no fields provided, all preserved", + name: "empty update - no fields provided, all mutable fields cleared (PUT semantics)", existing: existingService, payload: &mailinglistservice.UpdateGrpsioServicePayload{ UID: stringPtr("service-123"), - // All fields nil + // All fields nil - PUT semantics clears all mutable fields }, expected: &model.GrpsIOService{ UID: "service-123", - Type: "primary", // PRESERVED - Domain: "existing.domain.com", // PRESERVED - GroupID: 12345, // PRESERVED - Status: "active", // PRESERVED - Public: true, // PRESERVED - GlobalOwners: []string{"existing@example.com"}, // PRESERVED - ProjectUID: "project-123", // PRESERVED - CreatedAt: existingService.CreatedAt, + Type: "primary", // IMMUTABLE + Domain: "existing.domain.com", // IMMUTABLE + GroupID: 12345, // IMMUTABLE + Status: "", // CLEARED (PUT semantics) + Public: false, // CLEARED to default false (PUT semantics) + GlobalOwners: nil, // CLEARED (PUT semantics) + ProjectUID: "project-123", // IMMUTABLE + CreatedAt: existingService.CreatedAt, // IMMUTABLE }, }, } @@ -601,7 +601,7 @@ func TestConvertServiceUpdatePayloadToDomain(t *testing.T) { assert.Equal(t, tt.expected.Domain, result.Domain) assert.Equal(t, tt.expected.GroupID, result.GroupID) assert.Equal(t, tt.expected.Status, result.Status) - assert.Equal(t, tt.expected.Public, result.Public, "Public field should be preserved when not provided") + assert.Equal(t, tt.expected.Public, result.Public, "Public field should follow PUT semantics (nil clears to false)") assert.Equal(t, tt.expected.GlobalOwners, result.GlobalOwners) assert.Equal(t, tt.expected.ProjectUID, result.ProjectUID) assert.Equal(t, tt.expected.CreatedAt, result.CreatedAt) @@ -635,82 +635,82 @@ func TestConvertMailingListUpdatePayloadToDomain(t *testing.T) { expected *model.GrpsIOMailingList }{ { - name: "partial update - only title, preserve all other fields", + name: "partial update - only title, clear other mutable fields (PUT semantics)", existing: existingMailingList, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - Title: stringPtr("Updated Title"), - // All other fields nil - should preserve existing values + Title: "Updated Title", + // All other fields nil - PUT semantics clears mutable fields }, expected: &model.GrpsIOMailingList{ - UID: "ml-123", - GroupName: "existing-group", // PRESERVED - Public: false, // PRESERVED - this was the bug! - Type: "discussion_moderated", // PRESERVED - Description: "Existing description for the group", // PRESERVED + UID: "ml-123", // IMMUTABLE + GroupName: "existing-group", // IMMUTABLE + Public: false, // CLEARED to default false + Type: "", // CLEARED (PUT semantics) + Description: "", // CLEARED (PUT semantics) Title: "Updated Title", // Updated - ServiceUID: "service-123", // PRESERVED - ProjectUID: "project-123", // PRESERVED (immutable) - CreatedAt: existingMailingList.CreatedAt, // PRESERVED (immutable) + ServiceUID: "", // CLEARED (PUT semantics) + ProjectUID: "project-123", // IMMUTABLE + CreatedAt: existingMailingList.CreatedAt, // IMMUTABLE }, }, { - name: "partial update - only public field", + name: "partial update - only public field, clear other mutable fields (PUT semantics)", existing: existingMailingList, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - Public: boolPtr(true), - // All other fields nil - should preserve existing values + Public: true, + // All other fields nil - PUT semantics clears mutable fields }, expected: &model.GrpsIOMailingList{ - UID: "ml-123", - GroupName: "existing-group", // PRESERVED + UID: "ml-123", // IMMUTABLE + GroupName: "existing-group", // IMMUTABLE Public: true, // Updated - Type: "discussion_moderated", // PRESERVED - Description: "Existing description for the group", // PRESERVED - Title: "Existing Title", // PRESERVED - ServiceUID: "service-123", // PRESERVED - ProjectUID: "project-123", // PRESERVED - CreatedAt: existingMailingList.CreatedAt, + Type: "", // CLEARED (PUT semantics) + Description: "", // CLEARED (PUT semantics) + Title: "", // CLEARED (PUT semantics) + ServiceUID: "", // CLEARED (PUT semantics) + ProjectUID: "project-123", // IMMUTABLE + CreatedAt: existingMailingList.CreatedAt, // IMMUTABLE }, }, { name: "complete update - all fields provided", existing: existingMailingList, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - GroupName: stringPtr("new-group"), - Public: boolPtr(true), - Type: stringPtr("discussion_open"), - Description: stringPtr("New description that is long enough"), - Title: stringPtr("New Title"), - ServiceUID: stringPtr("new-service-456"), + GroupName: "new-group", + Public: true, + Type: "discussion_open", + Description: "New description that is long enough", + Title: "New Title", + ServiceUID: "new-service-456", }, expected: &model.GrpsIOMailingList{ - UID: "ml-123", // PRESERVED (immutable) - GroupName: "new-group", // Updated + UID: "ml-123", // IMMUTABLE + GroupName: "existing-group", // IMMUTABLE (can't change group name) Public: true, // Updated Type: "discussion_open", // Updated Description: "New description that is long enough", // Updated Title: "New Title", // Updated ServiceUID: "new-service-456", // Updated - ProjectUID: "project-123", // PRESERVED (immutable) + ProjectUID: "project-123", // IMMUTABLE CreatedAt: existingMailingList.CreatedAt, // PRESERVED (immutable) }, }, { - name: "empty update - no fields provided, all preserved", + name: "empty update - no fields provided, all mutable fields cleared (PUT semantics)", existing: existingMailingList, payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - // All fields nil + // All fields nil - PUT semantics clears all mutable fields }, expected: &model.GrpsIOMailingList{ - UID: "ml-123", - GroupName: "existing-group", // PRESERVED - Public: false, // PRESERVED - Type: "discussion_moderated", // PRESERVED - Description: "Existing description for the group", // PRESERVED - Title: "Existing Title", // PRESERVED - ServiceUID: "service-123", // PRESERVED - ProjectUID: "project-123", // PRESERVED - CreatedAt: existingMailingList.CreatedAt, + UID: "ml-123", // IMMUTABLE + GroupName: "existing-group", // IMMUTABLE + Public: false, // CLEARED to default false + Type: "", // CLEARED (PUT semantics) + Description: "", // CLEARED (PUT semantics) + Title: "", // CLEARED (PUT semantics) + ServiceUID: "", // CLEARED (PUT semantics) + ProjectUID: "project-123", // IMMUTABLE + CreatedAt: existingMailingList.CreatedAt, // IMMUTABLE }, }, } @@ -723,7 +723,7 @@ func TestConvertMailingListUpdatePayloadToDomain(t *testing.T) { // Check all fields except UpdatedAt timestamp assert.Equal(t, tt.expected.UID, result.UID) assert.Equal(t, tt.expected.GroupName, result.GroupName) - assert.Equal(t, tt.expected.Public, result.Public, "Public field should be preserved when not provided") + assert.Equal(t, tt.expected.Public, result.Public, "Public field should follow PUT semantics (nil clears to false)") assert.Equal(t, tt.expected.Type, result.Type) assert.Equal(t, tt.expected.Description, result.Description) assert.Equal(t, tt.expected.Title, result.Title) diff --git a/cmd/mailing-list-api/service/service_validators.go b/cmd/mailing-list-api/service/service_validators.go index 8305b1c..b45c33e 100644 --- a/cmd/mailing-list-api/service/service_validators.go +++ b/cmd/mailing-list-api/service/service_validators.go @@ -157,35 +157,35 @@ func validateUpdateImmutabilityConstraints(existing *model.GrpsIOService, payloa // Immutable Fields: type, project_uid, prefix, domain, group_id, url, group_name // Mutable Fields: global_owners, status, public only - if payload.Type != nil && *payload.Type != existing.Type { - return errors.NewValidation(fmt.Sprintf("field 'type' is immutable. Cannot change from '%s' to '%s'", existing.Type, *payload.Type)) + if payload.Type != existing.Type { + return errors.NewValidation(fmt.Sprintf("field 'type' is immutable. Cannot change from '%s' to '%s'", existing.Type, payload.Type)) } - if payload.ProjectUID != nil && *payload.ProjectUID != existing.ProjectUID { - return errors.NewValidation(fmt.Sprintf("field 'project_uid' is immutable. Cannot change from '%s' to '%s'", existing.ProjectUID, *payload.ProjectUID)) + if payload.ProjectUID != existing.ProjectUID { + return errors.NewValidation(fmt.Sprintf("field 'project_uid' is immutable. Cannot change from '%s' to '%s'", existing.ProjectUID, payload.ProjectUID)) } - // Check prefix immutability + // Check prefix immutability - only validate if explicitly provided if payload.Prefix != nil && *payload.Prefix != existing.Prefix { return errors.NewValidation(fmt.Sprintf("field 'prefix' is immutable. Cannot change from '%s' to '%s'", existing.Prefix, *payload.Prefix)) } - // Check domain immutability + // Check domain immutability - only validate if explicitly provided if payload.Domain != nil && *payload.Domain != existing.Domain { return errors.NewValidation(fmt.Sprintf("field 'domain' is immutable. Cannot change from '%s' to '%s'", existing.Domain, *payload.Domain)) } - // Check group_id immutability + // Check group_id immutability - only validate if explicitly provided if payload.GroupID != nil && *payload.GroupID != existing.GroupID { return errors.NewValidation(fmt.Sprintf("field 'group_id' is immutable. Cannot change from '%d' to '%d'", existing.GroupID, *payload.GroupID)) } - // Check url immutability + // Check url immutability - only validate if explicitly provided if payload.URL != nil && *payload.URL != existing.URL { return errors.NewValidation(fmt.Sprintf("field 'url' is immutable. Cannot change from '%s' to '%s'", existing.URL, *payload.URL)) } - // Check group_name immutability + // Check group_name immutability - only validate if explicitly provided if payload.GroupName != nil && *payload.GroupName != existing.GroupName { return errors.NewValidation(fmt.Sprintf("field 'group_name' is immutable. Cannot change from '%s' to '%s'", existing.GroupName, *payload.GroupName)) } @@ -270,35 +270,35 @@ func validateMailingListCreation(payload *mailinglistservice.CreateGrpsioMailing // validateMailingListUpdate validates update constraints for mailing lists func validateMailingListUpdate(ctx context.Context, existing *model.GrpsIOMailingList, parentService *model.GrpsIOService, payload *mailinglistservice.UpdateGrpsioMailingListPayload, serviceReader port.GrpsIOServiceReader) error { // Validate group_name immutability (critical business rule) - if payload.GroupName != nil && *payload.GroupName != existing.GroupName { + if payload.GroupName != existing.GroupName { return errors.NewValidation("field 'group_name' is immutable") } // Validate main group restrictions (critical business rule from Groups.io) if parentService != nil && isMainGroupForService(existing, parentService) { // Main groups must remain public announcement lists - if payload.Type != nil && *payload.Type != "announcement" { + if payload.Type != "announcement" { return errors.NewValidation("main group must be an announcement list") } - if payload.Public != nil && !*payload.Public { + if !payload.Public { return errors.NewValidation("main group must remain public") } } // Cannot set type to "custom" unless already "custom" (Groups.io business rule) - if payload.Type != nil && *payload.Type == "custom" && existing.Type != "custom" { + if payload.Type == "custom" && existing.Type != "custom" { return errors.NewValidation("cannot set type to \"custom\"") } // Cannot change visibility from private to public // TODO: LFXV2-479 - Migrate from boolean 'public' field to string 'visibility' field // for full Groups.io API compatibility (supporting "public", "private", "custom" values) - if payload.Public != nil && !existing.Public && *payload.Public { + if !existing.Public && payload.Public { return errors.NewValidation("cannot change visibility from private to public") } // Parent service change validation (allow within same project only) - if payload.ServiceUID != nil && *payload.ServiceUID != existing.ServiceUID { + if payload.ServiceUID != existing.ServiceUID { // Check if service reader is available for validation if serviceReader == nil { // Fallback to old restrictive behavior if no service reader provided @@ -310,11 +310,11 @@ func validateMailingListUpdate(ctx context.Context, existing *model.GrpsIOMailin } // Fetch the new parent service to validate the project ownership - newParentService, _, err := serviceReader.GetGrpsIOService(ctx, *payload.ServiceUID) + newParentService, _, err := serviceReader.GetGrpsIOService(ctx, payload.ServiceUID) if err != nil { slog.ErrorContext(ctx, "failed to retrieve new parent service for validation", "error", err, - "new_service_uid", *payload.ServiceUID, + "new_service_uid", payload.ServiceUID, "mailing_list_uid", existing.UID) return errors.NewValidation("new parent service not found") } @@ -326,7 +326,7 @@ func validateMailingListUpdate(ctx context.Context, existing *model.GrpsIOMailin "current_project_uid", existing.ProjectUID, "new_project_uid", newParentService.ProjectUID, "current_service_uid", existing.ServiceUID, - "new_service_uid", *payload.ServiceUID) + "new_service_uid", payload.ServiceUID) return errors.NewValidation("cannot move mailing list to service in different project") } @@ -334,11 +334,12 @@ func validateMailingListUpdate(ctx context.Context, existing *model.GrpsIOMailin "mailing_list_uid", existing.UID, "project_uid", existing.ProjectUID, "old_service_uid", existing.ServiceUID, - "new_service_uid", *payload.ServiceUID) + "new_service_uid", payload.ServiceUID) } // Cannot change committee without special handling - if payload.CommitteeUID != nil && *payload.CommitteeUID != existing.CommitteeUID { + committeeValue := payloadStringValue(payload.CommitteeUID) + if committeeValue != existing.CommitteeUID { // TODO: LFXV2-478 - Trigger committee member sync slog.Debug("committee change detected - member sync required", "mailing_list_uid", existing.UID) } @@ -346,8 +347,9 @@ func validateMailingListUpdate(ctx context.Context, existing *model.GrpsIOMailin // Description and title length validations now handled by GOA // Validate subject tag format if provided - if payload.SubjectTag != nil && *payload.SubjectTag != "" { - if !isValidSubjectTag(*payload.SubjectTag) { + subjectTagValue := payloadStringValue(payload.SubjectTag) + if subjectTagValue != "" { + if !isValidSubjectTag(subjectTagValue) { return errors.NewValidation("invalid subject tag format") } } @@ -529,3 +531,24 @@ func validateMemberDeleteProtection(member *model.GrpsIOMember) error { return nil } + +// ================================================================================== +// Enhanced Business Rule Validation (POST-PUT Conversion) +// ================================================================================== +// These functions validate business rules AFTER PUT payload conversion to prevent +// violations of critical constraints while maintaining pure PUT semantics. + +// validateServiceBusinessRules validates business rules after PUT conversion +// This prevents PUT semantics from violating mandatory business constraints +func validateServiceBusinessRules(service *model.GrpsIOService) error { + // Primary services MUST have GlobalOwners (critical business rule) + // This prevents clearing GlobalOwners via PUT from making primary services invalid + if service.Type == "primary" && len(service.GlobalOwners) == 0 { + return errors.NewValidation("primary services must have at least one global owner") + } + + // Additional service business rules can be added here + // Example: specific service types requiring certain fields + + return nil +} diff --git a/cmd/mailing-list-api/service/service_validators_test.go b/cmd/mailing-list-api/service/service_validators_test.go index 46f7b4a..dc50f25 100644 --- a/cmd/mailing-list-api/service/service_validators_test.go +++ b/cmd/mailing-list-api/service/service_validators_test.go @@ -380,35 +380,35 @@ func TestValidateUpdateImmutabilityConstraints(t *testing.T) { { name: "valid update with mutable fields only", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("primary"), - ProjectUID: stringPtr("project-123"), + Type: "primary", + ProjectUID: "project-123", Status: stringPtr("active"), GlobalOwners: []string{"newowner@example.com"}, - Public: boolPtr(true), + Public: true, }, expectErr: false, }, { name: "attempt to change type should fail", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("formation"), - ProjectUID: stringPtr("project-123"), + Type: "formation", + ProjectUID: "project-123", }, expectErr: true, }, { name: "attempt to change project_uid should fail", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("primary"), - ProjectUID: stringPtr("different-project"), + Type: "primary", + ProjectUID: "different-project", }, expectErr: true, }, { name: "attempt to change prefix should fail", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("primary"), - ProjectUID: stringPtr("project-123"), + Type: "primary", + ProjectUID: "project-123", Prefix: stringPtr("new-prefix"), }, expectErr: true, @@ -416,8 +416,8 @@ func TestValidateUpdateImmutabilityConstraints(t *testing.T) { { name: "attempt to change domain should fail", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("primary"), - ProjectUID: stringPtr("project-123"), + Type: "primary", + ProjectUID: "project-123", Domain: stringPtr("different.groups.io"), }, expectErr: true, @@ -425,8 +425,8 @@ func TestValidateUpdateImmutabilityConstraints(t *testing.T) { { name: "attempt to change group_id should fail", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("primary"), - ProjectUID: stringPtr("project-123"), + Type: "primary", + ProjectUID: "project-123", GroupID: int64Ptr(99999), }, expectErr: true, @@ -434,8 +434,8 @@ func TestValidateUpdateImmutabilityConstraints(t *testing.T) { { name: "attempt to change url should fail", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("primary"), - ProjectUID: stringPtr("project-123"), + Type: "primary", + ProjectUID: "project-123", URL: stringPtr("https://different.groups.io/g/test"), }, expectErr: true, @@ -443,8 +443,8 @@ func TestValidateUpdateImmutabilityConstraints(t *testing.T) { { name: "attempt to change group_name should fail", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("primary"), - ProjectUID: stringPtr("project-123"), + Type: "primary", + ProjectUID: "project-123", GroupName: stringPtr("different-group"), }, expectErr: true, @@ -452,8 +452,8 @@ func TestValidateUpdateImmutabilityConstraints(t *testing.T) { { name: "update with invalid email should fail", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("primary"), - ProjectUID: stringPtr("project-123"), + Type: "primary", + ProjectUID: "project-123", GlobalOwners: []string{"invalid-email"}, }, expectErr: true, @@ -461,8 +461,8 @@ func TestValidateUpdateImmutabilityConstraints(t *testing.T) { { name: "update with too many global owners should fail", payload: &mailinglistservice.UpdateGrpsioServicePayload{ - Type: stringPtr("primary"), - ProjectUID: stringPtr("project-123"), + Type: "primary", + ProjectUID: "project-123", GlobalOwners: []string{"owner1@example.com", "owner2@example.com", "owner3@example.com", "owner4@example.com", "owner5@example.com", "owner6@example.com", "owner7@example.com", "owner8@example.com", "owner9@example.com", "owner10@example.com", "owner11@example.com"}, }, expectErr: true, @@ -763,11 +763,11 @@ func TestValidateMailingListUpdateParentServiceChange(t *testing.T) { { name: "valid update without parent service change", payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - GroupName: stringPtr("sub-list"), // unchanged - ServiceUID: stringPtr("service-123"), // unchanged - Description: stringPtr("Updated description that is long enough"), - Type: stringPtr("discussion_moderated"), - Public: boolPtr(false), + GroupName: "sub-list", // unchanged + ServiceUID: "service-123", // unchanged + Description: "Updated description that is long enough", + Type: "discussion_moderated", + Public: false, }, setupMock: func(m *MockServiceReader) { // No mock calls expected since ServiceUID hasn't changed @@ -777,11 +777,11 @@ func TestValidateMailingListUpdateParentServiceChange(t *testing.T) { { name: "valid parent service change within same project", payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - GroupName: stringPtr("sub-list"), - ServiceUID: stringPtr("service-456"), // different service - Description: stringPtr("Updated description that is long enough"), - Type: stringPtr("discussion_open"), - Public: boolPtr(false), + GroupName: "sub-list", + ServiceUID: "service-456", // different service + Description: "Updated description that is long enough", + Type: "discussion_open", + Public: false, }, setupMock: func(m *MockServiceReader) { newService := &model.GrpsIOService{ @@ -797,11 +797,11 @@ func TestValidateMailingListUpdateParentServiceChange(t *testing.T) { { name: "blocked cross-project parent service change", payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - GroupName: stringPtr("sub-list"), - ServiceUID: stringPtr("service-different-project"), // different project - Description: stringPtr("Updated description that is long enough"), - Type: stringPtr("discussion_open"), - Public: boolPtr(false), + GroupName: "sub-list", + ServiceUID: "service-different-project", // different project + Description: "Updated description that is long enough", + Type: "discussion_open", + Public: false, }, setupMock: func(m *MockServiceReader) { differentProjectService := &model.GrpsIOService{ @@ -817,11 +817,11 @@ func TestValidateMailingListUpdateParentServiceChange(t *testing.T) { { name: "parent service change with non-existent service", payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - GroupName: stringPtr("sub-list"), - ServiceUID: stringPtr("non-existent-service"), - Description: stringPtr("Updated description that is long enough"), - Type: stringPtr("discussion_open"), - Public: boolPtr(false), + GroupName: "sub-list", + ServiceUID: "non-existent-service", + Description: "Updated description that is long enough", + Type: "discussion_open", + Public: false, }, setupMock: func(m *MockServiceReader) { m.On("GetGrpsIOService", ctx, "non-existent-service").Return(nil, uint64(0), errors.NewServiceUnavailable("service not found")) @@ -832,11 +832,11 @@ func TestValidateMailingListUpdateParentServiceChange(t *testing.T) { { name: "immutable group_name change should fail", payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - GroupName: stringPtr("different-list-name"), // changed - ServiceUID: stringPtr("service-123"), - Description: stringPtr("Updated description that is long enough"), - Type: stringPtr("discussion_open"), - Public: boolPtr(false), + GroupName: "different-list-name", // changed + ServiceUID: "service-123", + Description: "Updated description that is long enough", + Type: "discussion_open", + Public: false, }, setupMock: func(m *MockServiceReader) { // No mock calls expected since validation should fail before that @@ -847,11 +847,11 @@ func TestValidateMailingListUpdateParentServiceChange(t *testing.T) { { name: "private to public visibility change should fail", payload: &mailinglistservice.UpdateGrpsioMailingListPayload{ - GroupName: stringPtr("sub-list"), - ServiceUID: stringPtr("service-123"), - Description: stringPtr("Updated description that is long enough"), - Type: stringPtr("discussion_open"), - Public: boolPtr(true), // changing from private to public + GroupName: "sub-list", + ServiceUID: "service-123", + Description: "Updated description that is long enough", + Type: "discussion_open", + Public: true, // changing from private to public }, setupMock: func(m *MockServiceReader) { // No mock calls expected since validation should fail before that @@ -882,3 +882,74 @@ func TestValidateMailingListUpdateParentServiceChange(t *testing.T) { }) } } + +// ================================================================================== +// Enhanced Business Rule Validation Tests (POST-PUT Conversion) +// ================================================================================== + +func TestValidateServiceBusinessRules(t *testing.T) { + tests := []struct { + name string + service *model.GrpsIOService + expectErr bool + errMsg string + }{ + { + name: "primary service with GlobalOwners should pass", + service: &model.GrpsIOService{ + Type: "primary", + GlobalOwners: []string{"owner@example.com"}, + }, + expectErr: false, + }, + { + name: "primary service without GlobalOwners should fail", + service: &model.GrpsIOService{ + Type: "primary", + GlobalOwners: []string{}, // Empty - should fail + }, + expectErr: true, + errMsg: "primary services must have at least one global owner", + }, + { + name: "primary service with nil GlobalOwners should fail", + service: &model.GrpsIOService{ + Type: "primary", + GlobalOwners: nil, // Nil - should fail + }, + expectErr: true, + errMsg: "primary services must have at least one global owner", + }, + { + name: "formation service without GlobalOwners should pass", + service: &model.GrpsIOService{ + Type: "formation", + GlobalOwners: []string{}, // Empty is OK for formation + }, + expectErr: false, + }, + { + name: "shared service without GlobalOwners should pass", + service: &model.GrpsIOService{ + Type: "shared", + GlobalOwners: nil, // Nil is OK for shared + }, + expectErr: false, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validateServiceBusinessRules(tt.service) + + if tt.expectErr { + assert.Error(t, err) + if tt.errMsg != "" { + assert.Contains(t, err.Error(), tt.errMsg) + } + } else { + assert.NoError(t, err) + } + }) + } +} diff --git a/gen/http/mailing_list/client/cli.go b/gen/http/mailing_list/client/cli.go index 6b26d31..17773dc 100644 --- a/gen/http/mailing_list/client/cli.go +++ b/gen/http/mailing_list/client/cli.go @@ -153,10 +153,8 @@ func BuildUpdateGrpsioServicePayload(mailingListUpdateGrpsioServiceBody string, if err != nil { return nil, fmt.Errorf("invalid JSON for body, \nerror: %s, \nexample of valid JSON:\n%s", err, "'{\n \"auditors\": [\n \"auditor_user_id1\",\n \"auditor_user_id2\"\n ],\n \"domain\": \"lists.project.org\",\n \"global_owners\": [\n \"admin@example.com\"\n ],\n \"group_id\": 12345,\n \"group_name\": \"project-name\",\n \"prefix\": \"formation\",\n \"project_slug\": \"cncf\",\n \"project_uid\": \"7cad5a8d-19d0-41a4-81a6-043453daf9ee\",\n \"public\": true,\n \"status\": \"created\",\n \"type\": \"primary\",\n \"url\": \"https://lists.project.org\",\n \"writers\": [\n \"manager_user_id1\",\n \"manager_user_id2\"\n ]\n }'") } - if body.Type != nil { - if !(*body.Type == "primary" || *body.Type == "formation" || *body.Type == "shared") { - err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.type", *body.Type, []any{"primary", "formation", "shared"})) - } + if !(body.Type == "primary" || body.Type == "formation" || body.Type == "shared") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.type", body.Type, []any{"primary", "formation", "shared"})) } for _, e := range body.GlobalOwners { err = goa.MergeErrors(err, goa.ValidateFormat("body.global_owners[*]", e, goa.FormatEmail)) @@ -167,9 +165,7 @@ func BuildUpdateGrpsioServicePayload(mailingListUpdateGrpsioServiceBody string, if body.ProjectSlug != nil { err = goa.MergeErrors(err, goa.ValidatePattern("body.project_slug", *body.ProjectSlug, "^[a-z][a-z0-9_\\-]*[a-z0-9]$")) } - if body.ProjectUID != nil { - err = goa.MergeErrors(err, goa.ValidateFormat("body.project_uid", *body.ProjectUID, goa.FormatUUID)) - } + err = goa.MergeErrors(err, goa.ValidateFormat("body.project_uid", body.ProjectUID, goa.FormatUUID)) if body.URL != nil { err = goa.MergeErrors(err, goa.ValidateFormat("body.url", *body.URL, goa.FormatURI)) } @@ -225,6 +221,12 @@ func BuildUpdateGrpsioServicePayload(mailingListUpdateGrpsioServiceBody string, v.GlobalOwners[i] = val } } + { + var zero bool + if v.Public == zero { + v.Public = false + } + } if body.Writers != nil { v.Writers = make([]string, len(body.Writers)) for i, val := range body.Writers { @@ -434,23 +436,15 @@ func BuildUpdateGrpsioMailingListPayload(mailingListUpdateGrpsioMailingListBody if err != nil { return nil, fmt.Errorf("invalid JSON for body, \nerror: %s, \nexample of valid JSON:\n%s", err, "'{\n \"auditors\": [\n \"auditor_user_id1\",\n \"auditor_user_id2\"\n ],\n \"committee_filters\": [\n \"Voting Rep\",\n \"Alternate Voting Rep\"\n ],\n \"committee_uid\": \"7cad5a8d-19d0-41a4-81a6-043453daf9ee\",\n \"description\": \"Technical steering committee discussions\",\n \"group_name\": \"technical-steering-committee\",\n \"public\": false,\n \"service_uid\": \"7cad5a8d-19d0-41a4-81a6-043453daf9ee\",\n \"subject_tag\": \"[TSC]\",\n \"title\": \"Technical Steering Committee\",\n \"type\": \"discussion_moderated\",\n \"writers\": [\n \"manager_user_id1\",\n \"manager_user_id2\"\n ]\n }'") } - if body.GroupName != nil { - err = goa.MergeErrors(err, goa.ValidatePattern("body.group_name", *body.GroupName, "^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$")) - } - if body.GroupName != nil { - if utf8.RuneCountInString(*body.GroupName) < 3 { - err = goa.MergeErrors(err, goa.InvalidLengthError("body.group_name", *body.GroupName, utf8.RuneCountInString(*body.GroupName), 3, true)) - } + err = goa.MergeErrors(err, goa.ValidatePattern("body.group_name", body.GroupName, "^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$")) + if utf8.RuneCountInString(body.GroupName) < 3 { + err = goa.MergeErrors(err, goa.InvalidLengthError("body.group_name", body.GroupName, utf8.RuneCountInString(body.GroupName), 3, true)) } - if body.GroupName != nil { - if utf8.RuneCountInString(*body.GroupName) > 34 { - err = goa.MergeErrors(err, goa.InvalidLengthError("body.group_name", *body.GroupName, utf8.RuneCountInString(*body.GroupName), 34, false)) - } + if utf8.RuneCountInString(body.GroupName) > 34 { + err = goa.MergeErrors(err, goa.InvalidLengthError("body.group_name", body.GroupName, utf8.RuneCountInString(body.GroupName), 34, false)) } - if body.Type != nil { - if !(*body.Type == "announcement" || *body.Type == "discussion_moderated" || *body.Type == "discussion_open") { - err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.type", *body.Type, []any{"announcement", "discussion_moderated", "discussion_open"})) - } + if !(body.Type == "announcement" || body.Type == "discussion_moderated" || body.Type == "discussion_open") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.type", body.Type, []any{"announcement", "discussion_moderated", "discussion_open"})) } if body.CommitteeUID != nil { err = goa.MergeErrors(err, goa.ValidateFormat("body.committee_uid", *body.CommitteeUID, goa.FormatUUID)) @@ -460,34 +454,24 @@ func BuildUpdateGrpsioMailingListPayload(mailingListUpdateGrpsioMailingListBody err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.committee_filters[*]", e, []any{"Voting Rep", "Alternate Voting Rep", "Observer", "Emeritus", "None"})) } } - if body.Description != nil { - if utf8.RuneCountInString(*body.Description) < 11 { - err = goa.MergeErrors(err, goa.InvalidLengthError("body.description", *body.Description, utf8.RuneCountInString(*body.Description), 11, true)) - } + if utf8.RuneCountInString(body.Description) < 11 { + err = goa.MergeErrors(err, goa.InvalidLengthError("body.description", body.Description, utf8.RuneCountInString(body.Description), 11, true)) } - if body.Description != nil { - if utf8.RuneCountInString(*body.Description) > 500 { - err = goa.MergeErrors(err, goa.InvalidLengthError("body.description", *body.Description, utf8.RuneCountInString(*body.Description), 500, false)) - } + if utf8.RuneCountInString(body.Description) > 500 { + err = goa.MergeErrors(err, goa.InvalidLengthError("body.description", body.Description, utf8.RuneCountInString(body.Description), 500, false)) } - if body.Title != nil { - if utf8.RuneCountInString(*body.Title) < 5 { - err = goa.MergeErrors(err, goa.InvalidLengthError("body.title", *body.Title, utf8.RuneCountInString(*body.Title), 5, true)) - } + if utf8.RuneCountInString(body.Title) < 5 { + err = goa.MergeErrors(err, goa.InvalidLengthError("body.title", body.Title, utf8.RuneCountInString(body.Title), 5, true)) } - if body.Title != nil { - if utf8.RuneCountInString(*body.Title) > 100 { - err = goa.MergeErrors(err, goa.InvalidLengthError("body.title", *body.Title, utf8.RuneCountInString(*body.Title), 100, false)) - } + if utf8.RuneCountInString(body.Title) > 100 { + err = goa.MergeErrors(err, goa.InvalidLengthError("body.title", body.Title, utf8.RuneCountInString(body.Title), 100, false)) } if body.SubjectTag != nil { if utf8.RuneCountInString(*body.SubjectTag) > 50 { err = goa.MergeErrors(err, goa.InvalidLengthError("body.subject_tag", *body.SubjectTag, utf8.RuneCountInString(*body.SubjectTag), 50, false)) } } - if body.ServiceUID != nil { - err = goa.MergeErrors(err, goa.ValidateFormat("body.service_uid", *body.ServiceUID, goa.FormatUUID)) - } + err = goa.MergeErrors(err, goa.ValidateFormat("body.service_uid", body.ServiceUID, goa.FormatUUID)) if err != nil { return nil, err } @@ -811,15 +795,11 @@ func BuildUpdateGrpsioMailingListMemberPayload(mailingListUpdateGrpsioMailingLis err = goa.MergeErrors(err, goa.InvalidLengthError("body.job_title", *body.JobTitle, utf8.RuneCountInString(*body.JobTitle), 255, false)) } } - if body.DeliveryMode != nil { - if !(*body.DeliveryMode == "normal" || *body.DeliveryMode == "digest" || *body.DeliveryMode == "none") { - err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.delivery_mode", *body.DeliveryMode, []any{"normal", "digest", "none"})) - } + if !(body.DeliveryMode == "normal" || body.DeliveryMode == "digest" || body.DeliveryMode == "none") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.delivery_mode", body.DeliveryMode, []any{"normal", "digest", "none"})) } - if body.ModStatus != nil { - if !(*body.ModStatus == "none" || *body.ModStatus == "moderator" || *body.ModStatus == "owner") { - err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.mod_status", *body.ModStatus, []any{"none", "moderator", "owner"})) - } + if !(body.ModStatus == "none" || body.ModStatus == "moderator" || body.ModStatus == "owner") { + err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.mod_status", body.ModStatus, []any{"none", "moderator", "owner"})) } if err != nil { return nil, err @@ -868,6 +848,18 @@ func BuildUpdateGrpsioMailingListMemberPayload(mailingListUpdateGrpsioMailingLis DeliveryMode: body.DeliveryMode, ModStatus: body.ModStatus, } + { + var zero string + if v.DeliveryMode == zero { + v.DeliveryMode = "normal" + } + } + { + var zero string + if v.ModStatus == zero { + v.ModStatus = "none" + } + } v.UID = uid v.MemberUID = memberUID v.Version = version diff --git a/gen/http/mailing_list/client/encode_decode.go b/gen/http/mailing_list/client/encode_decode.go index b7f5c7c..9f00fa1 100644 --- a/gen/http/mailing_list/client/encode_decode.go +++ b/gen/http/mailing_list/client/encode_decode.go @@ -475,7 +475,7 @@ func (c *Client) BuildUpdateGrpsioServiceRequest(ctx context.Context, v any) (*h } } u := &url.URL{Scheme: c.scheme, Host: c.host, Path: UpdateGrpsioServiceMailingListPath(uid)} - req, err := http.NewRequest("PATCH", u.String(), nil) + req, err := http.NewRequest("PUT", u.String(), nil) if err != nil { return nil, goahttp.ErrInvalidURL("mailing-list", "update-grpsio-service", u.String(), err) } @@ -1133,7 +1133,7 @@ func (c *Client) BuildUpdateGrpsioMailingListRequest(ctx context.Context, v any) } } u := &url.URL{Scheme: c.scheme, Host: c.host, Path: UpdateGrpsioMailingListMailingListPath(uid)} - req, err := http.NewRequest("PATCH", u.String(), nil) + req, err := http.NewRequest("PUT", u.String(), nil) if err != nil { return nil, goahttp.ErrInvalidURL("mailing-list", "update-grpsio-mailing-list", u.String(), err) } @@ -1804,7 +1804,7 @@ func (c *Client) BuildUpdateGrpsioMailingListMemberRequest(ctx context.Context, memberUID = p.MemberUID } u := &url.URL{Scheme: c.scheme, Host: c.host, Path: UpdateGrpsioMailingListMemberMailingListPath(uid, memberUID)} - req, err := http.NewRequest("PATCH", u.String(), nil) + req, err := http.NewRequest("PUT", u.String(), nil) if err != nil { return nil, goahttp.ErrInvalidURL("mailing-list", "update-grpsio-mailing-list-member", u.String(), err) } diff --git a/gen/http/mailing_list/client/types.go b/gen/http/mailing_list/client/types.go index f869032..4155484 100644 --- a/gen/http/mailing_list/client/types.go +++ b/gen/http/mailing_list/client/types.go @@ -51,7 +51,7 @@ type CreateGrpsioServiceRequestBody struct { // "update-grpsio-service" endpoint HTTP request body. type UpdateGrpsioServiceRequestBody struct { // Service type - Type *string `form:"type,omitempty" json:"type,omitempty" xml:"type,omitempty"` + Type string `form:"type" json:"type" xml:"type"` // Service domain Domain *string `form:"domain,omitempty" json:"domain,omitempty" xml:"domain,omitempty"` // GroupsIO group ID @@ -66,13 +66,13 @@ type UpdateGrpsioServiceRequestBody struct { // Project slug identifier ProjectSlug *string `form:"project_slug,omitempty" json:"project_slug,omitempty" xml:"project_slug,omitempty"` // LFXv2 Project UID - ProjectUID *string `form:"project_uid,omitempty" json:"project_uid,omitempty" xml:"project_uid,omitempty"` + ProjectUID string `form:"project_uid" json:"project_uid" xml:"project_uid"` // Service URL URL *string `form:"url,omitempty" json:"url,omitempty" xml:"url,omitempty"` // GroupsIO group name GroupName *string `form:"group_name,omitempty" json:"group_name,omitempty" xml:"group_name,omitempty"` // Whether the service is publicly accessible - Public *bool `form:"public,omitempty" json:"public,omitempty" xml:"public,omitempty"` + Public bool `form:"public" json:"public" xml:"public"` // Manager user IDs who can edit/modify this service Writers []string `form:"writers,omitempty" json:"writers,omitempty" xml:"writers,omitempty"` // Auditor user IDs who can audit this service @@ -110,23 +110,23 @@ type CreateGrpsioMailingListRequestBody struct { // "update-grpsio-mailing-list" endpoint HTTP request body. type UpdateGrpsioMailingListRequestBody struct { // Mailing list group name - GroupName *string `form:"group_name,omitempty" json:"group_name,omitempty" xml:"group_name,omitempty"` + GroupName string `form:"group_name" json:"group_name" xml:"group_name"` // Whether the mailing list is publicly accessible - Public *bool `form:"public,omitempty" json:"public,omitempty" xml:"public,omitempty"` + Public bool `form:"public" json:"public" xml:"public"` // Mailing list type - Type *string `form:"type,omitempty" json:"type,omitempty" xml:"type,omitempty"` + Type string `form:"type" json:"type" xml:"type"` // Committee UUID for committee-based mailing lists CommitteeUID *string `form:"committee_uid,omitempty" json:"committee_uid,omitempty" xml:"committee_uid,omitempty"` // Committee member filters CommitteeFilters []string `form:"committee_filters,omitempty" json:"committee_filters,omitempty" xml:"committee_filters,omitempty"` // Mailing list description (11-500 characters) - Description *string `form:"description,omitempty" json:"description,omitempty" xml:"description,omitempty"` + Description string `form:"description" json:"description" xml:"description"` // Mailing list title - Title *string `form:"title,omitempty" json:"title,omitempty" xml:"title,omitempty"` + Title string `form:"title" json:"title" xml:"title"` // Subject tag prefix SubjectTag *string `form:"subject_tag,omitempty" json:"subject_tag,omitempty" xml:"subject_tag,omitempty"` // Service UUID - ServiceUID *string `form:"service_uid,omitempty" json:"service_uid,omitempty" xml:"service_uid,omitempty"` + ServiceUID string `form:"service_uid" json:"service_uid" xml:"service_uid"` // Manager user IDs who can edit/modify this service Writers []string `form:"writers,omitempty" json:"writers,omitempty" xml:"writers,omitempty"` // Auditor user IDs who can audit this service @@ -174,9 +174,9 @@ type UpdateGrpsioMailingListMemberRequestBody struct { // Member job title JobTitle *string `form:"job_title,omitempty" json:"job_title,omitempty" xml:"job_title,omitempty"` // Email delivery mode - DeliveryMode *string `form:"delivery_mode,omitempty" json:"delivery_mode,omitempty" xml:"delivery_mode,omitempty"` + DeliveryMode string `form:"delivery_mode" json:"delivery_mode" xml:"delivery_mode"` // Moderation status - ModStatus *string `form:"mod_status,omitempty" json:"mod_status,omitempty" xml:"mod_status,omitempty"` + ModStatus string `form:"mod_status" json:"mod_status" xml:"mod_status"` } // CreateGrpsioServiceResponseBody is the type of the "mailing-list" service @@ -1120,6 +1120,12 @@ func NewUpdateGrpsioServiceRequestBody(p *mailinglist.UpdateGrpsioServicePayload body.GlobalOwners[i] = val } } + { + var zero bool + if body.Public == zero { + body.Public = false + } + } if p.Writers != nil { body.Writers = make([]string, len(p.Writers)) for i, val := range p.Writers { @@ -1256,6 +1262,18 @@ func NewUpdateGrpsioMailingListMemberRequestBody(p *mailinglist.UpdateGrpsioMail DeliveryMode: p.DeliveryMode, ModStatus: p.ModStatus, } + { + var zero string + if body.DeliveryMode == zero { + body.DeliveryMode = "normal" + } + } + { + var zero string + if body.ModStatus == zero { + body.ModStatus = "none" + } + } return body } diff --git a/gen/http/mailing_list/server/server.go b/gen/http/mailing_list/server/server.go index 93fe478..a45a448 100644 --- a/gen/http/mailing_list/server/server.go +++ b/gen/http/mailing_list/server/server.go @@ -74,15 +74,15 @@ func New( {"Readyz", "GET", "/readyz"}, {"CreateGrpsioService", "POST", "/groupsio/services"}, {"GetGrpsioService", "GET", "/groupsio/services/{uid}"}, - {"UpdateGrpsioService", "PATCH", "/groupsio/services/{uid}"}, + {"UpdateGrpsioService", "PUT", "/groupsio/services/{uid}"}, {"DeleteGrpsioService", "DELETE", "/groupsio/services/{uid}"}, {"CreateGrpsioMailingList", "POST", "/groupsio/mailing-lists"}, {"GetGrpsioMailingList", "GET", "/groupsio/mailing-lists/{uid}"}, - {"UpdateGrpsioMailingList", "PATCH", "/groupsio/mailing-lists/{uid}"}, + {"UpdateGrpsioMailingList", "PUT", "/groupsio/mailing-lists/{uid}"}, {"DeleteGrpsioMailingList", "DELETE", "/groupsio/mailing-lists/{uid}"}, {"CreateGrpsioMailingListMember", "POST", "/groupsio/mailing-lists/{uid}/members"}, {"GetGrpsioMailingListMember", "GET", "/groupsio/mailing-lists/{uid}/members/{member_uid}"}, - {"UpdateGrpsioMailingListMember", "PATCH", "/groupsio/mailing-lists/{uid}/members/{member_uid}"}, + {"UpdateGrpsioMailingListMember", "PUT", "/groupsio/mailing-lists/{uid}/members/{member_uid}"}, {"DeleteGrpsioMailingListMember", "DELETE", "/groupsio/mailing-lists/{uid}/members/{member_uid}"}, {"Serve gen/http/openapi3.json", "GET", "/openapi.json"}, }, @@ -360,7 +360,7 @@ func MountUpdateGrpsioServiceHandler(mux goahttp.Muxer, h http.Handler) { h.ServeHTTP(w, r) } } - mux.Handle("PATCH", "/groupsio/services/{uid}", f) + mux.Handle("PUT", "/groupsio/services/{uid}", f) } // NewUpdateGrpsioServiceHandler creates a HTTP handler which loads the HTTP @@ -576,7 +576,7 @@ func MountUpdateGrpsioMailingListHandler(mux goahttp.Muxer, h http.Handler) { h.ServeHTTP(w, r) } } - mux.Handle("PATCH", "/groupsio/mailing-lists/{uid}", f) + mux.Handle("PUT", "/groupsio/mailing-lists/{uid}", f) } // NewUpdateGrpsioMailingListHandler creates a HTTP handler which loads the @@ -792,7 +792,7 @@ func MountUpdateGrpsioMailingListMemberHandler(mux goahttp.Muxer, h http.Handler h.ServeHTTP(w, r) } } - mux.Handle("PATCH", "/groupsio/mailing-lists/{uid}/members/{member_uid}", f) + mux.Handle("PUT", "/groupsio/mailing-lists/{uid}/members/{member_uid}", f) } // NewUpdateGrpsioMailingListMemberHandler creates a HTTP handler which loads diff --git a/gen/http/mailing_list/server/types.go b/gen/http/mailing_list/server/types.go index d1e99bb..1fd95f5 100644 --- a/gen/http/mailing_list/server/types.go +++ b/gen/http/mailing_list/server/types.go @@ -2139,16 +2139,18 @@ func NewGetGrpsioServicePayload(uid string, version *string, bearerToken *string // update-grpsio-service endpoint payload. func NewUpdateGrpsioServicePayload(body *UpdateGrpsioServiceRequestBody, uid string, version string, bearerToken *string, ifMatch *string) *mailinglist.UpdateGrpsioServicePayload { v := &mailinglist.UpdateGrpsioServicePayload{ - Type: body.Type, + Type: *body.Type, Domain: body.Domain, GroupID: body.GroupID, Status: body.Status, Prefix: body.Prefix, ProjectSlug: body.ProjectSlug, - ProjectUID: body.ProjectUID, + ProjectUID: *body.ProjectUID, URL: body.URL, GroupName: body.GroupName, - Public: body.Public, + } + if body.Public != nil { + v.Public = *body.Public } if body.GlobalOwners != nil { v.GlobalOwners = make([]string, len(body.GlobalOwners)) @@ -2156,6 +2158,9 @@ func NewUpdateGrpsioServicePayload(body *UpdateGrpsioServiceRequestBody, uid str v.GlobalOwners[i] = val } } + if body.Public == nil { + v.Public = false + } if body.Writers != nil { v.Writers = make([]string, len(body.Writers)) for i, val := range body.Writers { @@ -2240,14 +2245,14 @@ func NewGetGrpsioMailingListPayload(uid string, version string, bearerToken stri // update-grpsio-mailing-list endpoint payload. func NewUpdateGrpsioMailingListPayload(body *UpdateGrpsioMailingListRequestBody, uid string, version string, bearerToken *string, ifMatch *string) *mailinglist.UpdateGrpsioMailingListPayload { v := &mailinglist.UpdateGrpsioMailingListPayload{ - GroupName: body.GroupName, - Public: body.Public, - Type: body.Type, + GroupName: *body.GroupName, + Public: *body.Public, + Type: *body.Type, CommitteeUID: body.CommitteeUID, - Description: body.Description, - Title: body.Title, + Description: *body.Description, + Title: *body.Title, SubjectTag: body.SubjectTag, - ServiceUID: body.ServiceUID, + ServiceUID: *body.ServiceUID, } if body.CommitteeFilters != nil { v.CommitteeFilters = make([]string, len(body.CommitteeFilters)) @@ -2346,8 +2351,18 @@ func NewUpdateGrpsioMailingListMemberPayload(body *UpdateGrpsioMailingListMember LastName: body.LastName, Organization: body.Organization, JobTitle: body.JobTitle, - DeliveryMode: body.DeliveryMode, - ModStatus: body.ModStatus, + } + if body.DeliveryMode != nil { + v.DeliveryMode = *body.DeliveryMode + } + if body.ModStatus != nil { + v.ModStatus = *body.ModStatus + } + if body.DeliveryMode == nil { + v.DeliveryMode = "normal" + } + if body.ModStatus == nil { + v.ModStatus = "none" } v.UID = uid v.MemberUID = memberUID @@ -2406,6 +2421,12 @@ func ValidateCreateGrpsioServiceRequestBody(body *CreateGrpsioServiceRequestBody // ValidateUpdateGrpsioServiceRequestBody runs the validations defined on // Update-Grpsio-ServiceRequestBody func ValidateUpdateGrpsioServiceRequestBody(body *UpdateGrpsioServiceRequestBody) (err error) { + if body.Type == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("type", "body")) + } + if body.ProjectUID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("project_uid", "body")) + } if body.Type != nil { if !(*body.Type == "primary" || *body.Type == "formation" || *body.Type == "shared") { err = goa.MergeErrors(err, goa.InvalidEnumValueError("body.type", *body.Type, []any{"primary", "formation", "shared"})) @@ -2510,6 +2531,24 @@ func ValidateCreateGrpsioMailingListRequestBody(body *CreateGrpsioMailingListReq // ValidateUpdateGrpsioMailingListRequestBody runs the validations defined on // Update-Grpsio-Mailing-ListRequestBody func ValidateUpdateGrpsioMailingListRequestBody(body *UpdateGrpsioMailingListRequestBody) (err error) { + if body.GroupName == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("group_name", "body")) + } + if body.Public == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("public", "body")) + } + if body.Type == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("type", "body")) + } + if body.Description == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("description", "body")) + } + if body.Title == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("title", "body")) + } + if body.ServiceUID == nil { + err = goa.MergeErrors(err, goa.MissingFieldError("service_uid", "body")) + } if body.GroupName != nil { err = goa.MergeErrors(err, goa.ValidatePattern("body.group_name", *body.GroupName, "^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$")) } diff --git a/gen/http/openapi.json b/gen/http/openapi.json index ce058e0..5c3531c 100644 --- a/gen/http/openapi.json +++ b/gen/http/openapi.json @@ -1 +1 @@ -{"swagger":"2.0","info":{"title":"Mailing List Service","description":"Service for managing mailing lists in LFX","version":"0.0.1"},"host":"localhost:80","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/groupsio/mailing-lists":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-mailing-list mailing-list","description":"Create GroupsIO mailing list/subgroup with comprehensive validation","operationId":"mailing-list#create-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"Create-Grpsio-Mailing-ListRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListCreateGrpsioMailingListRequestBody","required":["group_name","public","type","description","title","service_uid"]}}],"responses":{"201":{"description":"Created response.","schema":{"$ref":"#/definitions/GrpsIoMailingListFull"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}":{"get":{"tags":["mailing-list"],"summary":"get-grpsio-mailing-list mailing-list","description":"Get GroupsIO mailing list details by UID","operationId":"mailing-list#get-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":true,"type":"string"}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/MailingListGetGrpsioMailingListResponseBody"},"headers":{"ETag":{"description":"ETag header value","type":"string"}}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-mailing-list mailing-list","description":"Delete GroupsIO mailing list","operationId":"mailing-list#delete-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","required":false,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":false,"type":"string"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"patch":{"tags":["mailing-list"],"summary":"update-grpsio-mailing-list mailing-list","description":"Update GroupsIO mailing list","operationId":"mailing-list#update-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":false,"type":"string"},{"name":"Update-Grpsio-Mailing-ListRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListUpdateGrpsioMailingListRequestBody"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/GrpsIoMailingListWithReadonlyAttributes"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}/members":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-mailing-list-member mailing-list","description":"Create a new member for a GroupsIO mailing list","operationId":"mailing-list#create-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID","required":true,"type":"string"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"Create-Grpsio-Mailing-List-MemberRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListCreateGrpsioMailingListMemberRequestBody","required":["email"]}}],"responses":{"201":{"description":"Created response.","schema":{"$ref":"#/definitions/GrpsIoMemberFull","required":["uid","mailing_list_uid","first_name","last_name","email","member_type","delivery_mode","mod_status","status","created_at","updated_at"]}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}/members/{member_uid}":{"get":{"tags":["mailing-list"],"summary":"get-grpsio-mailing-list-member mailing-list","description":"Get a member of a GroupsIO mailing list by UID","operationId":"mailing-list#get-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":true,"type":"string"}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/MailingListGetGrpsioMailingListMemberResponseBody"},"headers":{"ETag":{"description":"ETag header value","type":"string"}}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-mailing-list-member mailing-list","description":"Delete a member from a GroupsIO mailing list","operationId":"mailing-list#delete-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":true,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":true,"type":"string"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"patch":{"tags":["mailing-list"],"summary":"update-grpsio-mailing-list-member mailing-list","description":"Update a member of a GroupsIO mailing list","operationId":"mailing-list#update-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":true,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":true,"type":"string"},{"name":"Update-Grpsio-Mailing-List-MemberRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListUpdateGrpsioMailingListMemberRequestBody"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/GrpsIoMemberWithReadonlyAttributes"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/services":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-service mailing-list","description":"Create GroupsIO service with type-specific validation rules","operationId":"mailing-list#create-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"Create-Grpsio-ServiceRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListCreateGrpsioServiceRequestBody","required":["type","project_uid"]}}],"responses":{"201":{"description":"Created response.","schema":{"$ref":"#/definitions/GrpsIoServiceFull","required":["type","project_uid"]}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/services/{uid}":{"get":{"tags":["mailing-list"],"summary":"get-grpsio-service mailing-list","description":"Get groupsIO service details by ID","operationId":"mailing-list#get-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","required":false,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/MailingListGetGrpsioServiceResponseBody","required":["type","project_uid"]},"headers":{"ETag":{"description":"ETag header value","type":"string"}}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-service mailing-list","description":"Delete GroupsIO service","operationId":"mailing-list#delete-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","required":false,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":false,"type":"string"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"patch":{"tags":["mailing-list"],"summary":"update-grpsio-service mailing-list","description":"Update GroupsIO service","operationId":"mailing-list#update-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":false,"type":"string"},{"name":"Update-Grpsio-ServiceRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListUpdateGrpsioServiceRequestBody"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/GrpsIoServiceWithReadonlyAttributes","required":["type","project_uid"]}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/livez":{"get":{"tags":["mailing-list"],"summary":"livez mailing-list","description":"Check if the service is alive.","operationId":"mailing-list#livez","produces":["text/plain"],"responses":{"200":{"description":"OK response.","schema":{"type":"string","format":"byte"}}},"schemes":["http"]}},"/openapi.json":{"get":{"tags":["mailing-list"],"summary":"Download gen/http/openapi3.json","operationId":"mailing-list#/openapi.json","responses":{"200":{"description":"File downloaded","schema":{"type":"file"}}},"schemes":["http"]}},"/readyz":{"get":{"tags":["mailing-list"],"summary":"readyz mailing-list","description":"Check if the service is able to take inbound requests.","operationId":"mailing-list#readyz","produces":["text/plain"],"responses":{"200":{"description":"OK response.","schema":{"type":"string","format":"byte"}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"]}}},"definitions":{"BadRequestError":{"title":"BadRequestError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The request was invalid."}},"description":"Bad request - Invalid type, missing required fields, or validation failures","example":{"message":"The request was invalid."},"required":["message"]},"ConflictError":{"title":"ConflictError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The resource already exists."}},"description":"Conflict","example":{"message":"The resource already exists."},"required":["message"]},"GrpsIoMailingListFull":{"title":"GrpsIoMailingListFull","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Eos sed nihil provident illum omnis."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier (read-only)","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID (inherited from parent service)","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"uid":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"writers":{"type":"array","items":{"type":"string","example":"Rem aperiam."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoMailingListWithReadonlyAttributes":{"title":"GrpsIoMailingListWithReadonlyAttributes","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Sed earum rerum corporis quae."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Emeritus","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier (read-only)","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID (inherited from parent service)","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"uid":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"writers":{"type":"array","items":{"type":"string","example":"Temporibus assumenda error."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO mailing lists with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoMemberFull":{"title":"GrpsIoMemberFull","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Dolorum quia alias amet aspernatur ut nihil."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"none","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"groupsio_group_id":{"type":"integer","description":"Groups.io group ID","example":67890,"format":"int64"},"groupsio_member_id":{"type":"integer","description":"Groups.io member ID","example":12345,"format":"int64"},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"mailing_list_uid":{"type":"string","description":"Mailing list UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"direct","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"moderator","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"status":{"type":"string","description":"Member status","example":"pending"},"uid":{"type":"string","description":"Member UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255},"writers":{"type":"array","items":{"type":"string","example":"Possimus et et."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"digest","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"direct","mod_status":"owner","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]},"required":["uid","mailing_list_uid","first_name","last_name","email","member_type","delivery_mode","mod_status","status","created_at","updated_at"]},"GrpsIoMemberWithReadonlyAttributes":{"title":"GrpsIoMemberWithReadonlyAttributes","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Sint quos laudantium eligendi."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"none","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"groupsio_group_id":{"type":"integer","description":"Groups.io group ID","example":67890,"format":"int64"},"groupsio_member_id":{"type":"integer","description":"Groups.io member ID","example":12345,"format":"int64"},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"mailing_list_uid":{"type":"string","description":"Mailing list UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"committee","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"none","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"status":{"type":"string","description":"Member status","example":"pending"},"uid":{"type":"string","description":"Member UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255},"writers":{"type":"array","items":{"type":"string","example":"Nihil consectetur dolorem quas eum qui ex."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO mailing list members with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"digest","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"committee","mod_status":"owner","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoServiceFull":{"title":"GrpsIoServiceFull","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Sit et."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"virginia_barrows@schusterwehner.com","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"last_audited_by":{"type":"string","description":"The user ID who last audited the service","example":"user_id_12345"},"last_audited_time":{"type":"string","description":"The timestamp when the service was last audited","example":"2023-05-10T09:15:00Z","format":"date-time"},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"uid":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Ut id laboriosam aut aut eos sequi."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"GrpsIoServiceWithReadonlyAttributes":{"title":"GrpsIoServiceWithReadonlyAttributes","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Unde quis amet."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"dannie_weimann@greenfelder.biz","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"last_audited_by":{"type":"string","description":"The user ID who last audited the service","example":"user_id_12345"},"last_audited_time":{"type":"string","description":"The timestamp when the service was last audited","example":"2023-05-10T09:15:00Z","format":"date-time"},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"uid":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Non cupiditate et quasi modi quas eum."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO services with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"InternalServerError":{"title":"InternalServerError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"An internal server error occurred."}},"description":"Internal server error","example":{"message":"An internal server error occurred."},"required":["message"]},"MailingListCreateGrpsioMailingListMemberRequestBody":{"title":"MailingListCreateGrpsioMailingListMemberRequestBody","type":"object","properties":{"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"normal","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"direct","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"owner","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255}},"example":{"delivery_mode":"none","email":"john.doe@example.com","first_name":"John","job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","member_type":"committee","mod_status":"none","organization":"Example Corp","username":"jdoe"},"required":["email"]},"MailingListCreateGrpsioMailingListRequestBody":{"title":"MailingListCreateGrpsioMailingListRequestBody","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Occaecati dignissimos facere doloremque."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Alternate Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"writers":{"type":"array","items":{"type":"string","example":"Numquam deserunt."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]},"required":["group_name","public","type","description","title","service_uid"]},"MailingListCreateGrpsioServiceRequestBody":{"title":"MailingListCreateGrpsioServiceRequestBody","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Dolor cupiditate sit debitis assumenda."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"princess@hayes.net","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Aut deserunt."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"MailingListGetGrpsioMailingListMemberResponseBody":{"title":"MailingListGetGrpsioMailingListMemberResponseBody","$ref":"#/definitions/GrpsIoMemberWithReadonlyAttributes"},"MailingListGetGrpsioMailingListResponseBody":{"title":"MailingListGetGrpsioMailingListResponseBody","$ref":"#/definitions/GrpsIoMailingListWithReadonlyAttributes"},"MailingListGetGrpsioServiceResponseBody":{"title":"MailingListGetGrpsioServiceResponseBody","$ref":"#/definitions/GrpsIoServiceWithReadonlyAttributes"},"MailingListUpdateGrpsioMailingListMemberRequestBody":{"title":"MailingListUpdateGrpsioMailingListMemberRequestBody","type":"object","properties":{"delivery_mode":{"type":"string","description":"Email delivery mode","example":"digest","enum":["normal","digest","none"]},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"mod_status":{"type":"string","description":"Moderation status","example":"moderator","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255}},"example":{"delivery_mode":"none","first_name":"John","job_title":"Software Engineer","last_name":"Doe","mod_status":"moderator","organization":"Example Corp","username":"jdoe"}},"MailingListUpdateGrpsioMailingListRequestBody":{"title":"MailingListUpdateGrpsioMailingListRequestBody","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Voluptas asperiores suscipit."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Alternate Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"writers":{"type":"array","items":{"type":"string","example":"Non minus aspernatur eius vero."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]}},"MailingListUpdateGrpsioServiceRequestBody":{"title":"MailingListUpdateGrpsioServiceRequestBody","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Occaecati nam."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"scot.mayer@abshire.net","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Tempora voluptas nam eaque et quas voluptatem."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}},"NotFoundError":{"title":"NotFoundError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The resource was not found."}},"description":"Resource not found","example":{"message":"The resource was not found."},"required":["message"]},"ServiceUnavailableError":{"title":"ServiceUnavailableError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The service is unavailable."}},"description":"Service unavailable","example":{"message":"The service is unavailable."},"required":["message"]}},"securityDefinitions":{"jwt_header_Authorization":{"type":"apiKey","description":"Heimdall authorization","name":"Authorization","in":"header"}}} \ No newline at end of file +{"swagger":"2.0","info":{"title":"Mailing List Service","description":"Service for managing mailing lists in LFX","version":"0.0.1"},"host":"localhost:80","consumes":["application/json","application/xml","application/gob"],"produces":["application/json","application/xml","application/gob"],"paths":{"/groupsio/mailing-lists":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-mailing-list mailing-list","description":"Create GroupsIO mailing list/subgroup with comprehensive validation","operationId":"mailing-list#create-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"Create-Grpsio-Mailing-ListRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListCreateGrpsioMailingListRequestBody","required":["group_name","public","type","description","title","service_uid"]}}],"responses":{"201":{"description":"Created response.","schema":{"$ref":"#/definitions/GrpsIoMailingListFull"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}":{"get":{"tags":["mailing-list"],"summary":"get-grpsio-mailing-list mailing-list","description":"Get GroupsIO mailing list details by UID","operationId":"mailing-list#get-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":true,"type":"string"}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/MailingListGetGrpsioMailingListResponseBody"},"headers":{"ETag":{"description":"ETag header value","type":"string"}}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"put":{"tags":["mailing-list"],"summary":"update-grpsio-mailing-list mailing-list","description":"Update GroupsIO mailing list","operationId":"mailing-list#update-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":false,"type":"string"},{"name":"Update-Grpsio-Mailing-ListRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListUpdateGrpsioMailingListRequestBody","required":["group_name","public","type","description","title","service_uid"]}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/GrpsIoMailingListWithReadonlyAttributes"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-mailing-list mailing-list","description":"Delete GroupsIO mailing list","operationId":"mailing-list#delete-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","required":false,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":false,"type":"string"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}/members":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-mailing-list-member mailing-list","description":"Create a new member for a GroupsIO mailing list","operationId":"mailing-list#create-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID","required":true,"type":"string"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"Create-Grpsio-Mailing-List-MemberRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListCreateGrpsioMailingListMemberRequestBody","required":["email"]}}],"responses":{"201":{"description":"Created response.","schema":{"$ref":"#/definitions/GrpsIoMemberFull","required":["uid","mailing_list_uid","first_name","last_name","email","member_type","delivery_mode","mod_status","status","created_at","updated_at"]}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}/members/{member_uid}":{"get":{"tags":["mailing-list"],"summary":"get-grpsio-mailing-list-member mailing-list","description":"Get a member of a GroupsIO mailing list by UID","operationId":"mailing-list#get-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":true,"type":"string"}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/MailingListGetGrpsioMailingListMemberResponseBody"},"headers":{"ETag":{"description":"ETag header value","type":"string"}}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"put":{"tags":["mailing-list"],"summary":"update-grpsio-mailing-list-member mailing-list","description":"Update a member of a GroupsIO mailing list","operationId":"mailing-list#update-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":true,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":true,"type":"string"},{"name":"Update-Grpsio-Mailing-List-MemberRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListUpdateGrpsioMailingListMemberRequestBody"}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/GrpsIoMemberWithReadonlyAttributes"}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-mailing-list-member mailing-list","description":"Delete a member from a GroupsIO mailing list","operationId":"mailing-list#delete-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"type":"string","format":"uuid"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":true,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":true,"type":"string"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/services":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-service mailing-list","description":"Create GroupsIO service with type-specific validation rules","operationId":"mailing-list#create-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"Create-Grpsio-ServiceRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListCreateGrpsioServiceRequestBody","required":["type","project_uid"]}}],"responses":{"201":{"description":"Created response.","schema":{"$ref":"#/definitions/GrpsIoServiceFull","required":["type","project_uid"]}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/services/{uid}":{"get":{"tags":["mailing-list"],"summary":"get-grpsio-service mailing-list","description":"Get groupsIO service details by ID","operationId":"mailing-list#get-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","required":false,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/MailingListGetGrpsioServiceResponseBody","required":["type","project_uid"]},"headers":{"ETag":{"description":"ETag header value","type":"string"}}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"put":{"tags":["mailing-list"],"summary":"update-grpsio-service mailing-list","description":"Update GroupsIO service","operationId":"mailing-list#update-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","required":true,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":false,"type":"string"},{"name":"Update-Grpsio-ServiceRequestBody","in":"body","required":true,"schema":{"$ref":"#/definitions/MailingListUpdateGrpsioServiceRequestBody","required":["type","project_uid"]}}],"responses":{"200":{"description":"OK response.","schema":{"$ref":"#/definitions/GrpsIoServiceWithReadonlyAttributes","required":["type","project_uid"]}},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]},"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-service mailing-list","description":"Delete GroupsIO service","operationId":"mailing-list#delete-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","required":false,"type":"string","enum":["1"]},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"type":"string","format":"uuid"},{"name":"Authorization","in":"header","description":"JWT token issued by Heimdall","required":false,"type":"string"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","required":false,"type":"string"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"Bad Request response.","schema":{"$ref":"#/definitions/BadRequestError","required":["message"]}},"404":{"description":"Not Found response.","schema":{"$ref":"#/definitions/NotFoundError","required":["message"]}},"409":{"description":"Conflict response.","schema":{"$ref":"#/definitions/ConflictError","required":["message"]}},"500":{"description":"Internal Server Error response.","schema":{"$ref":"#/definitions/InternalServerError","required":["message"]}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"],"security":[{"jwt_header_Authorization":[]}]}},"/livez":{"get":{"tags":["mailing-list"],"summary":"livez mailing-list","description":"Check if the service is alive.","operationId":"mailing-list#livez","produces":["text/plain"],"responses":{"200":{"description":"OK response.","schema":{"type":"string","format":"byte"}}},"schemes":["http"]}},"/openapi.json":{"get":{"tags":["mailing-list"],"summary":"Download gen/http/openapi3.json","operationId":"mailing-list#/openapi.json","responses":{"200":{"description":"File downloaded","schema":{"type":"file"}}},"schemes":["http"]}},"/readyz":{"get":{"tags":["mailing-list"],"summary":"readyz mailing-list","description":"Check if the service is able to take inbound requests.","operationId":"mailing-list#readyz","produces":["text/plain"],"responses":{"200":{"description":"OK response.","schema":{"type":"string","format":"byte"}},"503":{"description":"Service Unavailable response.","schema":{"$ref":"#/definitions/ServiceUnavailableError","required":["message"]}}},"schemes":["http"]}}},"definitions":{"BadRequestError":{"title":"BadRequestError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The request was invalid."}},"description":"Bad request - Invalid type, missing required fields, or validation failures","example":{"message":"The request was invalid."},"required":["message"]},"ConflictError":{"title":"ConflictError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The resource already exists."}},"description":"Conflict","example":{"message":"The resource already exists."},"required":["message"]},"GrpsIoMailingListFull":{"title":"GrpsIoMailingListFull","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Eos sed nihil provident illum omnis."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier (read-only)","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID (inherited from parent service)","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"uid":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"writers":{"type":"array","items":{"type":"string","example":"Rem aperiam."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoMailingListWithReadonlyAttributes":{"title":"GrpsIoMailingListWithReadonlyAttributes","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Sed earum rerum corporis quae."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Emeritus","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier (read-only)","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID (inherited from parent service)","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"uid":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"writers":{"type":"array","items":{"type":"string","example":"Temporibus assumenda error."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO mailing lists with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoMemberFull":{"title":"GrpsIoMemberFull","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Dolorum quia alias amet aspernatur ut nihil."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"none","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"groupsio_group_id":{"type":"integer","description":"Groups.io group ID","example":67890,"format":"int64"},"groupsio_member_id":{"type":"integer","description":"Groups.io member ID","example":12345,"format":"int64"},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"mailing_list_uid":{"type":"string","description":"Mailing list UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"direct","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"moderator","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"status":{"type":"string","description":"Member status","example":"pending"},"uid":{"type":"string","description":"Member UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255},"writers":{"type":"array","items":{"type":"string","example":"Possimus et et."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"digest","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"direct","mod_status":"owner","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]},"required":["uid","mailing_list_uid","first_name","last_name","email","member_type","delivery_mode","mod_status","status","created_at","updated_at"]},"GrpsIoMemberWithReadonlyAttributes":{"title":"GrpsIoMemberWithReadonlyAttributes","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Sint quos laudantium eligendi."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"none","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"groupsio_group_id":{"type":"integer","description":"Groups.io group ID","example":67890,"format":"int64"},"groupsio_member_id":{"type":"integer","description":"Groups.io member ID","example":12345,"format":"int64"},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"mailing_list_uid":{"type":"string","description":"Mailing list UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"committee","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"none","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"status":{"type":"string","description":"Member status","example":"pending"},"uid":{"type":"string","description":"Member UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255},"writers":{"type":"array","items":{"type":"string","example":"Nihil consectetur dolorem quas eum qui ex."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO mailing list members with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"digest","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"committee","mod_status":"owner","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoServiceFull":{"title":"GrpsIoServiceFull","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Sit et."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"virginia_barrows@schusterwehner.com","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"last_audited_by":{"type":"string","description":"The user ID who last audited the service","example":"user_id_12345"},"last_audited_time":{"type":"string","description":"The timestamp when the service was last audited","example":"2023-05-10T09:15:00Z","format":"date-time"},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"uid":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Ut id laboriosam aut aut eos sequi."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"GrpsIoServiceWithReadonlyAttributes":{"title":"GrpsIoServiceWithReadonlyAttributes","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Unde quis amet."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"dannie_weimann@greenfelder.biz","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"last_audited_by":{"type":"string","description":"The user ID who last audited the service","example":"user_id_12345"},"last_audited_time":{"type":"string","description":"The timestamp when the service was last audited","example":"2023-05-10T09:15:00Z","format":"date-time"},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"uid":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Non cupiditate et quasi modi quas eum."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO services with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"InternalServerError":{"title":"InternalServerError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"An internal server error occurred."}},"description":"Internal server error","example":{"message":"An internal server error occurred."},"required":["message"]},"MailingListCreateGrpsioMailingListMemberRequestBody":{"title":"MailingListCreateGrpsioMailingListMemberRequestBody","type":"object","properties":{"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"normal","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"direct","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"owner","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255}},"example":{"delivery_mode":"none","email":"john.doe@example.com","first_name":"John","job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","member_type":"committee","mod_status":"none","organization":"Example Corp","username":"jdoe"},"required":["email"]},"MailingListCreateGrpsioMailingListRequestBody":{"title":"MailingListCreateGrpsioMailingListRequestBody","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Occaecati dignissimos facere doloremque."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Alternate Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"writers":{"type":"array","items":{"type":"string","example":"Numquam deserunt."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]},"required":["group_name","public","type","description","title","service_uid"]},"MailingListCreateGrpsioServiceRequestBody":{"title":"MailingListCreateGrpsioServiceRequestBody","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Dolor cupiditate sit debitis assumenda."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"princess@hayes.net","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Aut deserunt."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"MailingListGetGrpsioMailingListMemberResponseBody":{"title":"MailingListGetGrpsioMailingListMemberResponseBody","$ref":"#/definitions/GrpsIoMemberWithReadonlyAttributes"},"MailingListGetGrpsioMailingListResponseBody":{"title":"MailingListGetGrpsioMailingListResponseBody","$ref":"#/definitions/GrpsIoMailingListWithReadonlyAttributes"},"MailingListGetGrpsioServiceResponseBody":{"title":"MailingListGetGrpsioServiceResponseBody","$ref":"#/definitions/GrpsIoServiceWithReadonlyAttributes"},"MailingListUpdateGrpsioMailingListMemberRequestBody":{"title":"MailingListUpdateGrpsioMailingListMemberRequestBody","type":"object","properties":{"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"digest","enum":["normal","digest","none"]},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"moderator","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255}},"example":{"delivery_mode":"none","first_name":"John","job_title":"Software Engineer","last_name":"Doe","mod_status":"moderator","organization":"Example Corp","username":"jdoe"}},"MailingListUpdateGrpsioMailingListRequestBody":{"title":"MailingListUpdateGrpsioMailingListRequestBody","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Voluptas asperiores suscipit."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Alternate Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"writers":{"type":"array","items":{"type":"string","example":"Non minus aspernatur eius vero."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]},"required":["group_name","public","type","description","title","service_uid"]},"MailingListUpdateGrpsioServiceRequestBody":{"title":"MailingListUpdateGrpsioServiceRequestBody","type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Occaecati nam."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"scot.mayer@abshire.net","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Tempora voluptas nam eaque et quas voluptatem."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"NotFoundError":{"title":"NotFoundError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The resource was not found."}},"description":"Resource not found","example":{"message":"The resource was not found."},"required":["message"]},"ServiceUnavailableError":{"title":"ServiceUnavailableError","type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The service is unavailable."}},"description":"Service unavailable","example":{"message":"The service is unavailable."},"required":["message"]}},"securityDefinitions":{"jwt_header_Authorization":{"type":"apiKey","description":"Heimdall authorization","name":"Authorization","in":"header"}}} \ No newline at end of file diff --git a/gen/http/openapi.yaml b/gen/http/openapi.yaml index f45ee90..8ca98a7 100644 --- a/gen/http/openapi.yaml +++ b/gen/http/openapi.yaml @@ -147,17 +147,17 @@ paths: - http security: - jwt_header_Authorization: [] - delete: + put: tags: - mailing-list - summary: delete-grpsio-mailing-list mailing-list - description: Delete GroupsIO mailing list - operationId: mailing-list#delete-grpsio-mailing-list + summary: update-grpsio-mailing-list mailing-list + description: Update GroupsIO mailing list + operationId: mailing-list#update-grpsio-mailing-list parameters: - name: v in: query description: Version of the API - required: false + required: true type: string enum: - "1" @@ -177,9 +177,23 @@ paths: description: If-Match header value for conditional requests required: false type: string + - name: Update-Grpsio-Mailing-ListRequestBody + in: body + required: true + schema: + $ref: '#/definitions/MailingListUpdateGrpsioMailingListRequestBody' + required: + - group_name + - public + - type + - description + - title + - service_uid responses: - "204": - description: No Content response. + "200": + description: OK response. + schema: + $ref: '#/definitions/GrpsIoMailingListWithReadonlyAttributes' "400": description: Bad Request response. schema: @@ -214,17 +228,17 @@ paths: - http security: - jwt_header_Authorization: [] - patch: + delete: tags: - mailing-list - summary: update-grpsio-mailing-list mailing-list - description: Update GroupsIO mailing list - operationId: mailing-list#update-grpsio-mailing-list + summary: delete-grpsio-mailing-list mailing-list + description: Delete GroupsIO mailing list + operationId: mailing-list#delete-grpsio-mailing-list parameters: - name: v in: query description: Version of the API - required: true + required: false type: string enum: - "1" @@ -244,16 +258,9 @@ paths: description: If-Match header value for conditional requests required: false type: string - - name: Update-Grpsio-Mailing-ListRequestBody - in: body - required: true - schema: - $ref: '#/definitions/MailingListUpdateGrpsioMailingListRequestBody' responses: - "200": - description: OK response. - schema: - $ref: '#/definitions/GrpsIoMailingListWithReadonlyAttributes' + "204": + description: No Content response. "400": description: Bad Request response. schema: @@ -440,12 +447,12 @@ paths: - http security: - jwt_header_Authorization: [] - delete: + put: tags: - mailing-list - summary: delete-grpsio-mailing-list-member mailing-list - description: Delete a member from a GroupsIO mailing list - operationId: mailing-list#delete-grpsio-mailing-list-member + summary: update-grpsio-mailing-list-member mailing-list + description: Update a member of a GroupsIO mailing list + operationId: mailing-list#update-grpsio-mailing-list-member parameters: - name: v in: query @@ -476,9 +483,16 @@ paths: description: If-Match header value for conditional requests required: true type: string + - name: Update-Grpsio-Mailing-List-MemberRequestBody + in: body + required: true + schema: + $ref: '#/definitions/MailingListUpdateGrpsioMailingListMemberRequestBody' responses: - "204": - description: No Content response. + "200": + description: OK response. + schema: + $ref: '#/definitions/GrpsIoMemberWithReadonlyAttributes' "400": description: Bad Request response. schema: @@ -513,12 +527,12 @@ paths: - http security: - jwt_header_Authorization: [] - patch: + delete: tags: - mailing-list - summary: update-grpsio-mailing-list-member mailing-list - description: Update a member of a GroupsIO mailing list - operationId: mailing-list#update-grpsio-mailing-list-member + summary: delete-grpsio-mailing-list-member mailing-list + description: Delete a member from a GroupsIO mailing list + operationId: mailing-list#delete-grpsio-mailing-list-member parameters: - name: v in: query @@ -549,16 +563,9 @@ paths: description: If-Match header value for conditional requests required: true type: string - - name: Update-Grpsio-Mailing-List-MemberRequestBody - in: body - required: true - schema: - $ref: '#/definitions/MailingListUpdateGrpsioMailingListMemberRequestBody' responses: - "200": - description: OK response. - schema: - $ref: '#/definitions/GrpsIoMemberWithReadonlyAttributes' + "204": + description: No Content response. "400": description: Bad Request response. schema: @@ -729,17 +736,17 @@ paths: - http security: - jwt_header_Authorization: [] - delete: + put: tags: - mailing-list - summary: delete-grpsio-service mailing-list - description: Delete GroupsIO service - operationId: mailing-list#delete-grpsio-service + summary: update-grpsio-service mailing-list + description: Update GroupsIO service + operationId: mailing-list#update-grpsio-service parameters: - name: v in: query description: Version of the API - required: false + required: true type: string enum: - "1" @@ -759,9 +766,22 @@ paths: description: If-Match header value for conditional requests required: false type: string + - name: Update-Grpsio-ServiceRequestBody + in: body + required: true + schema: + $ref: '#/definitions/MailingListUpdateGrpsioServiceRequestBody' + required: + - type + - project_uid responses: - "204": - description: No Content response. + "200": + description: OK response. + schema: + $ref: '#/definitions/GrpsIoServiceWithReadonlyAttributes' + required: + - type + - project_uid "400": description: Bad Request response. schema: @@ -796,17 +816,17 @@ paths: - http security: - jwt_header_Authorization: [] - patch: + delete: tags: - mailing-list - summary: update-grpsio-service mailing-list - description: Update GroupsIO service - operationId: mailing-list#update-grpsio-service + summary: delete-grpsio-service mailing-list + description: Delete GroupsIO service + operationId: mailing-list#delete-grpsio-service parameters: - name: v in: query description: Version of the API - required: true + required: false type: string enum: - "1" @@ -826,19 +846,9 @@ paths: description: If-Match header value for conditional requests required: false type: string - - name: Update-Grpsio-ServiceRequestBody - in: body - required: true - schema: - $ref: '#/definitions/MailingListUpdateGrpsioServiceRequestBody' responses: - "200": - description: OK response. - schema: - $ref: '#/definitions/GrpsIoServiceWithReadonlyAttributes' - required: - - type - - project_uid + "204": + description: No Content response. "400": description: Bad Request response. schema: @@ -2173,6 +2183,7 @@ definitions: delivery_mode: type: string description: Email delivery mode + default: normal example: digest enum: - normal @@ -2198,6 +2209,7 @@ definitions: mod_status: type: string description: Moderation status + default: none example: moderator enum: - none @@ -2270,6 +2282,7 @@ definitions: public: type: boolean description: Whether the mailing list is publicly accessible + default: false example: false service_uid: type: string @@ -2322,6 +2335,13 @@ definitions: writers: - manager_user_id1 - manager_user_id2 + required: + - group_name + - public + - type + - description + - title + - service_uid MailingListUpdateGrpsioServiceRequestBody: title: MailingListUpdateGrpsioServiceRequestBody type: object @@ -2375,6 +2395,7 @@ definitions: public: type: boolean description: Whether the service is publicly accessible + default: false example: true status: type: string @@ -2421,6 +2442,9 @@ definitions: writers: - manager_user_id1 - manager_user_id2 + required: + - type + - project_uid NotFoundError: title: NotFoundError type: object diff --git a/gen/http/openapi3.json b/gen/http/openapi3.json index 27867e5..5c8b799 100644 --- a/gen/http/openapi3.json +++ b/gen/http/openapi3.json @@ -1 +1 @@ -{"openapi":"3.0.3","info":{"title":"Mailing List Service","description":"Service for managing mailing lists in LFX","version":"0.0.1"},"servers":[{"url":"http://localhost:80","description":"Default server for mailing-list"}],"paths":{"/groupsio/mailing-lists":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-mailing-list mailing-list","description":"Create GroupsIO mailing list/subgroup with comprehensive validation","operationId":"mailing-list#create-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGrpsioMailingListRequestBody"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]}}}},"responses":{"201":{"description":"Created response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMailingListFull"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request - Invalid data, missing required fields, or validation failures","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Parent service not found or committee not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Mailing list with same name already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}":{"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-mailing-list mailing-list","description":"Delete GroupsIO mailing list","operationId":"mailing-list#delete-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Mailing list not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict - ETag mismatch or deletion not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"get":{"tags":["mailing-list"],"summary":"get-grpsio-mailing-list mailing-list","description":"Get GroupsIO mailing list details by UID","operationId":"mailing-list#get-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"}],"responses":{"200":{"description":"OK response.","headers":{"ETag":{"description":"ETag header value","schema":{"type":"string","description":"ETag header value","example":"123"},"example":"123"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMailingListWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Mailing list not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"patch":{"tags":["mailing-list"],"summary":"update-grpsio-mailing-list mailing-list","description":"Update GroupsIO mailing list","operationId":"mailing-list#update-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGrpsioMailingListRequestBody"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]}}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMailingListWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Mailing list not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict - ETag mismatch or validation failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}/members":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-mailing-list-member mailing-list","description":"Create a new member for a GroupsIO mailing list","operationId":"mailing-list#create-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID","required":true,"schema":{"type":"string","description":"Mailing list UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"},"example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGrpsioMailingListMemberRequestBody"},"example":{"delivery_mode":"normal","email":"john.doe@example.com","first_name":"John","job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","member_type":"committee","mod_status":"owner","organization":"Example Corp","username":"jdoe"}}}},"responses":{"201":{"description":"Created response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMemberFull"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"normal","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"direct","mod_status":"moderator","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Mailing list not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Member already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}/members/{member_uid}":{"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-mailing-list-member mailing-list","description":"Delete a member from a GroupsIO mailing list","operationId":"mailing-list#delete-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"schema":{"type":"string","description":"Member UID -- unique identifier for the member","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"BadRequest: Bad request - Cannot remove sole owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict - ETag mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"get":{"tags":["mailing-list"],"summary":"get-grpsio-mailing-list-member mailing-list","description":"Get a member of a GroupsIO mailing list by UID","operationId":"mailing-list#get-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"schema":{"type":"string","description":"Member UID -- unique identifier for the member","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"}],"responses":{"200":{"description":"OK response.","headers":{"ETag":{"description":"ETag header value","schema":{"type":"string","description":"ETag header value","example":"123"},"example":"123"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMemberWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"none","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"committee","mod_status":"none","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"patch":{"tags":["mailing-list"],"summary":"update-grpsio-mailing-list-member mailing-list","description":"Update a member of a GroupsIO mailing list","operationId":"mailing-list#update-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"schema":{"type":"string","description":"Member UID -- unique identifier for the member","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGrpsioMailingListMemberRequestBody"},"example":{"delivery_mode":"normal","first_name":"John","job_title":"Software Engineer","last_name":"Doe","mod_status":"owner","organization":"Example Corp","username":"jdoe"}}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMemberWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"digest","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"direct","mod_status":"owner","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request - Invalid data or immutable field modification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict - ETag mismatch or validation failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/services":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-service mailing-list","description":"Create GroupsIO service with type-specific validation rules","operationId":"mailing-list#create-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGrpsioServiceRequestBody"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"responses":{"201":{"description":"Created response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoServiceFull"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request - Invalid type, missing required fields, or validation failures","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/services/{uid}":{"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-service mailing-list","description":"Delete GroupsIO service","operationId":"mailing-list#delete-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"schema":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"get":{"tags":["mailing-list"],"summary":"get-grpsio-service mailing-list","description":"Get groupsIO service details by ID","operationId":"mailing-list#get-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"schema":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"}],"responses":{"200":{"description":"OK response.","headers":{"ETag":{"description":"ETag header value","schema":{"type":"string","description":"ETag header value","example":"123"},"example":"123"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoServiceWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"patch":{"tags":["mailing-list"],"summary":"update-grpsio-service mailing-list","description":"Update GroupsIO service","operationId":"mailing-list#update-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"schema":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGrpsioServiceRequestBody"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoServiceWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/livez":{"get":{"tags":["mailing-list"],"summary":"livez mailing-list","description":"Check if the service is alive.","operationId":"mailing-list#livez","responses":{"200":{"description":"OK response.","content":{"text/plain":{"schema":{"type":"string","example":"OK","format":"binary"},"example":"OK"}}}}}},"/openapi.json":{"get":{"tags":["mailing-list"],"summary":"Download gen/http/openapi3.json","operationId":"mailing-list#/openapi.json","responses":{"200":{"description":"File downloaded"}}}},"/readyz":{"get":{"tags":["mailing-list"],"summary":"readyz mailing-list","description":"Check if the service is able to take inbound requests.","operationId":"mailing-list#readyz","responses":{"200":{"description":"OK response.","content":{"text/plain":{"schema":{"type":"string","example":"OK","format":"binary"},"example":"OK"}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}}}}},"components":{"schemas":{"BadRequestError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The request was invalid."}},"example":{"message":"The request was invalid."},"required":["message"]},"ConflictError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The resource already exists."}},"example":{"message":"The resource already exists."},"required":["message"]},"CreateGrpsioMailingListMemberRequestBody":{"type":"object","properties":{"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"none","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"direct","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"moderator","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255}},"example":{"delivery_mode":"none","email":"john.doe@example.com","first_name":"John","job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","member_type":"committee","mod_status":"none","organization":"Example Corp","username":"jdoe"},"required":["email"]},"CreateGrpsioMailingListRequestBody":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Vel enim optio similique dolores deleniti."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Alternate Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"writers":{"type":"array","items":{"type":"string","example":"Voluptatem omnis deleniti."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]},"required":["group_name","public","type","description","title","service_uid"]},"CreateGrpsioServiceRequestBody":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Laborum autem est sint neque."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"ernestine@bartell.info","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Qui ipsam."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"GrpsIoMailingListFull":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Tenetur sequi molestiae ut corrupti magnam."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Alternate Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier (read-only)","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID (inherited from parent service)","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"uid":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"writers":{"type":"array","items":{"type":"string","example":"Nisi dolorum et."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A complete representation of GroupsIO mailing lists with all attributes including access control and audit trail.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoMailingListWithReadonlyAttributes":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Animi eaque cupiditate exercitationem."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Alternate Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier (read-only)","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID (inherited from parent service)","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"uid":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"writers":{"type":"array","items":{"type":"string","example":"Optio vero aspernatur."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO mailing lists with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoMemberFull":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Quidem impedit voluptas quia rem."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"digest","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"groupsio_group_id":{"type":"integer","description":"Groups.io group ID","example":67890,"format":"int64"},"groupsio_member_id":{"type":"integer","description":"Groups.io member ID","example":12345,"format":"int64"},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"mailing_list_uid":{"type":"string","description":"Mailing list UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"committee","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"moderator","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"status":{"type":"string","description":"Member status","example":"pending"},"uid":{"type":"string","description":"Member UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255},"writers":{"type":"array","items":{"type":"string","example":"Rem aut aut est facere laborum enim."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A complete representation of a GroupsIO mailing list member with all attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"digest","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"committee","mod_status":"none","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]},"required":["uid","mailing_list_uid","first_name","last_name","email","member_type","delivery_mode","mod_status","status","created_at","updated_at"]},"GrpsIoMemberWithReadonlyAttributes":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Magni fugit similique."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"normal","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"groupsio_group_id":{"type":"integer","description":"Groups.io group ID","example":67890,"format":"int64"},"groupsio_member_id":{"type":"integer","description":"Groups.io member ID","example":12345,"format":"int64"},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"mailing_list_uid":{"type":"string","description":"Mailing list UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"committee","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"none","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"status":{"type":"string","description":"Member status","example":"pending"},"uid":{"type":"string","description":"Member UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255},"writers":{"type":"array","items":{"type":"string","example":"Qui minus sapiente neque ad."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO mailing list members with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"normal","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"committee","mod_status":"owner","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoServiceFull":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Doloremque nostrum sed quo ullam."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"jason@bechtelargerhold.net","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"last_audited_by":{"type":"string","description":"The user ID who last audited the service","example":"user_id_12345"},"last_audited_time":{"type":"string","description":"The timestamp when the service was last audited","example":"2023-05-10T09:15:00Z","format":"date-time"},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"uid":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Ut explicabo saepe."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A complete representation of GroupsIO services with all attributes including access control and audit trail.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"GrpsIoServiceWithReadonlyAttributes":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Corrupti quis aut voluptatem aut."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"kaya@pollichabshire.biz","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"last_audited_by":{"type":"string","description":"The user ID who last audited the service","example":"user_id_12345"},"last_audited_time":{"type":"string","description":"The timestamp when the service was last audited","example":"2023-05-10T09:15:00Z","format":"date-time"},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"uid":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Voluptatem voluptate vitae saepe mollitia."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO services with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"InternalServerError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"An internal server error occurred."}},"example":{"message":"An internal server error occurred."},"required":["message"]},"NotFoundError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The resource was not found."}},"example":{"message":"The resource was not found."},"required":["message"]},"ServiceUnavailableError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The service is unavailable."}},"example":{"message":"The service is unavailable."},"required":["message"]},"UpdateGrpsioMailingListMemberRequestBody":{"type":"object","properties":{"delivery_mode":{"type":"string","description":"Email delivery mode","example":"none","enum":["normal","digest","none"]},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"mod_status":{"type":"string","description":"Moderation status","example":"owner","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255}},"example":{"delivery_mode":"none","first_name":"John","job_title":"Software Engineer","last_name":"Doe","mod_status":"owner","organization":"Example Corp","username":"jdoe"}},"UpdateGrpsioMailingListRequestBody":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Aut amet."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"None","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"writers":{"type":"array","items":{"type":"string","example":"Amet sunt libero quod placeat et possimus."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]}},"UpdateGrpsioServiceRequestBody":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Et quas doloremque modi."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"anthony_berge@stark.name","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Molestias omnis aliquid aspernatur rerum consequatur."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}},"securitySchemes":{"jwt_header_Authorization":{"type":"http","description":"Heimdall authorization","scheme":"bearer"}}},"tags":[{"name":"mailing-list","description":"The mailing list service manages mailing lists and services"}]} \ No newline at end of file +{"openapi":"3.0.3","info":{"title":"Mailing List Service","description":"Service for managing mailing lists in LFX","version":"0.0.1"},"servers":[{"url":"http://localhost:80","description":"Default server for mailing-list"}],"paths":{"/groupsio/mailing-lists":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-mailing-list mailing-list","description":"Create GroupsIO mailing list/subgroup with comprehensive validation","operationId":"mailing-list#create-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGrpsioMailingListRequestBody"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]}}}},"responses":{"201":{"description":"Created response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMailingListFull"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request - Invalid data, missing required fields, or validation failures","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Parent service not found or committee not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Mailing list with same name already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}":{"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-mailing-list mailing-list","description":"Delete GroupsIO mailing list","operationId":"mailing-list#delete-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Mailing list not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict - ETag mismatch or deletion not allowed","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"get":{"tags":["mailing-list"],"summary":"get-grpsio-mailing-list mailing-list","description":"Get GroupsIO mailing list details by UID","operationId":"mailing-list#get-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"}],"responses":{"200":{"description":"OK response.","headers":{"ETag":{"description":"ETag header value","schema":{"type":"string","description":"ETag header value","example":"123"},"example":"123"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMailingListWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Mailing list not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"put":{"tags":["mailing-list"],"summary":"update-grpsio-mailing-list mailing-list","description":"Update GroupsIO mailing list","operationId":"mailing-list#update-grpsio-mailing-list","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGrpsioMailingListRequestBody"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]}}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMailingListWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Mailing list not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict - ETag mismatch or validation failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}/members":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-mailing-list-member mailing-list","description":"Create a new member for a GroupsIO mailing list","operationId":"mailing-list#create-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID","required":true,"schema":{"type":"string","description":"Mailing list UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"},"example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGrpsioMailingListMemberRequestBody"},"example":{"delivery_mode":"normal","email":"john.doe@example.com","first_name":"John","job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","member_type":"committee","mod_status":"owner","organization":"Example Corp","username":"jdoe"}}}},"responses":{"201":{"description":"Created response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMemberFull"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"normal","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"direct","mod_status":"moderator","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Mailing list not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Member already exists","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/mailing-lists/{uid}/members/{member_uid}":{"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-mailing-list-member mailing-list","description":"Delete a member from a GroupsIO mailing list","operationId":"mailing-list#delete-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"schema":{"type":"string","description":"Member UID -- unique identifier for the member","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"BadRequest: Bad request - Cannot remove sole owner","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict - ETag mismatch","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"get":{"tags":["mailing-list"],"summary":"get-grpsio-mailing-list-member mailing-list","description":"Get a member of a GroupsIO mailing list by UID","operationId":"mailing-list#get-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"schema":{"type":"string","description":"Member UID -- unique identifier for the member","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"}],"responses":{"200":{"description":"OK response.","headers":{"ETag":{"description":"ETag header value","schema":{"type":"string","description":"ETag header value","example":"123"},"example":"123"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMemberWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"none","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"committee","mod_status":"none","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"put":{"tags":["mailing-list"],"summary":"update-grpsio-mailing-list-member mailing-list","description":"Update a member of a GroupsIO mailing list","operationId":"mailing-list#update-grpsio-mailing-list-member","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Mailing list UID -- unique identifier for the mailing list","required":true,"schema":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"member_uid","in":"path","description":"Member UID -- unique identifier for the member","required":true,"schema":{"type":"string","description":"Member UID -- unique identifier for the member","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"example":"f47ac10b-58cc-4372-a567-0e02b2c3d479"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateGrpsioMailingListMemberRequestBody"},"example":{"delivery_mode":"normal","first_name":"John","job_title":"Software Engineer","last_name":"Doe","mod_status":"owner","organization":"Example Corp","username":"jdoe"}}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoMemberWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"digest","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"direct","mod_status":"owner","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request - Invalid data or immutable field modification","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Member not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict - ETag mismatch or validation failure","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/services":{"post":{"tags":["mailing-list"],"summary":"create-grpsio-service mailing-list","description":"Create GroupsIO service with type-specific validation rules","operationId":"mailing-list#create-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGrpsioServiceRequestBody"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"responses":{"201":{"description":"Created response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoServiceFull"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request - Invalid type, missing required fields, or validation failures","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/groupsio/services/{uid}":{"delete":{"tags":["mailing-list"],"summary":"delete-grpsio-service mailing-list","description":"Delete GroupsIO service","operationId":"mailing-list#delete-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"schema":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"responses":{"204":{"description":"No Content response."},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"get":{"tags":["mailing-list"],"summary":"get-grpsio-service mailing-list","description":"Get groupsIO service details by ID","operationId":"mailing-list#get-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"schema":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"}],"responses":{"200":{"description":"OK response.","headers":{"ETag":{"description":"ETag header value","schema":{"type":"string","description":"ETag header value","example":"123"},"example":"123"}},"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoServiceWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]},"put":{"tags":["mailing-list"],"summary":"update-grpsio-service mailing-list","description":"Update GroupsIO service","operationId":"mailing-list#update-grpsio-service","parameters":[{"name":"v","in":"query","description":"Version of the API","allowEmptyValue":true,"required":true,"schema":{"type":"string","description":"Version of the API","example":"1","enum":["1"]},"example":"1"},{"name":"uid","in":"path","description":"Service UID -- unique identifier for the service","required":true,"schema":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee"},{"name":"If-Match","in":"header","description":"If-Match header value for conditional requests","allowEmptyValue":true,"schema":{"type":"string","description":"If-Match header value for conditional requests","example":"123"},"example":"123"}],"requestBody":{"required":true,"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateGrpsioServiceRequestBody"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"responses":{"200":{"description":"OK response.","content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrpsIoServiceWithReadonlyAttributes"},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]}}}},"400":{"description":"BadRequest: Bad request","content":{"application/json":{"schema":{"$ref":"#/components/schemas/BadRequestError"},"example":{"message":"The request was invalid."}}}},"404":{"description":"NotFound: Resource not found","content":{"application/json":{"schema":{"$ref":"#/components/schemas/NotFoundError"},"example":{"message":"The resource was not found."}}}},"409":{"description":"Conflict: Conflict","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ConflictError"},"example":{"message":"The resource already exists."}}}},"500":{"description":"InternalServerError: Internal server error","content":{"application/json":{"schema":{"$ref":"#/components/schemas/InternalServerError"},"example":{"message":"An internal server error occurred."}}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}},"security":[{"jwt_header_Authorization":[]}]}},"/livez":{"get":{"tags":["mailing-list"],"summary":"livez mailing-list","description":"Check if the service is alive.","operationId":"mailing-list#livez","responses":{"200":{"description":"OK response.","content":{"text/plain":{"schema":{"type":"string","example":"OK","format":"binary"},"example":"OK"}}}}}},"/openapi.json":{"get":{"tags":["mailing-list"],"summary":"Download gen/http/openapi3.json","operationId":"mailing-list#/openapi.json","responses":{"200":{"description":"File downloaded"}}}},"/readyz":{"get":{"tags":["mailing-list"],"summary":"readyz mailing-list","description":"Check if the service is able to take inbound requests.","operationId":"mailing-list#readyz","responses":{"200":{"description":"OK response.","content":{"text/plain":{"schema":{"type":"string","example":"OK","format":"binary"},"example":"OK"}}},"503":{"description":"ServiceUnavailable: Service unavailable","content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceUnavailableError"},"example":{"message":"The service is unavailable."}}}}}}}},"components":{"schemas":{"BadRequestError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The request was invalid."}},"example":{"message":"The request was invalid."},"required":["message"]},"ConflictError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The resource already exists."}},"example":{"message":"The resource already exists."},"required":["message"]},"CreateGrpsioMailingListMemberRequestBody":{"type":"object","properties":{"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"none","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"committee","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"owner","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255}},"example":{"delivery_mode":"normal","email":"john.doe@example.com","first_name":"John","job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","member_type":"direct","mod_status":"none","organization":"Example Corp","username":"jdoe"},"required":["email"]},"CreateGrpsioMailingListRequestBody":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Explicabo deserunt sit vitae in dolorem."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Emeritus","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"writers":{"type":"array","items":{"type":"string","example":"Totam enim natus explicabo libero."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","description":"Technical steering committee discussions","group_name":"technical-steering-committee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","writers":["manager_user_id1","manager_user_id2"]},"required":["group_name","public","type","description","title","service_uid"]},"CreateGrpsioServiceRequestBody":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Laborum autem est sint neque."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"ernestine@bartell.info","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Qui ipsam."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"example":{"auditors":["auditor_user_id1","auditor_user_id2"],"domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","prefix":"formation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"GrpsIoMailingListFull":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Tenetur sequi molestiae ut corrupti magnam."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Alternate Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier (read-only)","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID (inherited from parent service)","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"uid":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"writers":{"type":"array","items":{"type":"string","example":"Nisi dolorum et."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A complete representation of GroupsIO mailing lists with all attributes including access control and audit trail.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoMailingListWithReadonlyAttributes":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Animi eaque cupiditate exercitationem."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"committee_filters":{"type":"array","items":{"type":"string","example":"Alternate Voting Rep","enum":["Voting Rep","Alternate Voting Rep","Observer","Emeritus","None"]},"description":"Committee member filters","example":["Voting Rep","Alternate Voting Rep"]},"committee_uid":{"type":"string","description":"Committee UUID for committee-based mailing lists","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"description":{"type":"string","description":"Mailing list description (11-500 characters)","example":"Technical steering committee discussions","minLength":11,"maxLength":500},"group_name":{"type":"string","description":"Mailing list group name","example":"technical-steering-committee","pattern":"^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$","minLength":3,"maxLength":34},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier (read-only)","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID (inherited from parent service)","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the mailing list is publicly accessible","default":false,"example":false},"service_uid":{"type":"string","description":"Service UUID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"subject_tag":{"type":"string","description":"Subject tag prefix","example":"[TSC]","maxLength":50},"title":{"type":"string","description":"Mailing list title","example":"Technical Steering Committee","minLength":5,"maxLength":100},"type":{"type":"string","description":"Mailing list type","example":"discussion_moderated","enum":["announcement","discussion_moderated","discussion_open"]},"uid":{"type":"string","description":"Mailing list UID -- unique identifier for the mailing list","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"writers":{"type":"array","items":{"type":"string","example":"Optio vero aspernatur."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO mailing lists with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"committee_filters":["Voting Rep","Alternate Voting Rep"],"committee_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","created_at":"2023-01-15T10:30:00Z","description":"Technical steering committee discussions","group_name":"technical-steering-committee","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":false,"service_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","subject_tag":"[TSC]","title":"Technical Steering Committee","type":"discussion_moderated","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoMemberFull":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Quidem impedit voluptas quia rem."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"digest","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"groupsio_group_id":{"type":"integer","description":"Groups.io group ID","example":67890,"format":"int64"},"groupsio_member_id":{"type":"integer","description":"Groups.io member ID","example":12345,"format":"int64"},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"mailing_list_uid":{"type":"string","description":"Mailing list UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"committee","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"moderator","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"status":{"type":"string","description":"Member status","example":"pending"},"uid":{"type":"string","description":"Member UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255},"writers":{"type":"array","items":{"type":"string","example":"Rem aut aut est facere laborum enim."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A complete representation of a GroupsIO mailing list member with all attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"digest","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"committee","mod_status":"none","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]},"required":["uid","mailing_list_uid","first_name","last_name","email","member_type","delivery_mode","mod_status","status","created_at","updated_at"]},"GrpsIoMemberWithReadonlyAttributes":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Magni fugit similique."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"normal","enum":["normal","digest","none"]},"email":{"type":"string","description":"Member email address","example":"john.doe@example.com","format":"email"},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"groupsio_group_id":{"type":"integer","description":"Groups.io group ID","example":67890,"format":"int64"},"groupsio_member_id":{"type":"integer","description":"Groups.io member ID","example":12345,"format":"int64"},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"last_reviewed_at":{"type":"string","description":"Last reviewed timestamp","example":"2023-01-15T14:30:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"Last reviewed by user ID","example":"admin@example.com"},"mailing_list_uid":{"type":"string","description":"Mailing list UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"member_type":{"type":"string","description":"Member type","default":"direct","example":"committee","enum":["committee","direct"]},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"none","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"status":{"type":"string","description":"Member status","example":"pending"},"uid":{"type":"string","description":"Member UID","example":"f47ac10b-58cc-4372-a567-0e02b2c3d479","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255},"writers":{"type":"array","items":{"type":"string","example":"Qui minus sapiente neque ad."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO mailing list members with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","delivery_mode":"normal","email":"john.doe@example.com","first_name":"John","groupsio_group_id":67890,"groupsio_member_id":12345,"job_title":"Software Engineer","last_name":"Doe","last_reviewed_at":"2023-01-15T14:30:00Z","last_reviewed_by":"admin@example.com","mailing_list_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","member_type":"committee","mod_status":"owner","organization":"Example Corp","status":"pending","uid":"f47ac10b-58cc-4372-a567-0e02b2c3d479","updated_at":"2023-06-20T14:45:30Z","username":"jdoe","writers":["manager_user_id1","manager_user_id2"]}},"GrpsIoServiceFull":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Doloremque nostrum sed quo ullam."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"jason@bechtelargerhold.net","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"last_audited_by":{"type":"string","description":"The user ID who last audited the service","example":"user_id_12345"},"last_audited_time":{"type":"string","description":"The timestamp when the service was last audited","example":"2023-05-10T09:15:00Z","format":"date-time"},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"uid":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Ut explicabo saepe."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A complete representation of GroupsIO services with all attributes including access control and audit trail.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"GrpsIoServiceWithReadonlyAttributes":{"type":"object","properties":{"auditors":{"type":"array","items":{"type":"string","example":"Corrupti quis aut voluptatem aut."},"description":"Auditor user IDs who can audit this service","example":["auditor_user_id1","auditor_user_id2"]},"created_at":{"type":"string","description":"The timestamp when the service was created (read-only)","example":"2023-01-15T10:30:00Z","format":"date-time"},"domain":{"type":"string","description":"Service domain","example":"lists.project.org"},"global_owners":{"type":"array","items":{"type":"string","example":"kaya@pollichabshire.biz","format":"email"},"description":"List of global owner email addresses (required for primary, forbidden for shared)","example":["admin@example.com"]},"group_id":{"type":"integer","description":"GroupsIO group ID","example":12345,"format":"int64"},"group_name":{"type":"string","description":"GroupsIO group name","example":"project-name"},"last_audited_by":{"type":"string","description":"The user ID who last audited the service","example":"user_id_12345"},"last_audited_time":{"type":"string","description":"The timestamp when the service was last audited","example":"2023-05-10T09:15:00Z","format":"date-time"},"last_reviewed_at":{"type":"string","description":"The timestamp when the service was last reviewed in RFC3339 format","example":"2025-08-04T09:00:00Z","format":"date-time"},"last_reviewed_by":{"type":"string","description":"The user ID who last reviewed this service","example":"user_id_12345"},"prefix":{"type":"string","description":"Email prefix (required for formation and shared, forbidden for primary)","example":"formation"},"project_name":{"type":"string","description":"Project name (read-only)","example":"Cloud Native Computing Foundation"},"project_slug":{"type":"string","description":"Project slug identifier","example":"cncf","format":"regexp","pattern":"^[a-z][a-z0-9_\\-]*[a-z0-9]$"},"project_uid":{"type":"string","description":"LFXv2 Project UID","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"public":{"type":"boolean","description":"Whether the service is publicly accessible","default":false,"example":true},"status":{"type":"string","description":"Service status","example":"created"},"type":{"type":"string","description":"Service type","example":"primary","enum":["primary","formation","shared"]},"uid":{"type":"string","description":"Service UID -- unique identifier for the service","example":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","format":"uuid"},"updated_at":{"type":"string","description":"The timestamp when the service was last updated (read-only)","example":"2023-06-20T14:45:30Z","format":"date-time"},"url":{"type":"string","description":"Service URL","example":"https://lists.project.org","format":"uri"},"writers":{"type":"array","items":{"type":"string","example":"Voluptatem voluptate vitae saepe mollitia."},"description":"Manager user IDs who can edit/modify this service","example":["manager_user_id1","manager_user_id2"]}},"description":"A representation of GroupsIO services with readonly attributes.","example":{"auditors":["auditor_user_id1","auditor_user_id2"],"created_at":"2023-01-15T10:30:00Z","domain":"lists.project.org","global_owners":["admin@example.com"],"group_id":12345,"group_name":"project-name","last_audited_by":"user_id_12345","last_audited_time":"2023-05-10T09:15:00Z","last_reviewed_at":"2025-08-04T09:00:00Z","last_reviewed_by":"user_id_12345","prefix":"formation","project_name":"Cloud Native Computing Foundation","project_slug":"cncf","project_uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","public":true,"status":"created","type":"primary","uid":"7cad5a8d-19d0-41a4-81a6-043453daf9ee","updated_at":"2023-06-20T14:45:30Z","url":"https://lists.project.org","writers":["manager_user_id1","manager_user_id2"]},"required":["type","project_uid"]},"InternalServerError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"An internal server error occurred."}},"example":{"message":"An internal server error occurred."},"required":["message"]},"NotFoundError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The resource was not found."}},"example":{"message":"The resource was not found."},"required":["message"]},"ServiceUnavailableError":{"type":"object","properties":{"message":{"type":"string","description":"Error message","example":"The service is unavailable."}},"example":{"message":"The service is unavailable."},"required":["message"]},"UpdateGrpsioMailingListMemberRequestBody":{"type":"object","properties":{"delivery_mode":{"type":"string","description":"Email delivery mode","default":"normal","example":"none","enum":["normal","digest","none"]},"first_name":{"type":"string","description":"Member first name","example":"John","minLength":1,"maxLength":255},"job_title":{"type":"string","description":"Member job title","example":"Software Engineer","maxLength":255},"last_name":{"type":"string","description":"Member last name","example":"Doe","minLength":1,"maxLength":255},"mod_status":{"type":"string","description":"Moderation status","default":"none","example":"moderator","enum":["none","moderator","owner"]},"organization":{"type":"string","description":"Member organization","example":"Example Corp","maxLength":255},"username":{"type":"string","description":"Member username","example":"jdoe","maxLength":255}},"example":{"delivery_mode":"normal","first_name":"John","job_title":"Software Engineer","last_name":"Doe","mod_status":"moderator","organization":"Example Corp","username":"jdoe"}}},"securitySchemes":{"jwt_header_Authorization":{"type":"http","description":"Heimdall authorization","scheme":"bearer"}}},"tags":[{"name":"mailing-list","description":"The mailing list service manages mailing lists and services"}]} \ No newline at end of file diff --git a/gen/http/openapi3.yaml b/gen/http/openapi3.yaml index de597fa..d190d3a 100644 --- a/gen/http/openapi3.yaml +++ b/gen/http/openapi3.yaml @@ -311,7 +311,7 @@ paths: message: The service is unavailable. security: - jwt_header_Authorization: [] - patch: + put: tags: - mailing-list summary: update-grpsio-mailing-list mailing-list @@ -354,7 +354,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateGrpsioMailingListRequestBody' + $ref: '#/components/schemas/CreateGrpsioMailingListRequestBody' example: auditors: - auditor_user_id1 @@ -775,7 +775,7 @@ paths: message: The service is unavailable. security: - jwt_header_Authorization: [] - patch: + put: tags: - mailing-list summary: update-grpsio-mailing-list-member mailing-list @@ -1220,7 +1220,7 @@ paths: message: The service is unavailable. security: - jwt_header_Authorization: [] - patch: + put: tags: - mailing-list summary: update-grpsio-service mailing-list @@ -1263,7 +1263,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/UpdateGrpsioServiceRequestBody' + $ref: '#/components/schemas/CreateGrpsioServiceRequestBody' example: auditors: - auditor_user_id1 @@ -1481,7 +1481,7 @@ components: type: string description: Member type default: direct - example: direct + example: committee enum: - committee - direct @@ -1489,7 +1489,7 @@ components: type: string description: Moderation status default: none - example: moderator + example: owner enum: - none - moderator @@ -1505,14 +1505,14 @@ components: example: jdoe maxLength: 255 example: - delivery_mode: none + delivery_mode: normal email: john.doe@example.com first_name: John job_title: Software Engineer last_name: Doe last_reviewed_at: "2023-01-15T14:30:00Z" last_reviewed_by: admin@example.com - member_type: committee + member_type: direct mod_status: none organization: Example Corp username: jdoe @@ -1525,7 +1525,7 @@ components: type: array items: type: string - example: Vel enim optio similique dolores deleniti. + example: Explicabo deserunt sit vitae in dolorem. description: Auditor user IDs who can audit this service example: - auditor_user_id1 @@ -1534,7 +1534,7 @@ components: type: array items: type: string - example: Alternate Voting Rep + example: Emeritus enum: - Voting Rep - Alternate Voting Rep @@ -1596,7 +1596,7 @@ components: type: array items: type: string - example: Voluptatem omnis deleniti. + example: Totam enim natus explicabo libero. description: Manager user IDs who can edit/modify this service example: - manager_user_id1 @@ -2658,6 +2658,7 @@ components: delivery_mode: type: string description: Email delivery mode + default: normal example: none enum: - normal @@ -2683,7 +2684,8 @@ components: mod_status: type: string description: Moderation status - example: owner + default: none + example: moderator enum: - none - moderator @@ -2699,211 +2701,13 @@ components: example: jdoe maxLength: 255 example: - delivery_mode: none + delivery_mode: normal first_name: John job_title: Software Engineer last_name: Doe - mod_status: owner + mod_status: moderator organization: Example Corp username: jdoe - UpdateGrpsioMailingListRequestBody: - type: object - properties: - auditors: - type: array - items: - type: string - example: Aut amet. - description: Auditor user IDs who can audit this service - example: - - auditor_user_id1 - - auditor_user_id2 - committee_filters: - type: array - items: - type: string - example: None - enum: - - Voting Rep - - Alternate Voting Rep - - Observer - - Emeritus - - None - description: Committee member filters - example: - - Voting Rep - - Alternate Voting Rep - committee_uid: - type: string - description: Committee UUID for committee-based mailing lists - example: 7cad5a8d-19d0-41a4-81a6-043453daf9ee - format: uuid - description: - type: string - description: Mailing list description (11-500 characters) - example: Technical steering committee discussions - minLength: 11 - maxLength: 500 - group_name: - type: string - description: Mailing list group name - example: technical-steering-committee - pattern: ^[a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]$ - minLength: 3 - maxLength: 34 - public: - type: boolean - description: Whether the mailing list is publicly accessible - example: false - service_uid: - type: string - description: Service UUID - example: 7cad5a8d-19d0-41a4-81a6-043453daf9ee - format: uuid - subject_tag: - type: string - description: Subject tag prefix - example: '[TSC]' - maxLength: 50 - title: - type: string - description: Mailing list title - example: Technical Steering Committee - minLength: 5 - maxLength: 100 - type: - type: string - description: Mailing list type - example: discussion_moderated - enum: - - announcement - - discussion_moderated - - discussion_open - writers: - type: array - items: - type: string - example: Amet sunt libero quod placeat et possimus. - description: Manager user IDs who can edit/modify this service - example: - - manager_user_id1 - - manager_user_id2 - example: - auditors: - - auditor_user_id1 - - auditor_user_id2 - committee_filters: - - Voting Rep - - Alternate Voting Rep - committee_uid: 7cad5a8d-19d0-41a4-81a6-043453daf9ee - description: Technical steering committee discussions - group_name: technical-steering-committee - public: false - service_uid: 7cad5a8d-19d0-41a4-81a6-043453daf9ee - subject_tag: '[TSC]' - title: Technical Steering Committee - type: discussion_moderated - writers: - - manager_user_id1 - - manager_user_id2 - UpdateGrpsioServiceRequestBody: - type: object - properties: - auditors: - type: array - items: - type: string - example: Et quas doloremque modi. - description: Auditor user IDs who can audit this service - example: - - auditor_user_id1 - - auditor_user_id2 - domain: - type: string - description: Service domain - example: lists.project.org - global_owners: - type: array - items: - type: string - example: anthony_berge@stark.name - format: email - description: List of global owner email addresses (required for primary, forbidden for shared) - example: - - admin@example.com - group_id: - type: integer - description: GroupsIO group ID - example: 12345 - format: int64 - group_name: - type: string - description: GroupsIO group name - example: project-name - prefix: - type: string - description: Email prefix (required for formation and shared, forbidden for primary) - example: formation - project_slug: - type: string - description: Project slug identifier - example: cncf - format: regexp - pattern: ^[a-z][a-z0-9_\-]*[a-z0-9]$ - project_uid: - type: string - description: LFXv2 Project UID - example: 7cad5a8d-19d0-41a4-81a6-043453daf9ee - format: uuid - public: - type: boolean - description: Whether the service is publicly accessible - example: true - status: - type: string - description: Service status - example: created - type: - type: string - description: Service type - example: primary - enum: - - primary - - formation - - shared - url: - type: string - description: Service URL - example: https://lists.project.org - format: uri - writers: - type: array - items: - type: string - example: Molestias omnis aliquid aspernatur rerum consequatur. - description: Manager user IDs who can edit/modify this service - example: - - manager_user_id1 - - manager_user_id2 - example: - auditors: - - auditor_user_id1 - - auditor_user_id2 - domain: lists.project.org - global_owners: - - admin@example.com - group_id: 12345 - group_name: project-name - prefix: formation - project_slug: cncf - project_uid: 7cad5a8d-19d0-41a4-81a6-043453daf9ee - public: true - status: created - type: primary - url: https://lists.project.org - writers: - - manager_user_id1 - - manager_user_id2 securitySchemes: jwt_header_Authorization: type: http diff --git a/gen/mailing_list/service.go b/gen/mailing_list/service.go index 7325dce..09385fe 100644 --- a/gen/mailing_list/service.go +++ b/gen/mailing_list/service.go @@ -562,9 +562,9 @@ type UpdateGrpsioMailingListMemberPayload struct { // Member job title JobTitle *string // Email delivery mode - DeliveryMode *string + DeliveryMode string // Moderation status - ModStatus *string + ModStatus string } // UpdateGrpsioMailingListPayload is the payload type of the mailing-list @@ -579,23 +579,23 @@ type UpdateGrpsioMailingListPayload struct { // Mailing list UID -- unique identifier for the mailing list UID *string // Mailing list group name - GroupName *string + GroupName string // Whether the mailing list is publicly accessible - Public *bool + Public bool // Mailing list type - Type *string + Type string // Committee UUID for committee-based mailing lists CommitteeUID *string // Committee member filters CommitteeFilters []string // Mailing list description (11-500 characters) - Description *string + Description string // Mailing list title - Title *string + Title string // Subject tag prefix SubjectTag *string // Service UUID - ServiceUID *string + ServiceUID string // Manager user IDs who can edit/modify this service Writers []string // Auditor user IDs who can audit this service @@ -614,7 +614,7 @@ type UpdateGrpsioServicePayload struct { // Service UID -- unique identifier for the service UID *string // Service type - Type *string + Type string // Service domain Domain *string // GroupsIO group ID @@ -629,13 +629,13 @@ type UpdateGrpsioServicePayload struct { // Project slug identifier ProjectSlug *string // LFXv2 Project UID - ProjectUID *string + ProjectUID string // Service URL URL *string // GroupsIO group name GroupName *string // Whether the service is publicly accessible - Public *bool + Public bool // Manager user IDs who can edit/modify this service Writers []string // Auditor user IDs who can audit this service