Skip to content

[CSM][BE] Add project metadata and statistics endpoints - #1337

Merged
Rashmika998 merged 2 commits into
wso2-open-operations:dev-app-csm-portalfrom
Rashmika998:feature/customer-portal-backend-v2-project-stats
Aug 3, 2026
Merged

Rashmika998 merged 2 commits into
wso2-open-operations:dev-app-csm-portalfrom
Rashmika998:feature/customer-portal-backend-v2-project-stats

Conversation

@Rashmika998

@Rashmika998 Rashmika998 commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

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}/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

Not a 1:1 mapping

entity-service's 7 endpoints fan out into these 8 differently-shaped, purpose-built portal views — internal/dto/project_stats.go replicates the Ballerina backend's own reshaping (ported from getProjectFilters/mapProjectFeatures/mapCaseStats/getConversationStats/mapProjectChangeRequestStatsResponse in utils.bal), including:

  • /filters and /features both read GetProjectMetadata and reshape it differently — there is no raw metadata-passthrough endpoint in this backend, matching the Ballerina backend, which never exposed one either.
  • /stats and /stats/support are graceful-degradation composites — each combines multiple independent entity-service calls (case/conversation/deployment/activity stats, and case/conversation stats respectively) and returns 200 even 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.
  • State-ID-based derived counts are hardcoded, not configurable — dto.caseStateIDOpen and the conversationStateID* 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: if cs-tools' ServiceNow instance uses different state IDs, these need to become configurable here too.
  • The Ballerina backend's /stats/cases fetches 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, and CLAUDE.md.

Test plan

  • go build ./..., go vet ./..., gofmt -l . all clean
  • gosec -fmt=text ./... reports 0 issues
  • openapi.yaml validated as well-formed YAML with all 8 new paths/15 new schemas present, zero dangling $refs
  • Manually smoke-tested against a live server with an unreachable upstream: the 6 hard-failure endpoints correctly map to 500; /stats and /stats/support correctly return 200 with all fields omitted when every underlying source fails
  • Wire up against a real running entity-service instance with #1336 merged first (this PR depends on it for the underlying routes to exist)

🤖 Generated with Claude Code

Linked issues

  • Closes wso2-enterprise/wso2-digital-team-project-management#902
  • Closes wso2-enterprise/wso2-digital-team-project-management#903
  • Closes wso2-enterprise/wso2-digital-team-project-management#904
  • Closes wso2-enterprise/wso2-digital-team-project-management#905
  • Closes wso2-enterprise/wso2-digital-team-project-management#906
  • Closes wso2-enterprise/wso2-digital-team-project-management#907
  • Closes wso2-enterprise/wso2-digital-team-project-management#908
  • Closes wso2-enterprise/wso2-digital-team-project-management#909

Summary by CodeRabbit

  • New Features

    • Added eight project endpoints for filters, feature flags, dashboard statistics, case and conversation metrics, support, time cards, and change requests.
    • Added project-level statistics with optional filtering and trend information.
    • Dashboard and support summaries continue to provide available results when some data sources are unavailable.
  • Documentation

    • Updated API documentation and examples to cover the new endpoints and 60 implemented routes.

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>
@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Rashmika998, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 49 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3a56d0c6-6303-4dc4-b560-646211c39c12

📥 Commits

Reviewing files that changed from the base of the PR and between 65bc7e3 and b9b02d5.

📒 Files selected for processing (2)
  • apps/customer-portal/backend-v2/CLAUDE.md
  • apps/customer-portal/backend-v2/internal/dto/project_stats.go
📝 Walkthrough

Walkthrough

Changes

The 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

Layer / File(s) Summary
Entity-service statistics contracts
internal/entity/types.go
Adds wire-format types for project metadata, features, dashboard metrics, and category-specific statistics.
Public DTO mappings
internal/dto/project_stats.go
Maps entity responses to API DTOs, filters restricted states, derives configured state counts, and supports partial composite results.
Entity-service client methods
internal/entity/projects.go
Adds typed GET methods for metadata and statistics, including optional case, creator, and date filters.
Authenticated project statistics handlers
internal/handler/project_stats.go
Adds request validation, entity-service calls, error handling, JSON responses, and partial dashboard/support responses.
Routes, schemas, and documentation
cmd/server/main.go, openapi.yaml, README.md, CLAUDE.md
Registers the endpoints and documents their schemas, query parameters, response behavior, and examples.

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
Loading

Possibly related PRs

Suggested labels: Type/New Feature, Area/Backend

Suggested reviewers: cloby99

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the implementation and test plan but omits most required template sections, including release notes, documentation, security checks, and test environment. Complete the required template sections and document release, documentation, security, environment, training, certification, marketing, samples, migrations, and learning details.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change by identifying the addition of project metadata and statistics endpoints.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Rashmika998

Copy link
Copy Markdown
Contributor Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (5)
apps/customer-portal/backend-v2/openapi.yaml (2)

5443-5473: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

ChoiceListItem duplicates ReferenceItem exactly.

Both schemas declare the same {id, label, count?} shape and the same required list. The descriptions explain the intent, but the two definitions cannot drift apart safely. Reference ReferenceItem from CasesTrend.severities and delete ChoiceListItem, or keep ChoiceListItem as an allOf of ReferenceItem if 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 value

Consider bounding the caseTypes query arrays.

Checkov flags these arrays because they declare no maxItems. The portal forwards every caseTypes value to entity-service as a separate query parameter. An unbounded list produces an unbounded upstream query string. Add a maxItems limit that matches the real number of case types.

♻️ Proposed change (apply to all three caseTypes parameters)
           schema:
             type: array
+            maxItems: 20
             items:
               type: string

If 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 win

Add unit tests for the graceful-degradation paths.

The entityProjectStatsClient interface makes the handlers testable with a fake client. The partial-failure behavior of /stats and /stats/support is 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 win

Both composite handlers issue independent upstream calls sequentially. GetProjectDashboardStats and GetProjectSupportStats each 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 to GetProjectCaseStats, GetProjectConversationStats, GetProjectDeploymentStats, and GetProjectStats concurrently with a sync.WaitGroup or errgroup, 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 to GetProjectCaseStats and GetProjectConversationStats concurrently 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 value

Update the internal/entity/projects.go entry in the same structure list.

This PR adds new DTO and handler entries here, but the entity/projects.go line in the same tree still lists only SearchProjects, GetProject. That file now also exposes GetProjectMetadata, GetProjectStats, GetProjectCaseStats, GetProjectConversationStats, GetProjectDeploymentStats, GetProjectTimeCardStats, and GetProjectChangeRequestStats. 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2ff44d6 and 65bc7e3.

📒 Files selected for processing (8)
  • apps/customer-portal/backend-v2/CLAUDE.md
  • apps/customer-portal/backend-v2/README.md
  • apps/customer-portal/backend-v2/cmd/server/main.go
  • apps/customer-portal/backend-v2/internal/dto/project_stats.go
  • apps/customer-portal/backend-v2/internal/entity/projects.go
  • apps/customer-portal/backend-v2/internal/entity/types.go
  • apps/customer-portal/backend-v2/internal/handler/project_stats.go
  • apps/customer-portal/backend-v2/openapi.yaml

Comment thread apps/customer-portal/backend-v2/CLAUDE.md
Comment thread apps/customer-portal/backend-v2/internal/dto/project_stats.go
- 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.
@Rashmika998
Rashmika998 merged commit 2f977db into wso2-open-operations:dev-app-csm-portal Aug 3, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants