Skip to content

refactor: GET all ecosystems API to support orgId as optional query param.#1556

Merged
pranalidhanavade merged 10 commits intomainfrom
refactor/api-to-get-all-ecosystem-by-orgid
Feb 11, 2026
Merged

refactor: GET all ecosystems API to support orgId as optional query param.#1556
pranalidhanavade merged 10 commits intomainfrom
refactor/api-to-get-all-ecosystem-by-orgid

Conversation

@pranalidhanavade
Copy link
Contributor

@pranalidhanavade pranalidhanavade commented Feb 4, 2026

What

  • Refactored get all ecosystems API to support orgId as optional query param. Added support to fetch ecosystem by ecosystem member, ecosystem lead.
  • Removed IS_ECOSYSTEM_ENABLED .env variable dependency from user and organization service.

Summary by CodeRabbit

  • New Features

    • Optional organization ID filter for ecosystem listings.
  • Bug Fixes / Validation

    • Stricter UUID/orgId validation and unified bad-request messages across endpoints.
    • Expanded role coverage for ecosystem actions and refined invitation/status handling.
    • Added several user/org-related error messages (e.g., user not found, already accepted).
  • API Changes

    • Creating an ecosystem now requires name, description, userId and orgId; several ecosystem fields made nullable; user profile email is now required.
  • Chores

    • Removed legacy static configuration data file.

@coderabbitai
Copy link

coderabbitai bot commented Feb 4, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Added optional orgId propagation and strict UUID/string validation across API Gateway and Ecosystem services/controllers/repos; introduced org-scoped repository query; adjusted interfaces nullability; removed a large static prisma data file; standardized response messages and simplified invite/error handling.

Changes

