Skip to content

[Customer Portal][BE] Enhance updates/levels/search response to return grouped update levels by type - #231

Merged
Rashmika998 merged 2 commits into
wso2-open-operations:customer-portal-milestone-1from
Rashmika998:customer-portal-milestone-1-updates-dev
Feb 23, 2026
Merged

Rashmika998 merged 2 commits into
wso2-open-operations:customer-portal-milestone-1from
Rashmika998:customer-portal-milestone-1-updates-dev

Conversation

@Rashmika998

@Rashmika998 Rashmika998 commented Feb 23, 2026 •

Copy link
Copy Markdown
Contributor

Description

This PR updates the updates/levels/search response structure to return a map grouped by update type, including their respective update levels.

Changes

  • Modified response structure of updates/levels/search
  • Grouped update levels under their corresponding update types
  • Updated response models/DTOs

New Response Structure

The endpoint now returns a map structured as:

{
"": {
updatesType: "",
updateLevels: [ ... ]
}
}

This allows consumers to easily access update levels categorized by their update type.

Reason

Previously, the endpoint returned a flat list of update levels.
The UI and consumer services require update levels grouped by update type for:

  • Cleaner rendering logic
  • Easier filtering
  • Better semantic representation of update hierarchy

Grouping at the backend reduces frontend transformation logic and improves API clarity.

Impact

⚠ Response structure changed (potential breaking change if consumers rely on the previous flat structure).

Consumers must adjust to the grouped map format.

Testing

  • Verified correct grouping by update type
  • Tested scenarios with multiple update types

Summary by CodeRabbit

  • New Features

    • Updates are grouped by update level for clearer organization.
    • Automatic classification into security vs. regular updates.
    • Update entries now include richer details: descriptions, instructions, bug fixes, file changes, bundle info, security advisories, and dependent releases.
  • Changes

    • Update type is now mandatory for each update record.
    • Search/update responses now return grouped update-level structures for consumption.

@coderabbitai

coderabbitai Bot commented Feb 23, 2026 •

Copy link
Copy Markdown
Contributor
📝 Walkthrough

Walkthrough

Restructures update types and processing to produce a grouped map of updates by update level (map), adds update-type/updateLevel fields and optional description details, introduces update-related constants, and refactors the search processing to classify and group updates before returning them.

Changes

Cohort / File(s) Summary
Type Definitions
apps/customer-portal/backend/modules/types/types.bal, apps/customer-portal/backend/modules/updates/types.bal
Modified UpdateDescription and UpdateDescriptionPayload: removed channel from payload, added updateLevel (int) and updateType (string) at top-level, expanded optional fields (description, instructions, bugFixes, filesAdded/Modified/Removed, bundlesInfoChanges, dependantReleases), and introduced UpdateLevelGroup.
Constants
apps/customer-portal/backend/modules/updates/constants.bal
Added constants: CHANNEL = "full", UPDATE_TYPE_SECURITY = "security", UPDATE_TYPE_REGULAR = "regular".
Processing Logic
apps/customer-portal/backend/modules/updates/utils.bal
Added isSecurityUpdate() and groupByUpdateLevel() helpers; refactored processSearchUpdatesBetweenUpdateLevels to build enriched UpdateDescription records, use CHANNEL constant, and return `maptypes:UpdateLevelGroup
Service Integration
apps/customer-portal/backend/service.bal
Updated updates/search handler to accept and return the grouped map<types:UpdateLevelGroup> result from the updated processing function.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Service
    participant Utils as "Updates Utils"
    participant API as "External API"

    Client->>Service: GET /updates/search (payload)
    Service->>Utils: processSearchUpdatesBetweenUpdateLevels(email, payload)
    Utils->>API: Request updates (channel = "full")
    API-->>Utils: Return raw update items
    Utils->>Utils: Enrich items (description, files, securityAdvisories, dependantReleases, timestamp)
    Utils->>Utils: For each item: isSecurityUpdate() → set updateType
    Utils->>Utils: groupByUpdateLevel() → map<UpdateLevelGroup>
    Utils-->>Service: Return grouped map<UpdateLevelGroup>
    Service-->>Client: HTTP 200 with grouped updates
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • shayanmalinda
  • sacheeramesh

Poem

🐰 In code I hop through grouped-update land,
Levels and types now neatly close at hand,
Security peeks where a warning may be,
Constants hum "full" and guide the spree,
Hooray — grouped maps blossom, tidy and grand!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description covers purpose, goals, approach, and testing, but is missing several required template sections (User stories, Release note, Documentation, Training, Certification, Marketing, Automation tests, Security checks, Samples, Related PRs, Migrations, Test environment, Learning). Complete the PR description by adding the missing template sections, at minimum providing N/A with justification for sections that don't apply.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: enhancing the updates/levels/search endpoint response to return grouped update levels by type.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 and usage tips.

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

🧹 Nitpick comments (2)
apps/customer-portal/backend/modules/types/types.bal (1)

914-920: New UpdateLevelGroup type looks good.

Clean grouping structure. One observation: the field name updateDescriptionLevels is somewhat verbose — consider whether updateDescriptions would be clearer, since each entry is an UpdateDescription rather than a "level." This is a minor naming nit.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/customer-portal/backend/modules/types/types.bal` around lines 914 - 920,
The field name updateDescriptionLevels on the UpdateLevelGroup record is overly
verbose—rename it to updateDescriptions to better reflect that it holds an array
of UpdateDescription items; update the type definition for UpdateLevelGroup and
then update every reference to UpdateLevelGroup.updateDescriptionLevels
(constructors, destructuring, JSON (de)serialization, test fixtures, and any
usages in functions or modules) to use updateDescriptions so compilation and
runtime behavior remain consistent.
apps/customer-portal/backend/modules/updates/utils.bal (1)

