[CSM][BE] Add project metadata and statistics endpoints - #1337
Conversation
Adds 8 more endpoints to apps/customer-portal/backend-v2 (60 total), consuming the 7 entity-service project-stats endpoints added in a companion PR (wso2-open-operations#1336): - GET /projects/{id}/filters - GET /projects/{id}/features - GET /projects/{id}/stats - GET /projects/{id}/stats/cases - GET /projects/{id}/stats/conversations - GET /projects/{id}/stats/support - GET /projects/{id}/stats/time-cards - GET /projects/{id}/stats/change-requests The mapping from entity-service's 7 endpoints to these 8 portal endpoints isn't 1:1: the Ballerina backend this is rewriting fans a handful of raw entity-service responses out into eight differently-shaped, purpose-built views rather than exposing them directly — internal/dto/project_stats.go replicates that fan-out (ported from the Ballerina backend's getProjectFilters/mapProjectFeatures/mapCaseStats/getConversationStats/ mapProjectChangeRequestStatsResponse in utils.bal), including: - /filters and /features both read GetProjectMetadata and reshape it — there is no raw metadata passthrough endpoint, matching the Ballerina backend, which never exposed one either. - /stats and /stats/support are graceful-degradation composites: each combines multiple independent entity-service calls and returns 200 even if every one of them fails, simply omitting that source's fields — matching the Ballerina backend's own behavior exactly. The other stats endpoints are single entity-service calls with normal hard-failure semantics (verified individually, not assumed from the composite endpoints' pattern). - State-ID-based derived counts (e.g. "how many cases are open") use the same default ServiceNow state IDs the Ballerina backend's own configuration defaults to — flagged in CLAUDE.md as needing to become configurable here too if cs-tools' ServiceNow instance differs. Also updates openapi.yaml (8 new paths, 15 new schemas), README.md, and CLAUDE.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 49 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. 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 (2)
📝 WalkthroughWalkthroughChangesThe backend-v2 service adds eight project metadata and statistics endpoints. It introduces entity-service contracts, DTO mappings, authenticated handlers, server routes, OpenAPI schemas, and documentation. Project Metadata and Statistics API
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant Client
participant ProjectStatsHandler
participant EntityClient
participant DTOMapper
Client->>ProjectStatsHandler: Request project dashboard statistics
ProjectStatsHandler->>EntityClient: Fetch project statistics sources
EntityClient-->>ProjectStatsHandler: Return source responses
ProjectStatsHandler->>DTOMapper: Build available dashboard fields
DTOMapper-->>ProjectStatsHandler: Return dashboard DTO
ProjectStatsHandler-->>Client: Return JSON response
Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 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 |
|
@coderabbitai review |
✅ Action performedReview finished.
|
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (5)
apps/customer-portal/backend-v2/openapi.yaml (2)
5443-5473: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
ChoiceListItemduplicatesReferenceItemexactly.Both schemas declare the same
{id, label, count?}shape and the samerequiredlist. The descriptions explain the intent, but the two definitions cannot drift apart safely. ReferenceReferenceItemfromCasesTrend.severitiesand deleteChoiceListItem, or keepChoiceListItemas anallOfofReferenceItemif the distinct name carries meaning for generated clients.🤖 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 `@apps/customer-portal/backend-v2/openapi.yaml` around lines 5443 - 5473, Remove the duplicate ChoiceListItem schema and update CasesTrend.severities to reference ReferenceItem directly. If the ChoiceListItem name is required by generated clients, replace its duplicated properties with an allOf reference to ReferenceItem while preserving the existing schema name.
385-393: 🚀 Performance & Scalability | 🔵 Trivial | 💤 Low valueConsider bounding the
caseTypesquery arrays.Checkov flags these arrays because they declare no
maxItems. The portal forwards everycaseTypesvalue to entity-service as a separate query parameter. An unbounded list produces an unbounded upstream query string. Add amaxItemslimit that matches the real number of case types.♻️ Proposed change (apply to all three
caseTypesparameters)schema: type: array + maxItems: 20 items: type: stringIf you add
maxItems, also enforce the limit in the handler, because the spec alone does not reject oversized input.Also applies to: 448-456, 567-575
🤖 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 `@apps/customer-portal/backend-v2/openapi.yaml` around lines 385 - 393, Bound all three OpenAPI caseTypes array schemas with a maxItems value matching the real number of supported case types, and enforce the same limit in the corresponding request handlers before forwarding values to entity-service. Update the shared validation or handler logic used by the caseTypes query parameters so oversized arrays are rejected consistently.Source: Linters/SAST tools
apps/customer-portal/backend-v2/internal/handler/project_stats.go (2)
31-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd unit tests for the graceful-degradation paths.
The
entityProjectStatsClientinterface makes the handlers testable with a fake client. The partial-failure behavior of/statsand/stats/supportis the most valuable case to lock down, because a later refactor can silently turn a tolerated failure into a hard failure. Tests for the 401 and invalid-UUID paths are also cheap.Do you want me to generate a table-driven test file with a fake
entityProjectStatsClient?🤖 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 `@apps/customer-portal/backend-v2/internal/handler/project_stats.go` around lines 31 - 39, The project stats handlers lack coverage for tolerated and validation failures. Add table-driven unit tests using a fake entityProjectStatsClient to verify partial failures for /stats and /stats/support still return their graceful-degradation responses, plus 401 and invalid-UUID cases; keep successful behavior covered and assert status and response details.
123-149: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winBoth composite handlers issue independent upstream calls sequentially.
GetProjectDashboardStatsandGetProjectSupportStatseach call entity-service one source at a time, so response latency is the sum of all calls. Every call is independent, and every failure is already tolerated, so the calls can run concurrently.
apps/customer-portal/backend-v2/internal/handler/project_stats.go#L123-L149: run the four calls toGetProjectCaseStats,GetProjectConversationStats,GetProjectDeploymentStats, andGetProjectStatsconcurrently with async.WaitGrouporerrgroup, and let each goroutine write only its own result pointer.apps/customer-portal/backend-v2/internal/handler/project_stats.go#L221-L233: run the two calls toGetProjectCaseStatsandGetProjectConversationStatsconcurrently using the same pattern.🤖 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 `@apps/customer-portal/backend-v2/internal/handler/project_stats.go` around lines 123 - 149, In apps/customer-portal/backend-v2/internal/handler/project_stats.go lines 123-149, update the composite handler around GetProjectDashboardStats to run GetProjectCaseStats, GetProjectConversationStats, GetProjectDeploymentStats, and GetProjectStats concurrently using a sync.WaitGroup or errgroup, with each goroutine writing only its own result pointer while preserving existing error logging and tolerated failures. In the same file lines 221-233, apply the same concurrency pattern to the GetProjectCaseStats and GetProjectConversationStats calls in GetProjectSupportStats.apps/customer-portal/backend-v2/README.md (1)
198-222: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUpdate the
internal/entity/projects.goentry in the same structure list.This PR adds new DTO and handler entries here, but the
entity/projects.goline in the same tree still lists onlySearchProjects, GetProject. That file now also exposesGetProjectMetadata,GetProjectStats,GetProjectCaseStats,GetProjectConversationStats,GetProjectDeploymentStats,GetProjectTimeCardStats, andGetProjectChangeRequestStats. Extend that line so the structure section stays accurate.🤖 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 `@apps/customer-portal/backend-v2/README.md` around lines 198 - 222, Update the internal/entity/projects.go entry in the README structure list to include GetProjectMetadata, GetProjectStats, GetProjectCaseStats, GetProjectConversationStats, GetProjectDeploymentStats, GetProjectTimeCardStats, and GetProjectChangeRequestStats alongside SearchProjects and GetProject, preserving the existing list format.
🤖 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/CLAUDE.md`:
- Around line 184-192: Update the endpoint-count statement in the project
statistics documentation to say seven entity-service endpoints, preserving the
listed calls: GetProjectMetadata, GetProjectCaseStats,
GetProjectConversationStats, GetProjectDeploymentStats, GetProjectStats,
GetProjectTimeCardStats, and GetProjectChangeRequestStats.
In `@apps/customer-portal/backend-v2/internal/dto/project_stats.go`:
- Around line 250-265: Introduce portal-owned DTO types for
ResolvedCountBreakdown, ProjectCaseStatsChangeRate, and CasesTrend, including
portal-owned nested fields that map choice-list items through
mapChoiceListItems. Update ProjectCaseStats and ProjectChangeRequestStats, plus
their existing mapping logic, to use and populate these DTOs instead of
entity.ResolvedCountBreakdown, entity.ProjectCaseStatsChangeRate, and
[]entity.CasesTrend; follow the established ProjectTimeCardStats convention and
preserve the current JSON shape.
---
Nitpick comments:
In `@apps/customer-portal/backend-v2/internal/handler/project_stats.go`:
- Around line 31-39: The project stats handlers lack coverage for tolerated and
validation failures. Add table-driven unit tests using a fake
entityProjectStatsClient to verify partial failures for /stats and
/stats/support still return their graceful-degradation responses, plus 401 and
invalid-UUID cases; keep successful behavior covered and assert status and
response details.
- Around line 123-149: In
apps/customer-portal/backend-v2/internal/handler/project_stats.go lines 123-149,
update the composite handler around GetProjectDashboardStats to run
GetProjectCaseStats, GetProjectConversationStats, GetProjectDeploymentStats, and
GetProjectStats concurrently using a sync.WaitGroup or errgroup, with each
goroutine writing only its own result pointer while preserving existing error
logging and tolerated failures. In the same file lines 221-233, apply the same
concurrency pattern to the GetProjectCaseStats and GetProjectConversationStats
calls in GetProjectSupportStats.
In `@apps/customer-portal/backend-v2/openapi.yaml`:
- Around line 5443-5473: Remove the duplicate ChoiceListItem schema and update
CasesTrend.severities to reference ReferenceItem directly. If the ChoiceListItem
name is required by generated clients, replace its duplicated properties with an
allOf reference to ReferenceItem while preserving the existing schema name.
- Around line 385-393: Bound all three OpenAPI caseTypes array schemas with a
maxItems value matching the real number of supported case types, and enforce the
same limit in the corresponding request handlers before forwarding values to
entity-service. Update the shared validation or handler logic used by the
caseTypes query parameters so oversized arrays are rejected consistently.
In `@apps/customer-portal/backend-v2/README.md`:
- Around line 198-222: Update the internal/entity/projects.go entry in the
README structure list to include GetProjectMetadata, GetProjectStats,
GetProjectCaseStats, GetProjectConversationStats, GetProjectDeploymentStats,
GetProjectTimeCardStats, and GetProjectChangeRequestStats alongside
SearchProjects and GetProject, preserving the existing list format.
🪄 Autofix (Beta)
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: cada168b-7602-434f-b189-6317a4fd28fc
📒 Files selected for processing (8)
apps/customer-portal/backend-v2/CLAUDE.mdapps/customer-portal/backend-v2/README.mdapps/customer-portal/backend-v2/cmd/server/main.goapps/customer-portal/backend-v2/internal/dto/project_stats.goapps/customer-portal/backend-v2/internal/entity/projects.goapps/customer-portal/backend-v2/internal/entity/types.goapps/customer-portal/backend-v2/internal/handler/project_stats.goapps/customer-portal/backend-v2/openapi.yaml
- Introduce portal-owned ResolvedCountBreakdown, CaseStatsChangeRate, and CasesTrend types in internal/dto/project_stats.go instead of embedding entity-service's response structs directly in ProjectCaseStats and ProjectChangeRequestStats — matches this package's "always map through dto" convention, and stops entity.CasesTrend.Severities from bypassing mapChoiceListItems. - Fix CLAUDE.md's endpoint-count claim: these 8 routes read from seven entity-service endpoints, not three.
2f977db
into
wso2-open-operations:dev-app-csm-portal
Summary
Adds 8 more endpoints to
apps/customer-portal/backend-v2(60 total), consuming the 7 entity-service project-stats endpoints added in a companion PR (#1336, currently open):GET /projects/{id}/filtersGET /projects/{id}/featuresGET /projects/{id}/statsGET /projects/{id}/stats/casesGET /projects/{id}/stats/conversationsGET /projects/{id}/stats/supportGET /projects/{id}/stats/time-cardsGET /projects/{id}/stats/change-requestsNot a 1:1 mapping
entity-service's 7 endpoints fan out into these 8 differently-shaped, purpose-built portal views —
internal/dto/project_stats.goreplicates the Ballerina backend's own reshaping (ported fromgetProjectFilters/mapProjectFeatures/mapCaseStats/getConversationStats/mapProjectChangeRequestStatsResponseinutils.bal), including:/filtersand/featuresboth readGetProjectMetadataand reshape it differently — there is no raw metadata-passthrough endpoint in this backend, matching the Ballerina backend, which never exposed one either./statsand/stats/supportare graceful-degradation composites — each combines multiple independent entity-service calls (case/conversation/deployment/activity stats, and case/conversation stats respectively) and returns200even if every one of them fails, simply omitting that source's fields from the response. This exactly matches the Ballerina backend's own behavior. The other four stats endpoints are single entity-service calls with normal hard-failure semantics — verified individually per-endpoint in the Ballerina backend's source, not assumed from the composite endpoints' pattern.dto.caseStateIDOpenand theconversationStateID*constants pick specific counts out of a state-count breakdown (e.g. "how many cases are open") using the same default ServiceNow state IDs the Ballerina backend's own configuration defaults to. Flagged in CLAUDE.md: ifcs-tools' ServiceNow instance uses different state IDs, these need to become configurable here too./stats/casesfetches change-request stats and never uses the result (apparent dead code) — not replicated here.Also updates
openapi.yaml(8 new paths, 15 new schemas),README.md, andCLAUDE.md.Test plan
go build ./...,go vet ./...,gofmt -l .all cleangosec -fmt=text ./...reports 0 issuesopenapi.yamlvalidated as well-formed YAML with all 8 new paths/15 new schemas present, zero dangling$refs/statsand/stats/supportcorrectly return200with all fields omitted when every underlying source fails#1336merged first (this PR depends on it for the underlying routes to exist)🤖 Generated with Claude Code
Linked issues
Summary by CodeRabbit
New Features
Documentation