Cohort / File(s) Summary
API Gateway — Controller & Service
apps/api-gateway/src/ecosystem/ecosystem.controller.ts, apps/api-gateway/src/ecosystem/ecosystem.service.ts
Added optional orgId query param with TrimStringParamPipe + ParseUUIDPipe and @ApiQuery; expanded role checks; unified invalid-UUID BadRequestExceptions; removed ConflictException branch in invite flow; service forwards { userId, orgId } to NATS.
Ecosystem Microservice — Controller & Service
apps/ecosystem/src/ecosystem.controller.ts, apps/ecosystem/src/ecosystem.service.ts
Signatures accept optional orgId; service short-circuits to org-scoped retrieval when orgId present; invitation flows re-ordered to validate invites earlier; improved RpcException/HttpStatus usage and richer error logging; use userEmail for invitation lookups.
Repository
apps/ecosystem/repositories/ecosystem.repository.ts
Added getAllEcosystemsByOrgId(orgId: string) (input validation, nested includes, ordering); many methods made nullable-returning; catch blocks now log full error objects; NotFound/BadRequest guards adjusted.
Interfaces / Types
apps/ecosystem/interfaces/ecosystem.interfaces.ts, apps/user/interfaces/user.interface.ts
Adjusted nullability/required fields: ICreateEcosystem fields now required; many fields changed to `string
Shared Messages
libs/common/src/response-messages/index.ts
Added new error keys (userNotFound, alreadyAccepted, orgIdMissing, userNotFoundForInvitation, unableToSendInvitationToLead) and renamed invitationRequiredinvitationRequiredFromPlatformAdmin.
Static Data Removal
libs/prisma-service/prisma/data/credebl-master-table.json
Removed a large static JSON configuration/data file used for initial configuration/reference.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant API_GW_Controller as API_GW_Controller
    participant API_GW_Service as API_GW_Service
    participant NATS
    participant Eco_Service as Ecosystem_Service
    participant Eco_Repo as Ecosystem_Repository

    Client->>API_GW_Controller: GET /ecosystem/all-ecosystem?orgId=<orgId>
    API_GW_Controller->>API_GW_Service: getEcosystems(userId, orgId)
    API_GW_Service->>NATS: sendNatsMessage('get-ecosystems', { userId, orgId })
    NATS->>Eco_Service: deliver get-ecosystems payload
    alt orgId provided
        Eco_Service->>Eco_Repo: getAllEcosystemsByOrgId(orgId)
        Eco_Repo-->>Eco_Service: ecosystems[]
    else orgId not provided
        Eco_Service->>Eco_Repo: fetch user-scoped ecosystems
        Eco_Repo-->>Eco_Service: ecosystems[]
    end
    Eco_Service-->>NATS: response payload
    NATS-->>API_GW_Service: response
    API_GW_Service-->>API_GW_Controller: ecosystems
    API_GW_Controller-->>Client: 200 OK + ecosystems
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

feature

Suggested reviewers

  • shitrerohit
  • sujitaw

Poem

🐰 I trimmed the IDs and hopped through each route,
I carried orgIds on NATS, quiet and stout,
Repos now fetch gardens by org and by name,
Messages aligned, invitations called by email's claim,
A little rabbit cheers this tidy route. 🥕

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: refactoring the GET all ecosystems API to support orgId as an optional query parameter, which is the primary objective of this pull request.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ 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
  • Commit unit tests in branch refactor/api-to-get-all-ecosystem-by-orgid

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.

@pranalidhanavade pranalidhanavade changed the title refactor: get all ecosystems api to support orgid as optional query param. refactor: GET all ecosystems API to support orgId as optional query param. Feb 4, 2026
@pranalidhanavade pranalidhanavade force-pushed the refactor/api-to-get-all-ecosystem-by-orgid branch from 6db9ce6 to 8712d4f Compare February 9, 2026 14:39
@pranalidhanavade
Copy link
Contributor Author

@coderabbitai full review

@coderabbitai
Copy link

coderabbitai bot commented Feb 9, 2026

✅ Actions performed

Full review triggered.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/user/interfaces/user.interface.ts (1)

3-12: ⚠️ Potential issue | 🔴 Critical

email field made required but database schema allows NULL — creates type mismatch with actual data.

The Prisma schema defines email as String? (optional/nullable), meaning existing database records can have email: null. With this interface change, methods like getUserById(), getProfile(), and getPublicProfile() will return data where the TypeScript type declares email: string but the actual value may be null, breaking type safety. Either make email optional again in the interface, add runtime validation to ensure email is never null, or update the database schema to enforce email as required.

🤖 Fix all issues with AI agents
In `@apps/api-gateway/src/ecosystem/ecosystem.controller.ts`:
- Around line 200-213: The ParseUUIDPipe on the orgId query in getEcosystems
currently rejects undefined, making the param effectively required; update the
ParseUUIDPipe instantiation to include optional: true and mark the route
parameter as optional (orgId?: string) so missing orgId won't trigger
validation, i.e., change the ParseUUIDPipe options to { optional: true,
exceptionFactory: ... } and update the orgId parameter signature in
getEcosystems to be optional.

In `@apps/ecosystem/repositories/ecosystem.repository.ts`:
- Around line 438-444: The orgId null/undefined check must run before any
database access: move the existing "if (!orgId) { throw new
NotFoundException(ResponseMessages.ecosystem.error.notFound); }" guard to the
top of the repository method (before the Prisma queries that reference orgId) or
remove it entirely if you rely on the service-level getEcosystemDashboard
validation; ensure the repository does not execute the Prisma query with a falsy
orgId and keep the subsequent ecosystem null check (throwing
ResponseMessages.ecosystem.error.ecosystemNotFound) intact.
🧹 Nitpick comments (4)
apps/ecosystem/repositories/ecosystem.repository.ts (1)

1-1: File-level eslint-disable for @typescript-eslint/no-explicit-any is overly broad.

This suppresses the rule for the entire file. Prefer inline // eslint-disable-next-line on the specific lines where any is needed (lines 168, 894, 1295).

apps/ecosystem/src/ecosystem.service.ts (3)

329-338: userEmail is derived before the user null check — reorder for clarity.

Line 332 accesses user?.email before line 334 checks if (!user). While this isn't a runtime bug (the throw prevents further use), the logical flow is misleading. Derive userEmail after the null guard.

Proposed fix
      const user = await this.ecosystemRepository.getUserById(reqUser);
-     const userEmail = user?.email || '';
 
      if (!user) {
        throw new RpcException({ status: HttpStatus.NOT_FOUND, message: 'User not found to send invitation' });
      }
 
+     const userEmail = user.email || '';
+
      const existingInvitation = await this.ecosystemRepository.getEcosystemInvitationsByEmail(userEmail, ecosystemId);

356-381: Empty-string fallback for userId can cause a silent incorrect query.

Line 358 sets const userId = result.userId || ''. Line 367 checks if (!result.userId) and throws, but line 381 uses the userId variable (which would be '' if result.userId is null) in getEcosystemOrg. If the throw on line 368 were ever removed or bypassed, getEcosystemOrg would query with an empty string. Use result.userId directly after the guard instead.

Proposed fix
-       const userId = result.userId || '';
        if (!role) {
          throw new Error('Error fetching ecosystem role');
        }

        if (!result.invitedOrg) {
          throw new Error('Organization ID is missing');
        }

        if (!result.userId) {
          throw new Error('User ID missing');
        }

-       const existingOrg = await this.ecosystemRepository.getEcosystemOrg(result.ecosystemId, userId);
+       const existingOrg = await this.ecosystemRepository.getEcosystemOrg(result.ecosystemId, result.userId);

194-196: Dead code: userId has already been used above.

userId is destructured and used on lines 181 and 183. By the time line 194 is reached, userId has already been passed to findAcceptedInvitationByUserId. This guard is unreachable in practice (if userId were falsy, the repository call would have already failed or returned no result).

Proposed fix — move the check to the top
  async createEcosystem(createEcosystemDto: ICreateEcosystem): Promise<IEcosystem> {
    try {
      const { userId } = createEcosystemDto;
+     if (!userId) {
+       throw new BadRequestException(ResponseMessages.ecosystem.error.userIdMissing);
+     }

      const invitation = await this.ecosystemRepository.findAcceptedInvitationByUserId(userId);
      ...
-     if (!userId) {
-       throw new BadRequestException(ResponseMessages.ecosystem.error.userIdMissing);
-     }

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@apps/api-gateway/src/ecosystem/ecosystem.controller.ts`:
- Around line 91-101: The controller currently uses the wrong exception filter
which maps all RpcException errors to 400; update the controller to preserve
upstream status codes by either replacing the import of CustomExceptionFilter
with the HTTP-level one from apps/api-gateway/common/exception-handler (so the
filter used by ecosystem.controller preserves RpcException status codes) or wrap
the call to this.ecosystemService.inviteMemberToEcosystem(...) in a try/catch
that catches RpcException, extracts the status (or maps RpcException.getError()
metadata) and rethrows an HttpException with the original status (e.g.,
HttpStatus.CONFLICT for duplicate invites) before returning the created
response; reference the RpcException type, the inviteMemberToEcosystem call, and
the CustomExceptionFilter import to locate the change.
🧹 Nitpick comments (3)
apps/ecosystem/repositories/ecosystem.repository.ts (2)

