From 62b5de5722a2345ad040fbc311ffe8d531a62def Mon Sep 17 00:00:00 2001 From: Jay Brown Date: Thu, 26 Feb 2026 21:46:00 +0000 Subject: [PATCH] Merged in bug/eula-email (pull request #212) fix eula issue when email not present in jwt * bug fix --- api/queryAPI/adminHandlers.go | 1 - api/queryAPI/api.gen.go | 559 +++++++++--------- api/queryAPI/eulaAdminHandlers.go | 24 +- .../eulaAdminHandlers_private_test.go | 175 ++++++ api/queryAPI/eulaHandlers_test.go | 166 ++++++ pkg/queryAPI/api.gen.go | 20 +- serviceAPIs/queryAPI.yaml | 15 +- 7 files changed, 648 insertions(+), 312 deletions(-) create mode 100644 api/queryAPI/eulaAdminHandlers_private_test.go diff --git a/api/queryAPI/adminHandlers.go b/api/queryAPI/adminHandlers.go index 2648ec07..288e4cc0 100644 --- a/api/queryAPI/adminHandlers.go +++ b/api/queryAPI/adminHandlers.go @@ -36,7 +36,6 @@ func getAdminUserForAudit(ctx echo.Context) (cognitoauth.UserInfo, error) { } // If auth is disabled, use a default system user for audit logging - // Note: Email must be a valid format (with TLD) to pass openapi_types.Email validation if os.Getenv("DISABLE_AUTH") == "true" { return cognitoauth.UserInfo{ Email: "system@test.local", diff --git a/api/queryAPI/api.gen.go b/api/queryAPI/api.gen.go index 8180514d..625b78c7 100644 --- a/api/queryAPI/api.gen.go +++ b/api/queryAPI/api.gen.go @@ -656,8 +656,8 @@ type EulaAgreement struct { // Id Agreement record ID Id openapi_types.UUID `json:"id"` - // UserEmail User's email at time of agreement - UserEmail openapi_types.Email `json:"userEmail"` + // UserEmail User identifier at time of agreement (email, username, or subject ID) + UserEmail string `json:"userEmail"` } // EulaAgreementList Paginated list of EULA agreements @@ -752,11 +752,11 @@ type EulaComplianceUser struct { // CognitoSubjectId User's Cognito subject ID CognitoSubjectId string `json:"cognitoSubjectId"` - // CurrentEmail User's current email from Cognito - CurrentEmail openapi_types.Email `json:"currentEmail"` + // CurrentEmail User's current email from Cognito (may be empty or a non-email identifier) + CurrentEmail string `json:"currentEmail"` - // Email User's email at time of agreement (null if not agreed) - Email nullable.Nullable[openapi_types.Email] `json:"email,omitempty"` + // Email User identifier at time of agreement (email, username, or subject ID; null if not agreed) + Email nullable.Nullable[string] `json:"email,omitempty"` } // EulaPublicResponse Public response for current EULA version @@ -800,8 +800,8 @@ type EulaVersion struct { // ActivatedAt When this version was activated ActivatedAt nullable.Nullable[time.Time] `json:"activatedAt,omitempty"` - // ActivatedBy Email of admin who activated this version - ActivatedBy nullable.Nullable[openapi_types.Email] `json:"activatedBy,omitempty"` + // ActivatedBy Identifier of admin who activated this version (email, username, or subject ID) + ActivatedBy nullable.Nullable[string] `json:"activatedBy,omitempty"` // Content Full EULA text in Markdown format Content string `json:"content"` @@ -809,8 +809,8 @@ type EulaVersion struct { // CreatedAt Creation timestamp CreatedAt time.Time `json:"createdAt"` - // CreatedBy Email of admin who created this version - CreatedBy openapi_types.Email `json:"createdBy"` + // CreatedBy Identifier of admin who created this version (email, username, or subject ID) + CreatedBy string `json:"createdBy"` // EffectiveDate When this version becomes effective (optional) EffectiveDate nullable.Nullable[time.Time] `json:"effectiveDate,omitempty"` @@ -2675,275 +2675,276 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "/wkAAP//LHP6SYqxAQA=", + "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", + "sQEA", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/api/queryAPI/eulaAdminHandlers.go b/api/queryAPI/eulaAdminHandlers.go index 504ee261..3da12411 100644 --- a/api/queryAPI/eulaAdminHandlers.go +++ b/api/queryAPI/eulaAdminHandlers.go @@ -56,7 +56,7 @@ func (s *Controllers) ListEulaVersions(ctx echo.Context, params ListEulaVersions "page", page, "page_size", pageSize) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ - Message: fmt.Sprintf("Failed to list EULA versions: %v", err), + Message: fmt.Sprintf("ListEulaVersions: failed to list EULA versions: %v", err), }) } @@ -334,7 +334,7 @@ func (s *Controllers) ListEulaAgreements(ctx echo.Context, params ListEulaAgreem "page", page, "page_size", pageSize) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ - Message: fmt.Sprintf("Failed to list EULA agreements: %v", err), + Message: fmt.Sprintf("ListEulaAgreements: failed to list EULA agreements: %v", err), }) } @@ -381,7 +381,7 @@ func (s *Controllers) GetEulaCompliance(ctx echo.Context, params GetEulaComplian s.cfg.GetAuthLogger().Error("GetEulaCompliance: failed to initialize AWS configuration", "error", err) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ - Message: fmt.Sprintf("Failed to initialize AWS configuration: %v", err), + Message: fmt.Sprintf("GetEulaCompliance: failed to initialize AWS configuration: %v", err), }) } @@ -414,12 +414,12 @@ func (s *Controllers) GetEulaCompliance(ctx echo.Context, params GetEulaComplian if err != nil { if errors.Is(err, eula.ErrNoCurrentVersion) { return ctx.JSON(http.StatusNotFound, ErrorMessage{ - Message: "No current EULA version configured", + Message: "GetEulaCompliance: no current EULA version configured", }) } if errors.Is(err, eula.ErrVersionNotFound) { return ctx.JSON(http.StatusNotFound, ErrorMessage{ - Message: "EULA version not found", + Message: "GetEulaCompliance: specified EULA version not found", }) } // Check if it's a Cognito error @@ -427,13 +427,13 @@ func (s *Controllers) GetEulaCompliance(ctx echo.Context, params GetEulaComplian s.cfg.GetAuthLogger().Error("GetEulaCompliance: Cognito error during compliance report", "error", err) return ctx.JSON(http.StatusBadGateway, ErrorMessage{ - Message: "Failed to fetch users from Cognito: " + err.Error(), + Message: fmt.Sprintf("GetEulaCompliance: failed to fetch users from Cognito: %v", err), }) } s.cfg.GetAuthLogger().Error("GetEulaCompliance: failed to generate compliance report", "error", err) return ctx.JSON(http.StatusInternalServerError, ErrorMessage{ - Message: fmt.Sprintf("Failed to generate compliance report: %v", err), + Message: fmt.Sprintf("GetEulaCompliance: failed to generate compliance report: %v", err), }) } @@ -470,12 +470,12 @@ func convertComplianceReportToAPI(result *eula.ComplianceReportResult) EulaCompl for _, u := range result.Users { user := EulaComplianceUser{ CognitoSubjectId: u.CognitoSubjectID, - CurrentEmail: openapi_types.Email(u.CurrentEmail), + CurrentEmail: u.CurrentEmail, Agreed: u.Agreed, } if u.Email != nil { - user.Email = nullable.NewNullableWithValue(openapi_types.Email(*u.Email)) + user.Email = nullable.NewNullableWithValue(*u.Email) } if u.AgreedAt != nil { user.AgreedAt = nullable.NewNullableWithValue(*u.AgreedAt) @@ -511,7 +511,7 @@ func convertVersionToAPI(v *repository.Eulaversion) EulaVersion { Title: v.Title, Content: v.Content, CreatedAt: v.Createdat.Time, - CreatedBy: openapi_types.Email(v.Createdby), + CreatedBy: v.Createdby, IsCurrent: v.Iscurrent, } @@ -524,7 +524,7 @@ func convertVersionToAPI(v *repository.Eulaversion) EulaVersion { version.ActivatedAt = nullable.NewNullableWithValue(v.Activatedat.Time) } if v.Activatedby != nil { - version.ActivatedBy = nullable.NewNullableWithValue(openapi_types.Email(*v.Activatedby)) + version.ActivatedBy = nullable.NewNullableWithValue(*v.Activatedby) } return version @@ -540,7 +540,7 @@ func convertAgreementToAPI(a *repository.ListEulaAgreementsRow) EulaAgreement { return EulaAgreement{ Id: openapi_types.UUID(a.ID), CognitoSubjectId: a.Cognitosubjectid, - UserEmail: openapi_types.Email(a.Useremail), + UserEmail: a.Useremail, EulaVersionId: openapi_types.UUID(a.Eulaversionid), EulaVersion: a.Eulaversionstring, AgreedAt: agreedAt, diff --git a/api/queryAPI/eulaAdminHandlers_private_test.go b/api/queryAPI/eulaAdminHandlers_private_test.go new file mode 100644 index 00000000..289e3545 --- /dev/null +++ b/api/queryAPI/eulaAdminHandlers_private_test.go @@ -0,0 +1,175 @@ +package queryapi + +import ( + "encoding/json" + "testing" + "time" + + "queryorchestration/internal/database/repository" + "queryorchestration/internal/eula" + + "github.com/google/uuid" + "github.com/jackc/pgx/v5/pgtype" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// TestConvertAgreementToAPI_NonEmailIdentifier verifies that agreements stored with +// non-email identifiers (username or subject ID fallbacks) serialize to JSON without error. +// This is the fix for bug #1: non-email userEmail values caused 500 on GET /admin/eula/agreements. +func TestConvertAgreementToAPI_NonEmailIdentifier(t *testing.T) { + tests := []struct { + name string + userEmail string + }{ + {name: "valid email", userEmail: "user@example.com"}, + {name: "username fallback", userEmail: "accessuser"}, + {name: "subject ID fallback", userEmail: "a1b2c3d4-e5f6-7890-abcd-ef1234567890"}, + {name: "empty string", userEmail: ""}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + row := &repository.ListEulaAgreementsRow{ + ID: uuid.New(), + Cognitosubjectid: "sub-123", + Useremail: tt.userEmail, + Eulaversionid: uuid.New(), + Agreedat: pgtype.Timestamptz{Time: time.Now(), Valid: true}, + Agreedfromip: "192.168.1.1", + Eulaversionstring: "1.0", + } + + result := convertAgreementToAPI(row) + + assert.Equal(t, tt.userEmail, string(result.UserEmail)) + + // The critical check: JSON marshal must not fail + data, err := json.Marshal(result) + require.NoError(t, err, "JSON marshal failed for userEmail=%q", tt.userEmail) + assert.Contains(t, string(data), `"userEmail"`) + }) + } +} + +// TestConvertVersionToAPI_NonEmailCreatedBy verifies that versions with non-email +// createdBy/activatedBy values serialize to JSON without error. +// This is the fix for bug #2: non-email createdBy/activatedBy caused 500 on GET /admin/eula. +func TestConvertVersionToAPI_NonEmailCreatedBy(t *testing.T) { + tests := []struct { + name string + createdBy string + activatedBy *string + }{ + {name: "valid emails", createdBy: "admin@example.com", activatedBy: strPtr("admin@example.com")}, + {name: "username createdBy", createdBy: "adminuser", activatedBy: nil}, + {name: "subject createdBy", createdBy: "a1b2c3d4-e5f6-7890", activatedBy: nil}, + {name: "username activatedBy", createdBy: "admin@test.com", activatedBy: strPtr("adminuser")}, + {name: "both non-email", createdBy: "sysadmin", activatedBy: strPtr("opsuser")}, + {name: "empty createdBy", createdBy: "", activatedBy: nil}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + version := &repository.Eulaversion{ + ID: uuid.New(), + Version: "1.0", + Title: "Test EULA", + Content: "# Test content", + Createdat: pgtype.Timestamptz{Time: time.Now(), Valid: true}, + Createdby: tt.createdBy, + Iscurrent: true, + } + + if tt.activatedBy != nil { + version.Activatedat = pgtype.Timestamptz{Time: time.Now(), Valid: true} + version.Activatedby = tt.activatedBy + } + + result := convertVersionToAPI(version) + + assert.Equal(t, tt.createdBy, string(result.CreatedBy)) + + // The critical check: JSON marshal must not fail + data, err := json.Marshal(result) + require.NoError(t, err, "JSON marshal failed for createdBy=%q, activatedBy=%v", tt.createdBy, tt.activatedBy) + assert.Contains(t, string(data), `"createdBy"`) + + if tt.activatedBy != nil { + assert.Contains(t, string(data), `"activatedBy"`) + } + }) + } +} + +// TestConvertComplianceReportToAPI_NonEmailIdentifiers verifies that compliance reports +// with non-email user identifiers serialize to JSON without error. +// This is the fix for bug #3: non-email currentEmail/email from Cognito caused 500 +// on GET /admin/eula/compliance. +func TestConvertComplianceReportToAPI_NonEmailIdentifiers(t *testing.T) { + tests := []struct { + name string + currentEmail string + email *string + }{ + {name: "valid emails", currentEmail: "user@example.com", email: strPtr("user@example.com")}, + {name: "username currentEmail", currentEmail: "accessuser", email: strPtr("accessuser")}, + {name: "empty currentEmail", currentEmail: "", email: nil}, + {name: "subject ID currentEmail", currentEmail: "a1b2c3d4-e5f6-7890", email: strPtr("a1b2c3d4-e5f6-7890")}, + {name: "mixed email and username", currentEmail: "user@example.com", email: strPtr("oldusername")}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + agreedAt := time.Now() + ip := "10.0.0.1" + user := eula.ComplianceReportUser{ + CognitoSubjectID: "sub-" + uuid.New().String()[:8], + CurrentEmail: tt.currentEmail, + Agreed: tt.email != nil, + } + if tt.email != nil { + user.Email = tt.email + user.AgreedAt = &agreedAt + user.AgreedFromIP = &ip + } + + result := &eula.ComplianceReportResult{ + Version: &repository.Eulaversion{ + ID: uuid.New(), + Version: "1.0", + Title: "Test EULA", + Content: "# Test", + Createdat: pgtype.Timestamptz{Time: time.Now(), Valid: true}, + Createdby: "admin@test.com", + Iscurrent: true, + }, + Summary: eula.ComplianceReportSummary{ + TotalUsers: 1, + AgreedCount: 1, + NotAgreedCount: 0, + CompliancePercentage: 100.0, + }, + Users: []eula.ComplianceReportUser{user}, + Page: 1, + PageSize: 50, + TotalPages: 1, + TotalItems: 1, + } + + report := convertComplianceReportToAPI(result) + + assert.Equal(t, tt.currentEmail, string(report.Users[0].CurrentEmail)) + + // The critical check: JSON marshal must not fail + data, err := json.Marshal(report) + require.NoError(t, err, "JSON marshal failed for currentEmail=%q, email=%v", tt.currentEmail, tt.email) + assert.Contains(t, string(data), `"currentEmail"`) + }) + } +} + +// strPtr returns a pointer to the given string. +func strPtr(s string) *string { + return &s +} diff --git a/api/queryAPI/eulaHandlers_test.go b/api/queryAPI/eulaHandlers_test.go index 3f11bbd4..9356c421 100644 --- a/api/queryAPI/eulaHandlers_test.go +++ b/api/queryAPI/eulaHandlers_test.go @@ -522,6 +522,172 @@ func TestListEulaAgreements(t *testing.T) { }) } +// TestListEulaAgreements_NonEmailIdentifier verifies that GET /admin/eula/agreements +// returns 200 (not 500) when agreements contain non-email user identifiers. +// Reproduces the bug where username/subject ID fallback values in userEmail caused +// JSON serialization failure via openapi_types.Email strict validation. +func TestListEulaAgreements_NonEmailIdentifier(t *testing.T) { + cfg := &ControllerConfig{} + test.CreateDB(t, cfg) + initializeTestConfig(t, cfg) + + svc := createControllerServices(cfg) + cons := queryapi.NewControllers(svc, cfg) + + versionID := createTestEulaVersion(t, svc.Eula, "nonemail-agree-"+uuid.New().String()[:8], "Non-Email Test", true) + + // Record agreements with non-email identifiers (simulating username/subject fallback) + nonEmailIdentifiers := []string{"accessuser", "bare-subject-id-12345", "admin_username"} + for _, identifier := range nonEmailIdentifiers { + _, err := svc.Eula.RecordAgreement(t.Context(), &eula.RecordAgreementInput{ + CognitoSubjectID: "user-" + uuid.New().String()[:8], + UserEmail: identifier, + EulaVersionID: *versionID, + IPAddress: "192.168.1.1", + }) + require.NoError(t, err) + } + + t.Run("returns 200 with non-email userEmail values", func(t *testing.T) { + ctx, rec := setupEulaTestContext(http.MethodGet, "/admin/eula/agreements", nil) + + page := int32(1) + pageSize := int32(50) + params := queryapi.ListEulaAgreementsParams{ + Page: &page, + PageSize: &pageSize, + } + + err := cons.ListEulaAgreements(ctx, params) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, rec.Code, "expected 200 OK, got %d: %s", rec.Code, rec.Body.String()) + + var response queryapi.EulaAgreementList + err = json.Unmarshal(rec.Body.Bytes(), &response) + require.NoError(t, err) + + // Verify non-email identifiers are present in the response + foundIdentifiers := make(map[string]bool) + for _, a := range response.Agreements { + foundIdentifiers[string(a.UserEmail)] = true + } + for _, id := range nonEmailIdentifiers { + assert.True(t, foundIdentifiers[id], "expected to find non-email identifier %q in response", id) + } + }) +} + +// TestListEulaVersions_NonEmailCreatedBy verifies that GET /admin/eula +// returns 200 (not 500) when versions have non-email createdBy/activatedBy values. +func TestListEulaVersions_NonEmailCreatedBy(t *testing.T) { + cfg := &ControllerConfig{} + test.CreateDB(t, cfg) + initializeTestConfig(t, cfg) + + svc := createControllerServices(cfg) + cons := queryapi.NewControllers(svc, cfg) + + // Create a version with a non-email createdBy (simulating username fallback) + versionStr := "nonemail-ver-" + uuid.New().String()[:8] + _, err := svc.Eula.CreateVersion(t.Context(), &eula.CreateVersionInput{ + Version: versionStr, + Title: "Non-Email CreatedBy Test", + Content: "# Test EULA\n\nContent.", + CreatedBy: "adminuser", // non-email identifier + }) + require.NoError(t, err) + + t.Run("returns 200 with non-email createdBy", func(t *testing.T) { + ctx, rec := setupEulaTestContext(http.MethodGet, "/admin/eula", nil) + + page := int32(1) + pageSize := int32(50) + params := queryapi.ListEulaVersionsParams{ + Page: &page, + PageSize: &pageSize, + } + + err := cons.ListEulaVersions(ctx, params) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, rec.Code, "expected 200 OK, got %d: %s", rec.Code, rec.Body.String()) + + var response queryapi.EulaVersionList + err = json.Unmarshal(rec.Body.Bytes(), &response) + require.NoError(t, err) + + // Find our version and verify the non-email createdBy is present + found := false + for _, v := range response.Versions { + if v.Version == versionStr { + assert.Equal(t, "adminuser", string(v.CreatedBy)) + found = true + break + } + } + assert.True(t, found, "expected to find version %q in response", versionStr) + }) +} + +// TestActivateEulaVersion_NonEmailActivatedBy verifies that activating a version +// with a non-email admin identifier doesn't cause serialization errors when +// the version is later retrieved. +func TestActivateEulaVersion_NonEmailActivatedBy(t *testing.T) { + cfg := &ControllerConfig{} + test.CreateDB(t, cfg) + initializeTestConfig(t, cfg) + + svc := createControllerServices(cfg) + cons := queryapi.NewControllers(svc, cfg) + + // Create version with non-email createdBy + versionStr := "nonemail-act-" + uuid.New().String()[:8] + created, err := svc.Eula.CreateVersion(t.Context(), &eula.CreateVersionInput{ + Version: versionStr, + Title: "Non-Email ActivatedBy Test", + Content: "# Test EULA\n\nContent.", + CreatedBy: "sysadmin", + }) + require.NoError(t, err) + + // Activate with non-email identifier + _, err = svc.Eula.ActivateVersion(t.Context(), created.ID, "opsadmin") + require.NoError(t, err) + + t.Run("returns 200 listing version with non-email activatedBy", func(t *testing.T) { + ctx, rec := setupEulaTestContext(http.MethodGet, "/admin/eula", nil) + + page := int32(1) + pageSize := int32(50) + params := queryapi.ListEulaVersionsParams{ + Page: &page, + PageSize: &pageSize, + } + + err := cons.ListEulaVersions(ctx, params) + require.NoError(t, err) + assert.Equal(t, http.StatusOK, rec.Code, "expected 200 OK, got %d: %s", rec.Code, rec.Body.String()) + + var response queryapi.EulaVersionList + err = json.Unmarshal(rec.Body.Bytes(), &response) + require.NoError(t, err) + + // Find our version and verify the non-email activatedBy is present + found := false + for _, v := range response.Versions { + if v.Version == versionStr { + assert.Equal(t, "sysadmin", string(v.CreatedBy)) + assert.True(t, v.ActivatedBy.IsSpecified(), "activatedBy should be set") + val, err := v.ActivatedBy.Get() + require.NoError(t, err) + assert.Equal(t, "opsadmin", string(val)) + found = true + break + } + } + assert.True(t, found, "expected to find version %q in response", versionStr) + }) +} + func TestGetEulaCompliance(t *testing.T) { // This test requires real Cognito access - opt-in via env var if os.Getenv("ENABLE_COGNITO_INTEGRATION_TESTS") != "true" { diff --git a/pkg/queryAPI/api.gen.go b/pkg/queryAPI/api.gen.go index 49a281d4..0f738113 100644 --- a/pkg/queryAPI/api.gen.go +++ b/pkg/queryAPI/api.gen.go @@ -654,8 +654,8 @@ type EulaAgreement struct { // Id Agreement record ID Id openapi_types.UUID `json:"id"` - // UserEmail User's email at time of agreement - UserEmail openapi_types.Email `json:"userEmail"` + // UserEmail User identifier at time of agreement (email, username, or subject ID) + UserEmail string `json:"userEmail"` } // EulaAgreementList Paginated list of EULA agreements @@ -750,11 +750,11 @@ type EulaComplianceUser struct { // CognitoSubjectId User's Cognito subject ID CognitoSubjectId string `json:"cognitoSubjectId"` - // CurrentEmail User's current email from Cognito - CurrentEmail openapi_types.Email `json:"currentEmail"` + // CurrentEmail User's current email from Cognito (may be empty or a non-email identifier) + CurrentEmail string `json:"currentEmail"` - // Email User's email at time of agreement (null if not agreed) - Email nullable.Nullable[openapi_types.Email] `json:"email,omitempty"` + // Email User identifier at time of agreement (email, username, or subject ID; null if not agreed) + Email nullable.Nullable[string] `json:"email,omitempty"` } // EulaPublicResponse Public response for current EULA version @@ -798,8 +798,8 @@ type EulaVersion struct { // ActivatedAt When this version was activated ActivatedAt nullable.Nullable[time.Time] `json:"activatedAt,omitempty"` - // ActivatedBy Email of admin who activated this version - ActivatedBy nullable.Nullable[openapi_types.Email] `json:"activatedBy,omitempty"` + // ActivatedBy Identifier of admin who activated this version (email, username, or subject ID) + ActivatedBy nullable.Nullable[string] `json:"activatedBy,omitempty"` // Content Full EULA text in Markdown format Content string `json:"content"` @@ -807,8 +807,8 @@ type EulaVersion struct { // CreatedAt Creation timestamp CreatedAt time.Time `json:"createdAt"` - // CreatedBy Email of admin who created this version - CreatedBy openapi_types.Email `json:"createdBy"` + // CreatedBy Identifier of admin who created this version (email, username, or subject ID) + CreatedBy string `json:"createdBy"` // EffectiveDate When this version becomes effective (optional) EffectiveDate nullable.Nullable[time.Time] `json:"effectiveDate,omitempty"` diff --git a/serviceAPIs/queryAPI.yaml b/serviceAPIs/queryAPI.yaml index f1caa235..405c8a45 100644 --- a/serviceAPIs/queryAPI.yaml +++ b/serviceAPIs/queryAPI.yaml @@ -4316,9 +4316,8 @@ components: example: "2025-12-15T10:30:00Z" createdBy: type: string - format: email maxLength: 320 - description: Email of admin who created this version + description: Identifier of admin who created this version (email, username, or subject ID) example: admin@example.com isCurrent: type: boolean @@ -4333,10 +4332,9 @@ components: example: "2026-01-01T00:00:00Z" activatedBy: type: string - format: email maxLength: 320 nullable: true - description: Email of admin who activated this version + description: Identifier of admin who activated this version (email, username, or subject ID) example: admin@example.com EulaVersionSummary: @@ -4607,9 +4605,8 @@ components: example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" userEmail: type: string - format: email maxLength: 320 - description: User's email at time of agreement + description: User identifier at time of agreement (email, username, or subject ID) example: user@example.com eulaVersionId: type: string @@ -4756,16 +4753,14 @@ components: example: "a1b2c3d4-e5f6-7890-abcd-ef1234567890" email: type: string - format: email maxLength: 320 nullable: true - description: User's email at time of agreement (null if not agreed) + description: User identifier at time of agreement (email, username, or subject ID; null if not agreed) example: user@example.com currentEmail: type: string - format: email maxLength: 320 - description: User's current email from Cognito + description: User's current email from Cognito (may be empty or a non-email identifier) example: user-updated@example.com agreed: type: boolean