Skip to content

[CSM Portal] Add change request approval action and problem creation - #1209

Merged
cloby99 merged 11 commits into
wso2-open-operations:mainfrom
rksk:cr-assess-transition-problem-create
Jul 23, 2026
Merged

cloby99 merged 11 commits into
wso2-open-operations:mainfrom
rksk:cr-assess-transition-problem-create

Conversation

@rksk

@rksk rksk commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

Describe the problems, issues, or needs driving this feature/fix and include links to related issues in the following format: Resolves issue1, issue2, etc.

Change requests had no path for a regular user to move a request out of its initial state — only a raw, unchecked field-level patch existed, and the existing approve/reject actions only appear once a request already reaches a later stage. Separately, problem records could only be searched and viewed — there was no way to create one anywhere in the stack. This PR adds both capabilities, propagated through the entity service, the CSM portal backend, and the webapp.

Goals

Describe the solutions that this feature/fix will introduce to resolve the problems described above

  • Let a regular user move a change request out of its initial state via a single, backend-validated action, and let the UI discover when that action is legal from the change request's own detail response (no hardcoded state checks in the frontend).
  • Add a create endpoint and a create form for problems, consistent with how change requests and incidents are already created.

Approach

Describe how you are implementing the solutions. Include an animated GIF or screenshot if the change affects the UI (email documentation@wso2.com to review all UI text). Include a link to a Markdown file or Google doc if the feature write-up is too long to paste here.

Change request approval action

  • Entity service: PatchChangeRequestRequest gains RequestApproval *bool; the change request detail type gains LegalNextStates []string. Both are pure passthrough — the data source performs all transition validation.
  • Backend: this service is a byte-level passthrough proxy for change requests, so the new field and the new response field flow through automatically; the work here is documenting both in openapi.yaml and adding direct handler test coverage for the patch endpoint (which had none before).
  • Webapp: a data-driven "Request approval" button on the change request detail page, shown only when legalNextStates includes "assess" — mirrors the existing CaseActionBar pattern of rendering off a backend-provided list rather than a hardcoded from-state check. Uses the existing patch mutation hook, which already invalidates the detail query on success, so the page updates without a manual reload. Errors surface through the existing error-banner pattern.

Problem creation

  • Entity service: new CreateProblem method on the problem service, new request domain type, reusing the existing problem-detail type for the created-record response. Origin-case and primary-incident IDs are converted between the public UUID and the data source's native id the same way every other cross-link in this file is.
  • Backend: new POST /problems handler mirroring the existing problem read handlers' auth pattern (401 with no authenticated user, no per-record access check since problems aren't project-scoped), a new entity-service client method, route registration, and openapi.yaml coverage including all four standard error responses.
  • Webapp: a new create-problem page (subject required; category/subcategory/origin-case-id/primary-incident-id optional), a new API hook, a "New problem" entry point on the problems tab (which had none before), and route registration — all mirroring the existing change-request/incident create flows. No priority field: priority isn't settable on creation for this record type.

User stories

Summary of user stories addressed by this change

  • As a CS engineer, I can move a change request out of its initial state without an unchecked, admin-only field edit.
  • As a CS engineer, I can create a new problem record directly from the portal instead of only viewing existing ones.

Release note

Brief description of the new feature or bug fix as it will appear in the release notes

Added a "Request approval" action for change requests and a problem-creation flow, including entity-service, backend, and webapp support.

Documentation

N/A — internal CS-engineer portal feature, no external product documentation is maintained for this workflow.

Training

N/A — no training content is maintained for this internal portal.

Certification

N/A — no certification exam covers this internal portal.

Marketing

N/A — internal-only feature, not customer facing.

Automation tests

  • Unit tests

    Added/extended Go handler tests and mocks in the CSM portal backend for both the change request patch endpoint (previously untested directly) and the new problem-creation handler (success and unauthenticated cases). Added webapp component/hook tests for the new problem-creation page and the change request detail page's new action button.

  • Integration tests

    None added at this layer; the underlying data-source behavior was already live-verified end to end (state transition + resulting downstream automation, and record creation with cross-links) before this PR.

Security checks

  • Followed secure coding standards in http://wso2.com/technical-reports/wso2-secure-engineering-guidelines? yes
  • Ran FindSecurityBugs plugin and verified report? N/A — this is a Go/TypeScript codebase, not Java; go vet and eslint were run clean instead
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets? yes

Samples

N/A — no sample projects reference this API.

Related PRs

None.

Migrations (if applicable)

N/A — no schema or data migration involved.

Test environment

Verified locally: go build/go test/go vet (entity service), make build/make test/make vet (CSM portal backend), pnpm build/pnpm test/pnpm lint (webapp).

Learning

Describe the research phase and any blog posts, patterns, libraries, or add-ons you used to solve the problem.