1-1: File-level eslint-disable for no-explicit-any is overly broad.

This suppresses the rule across the entire file. Prefer inline // eslint-disable-next-line on the specific lines that need it (lines 168, 894, 1295) to keep the rest of the file guarded.


1256-1299: New getAllEcosystemsByOrgId largely duplicates the query shape from getEcosystemsForEcosystemLead (lines 1217-1254) and getAllEcosystems (lines 276-305).

All three methods query ecosystem with deletedAt: null, include ecosystemOrgs → ecosystemRole + organisation, and order by createDateTime desc. Consider extracting a shared private helper that accepts an optional where filter for ecosystemOrgs to reduce duplication across these methods.

Also, the !orgId guard on line 1258 is a defensive no-op when the controller already guarantees a valid UUID via ParseUUIDPipe({ optional: true }). It won't hurt, but be aware that TypeScript's string type won't be falsy after pipe validation — only undefined would skip the call at the service layer.

#!/bin/bash
# Check how getAllEcosystemsByOrgId is called from the service layer
rg -n --type=ts -C3 'getAllEcosystemsByOrgId'
apps/api-gateway/src/ecosystem/ecosystem.controller.ts (1)

203-211: Inconsistent error message keys for orgId validation across endpoints.

getEcosystems (line 209) uses ResponseMessages.ecosystem.error.invalidOrgId, while getEcosystemDashboard (line 259) uses ResponseMessages.organisation.error.invalidOrgId for the same type of parameter. Pick one and use it consistently to avoid confusing API consumers with different error messages for the same kind of invalid input.

Also applies to: 254-261

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/ecosystem/src/ecosystem.service.ts (1)

442-448: ⚠️ Potential issue | 🟠 Major

Update return types to reflect nullable repository returns.

getUserByKeycloakId (line 442) declares Promise<user> and getEcosystemDetailsByUserId (line 446) declares Promise<ecosystem>, but the corresponding repository methods return Promise<user | null> and Promise<ecosystem | null>. While current callers in apps/api-gateway/src/authz/jwt.strategy.ts properly handle null returns with optional chaining checks (if (user?.id) and if (ecosystem?.id)), the service method signatures should be corrected to match the repository contract for type safety.