100-147: Use non-optional field access for required field on required value for clarity.

Line 128: description?.update-type uses optional field access (?.) on a required field. Since update-type is a required field in the UpdateDescription type (line 202 in updates/types.bal), and description is guaranteed to be non-null in this context, use direct access for clarity.

Suggested change
-            updateType: description?.update\-type,
+            updateType: description.update\-type,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/customer-portal/backend/modules/updates/utils.bal` around lines 100 -
147, The code in processSearchUpdatesBetweenUpdateLevels maps UpdateDescription
entries but uses optional access description?.update-type for a required field;
since UpdateDescription items are non-null here, change all occurrences of
description?.update-type (and any other required fields accessed with ?. like
description?.update-type) to direct field access (description.update-type) in
the mapping to reflect the type guarantees and improve clarity; locate this in
the mapping of UpdateDescription -> types:UpdateDescription within the
processSearchUpdatesBetweenUpdateLevels function.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/customer-portal/backend/modules/updates/utils.bal`:
- Around line 69-93: The map access at groupByUpdateLevel uses
groupedUpdateLevels[levelKey].updateType without nil-guarding; retrieve the
entry into a nilable temp (e.g., types:UpdateLevelGroup? levelGroup =
groupedUpdateLevels[levelKey]), check it with an is-type guard (if levelGroup is
types:UpdateLevelGroup) then set levelGroup.updateType = UPDATE_TYPE_SECURITY
and write it back (groupedUpdateLevels[levelKey] = levelGroup); do this instead
of directly mutating groupedUpdateLevels[levelKey] and keep the existing logic
that uses isSecurityUpdate(description), levelKey, UPDATE_TYPE_SECURITY and
UPDATE_TYPE_REGULAR.

---

Nitpick comments:
In `@apps/customer-portal/backend/modules/types/types.bal`:
- Around line 914-920: The field name updateDescriptionLevels on the
UpdateLevelGroup record is overly verbose—rename it to updateDescriptions to
better reflect that it holds an array of UpdateDescription items; update the
type definition for UpdateLevelGroup and then update every reference to
UpdateLevelGroup.updateDescriptionLevels (constructors, destructuring, JSON
(de)serialization, test fixtures, and any usages in functions or modules) to use
updateDescriptions so compilation and runtime behavior remain consistent.

In `@apps/customer-portal/backend/modules/updates/utils.bal`:
- Around line 100-147: The code in processSearchUpdatesBetweenUpdateLevels maps
UpdateDescription entries but uses optional access description?.update-type for
a required field; since UpdateDescription items are non-null here, change all
occurrences of description?.update-type (and any other required fields accessed
with ?. like description?.update-type) to direct field access
(description.update-type) in the mapping to reflect the type guarantees and
improve clarity; locate this in the mapping of UpdateDescription ->
types:UpdateDescription within the processSearchUpdatesBetweenUpdateLevels
function.
ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2ca4510 and 047d634.

📒 Files selected for processing (5)
  • apps/customer-portal/backend/modules/types/types.bal
  • apps/customer-portal/backend/modules/updates/constants.bal
  • apps/customer-portal/backend/modules/updates/types.bal
  • apps/customer-portal/backend/modules/updates/utils.bal
  • apps/customer-portal/backend/service.bal

Comment thread apps/customer-portal/backend/modules/updates/utils.bal
shayanmalinda
shayanmalinda previously approved these changes Feb 23, 2026

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

♻️ Duplicate comments (1)
apps/customer-portal/backend/modules/updates/utils.bal (1)

87-92: Nil-safety fix is correctly implemented — no write-back needed.