Followed this codebase's existing conventions throughout: the sibling boolean action-flag fields already on the change request patch type, the existing case-detail action-bar pattern for backend-driven legal-transition buttons, and the existing change-request/incident create-page structure for the new problem create page.

Summary by CodeRabbit

  • New Features
    • Added ServiceNow “Create problem” support (POST /problems) with a new guided page at /operations/problems/new.
    • Expanded the problems list to show State, Assignment group, and Assigned to.
    • Added type-ahead selectors for Origin case and Primary incident during problem creation.
    • Added “Request approval” for eligible change requests based on modeled legal next states.
  • Bug Fixes
    • Improved validation and error responses for problem creation and change-request approval flows, with updated tests.

rksk added 3 commits July 22, 2026 23:54
…nd problem creation

Adds a request-approval action to the change request patch flow (with the
resulting legal next states surfaced on the detail response) and a new
create endpoint for problems, matching the existing passthrough conventions
in this service. Both are pure forwarding with no added business logic; all
validation remains in the backing data source.
…em creation

Extends the change request patch endpoint to forward the request-approval
action and the resulting legal next states, and adds a new POST /problems
endpoint mirroring the existing problem read handlers' auth pattern (no
per-record access check, since problems aren't project-scoped). Documents
both in openapi.yaml, including the previously undocumented customer/review
approval flags on the same patch schema.
…reation flow

Adds a data-driven "Request approval" button on the change request detail
page, rendered only when the backend's legal-next-states list includes
that transition (mirrors CaseActionBar's pattern — no hardcoded from-state
check). Adds a full problem creation flow: a create page, a "New problem"
entry point on the problems tab, and the supporting API hook/types,
matching the existing change request/incident create flows. No priority
field on the create form since it isn't settable on create.
@coderabbitai

coderabbitai Bot commented Jul 22, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 78fd19da-8592-44be-b740-a7193901d856

📥 Commits

Reviewing files that changed from the base of the PR and between b086106 and 9e75401.

📒 Files selected for processing (5)
  • apps/csm-portal/backend/internal/handler/problems.go
  • apps/csm-portal/backend/openapi.yaml
  • entity-service/internal/service/interfaces.go
  • entity-service/internal/service/sn_change_request_service.go
  • entity-service/internal/service/sn_problem_service.go
🚧 Files skipped from review as they are similar to previous changes (5)
  • entity-service/internal/service/interfaces.go
  • apps/csm-portal/backend/internal/handler/problems.go
  • entity-service/internal/service/sn_change_request_service.go
  • entity-service/internal/service/sn_problem_service.go
  • apps/csm-portal/backend/openapi.yaml

📝 Walkthrough

Walkthrough

The PR adds ServiceNow problem creation across the entity service, portal backend, API contract, and webapp. It enriches problem search results and adds async selectors. Change requests now expose legal transitions and support a Request approval action.

Changes

Problem lifecycle

Layer / File(s) Summary
Problem creation backend
entity-service/internal/{domain,handler,server,service}/..., apps/csm-portal/backend/..., apps/csm-portal/backend/openapi.yaml
Adds POST /problems, request models, ServiceNow mapping, portal forwarding, 201 Created responses, and validation/error tests.
Problem search enrichment
entity-service/internal/service/sn_problem_service.go, apps/csm-portal/webapp/src/api/backend/types.ts, apps/csm-portal/webapp/src/features/csm-operations/components/ProblemsTab.tsx
Maps and displays problem state, assignment group, and assignee fields.
Problem creation webapp
apps/csm-portal/webapp/src/features/csm-operations/{api,pages}/..., apps/csm-portal/webapp/src/App.tsx
Adds the creation route, form, dependent category fields, case/incident linking, mutation handling, navigation, and tests.

Change-request approval

Layer / File(s) Summary
Approval contract and ServiceNow mapping
entity-service/internal/{domain,service}/..., apps/csm-portal/backend/openapi.yaml, apps/csm-portal/webapp/src/api/backend/types.ts
Adds requestApproval and legalNextStates through the API and ServiceNow mappings.
Request approval action
apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.tsx
Shows the approval button when "assess" is legal and submits the approval patch.
Approval handler validation
apps/csm-portal/backend/internal/handler/change_requests_test.go
Tests request validation, forwarding, response fields, and upstream error mapping.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: Type/Improvement, Area/Backend, Entity Service, App/CSM Portal

Sequence Diagram(s)