Proposed fix
-  async getUserByKeycloakId(keycloakId: string): Promise<user> {
+  async getUserByKeycloakId(keycloakId: string): Promise<user | null> {
     return this.ecosystemRepository.getUserByKeycloakId(keycloakId);
   }

-  async getEcosystemDetailsByUserId(userId: string): Promise<ecosystem> {
+  async getEcosystemDetailsByUserId(userId: string): Promise<ecosystem | null> {
     return this.ecosystemRepository.getEcosystemDetailsByUserId(userId);
   }
🤖 Fix all issues with AI agents
In `@apps/ecosystem/src/ecosystem.service.ts`:
- Around line 329-339: The code currently falls back to an empty string for a
missing user.email which can cause incorrect lookups in
ecosystemRepository.getEcosystemInvitationsByEmail and
ecosystemRepository.updateEcosystemInvitationStatusByEmail (also present in
inviteMemberToEcosystem); instead, after retrieving the user via
ecosystemRepository.getUserById, validate that user.email is present and
non-empty and if not throw an RpcException (e.g., HttpStatus.BAD_REQUEST or
NOT_FOUND with a clear message like "user email missing for invitation") before
calling getEcosystemInvitationsByEmail or updateEcosystemInvitationStatusByEmail
so no empty-string email is passed into those repository methods.
- Around line 181-196: The userId presence check is dead because userId is
destructured from createEcosystemDto and used by findAcceptedInvitationByUserId
before the guard; move the BadRequestException guard that checks userIdMissing
to run immediately after destructuring (i.e., right after const { userId } =
createEcosystemDto) or remove the guard entirely if the repository method
already validates/handles missing userId; update the control flow around
createEcosystemDto/userId, findAcceptedInvitationByUserId, and
checkEcosystemNameExist accordingly to ensure no repository calls are made with
a missing userId.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@apps/api-gateway/src/ecosystem/ecosystem.controller.ts`:
- Around line 203-213: TrimStringParamPipe currently calls value.trim() and will
throw when Nest passes undefined for an optional query, breaking the optional
ParseUUIDPipe for orgId; update TrimStringParamPipe.transform() in libs/common
(cast.helper.ts) to first guard for null/undefined and simply return the
original value (undefined) when absent, or alternatively remove
TrimStringParamPipe from the orgId decorator chain where ParseUUIDPipe is
configured as optional; ensure the transform() signature still accepts undefined
and does not call trim() unless value is a non-empty string.

In `@apps/ecosystem/src/ecosystem.service.ts`:
- Around line 293-300: In getEcosystems, before calling
ecosystemRepository.getAllEcosystemsByOrgId(orgId), verify the user is a member
of that org (unless they have PLATFORM_ADMIN); add a membership check such as
this.organizationService.isUserMember(orgId, userId) or
this.orgRepository.isMember(orgId, userId) and if it returns false throw a
ForbiddenException (or ResponseMessages.ecosystem.error.notMember); ensure the
check runs only when orgId is provided and short-circuit to allow PLATFORM_ADMIN
users to bypass membership validation.
🧹 Nitpick comments (2)
apps/api-gateway/src/ecosystem/ecosystem.controller.ts (1)

678-686: Minor inconsistency: TrimStringParamPipe used on some path params but not others.

getEcosystemDashboard (lines 244-263) applies TrimStringParamPipe before ParseUUIDPipe on path params, but createIntent and other endpoints (getIntents, updateIntent, deleteIntent) do not. Consider standardizing for consistency.

apps/ecosystem/src/ecosystem.service.ts (1)

365-390: userId fallback to '' on line 367 is unnecessary given the guard on lines 376-378.

Line 367 assigns const userId = result.userId || '', but lines 376-378 throw if !result.userId, so userId can never be '' at line 390. The fallback masks the intent — prefer assigning after the guard or removing the fallback.

Proposed fix
-        const userId = result.userId || '';
         if (!role) {
           throw new Error('Error fetching ecosystem role');
         }

         if (!result.invitedOrg) {
           throw new Error(ResponseMessages.ecosystem.error.orgIdMissing);
         }

         if (!result.userId) {
           throw new Error(ResponseMessages.ecosystem.error.userIdMissing);
         }

+        const userId = result.userId;
+
         const ecosystemOrgPayload = {

…arams

Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
@pranalidhanavade pranalidhanavade force-pushed the refactor/api-to-get-all-ecosystem-by-orgid branch from 43cde2f to 44bd506 Compare February 11, 2026 14:53
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
@sonarqubecloud
Copy link

@pranalidhanavade pranalidhanavade merged commit e76df82 into main Feb 11, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants