feat: platform admin and ecosystem#1551
Conversation
) * feat/add script to add platform admin keycloak and role Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/eslint issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/coderabbit comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/changes to fetch value from db Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * feat: ecosystem service and create ecosystem invitation API route Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: get all invitations api Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: removed userId parameter from send invitation and get invitation api Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: create and get ecosystem api routes Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warning Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings and suggestions Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings resolved Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * resolved comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * resolve comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * resolve comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> --------- Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> Co-authored-by: sujitaw <sujit.sutar@ayanworks.com>
…g roles (#1538) * feat/add script to add platform admin keycloak and role Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/eslint issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/coderabbit comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/changes to fetch value from db Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix pr comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix pr comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> --------- Signed-off-by: sujitaw <sujit.sutar@ayanworks.com>
* feat/add script to add platform admin keycloak and role Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/eslint issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/coderabbit comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * feat: ecosystem service and create ecosystem invitation API route Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: get all invitations api Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * wip Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * wip completed invite member and update status for invitation Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * wip Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * wip Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * feat/added ecosystem invitation workflow apis Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/code rabbit comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/minor typo issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/ pr comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/pr comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> --------- Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> Co-authored-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: sujitaw <sujit.sutar@ayanworks.com>
Signed-off-by: sahil.kamble@ayanworks.com <sahil.kamble@ayanworks.com>
Signed-off-by: sahil.kamble@ayanworks.com <sahil.kamble@ayanworks.com>
…1547) * feat: create intent APIs Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: create intent mapping and create intent APIs Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: sonarlint issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: move validations from ecosystem repository to ecosystem service Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: sonarlint issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: sonarlint issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: sonarlint issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit suggestions Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> --------- Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
* refactor: added isEcosystemEnabled flag in database in platform_config table Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: enable/disable ecosystem feature from database Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit suggestions Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: enable/disable ecosystem feature and delete intent API issue Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * coderabbit comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> --------- Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
📝 WalkthroughWalkthroughAdds a new NATS-backed "ecosystem" microservice and integrates it across the monorepo: DB schema/migrations and seed updates, gateway wiring (modules/guards/JWT/swagger), DTOs/controllers/services/repositories, email templates, CI/Docker/tsconfig, enums/constants, and moves intent-template surface to the ecosystem domain. (50 words) Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client
participant API as "API Gateway"
participant JWT as "JwtStrategy"
participant GatewaySvc as "Gateway EcosystemService"
participant NATS as "NATS Bus"
participant EcoMS as "Ecosystem Microservice"
participant DB as "Database"
Client->>API: Request with JWT
API->>JWT: Validate token
JWT->>GatewaySvc: fetch ecosystem roles (getUserByKeycloakId)
GatewaySvc->>NATS: publish request (get-user-by-keycloak-id)
NATS->>EcoMS: route to ecosystem handler
EcoMS->>DB: query user/ecosystem data
DB-->>EcoMS: return data
EcoMS-->>NATS: respond
NATS-->>GatewaySvc: deliver roles/data
GatewaySvc-->>JWT: return ecosystem roles
API->>Client: Response (enriched user context)
sequenceDiagram
autonumber
participant Admin
participant PlatformCtrl as "PlatformController"
participant PlatformSvc as "PlatformService"
participant NATS as "NATS Bus"
participant EcoMS as "Ecosystem Microservice"
participant Repo as "EcosystemRepository"
participant DB as "Database"
participant Email as "EmailService"
Admin->>PlatformCtrl: POST /invitations (email)
PlatformCtrl->>PlatformSvc: inviteUserToCreateEcosystem(email)
PlatformSvc->>NATS: publish invite-user-for-ecosystem
NATS->>EcoMS: invoke invitation handler
EcoMS->>Repo: createEcosystemInvitation(...)
Repo->>DB: insert invitation
Repo-->>EcoMS: invitation record
EcoMS->>Email: send invite email
Email-->>EcoMS: queued
EcoMS-->>NATS: return IEcosystemInvitations
NATS-->>PlatformSvc: deliver response
PlatformSvc-->>PlatformCtrl: return created invitation
PlatformCtrl-->>Admin: 201 Created + payload
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Suggested labels
Suggested reviewers
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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 |
There was a problem hiding this comment.
Actionable comments posted: 11
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
apps/utility/src/utilities.service.ts (1)
83-109:⚠️ Potential issue | 🟡 MinorSet
isSendingAlertbefore the async config fetch to make the single-flight guard effective.
Because the flag is set afterawait getPlatformConfigDetails, concurrent calls can both pass the guard and trigger parallel retry flows.Proposed fix
async handleLedgerAlert(emailDto: EmailDto): Promise<void> { const now = Date.now(); @@ if (this.isSendingAlert) { this.logger.log('Alert email sending already in progress, skipping...'); return; } - const platformConfigData = await this.utilitiesRepository.getPlatformConfigDetails(); - if (!platformConfigData) { - throw new NotFoundException(ResponseMessages.issuance.error.platformConfigNotFound); - } - - emailDto.emailFrom = platformConfigData?.emailFrom; - - // 3. Start async retry flow — do not block the caller - this.isSendingAlert = true; - this.sendWithRetry(emailDto).finally(() => { - this.isSendingAlert = false; - }); + this.isSendingAlert = true; + try { + const platformConfigData = await this.utilitiesRepository.getPlatformConfigDetails(); + if (!platformConfigData) { + throw new NotFoundException(ResponseMessages.issuance.error.platformConfigNotFound); + } + emailDto.emailFrom = platformConfigData?.emailFrom; + + // 3. Start async retry flow — do not block the caller + this.sendWithRetry(emailDto).finally(() => { + this.isSendingAlert = false; + }); + } catch (err) { + this.isSendingAlert = false; + throw err; + } }libs/prisma-service/prisma/seed.ts (1)
245-256:⚠️ Potential issue | 🔴 CriticalLogic bug: Condition always prevents record creation.
The condition
if (!userId && !orgId && !orgRoleId)at line 245 only executes the create block when ALL three are falsy/null. However, if any of them is null (e.g., user not found), you'd want to skip creation. The current logic means records are only created when the lookups fail, but then lines 248-250 would throw errors trying to access.idon null values.This appears to be inverted logic. Consider:
🐛 Proposed fix
- if (!userId && !orgId && !orgRoleId) { + if (userId && orgId && orgRoleId) { const platformOrganization = await prisma.user_org_roles.create({ data: { userId: userId.id, orgRoleId: orgRoleId.id, orgId: orgId.id } }); logger.log(platformOrganization); } else { - logger.log('Already seeding in org_roles'); + logger.log('Skipping user_org_roles: required entities not found'); }
🤖 Fix all issues with AI agents
In `@apps/api-gateway/src/ecosystem/dtos/send-ecosystem-invitation.ts`:
- Around line 37-49: UpdateEcosystemInvitationDto currently requires ecosystemId
which blocks deferred-ecosystem flows; make the property optional and allow null
by adding `@IsOptional`() and changing the TypeScript type to optional/nullable
(e.g., ecosystemId?: string | null), remove the `@IsNotEmpty`() decorator from the
ecosystemId field, keep `@IsUUID`()/@IsString() and the `@Transform`(trim) so
validations only run when a value is provided; target the
UpdateEcosystemInvitationDto -> ecosystemId property and adjust decorators/type
accordingly.
In `@apps/api-gateway/src/ecosystem/ecosystem.service.ts`:
- Around line 72-74: The deleteEcosystemOrgs method sends a payload key userIds
but the microservice handler expects orgIds, causing a mismatch; update the
payload in this.natsClient.sendNatsMessage call inside deleteEcosystemOrgs to
send { orgIds: userIds, ecosystemId } (or rename the method parameter to orgIds
and send { orgIds, ecosystemId }) so the key matches the handler for the
'delete-ecosystem-orgs' message in ecosystem.controller.
In `@apps/ecosystem/repositories/ecosystem.repository.ts`:
- Around line 119-171: The code in createNewEcosystem creates the ecosystem
before validating ecosystemRoleDetails, which can leave an orphaned ecosystem if
the role is missing; move the prisma.ecosystem_roles.findFirst() check and the
null check for ecosystemRoleDetails to before calling prisma.ecosystem.create
(and throw NotFoundException there), or perform both operations inside a single
atomic Prisma transaction so the create is rolled back if the role is absent;
reference ecosystemRoleDetails, prisma.ecosystem_roles.findFirst,
prisma.ecosystem.create, and the NotFoundException when making the change.
In `@apps/ecosystem/src/ecosystem.controller.ts`:
- Around line 239-242: The message handler deleteIntentTemplate currently
expects a bare string id but the gateway sends an object { id }; update the
handler signature to accept the payload object (e.g.,
deleteIntentTemplate(payload: { id: string }) or destructure
deleteIntentTemplate({ id }: { id: string })) and call
this.ecosystemService.deleteIntentTemplate(id) so the correct id is forwarded to
the service.
- Around line 193-206: The MessagePattern handlers getIntentTemplateById,
getIntentTemplatesByIntentId, and getIntentTemplatesByOrgId currently declare
primitive parameters but the gateway sends wrapped objects; update each handler
signature to accept the payload object (e.g., payload: { id: string } for
getIntentTemplateById, payload: { intentId: string } for
getIntentTemplatesByIntentId, payload: { orgId: string } for
getIntentTemplatesByOrgId), extract the id (const { id } = payload etc.) and
pass that extracted string to the corresponding ecosystemService methods
(ecosystemService.getIntentTemplateById, .getIntentTemplatesByIntentId,
.getIntentTemplatesByOrgId).
In `@apps/ecosystem/templates/invite-member-template.ts`:
- Around line 7-48: The template directly injects process.env values and lacks
validation/escaping; add a small pre-template step (similar to
CreateEcosystemInviteTemplate) that verifies required env vars (BRAND_LOGO,
PLATFORM_NAME, PUBLIC_PLATFORM_SUPPORT_EMAIL, POWERED_BY) are present and
throws/returns a clear error if missing, then HTML-escape each value (e.g.,
escapeBrandLogo, escapePlatformName, escapeSupportEmail, escapePoweredBy) and
use those escaped variables inside the returned string instead of process.env.*
to prevent runtime errors and injection.
- Around line 4-6: The template inserts ecosystemName into HTML without
escaping, creating an XSS risk; update sendInviteEmailTemplate to
sanitize/escape ecosystemName (e.g., pass ecosystemName through the existing
escapeHtml function or another trusted sanitizer) before embedding it in the
returned HTML string so all user-controlled values are escaped like the
safeEmail usage.
In `@Dockerfiles/Dockerfile.ecosystem`:
- Line 44: The Docker CMD currently runs migrations ("prisma migrate deploy") at
container start which risks concurrent runs in multi-replica deployments; remove
the migration step from the CMD in Dockerfile.ecosystem (the CMD that executes
"cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate &&
...") and instead run migrations outside the app container (e.g., as a separate
init container or Kubernetes Job, a CI/CD step before rollout, or via a
dedicated migration service/lock mechanism) so only one process performs "prisma
migrate deploy" while the app startup keeps only the generate and node start
steps.
- Around line 10-17: The Dockerfile runs `RUN pnpm i --ignore-scripts` without
copying the pnpm lockfile, making builds non-reproducible; update the Dockerfile
so it copies `pnpm-lock.yaml` into the image prior to running `pnpm i` (i.e.,
add a `COPY pnpm-lock.yaml ./` step before the `RUN pnpm i --ignore-scripts`
line), ensuring `package.json`, `pnpm-workspace.yaml`, and the lockfile are
present for deterministic installs.
In
`@libs/prisma-service/prisma/data/credebl-master-table/credebl-master-table.json`:
- Line 29: Replace the hardcoded "Admin@123" value for the JSON key
platformAdminKeycloakPassword with an empty string placeholder, and update the
seeding logic that consumes this field (the seed function that loads
credebl-master-table data) to read the real password from an environment
variable named PLATFORM_ADMIN_KEYCLOAK_PASSWORD at runtime instead of relying on
the JSON value; ensure the seed code falls back to an error or skip if the env
var is missing.
In `@libs/prisma-service/prisma/seed.ts`:
- Around line 141-167: The code in createEcosystemRoles reads ecosystemRoleData
from JSON parsed from configData but that key is missing in
credebl-master-table.json, causing ecosystemRoleData to be undefined and
ecosystemRoleData.map() to throw; fix by validating the parsed value before use:
after parsing configData (in createEcosystemRoles) check that ecosystemRoleData
is an array (e.g., typeof/Array.isArray check) and if missing either default to
an empty array or log and return early to avoid calling .map, and update the
error/log messages around prisma.ecosystem_roles.createMany to reflect the
conditional behavior so seeding only runs when valid data exists.
🟡 Minor comments (20)
pnpm-workspace.yaml-22-22 (1)
22-22:⚠️ Potential issue | 🟡 MinorMake
@nestjs/cliversion specifier consistent with other@nestjsdependencies.
@nestjs/cliis pinned to exact version11.0.10, while all other@nestjspackages in the catalog use caret (^) for patch flexibility. This inconsistency causes the CLI to miss patch updates; the latest version is11.0.14.Change
@nestjs/clito^11.0.10to match the dependency pattern and receive security/patch updates automatically.libs/org-roles/enums/index.ts-1-12 (1)
1-12:⚠️ Potential issue | 🟡 MinorInconsistent naming convention for ecosystem role values.
Existing roles use
snake_casevalues (e.g.,'owner','super_admin'), but the new ecosystem roles useTitle Case(e.g.,'Ecosystem Lead'). This inconsistency appears throughout the codebase:
- Defined in both
OrgRolesandEcosystemRolesenums- Stored in constants (
ECOSYSTEM_ROLESin common.constant.ts)- Used in database queries and seed data
While the
Title Caseformat is the current database value (not just display), this violates the established naming pattern and could cause confusion for developers working with role strings.Align ecosystem roles with the existing
snake_caseconvention by changing:
'Ecosystem Lead'→'ecosystem_lead''Ecosystem Member'→'ecosystem_member'Note: This will require a database migration to update existing role records.
apps/ecosystem/templates/create-ecosystem.templates.ts-13-20 (1)
13-20:⚠️ Potential issue | 🟡 MinorEnvironment variables are escaped before validation check.
escapeHtml()is called onprocess.env.BRAND_LOGO, etc. (lines 13-16) before checking if they exist (lines 17-20). If any env var isundefined,escapeHtml(undefined)will throw an error or produce unexpected results, bypassing your helpful error message.🔧 Proposed fix to reorder validation
public sendInviteEmailTemplate(isUserExist: boolean): string { const requiredEnvVars = [ 'FRONT_END_URL', 'PLATFORM_NAME', 'BRAND_LOGO', 'PUBLIC_PLATFORM_SUPPORT_EMAIL', 'POWERED_BY' ]; + const missingVars = requiredEnvVars.filter((varName) => !process.env[varName]); + if (missingVars.length > 0) { + throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`); + } + const brandLogo = escapeHtml(process.env.BRAND_LOGO); const platformName = escapeHtml(process.env.PLATFORM_NAME); const supportEmail = escapeHtml(process.env.PUBLIC_PLATFORM_SUPPORT_EMAIL); const poweredBy = escapeHtml(process.env.POWERED_BY); - const missingVars = requiredEnvVars.filter((varName) => !process.env[varName]); - if (0 < missingVars.length) { - throw new Error(`Missing required environment variables: ${missingVars.join(', ')}`); - }apps/api-gateway/src/authz/guards/ecosystem-roles.guard.ts-52-58 (1)
52-58:⚠️ Potential issue | 🟡 MinorFix the
find()callback to always return a boolean.
Current callback has no explicit return on the non-match path, and a lint error is already reported..some()is cleaner here.✅ Suggested fix
- // eslint-disable-next-line array-callback-return - const isPlatformAdminFlag = user.userOrgRoles.find((orgDetails) => { - if (orgDetails.orgRole.name === OrgRoles.PLATFORM_ADMIN) { - return true; - } - }); + const isPlatformAdminFlag = user.userOrgRoles.some( + (orgDetails) => orgDetails.orgRole.name === OrgRoles.PLATFORM_ADMIN + );apps/api-gateway/src/authz/jwt.strategy.ts-124-129 (1)
124-129:⚠️ Potential issue | 🟡 MinorResolve the array-bracket-newline lint error in ecosystem role list.
The current formatting violates the lint rule; tightening the brackets fixes it without changing logic.🧹 Suggested formatting fix
- const ecosystemRoleList = [ - ...new Set(ecosystemRole.map((record: { ecosystemRole: { name: string } }) => record.ecosystemRole.name)) - ]; + const ecosystemRoleList = [...new Set( + ecosystemRole.map((record: { ecosystemRole: { name: string } }) => record.ecosystemRole.name) + )];libs/common/src/response-messages/index.ts-163-287 (1)
163-287:⚠️ Potential issue | 🟡 MinorFix user-facing typos/misleading wording in ecosystem messages.
A few error strings are misleading or grammatically incorrect, which can confuse clients and support.📝 Suggested message-only corrections
- updatedEcosytemOrg: 'Updated ecosytem org successfully', + updatedEcosytemOrg: 'Updated ecosystem org successfully', - signTransactionNotApplicable: 'Signing transaction for w3c schema is not aapllicable', + signTransactionNotApplicable: 'Signing transaction for w3c schema is not applicable', - credentialDefinitionNotFound: 'Credential definition found', + credentialDefinitionNotFound: 'Credential definition not found', - invalidFormatOfIntentId: 'Invalid format of ecosystemId', + invalidFormatOfIntentId: 'Invalid format of intentId', - noRecordsFound: 'No records not found', + noRecordsFound: 'No records found',libs/prisma-service/prisma/seed.ts-694-702 (1)
694-702:⚠️ Potential issue | 🟡 MinorPassword decryption may produce empty string on failure.
At line 694,
CryptoJS.AES.decrypt()returns aWordArray. If decryption fails (wrong key, corrupted data),toString(CryptoJS.enc.Utf8)returns an empty string rather than throwing. Line 701 uses this potentially empty password directly.Consider validating the decrypted password:
🛡️ Proposed validation
const decryptedPassword = CryptoJS.AES.decrypt(platformAdminData.password, CRYPTO_PRIVATE_KEY); + const passwordString = decryptedPassword.toString(CryptoJS.enc.Utf8); + if (!passwordString) { + throw new Error('Failed to decrypt platform admin password. Check CRYPTO_PRIVATE_KEY.'); + } const token = await getKeycloakToken(); const user = { username: cachedConfig.platformEmail, email: cachedConfig.platformEmail, firstName: cachedConfig.platformName, lastName: cachedConfig.platformName, - password: decryptedPassword.toString(CryptoJS.enc.Utf8) + password: passwordString };libs/prisma-service/prisma/seed.ts-546-550 (1)
546-550:⚠️ Potential issue | 🟡 MinorPotential access to uninitialized
cachedConfig.
updateClientIdaccessescachedConfig.platformEmailat line 547, butcachedConfigis only populated bygetPlatformConfig(). Whilemain()callsgetPlatformConfig()beforeupdateClientId(), the function is exported and could be called independently, causing a runtime error.Consider adding a guard or making the dependency explicit:
🛡️ Proposed defensive check
export const updateClientId = async (): Promise<void> => { const { KEYCLOAK_MANAGEMENT_CLIENT_ID, CRYPTO_PRIVATE_KEY } = process.env; if (!KEYCLOAK_MANAGEMENT_CLIENT_ID || !CRYPTO_PRIVATE_KEY) { throw new Error('Missing required environment variables'); } + if (!cachedConfig) { + throw new Error('Platform config not loaded. Call getPlatformConfig() first.'); + } + const OLD_CLIENT_ID = process.env.PLATFORM_ADMIN_OLD_CLIENT_ID;apps/api-gateway/src/ecosystem/ecosystem.controller.ts-543-551 (1)
543-551:⚠️ Potential issue | 🟡 MinorMissing UUID validation on
orgIdparameter.The
orgIdparameter should be validated withParseUUIDPipefor consistency with other endpoints.apps/ecosystem/src/ecosystem.service.ts-636-642 (1)
636-642:⚠️ Potential issue | 🟡 MinorMissing error handling in
getTemplatesByOrgIdmethod.This method lacks the try/catch pattern with
ErrorHandlerused by similar methods likegetIntentTemplatesByOrgId.apps/api-gateway/src/ecosystem/ecosystem.controller.ts-304-317 (1)
304-317:⚠️ Potential issue | 🟡 MinorInconsistent HTTP status code: returning CREATED for an update operation.
Line 309 returns
HttpStatus.CREATEDfor successful org status update. UseHttpStatus.OKinstead.🔧 Proposed fix
if (0 < result.count) { const finalResponse: IResponse = { statusCode: HttpStatus.OK, message: ResponseMessages.ecosystem.success.updatedEcosytemOrg }; - return res.status(HttpStatus.CREATED).json(finalResponse); + return res.status(HttpStatus.OK).json(finalResponse); }apps/api-gateway/src/ecosystem/ecosystem.controller.ts-265-280 (1)
265-280:⚠️ Potential issue | 🟡 MinorInconsistent HTTP status code: returning CREATED for a delete operation.
Line 272 returns
HttpStatus.CREATEDfor successful deletion. UseHttpStatus.OKinstead.🔧 Proposed fix
if (0 < result.count) { const finalResponse: IResponse = { statusCode: HttpStatus.OK, message: `${result.count} ${ResponseMessages.ecosystem.success.deletionSuccessfull} for ecosystem id ${deleteUser.ecosystemId}` }; - return res.status(HttpStatus.CREATED).json(finalResponse); + return res.status(HttpStatus.OK).json(finalResponse); }apps/api-gateway/src/ecosystem/ecosystem.controller.ts-145-158 (1)
145-158:⚠️ Potential issue | 🟡 MinorInconsistent HTTP status code: returning CREATED for an update operation.
Line 150 returns
HttpStatus.CREATED(201) for a successful status update, but the HTTP semantics suggestHttpStatus.OK(200) should be used for updates. ThestatusCodein the response body correctly showsOK, but the actual HTTP response status isCREATED.🔧 Proposed fix
if (result) { const finalResponse: IResponse = { statusCode: HttpStatus.OK, message: `${ResponseMessages.ecosystem.success.updateInvitation} as ${updateInvitation.status}` }; - return res.status(HttpStatus.CREATED).json(finalResponse); + return res.status(HttpStatus.OK).json(finalResponse); }apps/ecosystem/src/ecosystem.service.ts-677-685 (1)
677-685:⚠️ Potential issue | 🟡 MinorMissing error handling in
deleteIntentmethod.Unlike other intent CRUD operations (
createIntent,updateIntent,getIntents), this method doesn't wrap the repository call in try/catch withErrorHandler.categorize(). If the repository throws, the error won't be properly formatted as anRpcException.🛡️ Proposed fix to add consistent error handling
async deleteIntent(data: { ecosystemId: string; intentId: string; userId: string }): Promise<object> { const { ecosystemId, intentId, userId } = data; - return this.ecosystemRepository.deleteIntent({ - ecosystemId, - intentId, - userId - }); + try { + return await this.ecosystemRepository.deleteIntent({ + ecosystemId, + intentId, + userId + }); + } catch (error) { + const errorResponse = ErrorHandler.categorize(error, 'Failed to delete intent'); + this.logger.error( + `[deleteIntent] - ${errorResponse.statusCode}: ${errorResponse.message}`, + ErrorHandler.format(error) + ); + throw new RpcException(errorResponse); + } }apps/api-gateway/src/ecosystem/ecosystem.service.ts-102-111 (1)
102-111:⚠️ Potential issue | 🟡 MinorIncorrect return type annotations.
getEcosystemDetailsByUserIdreturnsPromise<user>but should returnPromise<ecosystem>based on the method name and microservice implementationgetEcosystemOrgDetailsByUserIdreturnsPromise<user>but should returnPromise<{ ecosystemRole: { name: string } }[]>based on the microserviceThese incorrect types will cause TypeScript to allow invalid usage patterns.
🔧 Proposed fix
+import { ecosystem } from '@prisma/client'; + - async getEcosystemDetailsByUserId(userId: string): Promise<user> { + async getEcosystemDetailsByUserId(userId: string): Promise<ecosystem> { return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-ecosystem-details-by-userid', { userId }); } - async getEcosystemOrgDetailsByUserId(userId: string, ecosystemId: string): Promise<user> { + async getEcosystemOrgDetailsByUserId(userId: string, ecosystemId: string): Promise<{ ecosystemRole: { name: string } }[]> { return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-ecosystem-org-details-by-userid', { userId, ecosystemId }); }apps/api-gateway/src/ecosystem/ecosystem.controller.ts-88-117 (1)
88-117:⚠️ Potential issue | 🟡 MinorError handling swallows stack trace and may leak internal details.
The try/catch block catches all errors and returns a generic 500 response for non-conflict errors, which loses valuable debugging information. The error message from ConflictException is directly exposed to the client without sanitization.
Consider letting the
CustomExceptionFilterhandle errors consistently, or at least log the error before returning a generic response.♻️ Suggested improvement
try { await this.ecosystemService.inviteMemberToEcosystem( inviteMemberToEcosystem.orgId, reqUser.id, inviteMemberToEcosystem.ecosystemId ); const finalResponse: IResponse = { statusCode: HttpStatus.CREATED, message: ResponseMessages.ecosystem.success.memberInviteSucess }; return res.status(HttpStatus.CREATED).json(finalResponse); } catch (error) { if (error instanceof ConflictException || HttpStatus.CONFLICT === error.status) { return res.status(HttpStatus.CONFLICT).json({ - status: HttpStatus.CONFLICT, + statusCode: HttpStatus.CONFLICT, message: error.message }); } - const finalResponse: IResponse = { - statusCode: HttpStatus.INTERNAL_SERVER_ERROR, - message: ResponseMessages.errorMessages.serverError - }; - - return res.status(HttpStatus.INTERNAL_SERVER_ERROR).json(finalResponse); + // Let the global exception filter handle unexpected errors + throw error; }apps/api-gateway/src/ecosystem/ecosystem.controller.ts-599-612 (1)
599-612:⚠️ Potential issue | 🟡 MinorMissing UUID validation on
idparameter.The
idparameter lacksParseUUIDPipevalidation, unlike similar endpoints in this controller (e.g.,getIntentTemplateByIdat line 566). This inconsistency could allow malformed IDs to reach the service layer.🔧 Proposed fix
async updateIntentTemplate( - `@Param`('id') id: string, + `@Param`( + 'id', + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.oid4vpIntentToTemplate.error.invalidId); + } + }) + ) + id: string, `@Body`() updateIntentTemplateDto: UpdateIntentTemplateDto,apps/api-gateway/src/ecosystem/ecosystem.service.ts-119-121 (1)
119-121:⚠️ Potential issue | 🟡 MinorPayload structure inconsistency with other methods.
This method sends
iddirectly as a primitive while other methods wrap IDs in objects (e.g.,deleteIntentTemplatesends{ id }). This inconsistency was also flagged in the microservice controller review - they should be aligned.🔧 Suggested alignment with other methods
async getIntentTemplateById(id: string): Promise<object> { - return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-intent-template-by-id', id); + return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-intent-template-by-id', { id }); }Note: This requires the corresponding change in
apps/ecosystem/src/ecosystem.controller.tsas noted in the earlier review.apps/api-gateway/src/platform/platform.controller.ts-212-222 (1)
212-222:⚠️ Potential issue | 🟡 MinorMissing validation for environment variables in QR code URL construction.
The URL is constructed using
process.env.API_GATEWAY_PROTOCOLandprocess.env.API_ENDPOINTwithout checking if they are defined. If either is missing, the URL will contain "undefined", producing an invalid QR code.🛡️ Proposed fix to add validation
async getQrCode(`@Param`('referenceId') referenceId: string, `@Res`() res: Response): Promise<void> { + if (!process.env.API_GATEWAY_PROTOCOL || !process.env.API_ENDPOINT) { + throw new BadRequestException('API gateway configuration is incomplete'); + } const url = `${process.env.API_GATEWAY_PROTOCOL}://${process.env.API_ENDPOINT}/invitation/${referenceId}`; // Generate QR code as a buffer const qrCodeBuffer = await QRCode.toBuffer(url);apps/api-gateway/src/ecosystem/ecosystem.controller.ts-626-634 (1)
626-634:⚠️ Potential issue | 🟡 MinorMissing UUID validation on
idparameter for delete operation.Same issue as the update endpoint - add
ParseUUIDPipefor consistency and input validation.🔧 Proposed fix
- async deleteIntentTemplate(`@Param`('id') id: string, `@Res`() res: Response): Promise<Response> { + async deleteIntentTemplate( + `@Param`( + 'id', + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.oid4vpIntentToTemplate.error.invalidId); + } + }) + ) + id: string, + `@Res`() res: Response + ): Promise<Response> {
🧹 Nitpick comments (15)
libs/org-roles/enums/index.ts (1)
14-17: Consider removing duplicate enum or referencing OrgRoles.
EcosystemRolesduplicates values already defined inOrgRoles. This creates maintenance burden—if values change, both enums must be updated.♻️ Proposed refactor to reference OrgRoles
export enum EcosystemRoles { - ECOSYSTEM_LEAD = 'Ecosystem Lead', - ECOSYSTEM_MEMBER = 'Ecosystem Member' + ECOSYSTEM_LEAD = OrgRoles.ECOSYSTEM_LEAD, + ECOSYSTEM_MEMBER = OrgRoles.ECOSYSTEM_MEMBER }Alternatively, if
EcosystemRolesis used for type narrowing, consider using a union type instead:export type EcosystemRoles = OrgRoles.ECOSYSTEM_LEAD | OrgRoles.ECOSYSTEM_MEMBER;apps/api-gateway/src/ecosystem/dtos/delete-ecosystem-users.ts (2)
14-20: Redundant@IsString({ each: true })decorator.
@IsUUID('4', { each: true })already validates that each element is a valid UUID string. The additional@IsStringcheck is redundant.♻️ Proposed simplification
`@IsArray`({ message: 'orgId must be an array' }) `@ArrayNotEmpty`({ message: 'orgId cannot be empty' }) `@IsUUID`('4', { each: true }) - `@IsString`({ each: true }) `@Transform`(({ value }) => Array.isArray(value) ? value.map(v => v.trim()) : value ) orgIds: string[];
22-27: Redundant@IsStringdecorator for ecosystemId.Similar to above,
@IsUUID()already validates that the value is a valid UUID string.♻️ Proposed simplification
`@ApiProperty`({ example: '61ec22e3-9158-409d-874d-345ad2fc51e4' }) `@IsUUID`() `@IsNotEmpty`({ message: 'ecosystemId is required' }) - `@IsString`({ message: 'ecosystemId should be a string' }) `@Transform`(({ value }) => value?.trim()) ecosystemId: string;libs/prisma-service/prisma/migrations/20260114071535_create_ecosystem/migration.sql (1)
1-17: Consider adding unique constraint on ecosystem name.The
ecosystemtable allows duplicate names since there's no unique constraint. If ecosystem names should be unique across the platform, consider adding a unique index. If duplicates are intentional (e.g., soft-deleted ecosystems), this is acceptable.apps/ecosystem/repositories/ecosystem.repository.ts (2)
1149-1169: Potential race condition indeleteIntent.The method performs a
findFirstto check existence, then a separatedeleteMany. Between these operations, another process could delete the intent, causing inconsistent state. Since the FK constraint isON DELETE RESTRICT, there's no cascade risk, but the operation could be simplified.Consider using a single delete operation:
♻️ Simplified delete with existence check
async deleteIntent(data: { ecosystemId: string; intentId: string; userId: string }): Promise<intents> { - const intent = await this.prisma.intents.findFirst({ - where: { - id: data.intentId, - ecosystemId: data.ecosystemId - } - }); - - if (!intent) { - throw new NotFoundException('Intent not found in the given ecosystem'); - } - - await this.prisma.intents.deleteMany({ - where: { - id: data.intentId, - ecosystemId: data.ecosystemId - } - }); - - return intent; + try { + return await this.prisma.intents.delete({ + where: { + id: data.intentId, + // Add composite where if schema supports it, or verify ecosystemId after fetch + } + }); + } catch (error) { + if (error instanceof Prisma.PrismaClientKnownRequestError && error.code === 'P2025') { + throw new NotFoundException('Intent not found in the given ecosystem'); + } + throw error; + } }Note: The current implementation works correctly; this is an optimization suggestion.
1073-1087: Avoidanytype for where clause.Line 1075 uses
anytype for the where clause. Consider using Prisma's generated types for type safety:♻️ Use Prisma types
async getIntents(ecosystemId: string, intentId?: string): Promise<intents[]> { - // eslint-disable-next-line `@typescript-eslint/no-explicit-any` - const where: any = { + const where: Prisma.intentsWhereInput = { ecosystemId // ✅ ALWAYS applied };apps/ecosystem/interfaces/ecosystem.interfaces.ts (1)
44-58: Consider renaming toEcosystemOrgAgentto avoid confusion with existingOrgAgentinterfaces.This
OrgAgentinterface has a fundamentally different structure from theOrgAgentdefinitions in five other modules (connection, issuance, oid4vc-issuance, oid4vp-verifier, x509). The existing interfaces include audit tracking fields (createDateTime,createdBy,lastChangedDateTime,lastChangedBy), anagentId, and anorganisationfield, while the ecosystem version omits these and instead includesagentSpinUpStatus,walletName,agentsTypeId, andorgId.Since this is currently scoped to the ecosystem module with no cross-module imports, there's no immediate type conflict. However, the different naming could cause confusion if future code attempts to reuse or reference OrgAgent types across modules. Renaming to
EcosystemOrgAgentwould make the distinction explicit and prevent potential mistakes.apps/api-gateway/src/authz/guards/ecosystem-swagger.filter.ts (1)
13-28: Prune only HTTP operations and drop empty tag entries.
After deletions, a path can be left with only non-operation keys (e.g.,parameters), and the roottagslist may still includeecosystemwith no operations. Consider filtering only HTTP methods and removing empty paths/tags to keep the document clean. Please verify against your@nestjs/swagger/OpenAPIPathItem shape for the current version.♻️ Suggested refactor
+ const httpMethods = new Set(['get', 'post', 'put', 'patch', 'delete', 'options', 'head', 'trace']); if (!enabled) { if (!document.paths) { return document; } Object.keys(document.paths).forEach((path) => { - Object.keys(document.paths[path]).forEach((method) => { - const operation = document.paths[path][method]; + const pathItem = document.paths[path]; + Object.keys(pathItem).forEach((method) => { + if (!httpMethods.has(method)) { + return; + } + const operation = pathItem[method]; if (operation.tags?.includes('ecosystem')) { - delete document.paths[path][method]; + delete pathItem[method]; } }); - if (0 === Object.keys(document.paths[path]).length) { + const hasOps = Object.keys(pathItem).some((k) => httpMethods.has(k)); + if (!hasOps) { delete document.paths[path]; } }); + if (document.tags) { + document.tags = document.tags.filter((t) => t.name !== 'ecosystem'); + } }apps/ecosystem/dtos/update-intent.dto.ts (1)
1-11: Add@ApiPropertydecorators to ID fields for complete Swagger documentation and pattern consistency.While these fields are populated from URL parameters and decorators (bypassing the whitelist validation), they lack
@ApiPropertydecorators, preventing them from appearing in the OpenAPI schema. This creates a documentation gap in Swagger. Additionally, this breaks the pattern established byIntentBaseDto, which is fully decorated. Add@ApiPropertyto ensure consistency and accurate API documentation.Suggested addition
-import { ApiExtraModels } from '@nestjs/swagger'; +import { ApiExtraModels, ApiProperty } from '@nestjs/swagger'; import { IntentBaseDto } from './intent-base.dto'; `@ApiExtraModels`() export class UpdateIntentDto extends IntentBaseDto { + `@ApiProperty`() intentId: string; + `@ApiProperty`() ecosystemId: string; + `@ApiProperty`() userId: string; }apps/api-gateway/src/ecosystem/ecosystem.module.ts (1)
6-24: Simplify module imports to avoid unnecessary heavyweight dependencies.The gateway's ecosystem module imports the full
EcosystemServiceModulebut only needsEcosystemRepository(used in authz guards). Importing the entire app module bringsOrganizationModule,UserModule,PrismaService, and other providers that the gateway never uses. Additionally, the gateway registers its ownNATS_CLIENTseparately despite the imported module also providing one, creating redundancy.Consider exporting
EcosystemRepositoryfrom a lightweight dedicated module or importing it directly, rather than coupling to the full ecosystem app module.apps/api-gateway/src/platform/platform.controller.ts (2)
32-38: Unused import:userfrom@prisma/client.The
usertype imported from@prisma/clienton line 32 is used as parameter type increateInvitation,getInvitations, andupdateEcosystemConfigendpoints, but these endpoints already have guards that validate the user. Consider using the more descriptiveIUserRequestInterfacetype that's already imported (line 20) for consistency with other endpoints in this controller (getAllSchema,getAllCredDefs).
240-253: Consider consistent response structure.This endpoint constructs the response inline rather than using the
IResponseinterface pattern followed by other endpoints in this controller. While functionally correct, consider using the same pattern for consistency:♻️ Suggested refactor for consistency
async createInvitation( `@Body`() createEcosystemInvitationDto: CreateEcosystemInvitationDto, `@User`() reqUser: user, `@Res`() res: Response ): Promise<Response> { await this.platformService.inviteUserToCreateEcosystem(createEcosystemInvitationDto.email, reqUser.id); - return res.status(HttpStatus.CREATED).json({ - statusCode: HttpStatus.CREATED, - message: ResponseMessages.ecosystem.success.createInvitation - }); + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.ecosystem.success.createInvitation + }; + return res.status(HttpStatus.CREATED).json(finalResponse); }apps/api-gateway/src/ecosystem/ecosystem.controller.ts (1)
423-429: Hardcoded success messages instead of ResponseMessages constants.Intent template endpoints use hardcoded strings like
'Intent template created successfully'instead of centralizedResponseMessagesconstants used elsewhere in the codebase. This affects maintainability and i18n support.Consider adding these messages to
ResponseMessages.oid4vpIntentToTemplateor a similar namespace.apps/ecosystem/src/ecosystem.controller.ts (1)
23-26: Logger instance is declared but never used.The logger at line 26 is instantiated but none of the handler methods use it for logging. Consider either using it for request/error logging or removing it to reduce dead code.
apps/ecosystem/src/ecosystem.service.ts (1)
118-139: Consider using ErrorHandler for consistent error handling.This method creates custom
HttpExceptioninstances while other methods in this service useErrorHandler.categorize()andRpcException. Consider using the same pattern for consistency.
| export class UpdateEcosystemInvitationDto { | ||
| @ApiProperty({ enum: Invitation, example: Invitation.ACCEPTED }) | ||
| @Transform(({ value }) => ('string' === typeof value ? value.toLowerCase() : value)) | ||
| @IsEnum(Invitation, { message: `Status must be one of: ${Object.values(Invitation).join(', ')}` }) | ||
| @IsNotEmpty({ message: 'Status is required' }) | ||
| status: Invitation; | ||
|
|
||
| @ApiProperty({ example: '61ec22e3-9158-409d-874d-345ad2fc51e4' }) | ||
| @IsUUID() | ||
| @IsNotEmpty({ message: 'ecosystemId is required' }) | ||
| @IsString({ message: 'ecosystemId should be a string' }) | ||
| @Transform(({ value }) => value?.trim()) | ||
| ecosystemId: string; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find all usages of UpdateEcosystemInvitationDto
rg -n --type=ts 'UpdateEcosystemInvitationDto' -C 5Repository: credebl/platform
Length of output: 3673
🏁 Script executed:
#!/bin/bash
# Get the full updateEcosystemInvitationStatus endpoint implementation
sed -n '131,170p' apps/api-gateway/src/ecosystem/ecosystem.controller.tsRepository: credebl/platform
Length of output: 1294
🏁 Script executed:
#!/bin/bash
# Find the ecosystemService.updateEcosystemInvitationStatus implementation
rg -n 'updateEcosystemInvitationStatus' --type=ts -A 15 | head -60Repository: credebl/platform
Length of output: 4694
🏁 Script executed:
#!/bin/bash
# Find the ecosystem.service.ts updateEcosystemInvitationStatus method
rg -n 'updateEcosystemInvitationStatus' apps/ecosystem/src/ecosystem.service.ts -A 25Repository: credebl/platform
Length of output: 3655
Make ecosystemId optional in UpdateEcosystemInvitationDto to support the deferred-ecosystem workflow.
The ecosystem_invitations model allows ecosystemId to be null, supporting a workflow where invitations are created before an ecosystem is assigned. However, the current DTO requires ecosystemId, which would block updates to invitations created in this deferred state. The service code's null check on result?.ecosystemId (line 327 of ecosystem.service.ts) confirms that null values are expected.
Suggested change
- `@ApiProperty`({ example: '61ec22e3-9158-409d-874d-345ad2fc51e4' })
- `@IsUUID`()
- `@IsNotEmpty`({ message: 'ecosystemId is required' })
- `@IsString`({ message: 'ecosystemId should be a string' })
- `@Transform`(({ value }) => value?.trim())
- ecosystemId: string;
+ `@ApiPropertyOptional`({ example: '61ec22e3-9158-409d-874d-345ad2fc51e4' })
+ `@IsOptional`()
+ `@IsUUID`()
+ `@IsString`({ message: 'ecosystemId should be a string' })
+ `@Transform`(({ value }) => value?.trim())
+ ecosystemId?: string;🤖 Prompt for AI Agents
In `@apps/api-gateway/src/ecosystem/dtos/send-ecosystem-invitation.ts` around
lines 37 - 49, UpdateEcosystemInvitationDto currently requires ecosystemId which
blocks deferred-ecosystem flows; make the property optional and allow null by
adding `@IsOptional`() and changing the TypeScript type to optional/nullable
(e.g., ecosystemId?: string | null), remove the `@IsNotEmpty`() decorator from the
ecosystemId field, keep `@IsUUID`()/@IsString() and the `@Transform`(trim) so
validations only run when a value is provided; target the
UpdateEcosystemInvitationDto -> ecosystemId property and adjust decorators/type
accordingly.
| COPY package.json ./ | ||
| COPY pnpm-workspace.yaml ./ | ||
| #COPY package-lock.json ./ | ||
|
|
||
| ENV PUPPETEER_SKIP_DOWNLOAD=true | ||
|
|
||
| # Install dependencies while ignoring scripts (including Puppeteer's installation) | ||
| RUN pnpm i --ignore-scripts |
There was a problem hiding this comment.
Missing pnpm lockfile for reproducible builds.
The pnpm-lock.yaml file is not copied before running pnpm i. This means builds are not reproducible and may install different dependency versions across builds.
🔧 Proposed fix
# Copy package.json and package-lock.json
COPY package.json ./
COPY pnpm-workspace.yaml ./
-#COPY package-lock.json ./
+COPY pnpm-lock.yaml ./
ENV PUPPETEER_SKIP_DOWNLOAD=true
# Install dependencies while ignoring scripts (including Puppeteer's installation)
-RUN pnpm i --ignore-scripts
+RUN pnpm i --frozen-lockfile --ignore-scripts🤖 Prompt for AI Agents
In `@Dockerfiles/Dockerfile.ecosystem` around lines 10 - 17, The Dockerfile runs
`RUN pnpm i --ignore-scripts` without copying the pnpm lockfile, making builds
non-reproducible; update the Dockerfile so it copies `pnpm-lock.yaml` into the
image prior to running `pnpm i` (i.e., add a `COPY pnpm-lock.yaml ./` step
before the `RUN pnpm i --ignore-scripts` line), ensuring `package.json`,
`pnpm-workspace.yaml`, and the lockfile are present for deterministic installs.
| COPY --from=build /app/node_modules ./node_modules | ||
|
|
||
| # Set the command to run the microservice | ||
| CMD ["sh", "-c", "cd libs/prisma-service && npx prisma migrate deploy && npx prisma generate && cd ../.. && node dist/apps/ecosystem/main.js"] No newline at end of file |
There was a problem hiding this comment.
Running migrations in CMD poses risks for multi-replica deployments.
When multiple container instances start simultaneously, they will all attempt to run prisma migrate deploy concurrently, which can cause race conditions or migration failures.
Consider:
- Running migrations as a separate init container or job in your orchestration (e.g., Kubernetes Job)
- Using a migration lock mechanism
- Running migrations as part of CI/CD before deployment
🤖 Prompt for AI Agents
In `@Dockerfiles/Dockerfile.ecosystem` at line 44, The Docker CMD currently runs
migrations ("prisma migrate deploy") at container start which risks concurrent
runs in multi-replica deployments; remove the migration step from the CMD in
Dockerfile.ecosystem (the CMD that executes "cd libs/prisma-service && npx
prisma migrate deploy && npx prisma generate && ...") and instead run migrations
outside the app container (e.g., as a separate init container or Kubernetes Job,
a CI/CD step before rollout, or via a dedicated migration service/lock
mechanism) so only one process performs "prisma migrate deploy" while the app
startup keeps only the generate and node start steps.
libs/prisma-service/prisma/data/credebl-master-table/credebl-master-table.json
Show resolved
Hide resolved
Signed-off-by: sujitaw <sujit.sutar@ayanworks.com>
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In `@libs/common/src/response-messages/index.ts`:
- Line 234: Update the three incorrect response strings in the response-messages
export: change credentialDefinitionNotFound value from "Credential definition
found" to "Credential definition not found"; change invalidFormatOfIntentId
value to reference the correct field by replacing "ecosystemId" with "intentId"
(e.g., "Invalid format for intentId"); and fix noRecordsFound to a sensible
positive statement like "No records found" instead of "No records not found".
Locate and edit the keys credentialDefinitionNotFound, invalidFormatOfIntentId,
and noRecordsFound in the file to apply these text corrections.
- Line 182: Fix the typos in the response messages by updating the string values
for the relevant keys: change deleteEcosystemMember value from "You are deleted
as a ecosystem member" to "You have been removed as an ecosystem member" (or
"You are deleted as an ecosystem member" → "You are removed as an ecosystem
member"), and correct any other misspelling of "ecosytem" to "ecosystem" (e.g.,
the message at the key near line 193). Locate and edit the entries in
libs/common/src/response-messages/index.ts (keys like deleteEcosystemMember and
the other message containing "ecosytem") to use the corrected grammar and
spelling.
- Around line 203-287: The error messages object contains multiple typos and
grammatical issues; update the specified keys in the exported error object to
use the suggested wording: change orgAlreadyExists value from "Organization is
already exists in ecosystem" to "Organization already exists in ecosystem",
exists from "Ecosystem name already exist" to "Ecosystem name already exists",
signTransactionNotApplicable from "aapllicable" to "applicable",
requestSchemaTransaction from "Error while request schema transaction" to "Error
while requesting schema transaction", updateTransactionError from "Error while
update the transaction" to "Error while updating the transaction",
schemaAlreadyExist / schemaNameAlreadyExist / credDefAlreadyExist values from
"already exist" to "already exists",
OrgOrEcosystemNotFoundExceptionForEndorsementTransaction message change "cant"
to "can't", ecosystemRoleNotMatch change "not match" to "does not match",
orgEcoIdRequired change "OrgId & EcosystemId is required" to "OrgId &
EcosystemId are required", ecosystemMembersNotExists change "does not exists" to
"do not exist", ecosystemNotExists change "does not exists" to "does not exist",
and remove the leading space in the message for checkFailed; edit these string
literals in the error object in libs/common/src/response-messages/index.ts
accordingly.
🧹 Nitpick comments (3)
libs/prisma-service/prisma/schema.prisma (3)
738-755: Consider using a proper relation forledgersinstead of Json.The
ledgersfield is defined asJson?, but there's an existingledgersmodel in the schema (lines 275-289). Using a Json field bypasses referential integrity and makes querying/filtering by ledger properties less efficient.If an ecosystem can have multiple ledgers, consider a many-to-many relation table. If it's configuration metadata that doesn't map cleanly to the existing
ledgersmodel, the Json field is acceptable—please clarify the intent.
757-766: Missing audit fieldscreatedByandlastChangedBy.The
ecosystem_rolesmodel lackscreatedByandlastChangedByfields that are present in most other models (e.g.,ecosystem,ecosystem_orgs,org_roles). This inconsistency could complicate audit trails.Proposed fix
model ecosystem_roles { id String `@id` `@default`(uuid()) `@db.Uuid` name String `@unique` description String createDateTime DateTime `@default`(now()) `@db.Timestamptz`(6) + createdBy String `@default`("1") lastChangedDateTime DateTime `@default`(now()) `@db.Timestamptz`(6) + lastChangedBy String `@default`("1") deletedAt DateTime? `@db.Timestamp`(6) ecosystemOrgs ecosystem_orgs[] }
789-806: Missing relation forinvitedOrgto organisation model.The
invitedOrgfield stores an organisation UUID but lacks a foreign key relation. This means:
- No referential integrity at the database level
- No Prisma-generated navigation property for joins
Based on learnings, deletions involving
invitedOrgare handled at the application level, but having the relation defined would still improve query ergonomics and data integrity.Proposed fix
model ecosystem_invitations { id String `@id` `@default`(uuid()) `@db.Uuid` email String status String userId String? `@db.Uuid` type String ecosystemId String? `@db.Uuid` invitedOrg String? `@db.Uuid` createDateTime DateTime `@default`(now()) `@db.Timestamptz`(6) createdBy String `@db.Uuid` lastChangedDateTime DateTime `@default`(now()) `@db.Timestamptz`(6) lastChangedBy String `@db.Uuid` deletedAt DateTime? `@db.Timestamp`(6) ecosystem ecosystem? `@relation`(fields: [ecosystemId], references: [id]) user user? `@relation`(fields: [userId], references: [id]) + organisation organisation? `@relation`(fields: [invitedOrg], references: [id]) @@unique([email, ecosystemId]) }You would also need to add the backward relation in the
organisationmodel:model organisation { ... ecosystemOrgs ecosystem_orgs[] + ecosystemInvitations ecosystem_invitations[] }
Signed-off-by: sujitaw <sujit.sutar@ayanworks.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@libs/common/src/response-messages/index.ts`:
- Line 193: Rename the typo key updatedEcosytemOrg to updatedEcosystemOrg in the
response messages export and update any usages to the new key; specifically
change the key in the response-messages index export (symbol: updatedEcosytemOrg
-> updatedEcosystemOrg) and update the reference in the EcosystemController code
where the old key is used (symbol: updatedEcosytemOrg) to the corrected
updatedEcosystemOrg so both definition and usage match.
| updateInvitation: 'Status for ecosystem invitation updated successfully', | ||
| deletionSuccessfull: 'Record deleted successfully', | ||
| userStatusUpdated: 'User status updated successfully', | ||
| updatedEcosytemOrg: 'Updated ecosystem org successfully', |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, check if the file exists and examine the context around line 193
if [ -f "libs/common/src/response-messages/index.ts" ]; then
echo "=== File exists. Showing context around line 193 ==="
sed -n '185,200p' libs/common/src/response-messages/index.ts | cat -n
else
echo "File not found. Listing directory structure:"
find libs -name "*response*" -type f 2>/dev/null | head -20
fiRepository: credebl/platform
Length of output: 1322
🏁 Script executed:
#!/bin/bash
# Search for all usages of the misspelled key
echo "=== Searching for 'updatedEcosytemOrg' usages ==="
rg -n 'updatedEcosytemOrg' --type ts --type js --type tsx --type jsxRepository: credebl/platform
Length of output: 154
🏁 Script executed:
#!/bin/bash
# Also search for the corrected version to see if it exists
echo "=== Searching for 'updatedEcosystemOrg' (corrected version) ==="
rg -n 'updatedEcosystemOrg' --type ts --type js --type tsx --type jsxRepository: credebl/platform
Length of output: 168
🏁 Script executed:
#!/bin/bash
# Get a broader view of the ecosystem-related keys in the response-messages file
echo "=== Searching for ecosystem-related keys in index.ts ==="
rg -n 'cosystem|cosytem' libs/common/src/response-messages/ -A 1 -B 1Repository: credebl/platform
Length of output: 10350
🏁 Script executed:
#!/bin/bash
# Search for usages of the misspelled key without file type filter
echo "=== Searching for 'updatedEcosytemOrg' usages (all files) ==="
rg -n 'updatedEcosytemOrg' 2>/dev/null || echo "No matches found or rg error"Repository: credebl/platform
Length of output: 362
🏁 Script executed:
#!/bin/bash
# Search for ResponseMessages references to ecosystem
echo "=== Searching for ResponseMessages.ecosystem usages ==="
rg -n 'ResponseMessages\.ecosystem' 2>/dev/null | head -30 || echo "No matches"Repository: credebl/platform
Length of output: 4249
🏁 Script executed:
#!/bin/bash
# Search for any references to this specific key
echo "=== Searching for response references with updatedEcosytem ==="
rg -n 'updatedEcosytem' 2>/dev/null | head -50 || echo "No matches"Repository: credebl/platform
Length of output: 363
🏁 Script executed:
#!/bin/bash
# Check the complete ecosystem.success object structure
echo "=== Getting full ecosystem.success object ==="
sed -n '163,202p' libs/common/src/response-messages/index.ts | cat -nRepository: credebl/platform
Length of output: 2870
Rename updatedEcosytemOrg to updatedEcosystemOrg to fix the key name typo.
The key name contains "Ecosytem" instead of "Ecosystem" and is actively referenced in the codebase at apps/api-gateway/src/ecosystem/ecosystem.controller.ts:307. Updating this requires changes in two locations:
libs/common/src/response-messages/index.ts:193— rename the keyapps/api-gateway/src/ecosystem/ecosystem.controller.ts:307— update the reference
🤖 Prompt for AI Agents
In `@libs/common/src/response-messages/index.ts` at line 193, Rename the typo key
updatedEcosytemOrg to updatedEcosystemOrg in the response messages export and
update any usages to the new key; specifically change the key in the
response-messages index export (symbol: updatedEcosytemOrg ->
updatedEcosystemOrg) and update the reference in the EcosystemController code
where the old key is used (symbol: updatedEcosytemOrg) to the corrected
updatedEcosystemOrg so both definition and usage match.
Signed-off-by: sujitaw <sujit.sutar@ayanworks.com>
|
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 Fix all issues with AI agents
In `@apps/api-gateway/src/ecosystem/ecosystem.controller.ts`:
- Around line 304-310: The response for successful updates in the update handler
returns HTTP 201 (HttpStatus.CREATED) while the finalResponse.statusCode is
HttpStatus.OK; update the controller (in the block that checks result.count > 0)
to send a 200 response by changing res.status(HttpStatus.CREATED) to
res.status(HttpStatus.OK) (or use finalResponse.statusCode) so the HTTP status
matches the update operation and the message in ecosystem.controller.ts.
- Around line 145-151: The handler currently returns HTTP 201 on successful
invitation updates; change it to return 200 to match the operation and the
response body. In the block that checks `if (result)`, update the
`res.status(...)` call from `HttpStatus.CREATED` to `HttpStatus.OK` and ensure
the `finalResponse.statusCode` remains `HttpStatus.OK` (no other body changes).
Look for `result`, `finalResponse`,
`ResponseMessages.ecosystem.success.updateInvitation`,
`updateInvitation.status`, and the `res.status(...).json(...)` call to make this
single-line status change.
- Around line 265-279: In deleteEcosystemUsers, the response status codes are
incorrect: when ecosystemService.deleteEcosystemOrgs returns a positive
result.count, change the HTTP status from HttpStatus.CREATED to HttpStatus.OK
and set finalResponse.statusCode to HttpStatus.OK (keep the success message),
and when no records are deleted change the returned status from
HttpStatus.BAD_REQUEST to HttpStatus.NOT_FOUND and set finalResponse.statusCode
to HttpStatus.NOT_FOUND using ResponseMessages.ecosystem.error.noRecordsFound;
update the res.status(...) calls accordingly in the deleteEcosystemUsers method.
- Around line 71-90: In inviteMemberToEcosystem replace the plain throw new
Error('Missing request user id') with a NestJS HTTP exception (e.g., throw new
BadRequestException('Missing request user id') or throw new
UnauthorizedException(...) as appropriate for your auth logic) so the client
receives a 4xx, and update the `@ApiResponse` on the same handler from status:
HttpStatus.OK to status: HttpStatus.CREATED to match the handler's 201 response
in inviteMemberToEcosystem.
* feat: ecosystem service and create ecosystem invitation API route (#1540) * feat/add script to add platform admin keycloak and role Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/eslint issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/coderabbit comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/changes to fetch value from db Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * feat: ecosystem service and create ecosystem invitation API route Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: get all invitations api Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: removed userId parameter from send invitation and get invitation api Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: create and get ecosystem api routes Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warning Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings and suggestions Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings resolved Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * resolved comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * resolve comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * resolve comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> --------- Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> Co-authored-by: sujitaw <sujit.sutar@ayanworks.com> * feat: Add script to add platform admin to keycloak and create user org roles (#1538) * feat/add script to add platform admin keycloak and role Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/eslint issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/coderabbit comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/changes to fetch value from db Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix pr comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix pr comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> --------- Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * feat: ecosystem member invitation and management API's (#1545) * feat/add script to add platform admin keycloak and role Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/eslint issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/coderabbit comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * feat: ecosystem service and create ecosystem invitation API route Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: get all invitations api Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * wip Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * wip completed invite member and update status for invitation Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * wip Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * wip Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * feat/added ecosystem invitation workflow apis Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/code rabbit comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/minor typo issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/ pr comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/pr comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> --------- Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> Co-authored-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix/version for nest-cli Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * feat: add dockerfile for ecosystem Signed-off-by: sahil.kamble@ayanworks.com <sahil.kamble@ayanworks.com> * feat: add ecosystemt in github actions workflow Signed-off-by: sahil.kamble@ayanworks.com <sahil.kamble@ayanworks.com> * feat: create intent API endpoints and intent template mapping APIs (#1547) * feat: create intent APIs Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: create intent mapping and create intent APIs Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: sonarlint issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: move validations from ecosystem repository to ecosystem service Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: sonarlint issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: sonarlint issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: sonarlint issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit suggestions Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> --------- Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: resloved issue for docker build (#1549) Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: Enable/disable ecosystem (#1550) * refactor: added isEcosystemEnabled flag in database in platform_config table Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: enable/disable ecosystem feature from database Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit suggestions Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: enable/disable ecosystem feature and delete intent API issue Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * coderabbit comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> --------- Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix/code rabbit issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/code rabbit issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> --------- Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> Signed-off-by: sahil.kamble@ayanworks.com <sahil.kamble@ayanworks.com> Co-authored-by: sujitaw <sujit.sutar@ayanworks.com> Co-authored-by: sahil.kamble@ayanworks.com <sahil.kamble@ayanworks.com>


What
nest-cli.Summary by CodeRabbit
New Features
Permissions
Chores
✏️ Tip: You can customize this high-level summary in your review settings.