sequenceDiagram
  participant CreateProblemPage
  participant PortalBackend
  participant EntityService
  participant ServiceNow
  CreateProblemPage->>PortalBackend: POST /problems
  PortalBackend->>EntityService: Forward problem payload
  EntityService->>ServiceNow: Create problem
  ServiceNow-->>EntityService: ProblemDetail
  EntityService-->>PortalBackend: ProblemDetail
  PortalBackend-->>CreateProblemPage: 201 Created
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 76.92% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two main additions: change request approval and problem creation.
Description check ✅ Passed The description covers the required template sections and gives enough detail on purpose, approach, tests, and release notes.
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

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 Checkov (3.3.8)
apps/csm-portal/backend/openapi.yaml

Traceback (most recent call last):
File "/usr/local/bin/checkov", line 2, in
from checkov.main import Checkov
ModuleNotFoundError: No module named 'checkov'

🔧 golangci-lint (2.12.2)

level=error msg="[linters_context] typechecking error: pattern ./...: directory prefix . does not contain main module or its selected dependencies"


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.

rksk added 3 commits July 23, 2026 00:11
The problem search response now includes state, assignment group, and
assigned-to alongside id/number/subject (pure passthrough from the
upstream data source, now that it returns these fields too). Plumb the
extra fields through the entity-service domain type and into the
problems table, which gains State/Assignment group/Assigned to columns
following the same chip/em-dash conventions the incidents list already
uses.
Category/Subcategory were plain text inputs even though they're a
fixed, dependent choice list; Origin case and Primary incident were
raw UUID text fields with no way to find the right record. Replace
them: Category/Subcategory become a dependent pair of Selects, and
Origin case/Primary incident become type-ahead AsyncEntitySelect
pickers backed by two new (query, enabled) search hooks for cases and
incidents, following the same pattern already used on the change
request create form.
@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

CreateProblem was unmarshaling the create endpoint's wrapped
{message, problem} response directly into the flat problem-detail
struct, so fields like id came back empty and the post-create
redirect failed. Unwrap the envelope first, mirroring the existing
pattern already used for change request creation.
@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@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: 4

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/sn_change_request_service.go (1)

645-653: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Missing validation: requestApproval documented as mutually exclusive with isCustomerApproved/isCustomerReviewed, but nothing enforces it.

The OpenAPI contract states requestApproval is "Mutually exclusive with isCustomerApproved and isCustomerReviewed," but PatchChangeRequest only checks that at least one field is set — it never rejects a request that sets RequestApproval alongside IsCustomerApproved/IsCustomerReviewed. Such a request would currently be forwarded to ServiceNow unchecked.

🛡️ Proposed fix
 	if req.PlannedEndOn != nil {
 		if _, err := time.Parse(snCreatedOnLayout, *req.PlannedEndOn); err != nil {
 			return domain.PatchChangeRequestResponse{}, &apierror.ValidationError{Msg: "plannedEndOn must follow the format: YYYY-MM-DD HH:mm:ss"}
 		}
 	}
+	if req.RequestApproval != nil && (req.IsCustomerApproved != nil || req.IsCustomerReviewed != nil) {
+		return domain.PatchChangeRequestResponse{}, &apierror.ValidationError{Msg: "requestApproval cannot be combined with isCustomerApproved or isCustomerReviewed"}
+	}

As per coding guidelines, "Validate all input before calling the repository and return *apierror.ValidationError for invalid input."

Also applies to: 700-714

