From 9d1aed906dc82aca9e6f6a2153d9d972c7da7590 Mon Sep 17 00:00:00 2001 From: sujitaw Date: Tue, 17 Feb 2026 11:26:00 +0530 Subject: [PATCH 1/8] feat/add api for fetch ecosystem status Signed-off-by: sujitaw Signed-off-by: pranalidhanavade --- apps/ecosystem/repositories/ecosystem.repository.ts | 12 ++++++++++++ apps/ecosystem/src/ecosystem.service.ts | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/apps/ecosystem/repositories/ecosystem.repository.ts b/apps/ecosystem/repositories/ecosystem.repository.ts index afdb47954..79f33c3e1 100755 --- a/apps/ecosystem/repositories/ecosystem.repository.ts +++ b/apps/ecosystem/repositories/ecosystem.repository.ts @@ -1556,4 +1556,16 @@ export class EcosystemRepository { throw error; } } + + async getEcosystemEnableStatus(): Promise { + try { + const data = await this.prisma.platform_config.findFirst({ + select: { isEcosystemEnabled: true } + }); + return data.isEcosystemEnabled ? data.isEcosystemEnabled : false; + } catch (error) { + this.logger.error(`getEcosystemEnableStatus error: ${error}`); + throw error; + } + } } diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 145193226..5a47ada1e 100755 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -863,4 +863,8 @@ export class EcosystemService { async getEcosystemEnableStatus(): Promise { return this.ecosystemRepository.getEcosystemEnableStatus(); } + + async getEcosystemEnableStatus(): Promise { + return this.ecosystemRepository.getEcosystemEnableStatus(); + } } From 193e82b43642aaff399a89b4850a3759ce8d01f1 Mon Sep 17 00:00:00 2001 From: sujitaw Date: Wed, 18 Feb 2026 12:27:08 +0530 Subject: [PATCH 2/8] fix/code rabbit comments Signed-off-by: sujitaw Signed-off-by: pranalidhanavade --- apps/ecosystem/repositories/ecosystem.repository.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/ecosystem/repositories/ecosystem.repository.ts b/apps/ecosystem/repositories/ecosystem.repository.ts index 79f33c3e1..437c8f6b2 100755 --- a/apps/ecosystem/repositories/ecosystem.repository.ts +++ b/apps/ecosystem/repositories/ecosystem.repository.ts @@ -1562,7 +1562,7 @@ export class EcosystemRepository { const data = await this.prisma.platform_config.findFirst({ select: { isEcosystemEnabled: true } }); - return data.isEcosystemEnabled ? data.isEcosystemEnabled : false; + return data?.isEcosystemEnabled ?? false; } catch (error) { this.logger.error(`getEcosystemEnableStatus error: ${error}`); throw error; From 142e0b45f90b53e97b3ebb4030629fc7ddec7106 Mon Sep 17 00:00:00 2001 From: sujitaw Date: Wed, 18 Feb 2026 12:44:02 +0530 Subject: [PATCH 3/8] fix/code rabbit comments Signed-off-by: sujitaw Signed-off-by: pranalidhanavade --- apps/ecosystem/repositories/ecosystem.repository.ts | 12 ------------ apps/ecosystem/src/ecosystem.service.ts | 4 ---- 2 files changed, 16 deletions(-) diff --git a/apps/ecosystem/repositories/ecosystem.repository.ts b/apps/ecosystem/repositories/ecosystem.repository.ts index 437c8f6b2..afdb47954 100755 --- a/apps/ecosystem/repositories/ecosystem.repository.ts +++ b/apps/ecosystem/repositories/ecosystem.repository.ts @@ -1556,16 +1556,4 @@ export class EcosystemRepository { throw error; } } - - async getEcosystemEnableStatus(): Promise { - try { - const data = await this.prisma.platform_config.findFirst({ - select: { isEcosystemEnabled: true } - }); - return data?.isEcosystemEnabled ?? false; - } catch (error) { - this.logger.error(`getEcosystemEnableStatus error: ${error}`); - throw error; - } - } } diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 5a47ada1e..145193226 100755 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -863,8 +863,4 @@ export class EcosystemService { async getEcosystemEnableStatus(): Promise { return this.ecosystemRepository.getEcosystemEnableStatus(); } - - async getEcosystemEnableStatus(): Promise { - return this.ecosystemRepository.getEcosystemEnableStatus(); - } } From 2e50249c4bb594ea2d17074c4da8cfed54fe0ce2 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Tue, 17 Feb 2026 23:40:01 +0530 Subject: [PATCH 4/8] refactor: separate intent controller from ecosystem controller Signed-off-by: pranalidhanavade --- .../src/ecosystem/ecosystem.controller.ts | 593 +----------------- .../src/ecosystem/ecosystem.module.ts | 3 +- .../src/ecosystem/intent/intent.controller.ts | 586 +++++++++++++++++ .../repositories/ecosystem.repository.ts | 2 +- 4 files changed, 616 insertions(+), 568 deletions(-) create mode 100755 apps/api-gateway/src/ecosystem/intent/intent.controller.ts diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index 9383cd857..23de930da 100755 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -2,7 +2,6 @@ import { ApiBearerAuth, ApiForbiddenResponse, ApiOperation, - ApiParam, ApiQuery, ApiResponse, ApiTags, @@ -43,13 +42,6 @@ import { User } from '../authz/decorators/user.decorator'; import { CreateEcosystemDto, PaginationGetAllEcosystem } from 'apps/ecosystem/dtos/create-ecosystem-dto'; import { DeleteEcosystemOrgDto } from './dtos/delete-ecosystem-users'; import { GetEcosystemInvitationsQueryDto, UpdateEcosystemOrgStatusDto } from './dtos/ecosystem'; -import { IIntentTemplateList } from '@credebl/common/interfaces/intents-template.interface'; -import { CreateIntentDto } from 'apps/ecosystem/dtos/create-intent.dto'; -import { UpdateIntentDto } from 'apps/ecosystem/dtos/update-intent.dto'; -import { GetAllIntentTemplatesResponseDto } from '../utilities/dtos/get-all-intent-templates-response.dto'; -import { GetAllIntentTemplatesDto } from '../utilities/dtos/get-all-intent-templates.dto'; -import { GetIntentTemplateByIntentAndOrgDto } from '../utilities/dtos/get-intent-template-by-intent-and-org.dto'; -import { CreateIntentTemplateDto, UpdateIntentTemplateDto } from '../utilities/dtos/intent-template.dto'; import { EcosystemFeatureGuard } from '../authz/guards/ecosystem-feature-guard'; import { EcosystemOrgStatus, Invitation, InvitationViewRole } from '@credebl/enum/enum'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; @@ -70,10 +62,10 @@ import { TrimStringParamPipe } from '@credebl/common/cast.helper'; export class EcosystemController { constructor(private readonly ecosystemService: EcosystemService) {} - @Post('/invite-member') + @Post('/invitation') @ApiOperation({ summary: 'Invite member to ecosystem', - description: 'Send invitation for users to join the ecosystem' + description: 'Creates an invitation to add an organization to an ecosystem.' }) @ApiResponse({ status: HttpStatus.OK, @@ -103,10 +95,10 @@ export class EcosystemController { return res.status(HttpStatus.CREATED).json(finalResponse); } - @Post('/update-invitation-status') + @Post('/invitation/status') @ApiOperation({ - summary: 'Update status for Invitation (org owner)', - description: 'Update status for Invitation (org owner)' + summary: 'Update invitation status', + description: 'Updates the status of an existing ecosystem invitation (accept or reject).' }) @ApiResponse({ status: HttpStatus.OK, @@ -155,10 +147,10 @@ export class EcosystemController { * @param orgId The ID of the organization * @returns Created ecosystem details */ - @Post('/:orgId/create') + @Post('/:orgId') @ApiOperation({ - summary: 'Create a new ecosystem', - description: 'Create a new ecosystem' + summary: 'Create ecosystem', + description: 'Creates a new ecosystem for the specified organization.' }) @ApiResponse({ status: HttpStatus.CREATED, @@ -193,11 +185,10 @@ export class EcosystemController { */ @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) @ApiBearerAuth() - @Get('/all-ecosystem') + @Get('/') @ApiOperation({ - summary: 'Get Ecosystems', - description: - 'Fetch ecosystems for Platform Admin and Ecosystem Lead, with optional filtering by organization using orgId.' + summary: 'Get ecosystems', + description: 'Retrieves all ecosystems accessible to the current user.' }) @ApiQuery({ name: 'orgId', @@ -235,10 +226,10 @@ export class EcosystemController { * @param orgId ID of the organization * @returns Details of specific ecosystem */ - @Get('/:ecosystemId/:orgId/dashboard') + @Get('/:ecosystemId/org/:orgId') @ApiOperation({ - summary: 'Get ecosystem dashboard details (platform admin and organization owner)', - description: 'Fetch ecosystem dashboard data for a specific organization' + summary: 'Get ecosystem dashboard', + description: 'Retrieves details for a specific ecosystem and organization.' }) @ApiResponse({ status: HttpStatus.OK, @@ -279,10 +270,10 @@ export class EcosystemController { }); } - @Delete('/delete-ecosystem-users') + @Delete('/member') @ApiOperation({ - summary: 'Delete ecosystem users', - description: 'Delete ecosystem users' + summary: 'Delete ecosystem members', + description: 'Removes one or more members from an ecosystem.' }) @ApiResponse({ status: HttpStatus.OK, @@ -308,10 +299,10 @@ export class EcosystemController { return res.status(HttpStatus.BAD_REQUEST).json(finalResponse); } - @Put('/update-org-status') + @Put('/member/status') @ApiOperation({ - summary: 'Updates status for ecosystem org (ecosystem lead)', - description: 'Updates status for ecosystem org' + summary: 'Update member status', + description: 'Updates the status of an ecosystem member or organization within the ecosystem.' }) @ApiResponse({ status: HttpStatus.OK, @@ -351,10 +342,10 @@ export class EcosystemController { return res.status(HttpStatus.BAD_REQUEST).json(finalResponse); } - @Get('/get-ecosystem-orgs') + @Get('/members') @ApiOperation({ - summary: 'Get all Orgs for ecosystem', - description: 'Get all Orgs for ecosystem' + summary: 'Get ecosystem members', + description: 'Retrieves all members associated with an ecosystem.' }) @ApiResponse({ status: HttpStatus.OK, @@ -391,10 +382,10 @@ export class EcosystemController { }); } - @Get('/:orgId/get-member-invitations') + @Get('/invitations') @ApiOperation({ - summary: 'Get invitations for ecosystem members (org owner)', - description: 'Get invitations for ecosystem members' + summary: 'Get ecosystem invitations', + description: 'Retrieves all invitations related to ecosystem membership.' }) @ApiResponse({ status: HttpStatus.OK, @@ -410,7 +401,7 @@ export class EcosystemController { @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) @ApiBearerAuth() async getEcosystemMemberInvitations( - @Param( + @Query( 'orgId', new ParseUUIDPipe({ exceptionFactory: (): Error => { @@ -436,534 +427,4 @@ export class EcosystemController { data: invitationData }); } - // Intent Template CRUD operations - /** - * Create a new intent template mapping - * @param createIntentTemplateDto The intent template mapping details - * @param res The response object - * @returns The created intent template mapping - */ - @Post('/intent-templates') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ summary: 'Create Intent Template', description: 'Create a new intent template mapping.' }) - @ApiResponse({ - status: HttpStatus.CREATED, - description: 'Intent template created successfully', - type: ApiResponseDto - }) - async createIntentTemplate( - @Body() createIntentTemplateDto: CreateIntentTemplateDto, - @User() user: user, - @Res() res: Response - ): Promise { - const intentTemplate = await this.ecosystemService.createIntentTemplate(createIntentTemplateDto, user); - const finalResponse: IResponse = { - statusCode: HttpStatus.CREATED, - message: 'Intent template created successfully', - data: intentTemplate - }; - return res.status(HttpStatus.CREATED).json(finalResponse); - } - - /** - * Get all intent templates - * @param res The response object - * @returns List of all intent templates - */ - @Get('/intent-templates') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ summary: 'Get All Intent Templates', description: 'Retrieve all intent template mappings.' }) - @ApiResponse({ - status: HttpStatus.OK, - description: 'Intent templates retrieved successfully', - type: GetAllIntentTemplatesResponseDto - }) - async getAllIntentTemplates( - @Query() intentTemplateSearchCriteria: GetAllIntentTemplatesDto, - @Res() res: Response - ): Promise { - const intentTemplates: IIntentTemplateList = - await this.ecosystemService.getAllIntentTemplatesByQuery(intentTemplateSearchCriteria); - const finalResponse: IResponse = { - statusCode: HttpStatus.OK, - message: 'Intent templates retrieved successfully', - data: intentTemplates - }; - return res.status(HttpStatus.OK).json(finalResponse); - } - - /** - * Get intent template by intent name and verifier organization ID - * @param body The intent name and verifier organization ID - * @param res The response object - * @returns The intent template details (org-specific if exists, otherwise global) - */ - @Get('/intent-templates/by-intent-and-org') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ - summary: 'Get Intent Template by Intent and Organization', - description: - 'Retrieve intent template details by intent name and verifier organization ID. Returns org-specific template if mapped, otherwise returns global template.' - }) - @ApiResponse({ status: HttpStatus.OK, description: 'Intent template retrieved successfully', type: ApiResponseDto }) - async getIntentTemplateByIntentAndOrg( - @Query() getIntentTemplateByIntentAndOrgDto: GetIntentTemplateByIntentAndOrgDto, - @Res() res: Response - ): Promise { - const intentTemplate = await this.ecosystemService.getIntentTemplateByIntentAndOrg( - getIntentTemplateByIntentAndOrgDto.intentName, - getIntentTemplateByIntentAndOrgDto.verifierOrgId - ); - const finalResponse: IResponse = { - statusCode: HttpStatus.OK, - message: intentTemplate ? 'Intent template retrieved successfully' : 'No intent template found', - data: intentTemplate - }; - return res.status(HttpStatus.OK).json(finalResponse); - } - - /** - * Get intent templates by intent ID - * @param intentId The intent ID - * @param res The response object - * @returns List of intent templates for the intent - */ - @Get('/intent-templates/intent/:intentId') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ - summary: 'Get Intent Templates by Intent ID', - description: 'Retrieve all intent templates for a specific intent.' - }) - @ApiResponse({ status: HttpStatus.OK, description: 'Intent templates retrieved successfully', type: ApiResponseDto }) - async getIntentTemplatesByIntentId( - @Param( - 'intentId', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException('Invalid intent ID format'); - } - }) - ) - intentId: string, - @Res() res: Response - ): Promise { - const intentTemplates = await this.ecosystemService.getIntentTemplatesByIntentId(intentId); - const finalResponse: IResponse = { - statusCode: HttpStatus.OK, - message: 'Intent templates retrieved successfully', - data: intentTemplates - }; - return res.status(HttpStatus.OK).json(finalResponse); - } - - /** - * Get intent templates by organization ID - * @param orgId The organization ID - * @param res The response object - * @returns List of intent templates for the organization - */ - @Get('/intent-templates/org/:orgId') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD, OrgRoles.OWNER) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ - summary: 'Get Intent Templates by Organization ID', - description: 'Retrieve all intent templates for a specific organization.' - }) - @ApiResponse({ status: HttpStatus.OK, description: 'Intent templates retrieved successfully', type: ApiResponseDto }) - async getIntentTemplatesByOrgId( - @Param( - 'orgId', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException('Invalid orgId format'); - } - }) - ) - orgId: string, - @Res() res: Response - ): Promise { - const intentTemplates = await this.ecosystemService.getIntentTemplatesByOrgId(orgId); - const finalResponse: IResponse = { - statusCode: HttpStatus.OK, - message: 'Intent templates retrieved successfully', - data: intentTemplates - }; - return res.status(HttpStatus.OK).json(finalResponse); - } - - /** - * Get intent template by ID - * @param id The intent template ID - * @param res The response object - * @returns The intent template details - */ - @Get('/intent-templates/:id') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ summary: 'Get Intent Template by ID', description: 'Retrieve intent template details by ID.' }) - @ApiResponse({ status: HttpStatus.OK, description: 'Intent template retrieved successfully', type: ApiResponseDto }) - async getIntentTemplateById( - @Param( - 'id', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.oid4vpIntentToTemplate.error.invalidId); - } - }) - ) - id: string, - @Res() res: Response - ): Promise { - const intentTemplate = await this.ecosystemService.getIntentTemplateById(id); - const finalResponse: IResponse = { - statusCode: HttpStatus.OK, - message: 'Intent template retrieved successfully', - data: intentTemplate - }; - return res.status(HttpStatus.OK).json(finalResponse); - } - - /** - * Update intent template - * @param id The intent template ID - * @param updateIntentTemplateDto The updated intent template details - * @param res The response object - * @returns The updated intent template - */ - @Put('/intent-templates/:id') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ summary: 'Update Intent Template', description: 'Update an existing intent template mapping.' }) - @ApiResponse({ status: HttpStatus.OK, description: 'Intent template updated successfully', type: ApiResponseDto }) - async updateIntentTemplate( - @Param( - 'id', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.oid4vpIntentToTemplate.error.invalidId); - } - }) - ) - id: string, - @Body() updateIntentTemplateDto: UpdateIntentTemplateDto, - @User() user: user, - @Res() res: Response - ): Promise { - const intentTemplate = await this.ecosystemService.updateIntentTemplate(id, updateIntentTemplateDto, user); - const finalResponse: IResponse = { - statusCode: HttpStatus.OK, - message: 'Intent template updated successfully', - data: intentTemplate - }; - return res.status(HttpStatus.OK).json(finalResponse); - } - - /** - * Delete intent template - * @param id The intent template ID - * @param res The response object - * @returns The deleted intent template - */ - @Delete('/intent-templates/:id') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ summary: 'Delete Intent Template', description: 'Delete an intent template mapping.' }) - @ApiResponse({ status: HttpStatus.OK, description: 'Intent template deleted successfully', type: ApiResponseDto }) - async deleteIntentTemplate(@Param('id') id: string, @Res() res: Response): Promise { - const intentTemplate = await this.ecosystemService.deleteIntentTemplate(id); - const finalResponse: IResponse = { - statusCode: HttpStatus.OK, - message: 'Intent template deleted successfully', - data: intentTemplate - }; - return res.status(HttpStatus.OK).json(finalResponse); - } - - /** - * Create Intent - * @param createIntentDto - * @returns Created intent - */ - @Post('/intents/:ecosystemId') - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiBearerAuth() - @ApiOperation({ - summary: 'Create Intent', - description: 'Create a new intent' - }) - @ApiResponse({ - status: HttpStatus.CREATED, - description: 'Intent created successfully', - type: ApiResponseDto - }) - async createIntent( - @Body() createIntentDto: CreateIntentDto, - @Param( - 'ecosystemId', - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId); - } - }) - ) - ecosystemId: string, - @User() user: user, - @Res() res: Response - ): Promise { - createIntentDto.ecosystemId = ecosystemId; - createIntentDto.userId = user?.id; - - const intent = await this.ecosystemService.createIntent(createIntentDto); - - const finalResponse: IResponse = { - statusCode: HttpStatus.CREATED, - message: ResponseMessages.ecosystem.success.intentCreated, - data: intent - }; - - return res.status(HttpStatus.CREATED).json(finalResponse); - } - - @Get('/intents/:ecosystemId') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ - summary: 'Get intents by ecosystem', - description: 'Retrieve all intents of an ecosystem or a specific intent if intentId is provided' - }) - @ApiQuery({ - name: 'intentId', - required: false, - type: String - }) - @ApiResponse({ - status: HttpStatus.OK, - description: 'Intents fetched successfully', - type: ApiResponseDto - }) - async getIntents( - @Res() res: Response, - @Param( - 'ecosystemId', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId); - } - }) - ) - ecosystemId: string, - @Query() pageDto: PaginationDto, - @Query( - 'intentId', - new ParseUUIDPipe({ - optional: true, - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfIntentId); - } - }) - ) - intentId?: string - ): Promise { - const intents = await this.ecosystemService.getIntents(ecosystemId, pageDto, intentId?.trim()); - - return res.status(HttpStatus.OK).json({ - statusCode: HttpStatus.OK, - message: ResponseMessages.ecosystem.success.fetchIntents, - data: intents - }); - } - // verification template details by org Id - /** - * Get template details by org ID - */ - @Get('/:orgId/templates') - @Roles(OrgRoles.ECOSYSTEM_LEAD, OrgRoles.OWNER) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiBearerAuth() - @ApiOperation({ - summary: 'Get template details by orgId', - description: 'Retrieve verification template details by orgId' - }) - @ApiParam({ - name: 'orgId', - required: true, - description: 'Organization ID' - }) - @ApiResponse({ - status: HttpStatus.OK, - description: 'Template details fetched successfully' - }) - async getTemplateByIntentId( - @Param( - 'orgId', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.ecosystem.error.invalidOrgId); - } - }) - ) - orgId: string, - @Res() res: Response, - @Query() pageDto: PaginationDto - ): Promise { - const templates = await this.ecosystemService.getVerificationTemplates(orgId, pageDto); - - return res.status(HttpStatus.OK).json({ - statusCode: HttpStatus.OK, - message: ResponseMessages.ecosystem.success.fetchVerificationTemplates, - data: templates - }); - } - - /** - * Update intent - * @param id Intent ID - * @param updateIntentDto - * @returns Updated intent - */ - @Put('/intents/:ecosystemId/:intentId') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ - summary: 'Update Intent', - description: 'Update an existing intent within an ecosystem' - }) - @ApiResponse({ - status: HttpStatus.OK, - description: 'Intent updated successfully', - type: ApiResponseDto - }) - async updateIntent( - @Param( - 'ecosystemId', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId); - } - }) - ) - ecosystemId: string, - @Param( - 'intentId', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfIntentId); - } - }) - ) - intentId: string, - @Body() updateIntentDto: UpdateIntentDto, - @User() user: user, - @Res() res: Response - ): Promise { - updateIntentDto.userId = user?.id; - updateIntentDto.intentId = intentId; - updateIntentDto.ecosystemId = ecosystemId; - - const intent = await this.ecosystemService.updateIntent(updateIntentDto); - - const finalResponse: IResponse = { - statusCode: HttpStatus.OK, - message: ResponseMessages.ecosystem.success.intentUpdated, - data: intent - }; - - return res.status(HttpStatus.OK).json(finalResponse); - } - /** - * Delete intent - * @param id Intent ID - * @returns Deleted intent - */ - @Delete('/intents/:ecosystemId/:intentId') - @ApiBearerAuth() - @Roles(OrgRoles.ECOSYSTEM_LEAD) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiOperation({ - summary: 'Delete Intent', - description: 'Delete an intent within an ecosystem' - }) - @ApiResponse({ - status: HttpStatus.OK, - description: 'Intent deleted successfully', - type: ApiResponseDto - }) - async deleteIntent( - @Param( - 'ecosystemId', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId); - } - }) - ) - ecosystemId: string, - @Param( - 'intentId', - TrimStringParamPipe, - new ParseUUIDPipe({ - exceptionFactory: (): Error => { - throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfIntentId); - } - }) - ) - intentId: string, - - @User() user: user, - @Res() res: Response - ): Promise { - const intent = await this.ecosystemService.deleteIntent(ecosystemId, intentId, user.id); - - return res.status(HttpStatus.OK).json({ - statusCode: HttpStatus.OK, - message: ResponseMessages.ecosystem.success.deleteIntent, - data: intent - }); - } - - @Get('/dashboard/summary') - @Roles(OrgRoles.PLATFORM_ADMIN) - @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) - @ApiBearerAuth() - @ApiOperation({ - summary: 'Get Count for Ecosystem dashboard', - description: 'Get Count for Ecosystem dashboard' - }) - @ApiResponse({ - status: HttpStatus.OK, - description: 'Dashboard count fetched successfully' - }) - async getDashboardCountEcosystem(@Res() res: Response): Promise { - const dashboard = await this.ecosystemService.getDashboardCountEcosystem(); - - return res.status(HttpStatus.OK).json({ - statusCode: HttpStatus.OK, - message: ResponseMessages.ecosystem.success.dashboard, - data: dashboard - }); - } } diff --git a/apps/api-gateway/src/ecosystem/ecosystem.module.ts b/apps/api-gateway/src/ecosystem/ecosystem.module.ts index 1b42d9028..8a956e850 100644 --- a/apps/api-gateway/src/ecosystem/ecosystem.module.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.module.ts @@ -4,6 +4,7 @@ import { CommonConstants } from '@credebl/common/common.constant'; import { EcosystemController } from './ecosystem.controller'; import { EcosystemService } from './ecosystem.service'; import { EcosystemModule as EcosystemServiceModule } from 'apps/ecosystem/src/ecosystem.module'; +import { IntentController } from './intent/intent.controller'; import { Module } from '@nestjs/common'; import { NATSClient } from '@credebl/common/NATSClient'; import { getNatsOptions } from '@credebl/common/nats.config'; @@ -19,7 +20,7 @@ import { getNatsOptions } from '@credebl/common/nats.config'; } ]) ], - controllers: [EcosystemController], + controllers: [EcosystemController, IntentController], providers: [EcosystemService, NATSClient], exports: [EcosystemService, EcosystemServiceModule] }) diff --git a/apps/api-gateway/src/ecosystem/intent/intent.controller.ts b/apps/api-gateway/src/ecosystem/intent/intent.controller.ts new file mode 100755 index 000000000..795bada61 --- /dev/null +++ b/apps/api-gateway/src/ecosystem/intent/intent.controller.ts @@ -0,0 +1,586 @@ +import { + ApiBearerAuth, + ApiForbiddenResponse, + ApiOperation, + ApiParam, + ApiQuery, + ApiResponse, + ApiTags, + ApiUnauthorizedResponse +} from '@nestjs/swagger'; +import { + BadRequestException, + Body, + Controller, + Delete, + Get, + HttpStatus, + Param, + ParseUUIDPipe, + Post, + Put, + Query, + Res, + UseFilters, + UseGuards +} from '@nestjs/common'; +import type { user as PrismaUser } from '@prisma/client'; +import { ApiResponseDto } from '../../dtos/apiResponse.dto'; +import { Response } from 'express'; +import { AuthGuard } from '@nestjs/passport'; +import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; +import { IResponse } from '@credebl/common/interfaces/response.interface'; +import { OrgRoles } from 'libs/org-roles/enums'; +import { ResponseMessages } from '@credebl/common/response-messages'; +import { Roles } from '../../authz/decorators/roles.decorator'; +import { UnauthorizedErrorDto } from '../../dtos/unauthorized-error.dto'; +import { EcosystemRolesGuard } from '../../authz/guards/ecosystem-roles.guard'; +import { User } from '../../authz/decorators/user.decorator'; +import { IIntentTemplateList } from '@credebl/common/interfaces/intents-template.interface'; +import { CreateIntentDto } from 'apps/ecosystem/dtos/create-intent.dto'; +import { UpdateIntentDto } from 'apps/ecosystem/dtos/update-intent.dto'; +import { GetAllIntentTemplatesResponseDto } from '../../utilities/dtos/get-all-intent-templates-response.dto'; +import { GetAllIntentTemplatesDto } from '../../utilities/dtos/get-all-intent-templates.dto'; +import { GetIntentTemplateByIntentAndOrgDto } from '../../utilities/dtos/get-intent-template-by-intent-and-org.dto'; +import { CreateIntentTemplateDto, UpdateIntentTemplateDto } from '../../utilities/dtos/intent-template.dto'; +import { EcosystemFeatureGuard } from '../../authz/guards/ecosystem-feature-guard'; +import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; +import { TrimStringParamPipe } from '@credebl/common/cast.helper'; +import { EcosystemService } from '../ecosystem.service'; +import { ForbiddenErrorDto } from '../../dtos/forbidden-error.dto'; + +@UseFilters(CustomExceptionFilter) +@Controller('intent') +@ApiTags('intent') +@UseGuards(EcosystemFeatureGuard) +@ApiUnauthorizedResponse({ + description: 'Unauthorized', + type: UnauthorizedErrorDto +}) +@ApiForbiddenResponse({ + description: 'Forbidden', + type: ForbiddenErrorDto +}) +export class IntentController { + constructor(private readonly ecosystemService: EcosystemService) {} + + /** + * Create Intent + * @param createIntentDto + * @returns Created intent + */ + @Post('/ecosystem/:ecosystemId') + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiBearerAuth() + @ApiOperation({ + summary: 'Create Intent', + description: 'Creates a new intent within the specified ecosystem.' + }) + @ApiResponse({ + status: HttpStatus.CREATED, + description: 'Intent created successfully', + type: ApiResponseDto + }) + async createIntent( + @Body() createIntentDto: CreateIntentDto, + @Param( + 'ecosystemId', + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId); + } + }) + ) + ecosystemId: string, + @User() user: PrismaUser, + @Res() res: Response + ): Promise { + createIntentDto.ecosystemId = ecosystemId; + createIntentDto.userId = user?.id; + + const intent = await this.ecosystemService.createIntent(createIntentDto); + + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: ResponseMessages.ecosystem.success.intentCreated, + data: intent + }; + + return res.status(HttpStatus.CREATED).json(finalResponse); + } + + @Get('/ecosystem/:ecosystemId') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ + summary: 'Get intents by ecosystem', + description: 'Retrieves all intents associated with a specific ecosystem.' + }) + @ApiQuery({ + name: 'intentId', + required: false, + type: String + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Intents fetched successfully', + type: ApiResponseDto + }) + async getIntents( + @Res() res: Response, + @Param( + 'ecosystemId', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId); + } + }) + ) + ecosystemId: string, + @Query() pageDto: PaginationDto, + @Query( + 'intentId', + new ParseUUIDPipe({ + optional: true, + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfIntentId); + } + }) + ) + intentId?: string + ): Promise { + const intents = await this.ecosystemService.getIntents(ecosystemId, pageDto, intentId?.trim()); + + return res.status(HttpStatus.OK).json({ + statusCode: HttpStatus.OK, + message: ResponseMessages.ecosystem.success.fetchIntents, + data: intents + }); + } + + /** + * Update intent + * @param id Intent ID + * @param updateIntentDto + * @returns Updated intent + */ + @Put('/ecosystem/:ecosystemId/:intentId') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ + summary: 'Update Intent', + description: 'Updates an existing intent within the specified ecosystem.' + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Intent updated successfully', + type: ApiResponseDto + }) + async updateIntent( + @Param( + 'ecosystemId', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId); + } + }) + ) + ecosystemId: string, + @Param( + 'intentId', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfIntentId); + } + }) + ) + intentId: string, + @Body() updateIntentDto: UpdateIntentDto, + @User() user: PrismaUser, + @Res() res: Response + ): Promise { + updateIntentDto.userId = user?.id; + updateIntentDto.intentId = intentId; + updateIntentDto.ecosystemId = ecosystemId; + + const intent = await this.ecosystemService.updateIntent(updateIntentDto); + + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: ResponseMessages.ecosystem.success.intentUpdated, + data: intent + }; + + return res.status(HttpStatus.OK).json(finalResponse); + } + /** + * Delete intent + * @param id Intent ID + * @returns Deleted intent + */ + @Delete('/ecosystem/:ecosystemId/:intentId') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ + summary: 'Delete Intent', + description: 'Deletes an intent from the specified ecosystem.' + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Intent deleted successfully', + type: ApiResponseDto + }) + async deleteIntent( + @Param( + 'ecosystemId', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId); + } + }) + ) + ecosystemId: string, + @Param( + 'intentId', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfIntentId); + } + }) + ) + intentId: string, + + @User() user: PrismaUser, + @Res() res: Response + ): Promise { + const intent = await this.ecosystemService.deleteIntent(ecosystemId, intentId, user.id); + + return res.status(HttpStatus.OK).json({ + statusCode: HttpStatus.OK, + message: ResponseMessages.ecosystem.success.deleteIntent, + data: intent + }); + } + + // verification template details by org Id + /** + * Get template details by org ID + */ + @Get('/org/:orgId/verification-templates') + @Roles(OrgRoles.ECOSYSTEM_LEAD, OrgRoles.OWNER) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiBearerAuth() + @ApiOperation({ + summary: 'Get template details by orgId', + description: 'Retrieve verification template details by orgId' + }) + @ApiParam({ + name: 'orgId', + required: true, + description: 'Organization ID' + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Template details fetched successfully' + }) + async getTemplateByIntentId( + @Param( + 'orgId', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.ecosystem.error.invalidOrgId); + } + }) + ) + orgId: string, + @Res() res: Response, + @Query() pageDto: PaginationDto + ): Promise { + const templates = await this.ecosystemService.getVerificationTemplates(orgId, pageDto); + + return res.status(HttpStatus.OK).json({ + statusCode: HttpStatus.OK, + message: ResponseMessages.ecosystem.success.fetchVerificationTemplates, + data: templates + }); + } + + // Intent Template CRUD operations + /** + * Create a new intent template mapping + * @param createIntentTemplateDto The intent template mapping details + * @param res The response object + * @returns The created intent template mapping + */ + @Post('/template') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ summary: 'Create intent template', description: 'Creates a new intent template mapping.' }) + @ApiResponse({ + status: HttpStatus.CREATED, + description: 'Intent template created successfully', + type: ApiResponseDto + }) + async createIntentTemplate( + @Body() createIntentTemplateDto: CreateIntentTemplateDto, + @User() user: PrismaUser, + @Res() res: Response + ): Promise { + const intentTemplate = await this.ecosystemService.createIntentTemplate(createIntentTemplateDto, user); + const finalResponse: IResponse = { + statusCode: HttpStatus.CREATED, + message: 'Intent template created successfully', + data: intentTemplate + }; + return res.status(HttpStatus.CREATED).json(finalResponse); + } + + /** + * Get all intent templates + * @param res The response object + * @returns List of all intent templates + */ + @Get('/templates') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ + summary: 'Get All Intent Templates', + description: 'Retrieves a list of all available intent templates.' + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Intent templates retrieved successfully', + type: GetAllIntentTemplatesResponseDto + }) + async getAllIntentTemplates( + @Query() intentTemplateSearchCriteria: GetAllIntentTemplatesDto, + @Res() res: Response + ): Promise { + const intentTemplates: IIntentTemplateList = + await this.ecosystemService.getAllIntentTemplatesByQuery(intentTemplateSearchCriteria); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: 'Intent templates retrieved successfully', + data: intentTemplates + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + /** + * Get intent template by intent name and verifier organization ID + * @param body The intent name and verifier organization ID + * @param res The response object + * @returns The intent template details (org-specific if exists, otherwise global) + */ + @Get('/:intentName/org/:orgId/templates') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ + summary: 'Get Intent Template by Intent and Organization', + description: + 'Retrieves the template mapped to a specific intent and organization. Returns organization-specific template if available, otherwise default template.' + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Intent template retrieved successfully', type: ApiResponseDto }) + async getIntentTemplateByIntentAndOrg( + @Query() getIntentTemplateByIntentAndOrgDto: GetIntentTemplateByIntentAndOrgDto, + @Res() res: Response + ): Promise { + const intentTemplate = await this.ecosystemService.getIntentTemplateByIntentAndOrg( + getIntentTemplateByIntentAndOrgDto.intentName, + getIntentTemplateByIntentAndOrgDto.verifierOrgId + ); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: intentTemplate ? 'Intent template retrieved successfully' : 'No intent template found', + data: intentTemplate + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + /** + * Get intent templates by intent ID + * @param intentId The intent ID + * @param res The response object + * @returns List of intent templates for the intent + */ + @Get('/:intentId/templates') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ + summary: 'Get templates by intent', + description: 'Retrieves all templates associated with a specific intent.' + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Intent templates retrieved successfully', type: ApiResponseDto }) + async getIntentTemplatesByIntentId( + @Param( + 'intentId', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException('Invalid intent ID format'); + } + }) + ) + intentId: string, + @Res() res: Response + ): Promise { + const intentTemplates = await this.ecosystemService.getIntentTemplatesByIntentId(intentId); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: 'Intent templates retrieved successfully', + data: intentTemplates + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + /** + * Get intent templates by organization ID + * @param orgId The organization ID + * @param res The response object + * @returns List of intent templates for the organization + */ + @Get('/org/:orgId/templates/') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD, OrgRoles.OWNER) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ + summary: 'Get templates by organization', + description: 'Retrieves all templates associated with a specific organization.' + }) + @ApiParam({ + name: 'orgId', + required: true, + description: 'Organization ID' + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Intent templates retrieved successfully', type: ApiResponseDto }) + async getIntentTemplatesByOrgId( + @Param( + 'orgId', + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException('Invalid orgId format'); + } + }) + ) + orgId: string, + @Res() res: Response + ): Promise { + const intentTemplates = await this.ecosystemService.getIntentTemplatesByOrgId(orgId); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: 'Intent templates retrieved successfully', + data: intentTemplates + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + /** + * Get intent template by ID + * @param id The intent template ID + * @param res The response object + * @returns The intent template details + */ + @Get('/template/:id') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ + summary: 'Get template by ID', + description: 'Retrieves details of a specific intent template using its unique identifier.' + }) + @ApiResponse({ status: HttpStatus.OK, description: 'Intent template retrieved successfully', type: ApiResponseDto }) + async getIntentTemplateById( + @Param( + 'id', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.oid4vpIntentToTemplate.error.invalidId); + } + }) + ) + id: string, + @Res() res: Response + ): Promise { + const intentTemplate = await this.ecosystemService.getIntentTemplateById(id); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: 'Intent template retrieved successfully', + data: intentTemplate + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + /** + * Update intent template + * @param id The intent template ID + * @param updateIntentTemplateDto The updated intent template details + * @param res The response object + * @returns The updated intent template + */ + @Put('/template/:id') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ summary: 'Update Intent Template', description: 'Updates an existing intent template mapping.' }) + @ApiResponse({ status: HttpStatus.OK, description: 'Intent template updated successfully', type: ApiResponseDto }) + async updateIntentTemplate( + @Param( + 'id', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.oid4vpIntentToTemplate.error.invalidId); + } + }) + ) + id: string, + @Body() updateIntentTemplateDto: UpdateIntentTemplateDto, + @User() user: PrismaUser, + @Res() res: Response + ): Promise { + const intentTemplate = await this.ecosystemService.updateIntentTemplate(id, updateIntentTemplateDto, user); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: 'Intent template updated successfully', + data: intentTemplate + }; + return res.status(HttpStatus.OK).json(finalResponse); + } + + /** + * Delete intent template + * @param id The intent template ID + * @param res The response object + * @returns The deleted intent template + */ + @Delete('/template/:id') + @ApiBearerAuth() + @Roles(OrgRoles.ECOSYSTEM_LEAD) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiOperation({ summary: 'Delete Intent Template', description: 'Deletes an existing intent template mapping.' }) + @ApiResponse({ status: HttpStatus.OK, description: 'Intent template deleted successfully', type: ApiResponseDto }) + async deleteIntentTemplate(@Param('id') id: string, @Res() res: Response): Promise { + const intentTemplate = await this.ecosystemService.deleteIntentTemplate(id); + const finalResponse: IResponse = { + statusCode: HttpStatus.OK, + message: 'Intent template deleted successfully', + data: intentTemplate + }; + return res.status(HttpStatus.OK).json(finalResponse); + } +} diff --git a/apps/ecosystem/repositories/ecosystem.repository.ts b/apps/ecosystem/repositories/ecosystem.repository.ts index afdb47954..b1f52319e 100755 --- a/apps/ecosystem/repositories/ecosystem.repository.ts +++ b/apps/ecosystem/repositories/ecosystem.repository.ts @@ -377,8 +377,8 @@ export class EcosystemRepository { } async updateEcosystemInvitationStatusByEmail( - email: string, orgId: string, + email: string, ecosystemId: string, status: Invitation ): Promise { From c759c8091cd495a32f24b141594692eeb9e411b6 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 18 Feb 2026 13:06:39 +0530 Subject: [PATCH 5/8] fix: error handling for already present intent Signed-off-by: pranalidhanavade --- .../src/ecosystem/ecosystem.controller.ts | 4 ++-- .../src/ecosystem/intent/intent.controller.ts | 14 +++++++++++++- .../repositories/ecosystem.repository.ts | 15 +++++++++++++++ apps/ecosystem/src/ecosystem.service.ts | 17 +++++++++++++++-- libs/common/src/response-messages/index.ts | 1 + 5 files changed, 46 insertions(+), 5 deletions(-) diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index 23de930da..9f5d7e608 100755 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -147,7 +147,7 @@ export class EcosystemController { * @param orgId The ID of the organization * @returns Created ecosystem details */ - @Post('/:orgId') + @Post('/') @ApiOperation({ summary: 'Create ecosystem', description: 'Creates a new ecosystem for the specified organization.' @@ -162,7 +162,7 @@ export class EcosystemController { @Roles(OrgRoles.OWNER) async createNewEcosystem( @Body() createEcosystemDto: CreateEcosystemDto, - @Param('orgId') orgId: string, + @Query('orgId') orgId: string, @User() user: user, @Res() res: Response ): Promise { diff --git a/apps/api-gateway/src/ecosystem/intent/intent.controller.ts b/apps/api-gateway/src/ecosystem/intent/intent.controller.ts index 795bada61..95db15690 100755 --- a/apps/api-gateway/src/ecosystem/intent/intent.controller.ts +++ b/apps/api-gateway/src/ecosystem/intent/intent.controller.ts @@ -574,7 +574,19 @@ export class IntentController { @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) @ApiOperation({ summary: 'Delete Intent Template', description: 'Deletes an existing intent template mapping.' }) @ApiResponse({ status: HttpStatus.OK, description: 'Intent template deleted successfully', type: ApiResponseDto }) - async deleteIntentTemplate(@Param('id') id: string, @Res() res: Response): Promise { + async deleteIntentTemplate( + @Param( + 'id', + TrimStringParamPipe, + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.oid4vpIntentToTemplate.error.invalidId); + } + }) + ) + id: string, + @Res() res: Response + ): Promise { const intentTemplate = await this.ecosystemService.deleteIntentTemplate(id); const finalResponse: IResponse = { statusCode: HttpStatus.OK, diff --git a/apps/ecosystem/repositories/ecosystem.repository.ts b/apps/ecosystem/repositories/ecosystem.repository.ts index b1f52319e..0a5ade5a6 100755 --- a/apps/ecosystem/repositories/ecosystem.repository.ts +++ b/apps/ecosystem/repositories/ecosystem.repository.ts @@ -213,6 +213,20 @@ export class EcosystemRepository { } } + async checkIntentExist(name?: string): Promise { + try { + const checkIntentExist = await this.prisma.intents.findFirst({ + where: { + name + } + }); + return checkIntentExist; + } catch (error) { + this.logger.error(`error: ${JSON.stringify(error)}`); + throw new InternalServerErrorException(error); + } + } + async checkEcosystemCreatedByUser(userId: string): Promise { if (!userId) { throw new BadRequestException('userId missing'); @@ -930,6 +944,7 @@ export class EcosystemRepository { const invitation = await prisma.ecosystem_invitations.findFirst({ where: { email, + status: Invitation.ACCEPTED, ecosystemId: null }, orderBy: { diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 145193226..6a32ee35e 100755 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -76,7 +76,7 @@ export class EcosystemService { const existingInvitation = await this.ecosystemRepository.getPendingInvitationByEmail(email); - if (existingInvitation) { + if (existingInvitation?.type === InviteType.MEMBER) { throw new RpcException({ statusCode: HttpStatus.CONFLICT, message: ResponseMessages.ecosystem.error.invitationAlreadySent @@ -252,8 +252,8 @@ export class EcosystemService { if (checkUser && Invitation.REJECTED === checkUser.status && ecosystemId === checkUser.ecosystemId) { const reopenedInvitation = await this.ecosystemRepository.updateEcosystemInvitationStatusByEmail( - userEmail, orgId, + userEmail, ecosystemId, Invitation.PENDING ); @@ -738,6 +738,13 @@ export class EcosystemService { if (!ecosystem) { throw new NotFoundException(ResponseMessages.ecosystem.error.ecosystemNotFound); } + + const intentExist = await this.ecosystemRepository.checkIntentExist(createIntentDto?.name); + + if (intentExist) { + throw new ConflictException(ResponseMessages.ecosystem.error.intentAlreadyExists); + } + return this.ecosystemRepository.createIntent({ name, description, @@ -799,6 +806,12 @@ export class EcosystemService { throw new BadRequestException(ResponseMessages.ecosystem.error.userIdMissing); } + const intentExist = await this.ecosystemRepository.checkIntentExist(updateIntentDto?.name); + + if (intentExist) { + throw new ConflictException(ResponseMessages.ecosystem.error.intentAlreadyExists); + } + const intent = await this.ecosystemRepository.updateIntent({ name, description, diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 7695b9f5c..a9d2fc3e3 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -275,6 +275,7 @@ export const ResponseMessages = { invalidFormatOfEcosystemId: 'Invalid format of ecosystemId', invalidFormatOfIntentId: 'Invalid format of intentId', intentIdIsRequired: 'IntentId is required', + intentAlreadyExists: 'Intent with this name already exists', emailOrPlatformAdminIdMissing: 'Email or platformAdminId missing', platformIdRequired: 'PlatformId is required', userIdMissing: 'UserId is required', From 9e6bacd048dbd46459cdcf1149cca855b7820ad9 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 18 Feb 2026 13:13:05 +0530 Subject: [PATCH 6/8] fix: coderabbit issues Signed-off-by: pranalidhanavade --- .../src/ecosystem/intent/intent.controller.ts | 17 +++++++++++------ ...t-intent-template-by-intent-and-org.dto.ts | 19 ------------------- 2 files changed, 11 insertions(+), 25 deletions(-) delete mode 100644 apps/api-gateway/src/utilities/dtos/get-intent-template-by-intent-and-org.dto.ts diff --git a/apps/api-gateway/src/ecosystem/intent/intent.controller.ts b/apps/api-gateway/src/ecosystem/intent/intent.controller.ts index 95db15690..454ca6159 100755 --- a/apps/api-gateway/src/ecosystem/intent/intent.controller.ts +++ b/apps/api-gateway/src/ecosystem/intent/intent.controller.ts @@ -41,7 +41,6 @@ import { CreateIntentDto } from 'apps/ecosystem/dtos/create-intent.dto'; import { UpdateIntentDto } from 'apps/ecosystem/dtos/update-intent.dto'; import { GetAllIntentTemplatesResponseDto } from '../../utilities/dtos/get-all-intent-templates-response.dto'; import { GetAllIntentTemplatesDto } from '../../utilities/dtos/get-all-intent-templates.dto'; -import { GetIntentTemplateByIntentAndOrgDto } from '../../utilities/dtos/get-intent-template-by-intent-and-org.dto'; import { CreateIntentTemplateDto, UpdateIntentTemplateDto } from '../../utilities/dtos/intent-template.dto'; import { EcosystemFeatureGuard } from '../../authz/guards/ecosystem-feature-guard'; import { PaginationDto } from '@credebl/common/dtos/pagination.dto'; @@ -395,13 +394,19 @@ export class IntentController { }) @ApiResponse({ status: HttpStatus.OK, description: 'Intent template retrieved successfully', type: ApiResponseDto }) async getIntentTemplateByIntentAndOrg( - @Query() getIntentTemplateByIntentAndOrgDto: GetIntentTemplateByIntentAndOrgDto, + @Param('intentName') intentName: string, + @Param( + 'orgId', + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId); + } + }) + ) + orgId: string, @Res() res: Response ): Promise { - const intentTemplate = await this.ecosystemService.getIntentTemplateByIntentAndOrg( - getIntentTemplateByIntentAndOrgDto.intentName, - getIntentTemplateByIntentAndOrgDto.verifierOrgId - ); + const intentTemplate = await this.ecosystemService.getIntentTemplateByIntentAndOrg(intentName, orgId); const finalResponse: IResponse = { statusCode: HttpStatus.OK, message: intentTemplate ? 'Intent template retrieved successfully' : 'No intent template found', diff --git a/apps/api-gateway/src/utilities/dtos/get-intent-template-by-intent-and-org.dto.ts b/apps/api-gateway/src/utilities/dtos/get-intent-template-by-intent-and-org.dto.ts deleted file mode 100644 index 81a4b5924..000000000 --- a/apps/api-gateway/src/utilities/dtos/get-intent-template-by-intent-and-org.dto.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { IsNotEmpty, IsString } from 'class-validator'; - -import { ApiProperty } from '@nestjs/swagger'; -import { Transform } from 'class-transformer'; -import { trim } from '@credebl/common/cast.helper'; - -export class GetIntentTemplateByIntentAndOrgDto { - @ApiProperty({ example: 'WEB_CHECKIN', description: 'Intent name' }) - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'intentName is required' }) - @IsString() - intentName: string; - - @ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', description: 'Verifier Organization ID' }) - @Transform(({ value }) => trim(value)) - @IsNotEmpty({ message: 'verifierOrgId is required' }) - @IsString() - verifierOrgId: string; -} From 473c0f11ca5ed23e5378c1d764197b6303b8e912 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 18 Feb 2026 13:48:19 +0530 Subject: [PATCH 7/8] fix: coderabbit issues Signed-off-by: pranalidhanavade --- .../src/ecosystem/ecosystem.controller.ts | 10 +++++++++- .../ecosystem/repositories/ecosystem.repository.ts | 14 ++++++++++---- apps/ecosystem/src/ecosystem.service.ts | 6 +++--- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index 9f5d7e608..5ce1c8138 100755 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -162,7 +162,15 @@ export class EcosystemController { @Roles(OrgRoles.OWNER) async createNewEcosystem( @Body() createEcosystemDto: CreateEcosystemDto, - @Query('orgId') orgId: string, + @Query( + 'orgId', + new ParseUUIDPipe({ + exceptionFactory: (): Error => { + throw new BadRequestException(ResponseMessages.ecosystem.error.invalidOrgId); + } + }) + ) + orgId: string, @User() user: user, @Res() res: Response ): Promise { diff --git a/apps/ecosystem/repositories/ecosystem.repository.ts b/apps/ecosystem/repositories/ecosystem.repository.ts index 0a5ade5a6..1e6a96197 100755 --- a/apps/ecosystem/repositories/ecosystem.repository.ts +++ b/apps/ecosystem/repositories/ecosystem.repository.ts @@ -213,14 +213,20 @@ export class EcosystemRepository { } } - async checkIntentExist(name?: string): Promise { + async checkIntentExist(name: string, ecosystemId: string, excludeIntentId?: string): Promise { try { - const checkIntentExist = await this.prisma.intents.findFirst({ + return await this.prisma.intents.findFirst({ where: { - name + ecosystemId, + name: { + equals: name.trim(), + mode: 'insensitive' + }, + ...(excludeIntentId && { + id: { not: excludeIntentId } + }) } }); - return checkIntentExist; } catch (error) { this.logger.error(`error: ${JSON.stringify(error)}`); throw new InternalServerErrorException(error); diff --git a/apps/ecosystem/src/ecosystem.service.ts b/apps/ecosystem/src/ecosystem.service.ts index 6a32ee35e..d9440c16f 100755 --- a/apps/ecosystem/src/ecosystem.service.ts +++ b/apps/ecosystem/src/ecosystem.service.ts @@ -739,7 +739,7 @@ export class EcosystemService { throw new NotFoundException(ResponseMessages.ecosystem.error.ecosystemNotFound); } - const intentExist = await this.ecosystemRepository.checkIntentExist(createIntentDto?.name); + const intentExist = await this.ecosystemRepository.checkIntentExist(createIntentDto.name, ecosystemId); if (intentExist) { throw new ConflictException(ResponseMessages.ecosystem.error.intentAlreadyExists); @@ -800,13 +800,13 @@ export class EcosystemService { */ async updateIntent(updateIntentDto: UpdateIntentDto): Promise { try { - const { userId, name, description } = updateIntentDto; + const { userId, name, description, ecosystemId, intentId } = updateIntentDto; if (!userId) { throw new BadRequestException(ResponseMessages.ecosystem.error.userIdMissing); } - const intentExist = await this.ecosystemRepository.checkIntentExist(updateIntentDto?.name); + const intentExist = await this.ecosystemRepository.checkIntentExist(name, ecosystemId, intentId); if (intentExist) { throw new ConflictException(ResponseMessages.ecosystem.error.intentAlreadyExists); From 43a28ed7dc138bcdd13510548bda4133637c8c48 Mon Sep 17 00:00:00 2001 From: pranalidhanavade Date: Wed, 18 Feb 2026 18:29:11 +0530 Subject: [PATCH 8/8] fix: code conflicts Signed-off-by: pranalidhanavade --- .../src/ecosystem/ecosystem.controller.ts | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts index 5ce1c8138..1aa771716 100755 --- a/apps/api-gateway/src/ecosystem/ecosystem.controller.ts +++ b/apps/api-gateway/src/ecosystem/ecosystem.controller.ts @@ -435,4 +435,26 @@ export class EcosystemController { data: invitationData }); } + + @Get('/dashboard/summary') + @Roles(OrgRoles.PLATFORM_ADMIN) + @UseGuards(AuthGuard('jwt'), EcosystemRolesGuard) + @ApiBearerAuth() + @ApiOperation({ + summary: 'Get Count for Ecosystem dashboard', + description: 'Get Count for Ecosystem dashboard' + }) + @ApiResponse({ + status: HttpStatus.OK, + description: 'Dashboard count fetched successfully' + }) + async getDashboardCountEcosystem(@Res() res: Response): Promise { + const dashboard = await this.ecosystemService.getDashboardCountEcosystem(); + + return res.status(HttpStatus.OK).json({ + statusCode: HttpStatus.OK, + message: ResponseMessages.ecosystem.success.dashboard, + data: dashboard + }); + } }