fix(customer-portal): CORS, case-search wire format, and case-type handling - #1378
Conversation
GET /projects/{id}/cases/stats validated caseTypes as UUIDs and converted
them via uuidsToSysids -- a straight bug, since caseTypes is this service's
own domain vocabulary (case/service_request/security_report_analysis/
announcement/engagement), not an ID of anything. Fixed to validate and
translate the same way case search's type filter already does.
Separately, "default_case" -- ServiceNow's own raw caseType wire value, and
the value the production customer-portal frontend actually sends -- was
being rejected outright: this service's own domain vocabulary uses "case"
instead (tied to the Postgres case_type_enum), an inconsistency versus every
other case type value, which maps 1:1 between the domain and SN layers.
Added "default_case" as an explicit input alias, normalized to "case" before
validation/translation/the Postgres repository ever see it, at every entry
point that accepts a case type: case search filters, case creation (both
data sources), and the case-stats endpoint.
Also fixed validCaseType and the case-search group-by bucket list, both of
which were missing "announcement" despite the Postgres enum and openapi.yaml
already documenting it as a valid 5th case type.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…at in backend-v2
- Add CORS middleware, outermost in the chain (wraps Auth). There was no CORS
handling at all; a preflight OPTIONS request carries no JWT, so Auth was
rejecting every preflight with 401 before the browser ever saw a CORS
header -- which the browser reports as "blocked by CORS policy", masking
the real cause. Deliberately never sets Access-Control-Allow-Credentials:
this backend authenticates via a caller-supplied header, never cookies, so
reflecting any Origin back is safe only as long as that stays true.
- Surface entity-service's own validation message on a 400 instead of a
generic string. internal/entity/client.go was putting entity-service's raw
{"code","message"} JSON blob into apiErr.Body instead of extracting just
the message (unlike the registry/usermanagement clients); fixed via a
shared newUpstreamError helper. Logs now include the exact upstream reason.
- Rebuild POST /cases/search's request handling: entity-service redesigned
its case-search filters into a generic predicate array and now rejects the
old named-field shape outright. The frontend was never updated, so
dto.CaseSearchRequest keeps the old shape as this backend's own stable
contract, and dto.BuildEntitySearchCasesRequest translates it into
entity-service's current contract -- the same dto-layer pattern already
used for every response, just applied to this one request.
- Split the product-consumption service's two independently-configurable
upstream base URLs (subscription/license vs usage-tracking) instead of
treating them as one; renamed PRODUCT_CONSUMPTION_BASE_URL to
PRODUCT_CONSUMPTION_SUBSCRIPTION_URL to match.
- Resolve a route ambiguity that panicked the server at startup: two case
search filter routes could not both be registered as literal
net/http.ServeMux patterns; merged under one wildcard pattern with manual
dispatch.
- Remove internal comparisons to the app this replaces from comments/docs
throughout; state each behavior/rationale directly as this backend's own
design.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 42 minutes You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (1)
📝 WalkthroughWalkthroughThe PR updates customer-portal configuration, CORS handling, case-search translation, upstream error propagation, and API documentation. It also normalizes entity-service case-type aliases and updates ServiceNow case validation and OpenAPI contracts. ChangesCustomer portal case search
Portal runtime and errors
Documentation and API descriptions
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant PortalClient
participant CasesHandler
participant CaseSearchDTO
participant EntityService
PortalClient->>CasesHandler: submit case search
CasesHandler->>CaseSearchDTO: translate named filters
CaseSearchDTO->>EntityService: send predicates, sorting, and pagination
EntityService-->>CasesHandler: return case results
CasesHandler-->>PortalClient: return portal response
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
entity-service/internal/service/case_service.go (1)
48-54: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winDon’t expose unsupported announcement creation.
announcementis invalidCaseType, sovalidateCreateCaseRequestaccepts it before the Postgres service rejects non-casetypes. Ifannouncementis only supported for search/stats, exclude it from the create whitelist or route it through a ServiceNow-only create path that already hasannouncement-specific required fields and payload handling.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@entity-service/internal/service/case_service.go` around lines 48 - 54, Update validCaseType used by validateCreateCaseRequest to prevent announcement from being accepted by the Postgres-backed create flow; remove announcement from the create whitelist unless creation is explicitly routed through an existing ServiceNow-specific path with its required fields and payload handling.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/customer-portal/backend-v2/asyncapi.yaml`:
- Around line 27-30: Update the UserMessage description to reference obtaining
conversationId from the POST /projects/{id}/conversations creation response or
an existing-conversation lookup, removing the inaccurate reference to the
message POST endpoint while preserving the requirement that conversationId is
provided.
In `@apps/customer-portal/backend-v2/internal/dto/conversation.go`:
- Around line 72-75: Update the conversation state comment near the
status-to-entity mapping to use “abandoned” and “closed” and accurately describe
the map’s enum-string values rather than numeric IDs. If numeric lookup IDs
remain relevant, document them separately; otherwise remove that reference.
In `@apps/customer-portal/backend-v2/internal/handler/response.go`:
- Around line 118-129: The summarizeErr function currently logs apiErr.Body for
every apierror.Error, exposing truncated raw fallback response bodies. Update
summarizeErr to include the body only for the documented entity-service
validation message shape, and otherwise return a summary containing only the
upstream status; preserve the existing generic fallback for non-apierror values.
In `@entity-service/openapi.yaml`:
- Around line 651-659: Add default_case to both OpenAPI enum definitions: the
caseTypes query parameter at entity-service/openapi.yaml lines 651-659 and
CreateCaseRequest.type at lines 6013-6024. Keep both enums synchronized with
runtime validation and the supported portal alias.
- Around line 6014-6024: The CreateCaseRequest attachments schema currently
requires at least one item, conflicting with the optional attachments behavior
for security_report_analysis. Update the attachments definition in
CreateCaseRequest to remove the minimum-item requirement and allow the property
to be omitted or provided as an empty array, while preserving its existing item
validation.
---
Outside diff comments:
In `@entity-service/internal/service/case_service.go`:
- Around line 48-54: Update validCaseType used by validateCreateCaseRequest to
prevent announcement from being accepted by the Postgres-backed create flow;
remove announcement from the create whitelist unless creation is explicitly
routed through an existing ServiceNow-specific path with its required fields and
payload handling.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 4f03b4e8-7414-43df-badc-6d7b2839b7e2
📒 Files selected for processing (57)
apps/customer-portal/backend-v2/.env.exampleapps/customer-portal/backend-v2/CLAUDE.mdapps/customer-portal/backend-v2/README.mdapps/customer-portal/backend-v2/asyncapi.yamlapps/customer-portal/backend-v2/cmd/server/main.goapps/customer-portal/backend-v2/internal/aichatagent/client.goapps/customer-portal/backend-v2/internal/aichatagent/types.goapps/customer-portal/backend-v2/internal/aichatagent/ws.goapps/customer-portal/backend-v2/internal/dto/attachment.goapps/customer-portal/backend-v2/internal/dto/case.goapps/customer-portal/backend-v2/internal/dto/case_feedback.goapps/customer-portal/backend-v2/internal/dto/case_test.goapps/customer-portal/backend-v2/internal/dto/case_time_cards.goapps/customer-portal/backend-v2/internal/dto/contacts.goapps/customer-portal/backend-v2/internal/dto/conversation.goapps/customer-portal/backend-v2/internal/dto/deployed_product_metrics.goapps/customer-portal/backend-v2/internal/dto/escalation.goapps/customer-portal/backend-v2/internal/dto/global.goapps/customer-portal/backend-v2/internal/dto/instance.goapps/customer-portal/backend-v2/internal/dto/project_stats.goapps/customer-portal/backend-v2/internal/dto/registry.goapps/customer-portal/backend-v2/internal/dto/time_card.goapps/customer-portal/backend-v2/internal/dto/user.goapps/customer-portal/backend-v2/internal/entity/client.goapps/customer-portal/backend-v2/internal/entity/client_test.goapps/customer-portal/backend-v2/internal/entity/types.goapps/customer-portal/backend-v2/internal/handler/ai_chat.goapps/customer-portal/backend-v2/internal/handler/cases.goapps/customer-portal/backend-v2/internal/handler/contacts.goapps/customer-portal/backend-v2/internal/handler/deployed_products.goapps/customer-portal/backend-v2/internal/handler/instances.goapps/customer-portal/backend-v2/internal/handler/product_consumption.goapps/customer-portal/backend-v2/internal/handler/project_stats.goapps/customer-portal/backend-v2/internal/handler/registry.goapps/customer-portal/backend-v2/internal/handler/response.goapps/customer-portal/backend-v2/internal/handler/response_test.goapps/customer-portal/backend-v2/internal/handler/time_cards.goapps/customer-portal/backend-v2/internal/handler/websocket.goapps/customer-portal/backend-v2/internal/middleware/auth.goapps/customer-portal/backend-v2/internal/middleware/cors.goapps/customer-portal/backend-v2/internal/middleware/cors_test.goapps/customer-portal/backend-v2/internal/productconsumption/client.goapps/customer-portal/backend-v2/internal/productconsumption/subscription.goapps/customer-portal/backend-v2/internal/productconsumption/types.goapps/customer-portal/backend-v2/internal/usermanagement/client.goapps/customer-portal/backend-v2/internal/usermanagement/types.goapps/customer-portal/backend-v2/internal/usermanagement/usermanagement.goapps/customer-portal/backend-v2/openapi.yamlentity-service/CLAUDE.mdentity-service/internal/service/case_filters.goentity-service/internal/service/case_filters_test.goentity-service/internal/service/case_service.goentity-service/internal/service/sn_case_service.goentity-service/internal/service/sn_case_service_create_test.goentity-service/internal/service/sn_project_stats_service.goentity-service/internal/service/sn_project_stats_service_test.goentity-service/openapi.yaml
…gs on PR wso2-open-operations#1378 - asyncapi.yaml: fix a stale UserMessage.conversationId description pointing at the follow-up-message endpoint (which requires conversationId, not returns one) instead of the actual conversation-creation endpoint. - dto/conversation.go: fix "abandonded"/"close" typos and remove a numeric state-ID list that described a different layer's (entity-service's SN translation) internal representation, not this map's actual string values. - entity/client.go: newUpstreamError no longer falls back to a raw upstream body excerpt when the response isn't the documented {"message":...} shape -- Body is left empty instead, so summarizeErr's logs and mapUpstreamError's 400 passthrough can never surface unbounded, non-message upstream content. - entity-service/openapi.yaml: add the default_case alias to both the caseTypes query param and CreateCaseRequest.type enums (the description already documented it as accepted, but the enum itself rejected it); drop attachments' minItems: 1 to match security_report_analysis's actual optional-attachments validation. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR fixes several integration mismatches between the customer-portal backend-v2 and entity-service, focusing on (1) case-type vocabulary/alias handling, (2) adapting to entity-service’s new case-search filter wire format, and (3) enabling browser calls via CORS while improving upstream error surfacing.
Changes:
- entity-service: Treat case types as domain enums (not UUIDs), add
default_casealias normalization, and include missingannouncementin validation/group-by/OpenAPI. - customer-portal backend-v2: Add CORS middleware and translate the portal’s stable
POST /cases/searchrequest shape into entity-service’s predicate-array contract. - customer-portal backend-v2: Improve upstream error extraction/logging, split product-consumption base URLs, and fix a ServeMux route ambiguity.
Reviewed changes
Copilot reviewed 57 out of 57 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| entity-service/openapi.yaml | Document expanded case-type enums and alias. |
| entity-service/internal/service/sn_project_stats_service.go | Fix caseTypes validation/mapping to domain vocabulary. |
| entity-service/internal/service/sn_project_stats_service_test.go | Regression tests for stats caseTypes behavior. |
| entity-service/internal/service/sn_case_service.go | Add announcement group-by value; normalize type validation call. |
| entity-service/internal/service/sn_case_service_create_test.go | Test default_case alias create-case behavior. |
| entity-service/internal/service/case_service.go | Add alias map + normalization; include announcement. |
| entity-service/internal/service/case_filters.go | Normalize type filter values (alias handling). |
| entity-service/internal/service/case_filters_test.go | Test default_case alias normalization in filters. |
| entity-service/CLAUDE.md | Document enum-alias normalization convention. |
| apps/customer-portal/backend-v2/README.md | Update docs for product-consumption env var rename. |
| apps/customer-portal/backend-v2/openapi.yaml | Remove legacy “Ballerina reference” phrasing in docs. |
| apps/customer-portal/backend-v2/internal/usermanagement/usermanagement.go | Doc cleanup around upstream error passthrough. |
| apps/customer-portal/backend-v2/internal/usermanagement/types.go | Doc cleanup for role conversion helpers. |
| apps/customer-portal/backend-v2/internal/usermanagement/client.go | Doc cleanup for error mapping behavior. |
| apps/customer-portal/backend-v2/internal/productconsumption/types.go | Doc cleanup for wire-format mirroring. |
| apps/customer-portal/backend-v2/internal/productconsumption/subscription.go | Doc cleanup for text payload expectation. |
| apps/customer-portal/backend-v2/internal/productconsumption/client.go | Rename config fields; split subscription vs tracking base URLs. |
| apps/customer-portal/backend-v2/internal/middleware/cors.go | Add CORS middleware with preflight handling. |
| apps/customer-portal/backend-v2/internal/middleware/cors_test.go | Tests for preflight, origins, and credentials header absence. |
| apps/customer-portal/backend-v2/internal/middleware/auth.go | Clarify JWT claim expectations and security headers docs. |
| apps/customer-portal/backend-v2/internal/handler/websocket.go | Doc cleanup re: “wire compatibility” wording. |
| apps/customer-portal/backend-v2/internal/handler/time_cards.go | Doc cleanup for read-only time-card behavior. |
| apps/customer-portal/backend-v2/internal/handler/response.go | Pass through upstream 400 message; enhance log summarization. |
| apps/customer-portal/backend-v2/internal/handler/response_test.go | Tests for 400 pass-through and summarizeErr behavior. |
| apps/customer-portal/backend-v2/internal/handler/registry.go | Doc cleanup for upstream message passthrough and tokenID. |
| apps/customer-portal/backend-v2/internal/handler/project_stats.go | Doc cleanup re: graceful degradation behavior. |
| apps/customer-portal/backend-v2/internal/handler/product_consumption.go | Doc cleanup for authorization gate behavior. |
| apps/customer-portal/backend-v2/internal/handler/instances.go | Doc cleanup for fan-out + DataSource asymmetry. |
| apps/customer-portal/backend-v2/internal/handler/deployed_products.go | Doc cleanup for date range validation and path quirks. |
| apps/customer-portal/backend-v2/internal/handler/contacts.go | Doc cleanup for fixed success response shape. |
| apps/customer-portal/backend-v2/internal/handler/cases.go | Switch search request DTO + translate to entity predicate filters. |
| apps/customer-portal/backend-v2/internal/handler/ai_chat.go | Doc cleanup for endpoint limitations/error handling. |
| apps/customer-portal/backend-v2/internal/entity/types.go | Update entity client types to predicate-array filter contract. |
| apps/customer-portal/backend-v2/internal/entity/client.go | Extract entity-service message field into apierror.Body. |
| apps/customer-portal/backend-v2/internal/entity/client_test.go | Tests for upstream error message extraction behavior. |
| apps/customer-portal/backend-v2/internal/dto/user.go | Doc cleanup for DTO purpose statement. |
| apps/customer-portal/backend-v2/internal/dto/time_card.go | Doc cleanup for hidden fields rationale. |
| apps/customer-portal/backend-v2/internal/dto/registry.go | Doc cleanup for registry naming constraints. |
| apps/customer-portal/backend-v2/internal/dto/project_stats.go | Doc cleanup for mapping/graceful degradation/state IDs. |
| apps/customer-portal/backend-v2/internal/dto/instance.go | Doc cleanup for mapping and DataSource forwarding notes. |
| apps/customer-portal/backend-v2/internal/dto/global.go | Doc cleanup for feature flags and request translation. |
| apps/customer-portal/backend-v2/internal/dto/escalation.go | Doc cleanup for action validation and request injection. |
| apps/customer-portal/backend-v2/internal/dto/deployed_product_metrics.go | Doc cleanup for date-range helpers and response trimming. |
| apps/customer-portal/backend-v2/internal/dto/conversation.go | Doc cleanup for state mapping description. |
| apps/customer-portal/backend-v2/internal/dto/contacts.go | Doc cleanup for email validation constraint statement. |
| apps/customer-portal/backend-v2/internal/dto/case.go | Add portal case-search DTO + translation to entity predicates. |
| apps/customer-portal/backend-v2/internal/dto/case_time_cards.go | Doc cleanup; clarify grouping behavior. |
| apps/customer-portal/backend-v2/internal/dto/case_test.go | Tests for case-search predicate translation. |
| apps/customer-portal/backend-v2/internal/dto/case_feedback.go | Doc cleanup for trimmed emoji mapping. |
| apps/customer-portal/backend-v2/internal/dto/attachment.go | Doc cleanup for passthrough shapes and injection behavior. |
| apps/customer-portal/backend-v2/internal/aichatagent/ws.go | Doc cleanup for WS OAuth2 configuration separation. |
| apps/customer-portal/backend-v2/internal/aichatagent/types.go | Doc cleanup for wire-format mirroring. |
| apps/customer-portal/backend-v2/internal/aichatagent/client.go | Doc cleanup for service description. |
| apps/customer-portal/backend-v2/cmd/server/main.go | Add CORS outer middleware; adjust config/env vars/routes. |
| apps/customer-portal/backend-v2/CLAUDE.md | Update internal architecture docs for new behaviors. |
| apps/customer-portal/backend-v2/asyncapi.yaml | Doc updates reflecting current WebSocket constraints. |
| apps/customer-portal/backend-v2/.env.example | Rename product-consumption env var; remove WS origins var. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…Vary header
Every upstream client (registry, updates, scim, productconsumption,
aichatagent, usermanagement) built its own non-2xx apierror.Error by falling
back to a raw response excerpt when the body wasn't {"message": "..."}
shaped — the same raw-content-leak risk already fixed for the entity client.
Add apierror.NewUpstreamError as the one shared constructor (Body is the
parsed message, or empty) and route every client through it instead of
duplicating the logic. Also switch cors.go's Vary header to Add so it
doesn't clobber a Vary value a downstream handler already set.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…case creation, fix createdBy filter conflict validCaseType gaining "announcement" (for case search/stats filters) had the side effect of also letting POST /cases accept type=announcement, but no code path builds a valid payload for one: the Postgres path already rejects every type but "case", and the ServiceNow path silently drops subject/description since its payload-building switch has no case for it. Reject it explicitly instead, and stop advertising it as creatable in openapi.yaml. Separately, backend-v2's case-search filter builder appended both a createdBy+in filter (from CreatedBy) and a createdBy+eq filter (from CreatedByMe) when a client set both — entity-service's filters array is AND-only, so the two together could never both match, silently returning an empty result set. CreatedByMe now takes precedence and CreatedBy is dropped entirely when both are set. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@entity-service/openapi.yaml`:
- Around line 6013-6026: Update the CreateCaseRequest properties block to
declare engagementType alongside the engagement case type, using the exact enum
values accepted by the endpoint’s runtime validation. Ensure the field is
modeled with the same required/optional behavior and schema type as runtime
validation, so generated clients can construct valid engagement requests while
preserving existing case types.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f980c87e-f737-4024-9b4a-e12f326ff80f
📒 Files selected for processing (17)
apps/customer-portal/backend-v2/CLAUDE.mdapps/customer-portal/backend-v2/internal/aichatagent/client.goapps/customer-portal/backend-v2/internal/apierror/apierror.goapps/customer-portal/backend-v2/internal/apierror/apierror_test.goapps/customer-portal/backend-v2/internal/dto/case.goapps/customer-portal/backend-v2/internal/dto/case_test.goapps/customer-portal/backend-v2/internal/entity/client.goapps/customer-portal/backend-v2/internal/middleware/cors.goapps/customer-portal/backend-v2/internal/productconsumption/client.goapps/customer-portal/backend-v2/internal/registry/client.goapps/customer-portal/backend-v2/internal/scim/client.goapps/customer-portal/backend-v2/internal/updates/client.goapps/customer-portal/backend-v2/internal/usermanagement/client.goapps/customer-portal/backend-v2/internal/usermanagement/usermanagement.goentity-service/internal/service/case_service.goentity-service/internal/service/sn_case_service_create_test.goentity-service/openapi.yaml
🚧 Files skipped from review as they are similar to previous changes (8)
- apps/customer-portal/backend-v2/internal/aichatagent/client.go
- apps/customer-portal/backend-v2/internal/entity/client.go
- entity-service/internal/service/sn_case_service_create_test.go
- apps/customer-portal/backend-v2/internal/middleware/cors.go
- apps/customer-portal/backend-v2/internal/dto/case.go
- apps/customer-portal/backend-v2/internal/dto/case_test.go
- apps/customer-portal/backend-v2/internal/productconsumption/client.go
- apps/customer-portal/backend-v2/CLAUDE.md
The engagement case type's own description already said it requires engagementType, and the Go validation enforces it, but the OpenAPI schema never declared the property -- generated clients had no way to model it. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
8bc8550
into
wso2-open-operations:dev-app-csm-portal
Summary
entity-service
GET /projects/{id}/cases/stats'scaseTypeswas validated as UUIDs and sysid-converted — a bug, since it's this service's own domain vocabulary (case/service_request/security_report_analysis/announcement/engagement), not an ID of anything. Fixed to validate/translate the same way case search'stypefilter does.default_case(ServiceNow's raw wire value, and what the production customer-portal frontend actually sends) was rejected outright — this service's own vocabulary usescaseinstead (tied to the Postgrescase_type_enum). Addeddefault_caseas an explicit input alias, normalized tocasebefore validation/translation/the Postgres repository ever see it, at every case-type entry point (search, create, stats).validCaseTypeand the case-search group-by bucket list, both missingannouncementdespite the Postgres enum andopenapi.yamlalready documenting it as valid.customer-portal backend-v2
Authwas rejecting every preflightOPTIONSrequest with 401 (no JWT on a preflight), which browsers report as a CORS error, masking the real cause. Never setsAccess-Control-Allow-Credentials(this backend has no cookie-based session to protect).POST /cases/search: entity-service redesigned its case-search filters into a generic predicate array and now rejects the old named-field shape outright. The frontend was never updated, sodto.CaseSearchRequestkeeps the old shape as this backend's stable contract, anddto.BuildEntitySearchCasesRequesttranslates it into entity-service's current contract.PRODUCT_CONSUMPTION_BASE_URL→PRODUCT_CONSUMPTION_SUBSCRIPTION_URL.net/http.ServeMuxroute ambiguity that panicked the server at startup (two case-search-adjacent routes couldn't both register as literal patterns).Test plan
go build ./...,go vet ./...,gofmt -l .clean for bothentity-serviceandapps/customer-portal/backend-v2gosec -fmt=text ./...— 0 issues in bothgo test ./...passes in both, including new regression tests for the CORS middleware, the case-type alias, and the case-search predicate translation🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
default_case, with improved case statistics filtering.Bug Fixes
Documentation