🤖 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/sn_change_request_service.go` around lines
645 - 653, The PatchChangeRequest validation must reject requests that set
RequestApproval together with IsCustomerApproved or IsCustomerReviewed. Update
the validation in PatchChangeRequest, including the corresponding checks around
the later validation block, to return an apierror.ValidationError before
invoking the repository while preserving valid combinations.

Source: Coding guidelines

🧹 Nitpick comments (1)
apps/csm-portal/backend/openapi.yaml (1)

6100-6106: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider marking legalNextStates read-only for consistency with Case.nextStates.

Case.nextStates (a server-computed transition list) is marked readOnly: true; legalNextStates serves the identical purpose for change requests but omits it.

🤖 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/csm-portal/backend/openapi.yaml` around lines 6100 - 6106, Update the
legalNextStates schema property to include readOnly: true, matching the
server-computed Case.nextStates schema while preserving its existing array type,
items, and description.
🤖 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/csm-portal/backend/internal/handler/problems.go`:
- Around line 103-108: Update the create handler around the JSON validation
before entity.CreateProblem to decode a required object into the strict
create-request type, reject unknown fields and non-object JSON, require subject,
and validate optional UUID link fields. Return bad-request responses for
malformed JSON, missing subject, and invalid linking IDs, and only forward the
validated payload to CreateProblem.

In `@apps/csm-portal/backend/openapi.yaml`:
- Around line 3490-3505: Enforce mutual exclusivity for isCustomerApproved,
isCustomerReviewed, and requestApproval in the PatchChangeRequest schema and
validation flow, rejecting any PATCH that sets more than one of these fields.
Add the appropriate oneOf/not constraint in openapi.yaml and ensure the
corresponding implementation rejects contradictory combinations before
forwarding the request upstream.

In `@entity-service/internal/service/interfaces.go`:
- Around line 378-381: The CreateProblem documentation in the service interface
incorrectly declares OriginCaseID as required. Reconcile the comment with the
established optional contract in domain.CreateProblemRequest, the OpenAPI
payload, and sn_problem_service.go by documenting only Subject as required; do
not add validation unless the API contract is intentionally being changed.

In `@entity-service/internal/service/sn_problem_service.go`:
- Around line 186-225: Update CreateProblem to validate req.Subject before
constructing or sending the ServiceNow payload, rejecting empty or
whitespace-only values with the package’s existing apierror.ValidationError
pattern used by PatchChangeRequest. Return the validation error immediately and
preserve the existing UUID validation and outbound request flow for valid
subjects.

---

Outside diff comments:
In `@entity-service/internal/service/sn_change_request_service.go`:
- Around line 645-653: The PatchChangeRequest validation must reject requests
that set RequestApproval together with IsCustomerApproved or IsCustomerReviewed.
Update the validation in PatchChangeRequest, including the corresponding checks
around the later validation block, to return an apierror.ValidationError before
invoking the repository while preserving valid combinations.

---

Nitpick comments:
In `@apps/csm-portal/backend/openapi.yaml`:
- Around line 6100-6106: Update the legalNextStates schema property to include
readOnly: true, matching the server-computed Case.nextStates schema while
preserving its existing array type, items, and description.
🪄 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

Run ID: 68e21bc5-0a8b-4ad7-ac6c-0f007e8597e4

📥 Commits

Reviewing files that changed from the base of the PR and between e199e0b and b086106.

📒 Files selected for processing (24)
  • apps/csm-portal/backend/cmd/server/main.go
  • apps/csm-portal/backend/internal/entity/entity.go
  • apps/csm-portal/backend/internal/handler/change_requests_test.go
  • apps/csm-portal/backend/internal/handler/helpers_test.go
  • apps/csm-portal/backend/internal/handler/problems.go
  • apps/csm-portal/backend/internal/handler/problems_test.go
  • apps/csm-portal/backend/openapi.yaml
  • apps/csm-portal/webapp/src/App.tsx
  • apps/csm-portal/webapp/src/api/backend/types.ts
  • apps/csm-portal/webapp/src/constants/apiConstants.ts
  • apps/csm-portal/webapp/src/features/csm-operations/api/usePostProblem.ts
  • apps/csm-portal/webapp/src/features/csm-operations/api/useSearchCasesForSelect.ts
  • apps/csm-portal/webapp/src/features/csm-operations/api/useSearchIncidentsForSelect.ts
  • apps/csm-portal/webapp/src/features/csm-operations/components/ProblemsTab.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CreateProblemPage.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CreateProblemPage.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.test.tsx
  • apps/csm-portal/webapp/src/features/csm-operations/pages/CsmChangeRequestDetailPage.tsx
  • entity-service/internal/domain/entity.go
  • entity-service/internal/handler/problem_handler.go
  • entity-service/internal/server/routes.go
  • entity-service/internal/service/interfaces.go
  • entity-service/internal/service/sn_change_request_service.go
  • entity-service/internal/service/sn_problem_service.go

Comment thread apps/csm-portal/backend/internal/handler/problems.go
Comment thread apps/csm-portal/backend/openapi.yaml
Comment thread entity-service/internal/service/interfaces.go
Comment thread entity-service/internal/service/sn_problem_service.go
rksk added 4 commits July 23, 2026 03:17
Decode the problem-create request into a strict typed struct (rejecting
unknown fields) instead of only checking json.Valid, which let through
malformed payloads like null, arrays, and objects with unexpected shape.
Require a non-blank subject and validate the optional UUID-formatted
linking fields, matching the existing create-incident validation pattern.
The change-request patch handler documented isCustomerApproved,
isCustomerReviewed, and requestApproval as mutually exclusive but never
rejected a request that set more than one. Add the check to the service
layer and encode the same constraint in the OpenAPI schema so it's
enforced, not just described.
The interface comment claimed the origin-case link was required alongside
the subject, but the domain type, the OpenAPI schema, and the actual
implementation all treat it as optional. Doc-only fix, no behavior change.
CreateProblem forwarded the subject straight to the backing data source
without checking it was non-blank, unlike sibling validation elsewhere in
this service (e.g. change-request title). Reject blank/whitespace-only
subjects with a validation error before making the call.
@rksk

rksk commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

@coderabbitai resume

@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Reviews resumed.

@cloby99
cloby99 merged commit 7a998a5 into wso2-open-operations:main Jul 23, 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