In Ballerina, mutation is tied to storage identity, and when a value stored in some storage location is mutated, the change is visible through all variables referring to the value. Structured values (records, arrays) are stored by reference in maps, so group.updateType = UPDATE_TYPE_SECURITY correctly mutates the map entry in-place; the write-back proposed in the previous review comment is unnecessary. The implementation is correct.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/customer-portal/backend/modules/updates/utils.bal` around lines 87 - 92,
The existing nil-safety handling is correct: when isSecurityUpdate(description)
is true, the local variable group (types:UpdateLevelGroup?) obtained from
groupedUpdateLevels[levelKey] is a reference to the stored record, so assigning
group.updateType = UPDATE_TYPE_SECURITY mutates the map entry in-place; no
additional write-back or reassignment is needed—leave the code in the
isSecurityUpdate(...) block as-is and remove any previous suggestion to write
group back into groupedUpdateLevels.
🧹 Nitpick comments (1)
apps/customer-portal/backend/modules/updates/utils.bal (1)

76-92: Redundant updateType assignment when the group is newly created for a security update.

When the group is first initialized at lines 76–81 for a SECURITY description, updateType is already set to UPDATE_TYPE_SECURITY. Lines 87–92 then unconditionally set it to UPDATE_TYPE_SECURITY a second time. The check on lines 87–92 is only semantically necessary for subsequent descriptions of the same key that are SECURITY after the group was initialized as REGULAR. While not a bug, collapsing the two paths would eliminate the redundancy:

♻️ Proposed simplification
     if !groupedUpdateLevels.hasKey(levelKey) {
         groupedUpdateLevels[levelKey] = {
-            updateType: isSecurityUpdate(description) ? UPDATE_TYPE_SECURITY : UPDATE_TYPE_REGULAR,
+            updateType: UPDATE_TYPE_REGULAR,
             updateDescriptionLevels: []
         };
     }
     types:UpdateDescription[]? existingLevels = groupedUpdateLevels[levelKey]?.updateDescriptionLevels;
     if existingLevels is types:UpdateDescription[] {
         existingLevels.push(description);
     }

     if isSecurityUpdate(description) {
         types:UpdateLevelGroup? group = groupedUpdateLevels[levelKey];
         if group is types:UpdateLevelGroup {
             group.updateType = UPDATE_TYPE_SECURITY;
         }
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/customer-portal/backend/modules/updates/utils.bal` around lines 76 - 92,
The group initialization already sets updateType based on
isSecurityUpdate(description), so remove the redundant unconditional assignment
that re-sets UPDATE_TYPE_SECURITY for newly created groups; instead only upgrade
an existing group to UPDATE_TYPE_SECURITY when a subsequent description is
security by guarding the assignment with a check that the group already existed
(e.g., only run the block setting group.updateType = UPDATE_TYPE_SECURITY when
groupedUpdateLevels.hasKey(levelKey) was true or when you detect the group was
not just created); reference groupedUpdateLevels, levelKey, isSecurityUpdate,
UPDATE_TYPE_SECURITY, UPDATE_TYPE_REGULAR, types:UpdateLevelGroup and
updateDescriptionLevels when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@apps/customer-portal/backend/modules/updates/utils.bal`:
- Around line 87-92: The existing nil-safety handling is correct: when
isSecurityUpdate(description) is true, the local variable group
(types:UpdateLevelGroup?) obtained from groupedUpdateLevels[levelKey] is a
reference to the stored record, so assigning group.updateType =
UPDATE_TYPE_SECURITY mutates the map entry in-place; no additional write-back or
reassignment is needed—leave the code in the isSecurityUpdate(...) block as-is
and remove any previous suggestion to write group back into groupedUpdateLevels.

---

Nitpick comments:
In `@apps/customer-portal/backend/modules/updates/utils.bal`:
- Around line 76-92: The group initialization already sets updateType based on
isSecurityUpdate(description), so remove the redundant unconditional assignment
that re-sets UPDATE_TYPE_SECURITY for newly created groups; instead only upgrade
an existing group to UPDATE_TYPE_SECURITY when a subsequent description is
security by guarding the assignment with a check that the group already existed
(e.g., only run the block setting group.updateType = UPDATE_TYPE_SECURITY when
groupedUpdateLevels.hasKey(levelKey) was true or when you detect the group was
not just created); reference groupedUpdateLevels, levelKey, isSecurityUpdate,
UPDATE_TYPE_SECURITY, UPDATE_TYPE_REGULAR, types:UpdateLevelGroup and
updateDescriptionLevels when making this change.
ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 047d634 and 130248f.

📒 Files selected for processing (1)
  • apps/customer-portal/backend/modules/updates/utils.bal

@Rashmika998
Rashmika998 merged commit 3340268 into wso2-open-operations:customer-portal-milestone-1 Feb 23, 2026
1 check passed
@Rashmika998 Rashmika998 moved this from Done to Staging Deployed in Customer Portal Development Feb 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

App/Customer Portal Area/Backend Type/Improvement Marks enhancements or improvements to existing features

Projects

Status: Staging Deployed

Development

Successfully merging this pull request may close these issues.

2 participants