Skip to content

Commit bbc11bb

Browse files
fix: error handling for intent api endpoints (#1560)
* fix: error handling for intent api endpoints Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * removed commented code Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issue Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> --------- Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com>
1 parent e76df82 commit bbc11bb

8 files changed

Lines changed: 137 additions & 37 deletions

File tree

apps/api-gateway/src/ecosystem/ecosystem.controller.ts

Lines changed: 53 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ import { CreateEcosystemDto, PaginationGetAllEcosystem } from 'apps/ecosystem/dt
4444
import { DeleteEcosystemOrgDto } from './dtos/delete-ecosystem-users';
4545
import { GetEcosystemInvitationsQueryDto, UpdateEcosystemOrgStatusDto } from './dtos/ecosystem';
4646
import { IIntentTemplateList } from '@credebl/common/interfaces/intents-template.interface';
47-
import { IUserRequest } from '@credebl/user-request/user-request.interface';
4847
import { CreateIntentDto } from 'apps/ecosystem/dtos/create-intent.dto';
4948
import { UpdateIntentDto } from 'apps/ecosystem/dtos/update-intent.dto';
5049
import { GetAllIntentTemplatesResponseDto } from '../utilities/dtos/get-all-intent-templates-response.dto';
@@ -264,7 +263,7 @@ export class EcosystemController {
264263
TrimStringParamPipe,
265264
new ParseUUIDPipe({
266265
exceptionFactory: (): Error => {
267-
throw new BadRequestException(ResponseMessages.organisation.error.invalidOrgId);
266+
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidOrgId);
268267
}
269268
})
270269
)
@@ -547,6 +546,7 @@ export class EcosystemController {
547546
async getIntentTemplatesByIntentId(
548547
@Param(
549548
'intentId',
549+
TrimStringParamPipe,
550550
new ParseUUIDPipe({
551551
exceptionFactory: (): Error => {
552552
throw new BadRequestException('Invalid intent ID format');
@@ -580,7 +580,19 @@ export class EcosystemController {
580580
description: 'Retrieve all intent templates for a specific organization.'
581581
})
582582
@ApiResponse({ status: HttpStatus.OK, description: 'Intent templates retrieved successfully', type: ApiResponseDto })
583-
async getIntentTemplatesByOrgId(@Param('orgId') orgId: string, @Res() res: Response): Promise<Response> {
583+
async getIntentTemplatesByOrgId(
584+
@Param(
585+
'orgId',
586+
TrimStringParamPipe,
587+
new ParseUUIDPipe({
588+
exceptionFactory: (): Error => {
589+
throw new BadRequestException('Invalid orgId format');
590+
}
591+
})
592+
)
593+
orgId: string,
594+
@Res() res: Response
595+
): Promise<Response> {
584596
const intentTemplates = await this.ecosystemService.getIntentTemplatesByOrgId(orgId);
585597
const finalResponse: IResponse = {
586598
statusCode: HttpStatus.OK,
@@ -605,6 +617,7 @@ export class EcosystemController {
605617
async getIntentTemplateById(
606618
@Param(
607619
'id',
620+
TrimStringParamPipe,
608621
new ParseUUIDPipe({
609622
exceptionFactory: (): Error => {
610623
throw new BadRequestException(ResponseMessages.oid4vpIntentToTemplate.error.invalidId);
@@ -637,9 +650,18 @@ export class EcosystemController {
637650
@ApiOperation({ summary: 'Update Intent Template', description: 'Update an existing intent template mapping.' })
638651
@ApiResponse({ status: HttpStatus.OK, description: 'Intent template updated successfully', type: ApiResponseDto })
639652
async updateIntentTemplate(
640-
@Param('id') id: string,
653+
@Param(
654+
'id',
655+
TrimStringParamPipe,
656+
new ParseUUIDPipe({
657+
exceptionFactory: (): Error => {
658+
throw new BadRequestException(ResponseMessages.oid4vpIntentToTemplate.error.invalidId);
659+
}
660+
})
661+
)
662+
id: string,
641663
@Body() updateIntentTemplateDto: UpdateIntentTemplateDto,
642-
@User() user: IUserRequest,
664+
@User() user: user,
643665
@Res() res: Response
644666
): Promise<Response> {
645667
const intentTemplate = await this.ecosystemService.updateIntentTemplate(id, updateIntentTemplateDto, user);
@@ -741,6 +763,7 @@ export class EcosystemController {
741763
@Res() res: Response,
742764
@Param(
743765
'ecosystemId',
766+
TrimStringParamPipe,
744767
new ParseUUIDPipe({
745768
exceptionFactory: (): Error => {
746769
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId);
@@ -749,9 +772,18 @@ export class EcosystemController {
749772
)
750773
ecosystemId: string,
751774
@Query() pageDto: PaginationDto,
752-
@Query('intentId') intentId?: string
775+
@Query(
776+
'intentId',
777+
new ParseUUIDPipe({
778+
optional: true,
779+
exceptionFactory: (): Error => {
780+
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfIntentId);
781+
}
782+
})
783+
)
784+
intentId?: string
753785
): Promise<Response> {
754-
const intents = await this.ecosystemService.getIntents(ecosystemId, pageDto, intentId);
786+
const intents = await this.ecosystemService.getIntents(ecosystemId, pageDto, intentId?.trim());
755787

756788
return res.status(HttpStatus.OK).json({
757789
statusCode: HttpStatus.OK,
@@ -781,7 +813,16 @@ export class EcosystemController {
781813
description: 'Template details fetched successfully'
782814
})
783815
async getTemplateByIntentId(
784-
@Param('orgId') orgId: string,
816+
@Param(
817+
'orgId',
818+
TrimStringParamPipe,
819+
new ParseUUIDPipe({
820+
exceptionFactory: (): Error => {
821+
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidOrgId);
822+
}
823+
})
824+
)
825+
orgId: string,
785826
@Res() res: Response,
786827
@Query() pageDto: PaginationDto
787828
): Promise<Response> {
@@ -816,6 +857,7 @@ export class EcosystemController {
816857
async updateIntent(
817858
@Param(
818859
'ecosystemId',
860+
TrimStringParamPipe,
819861
new ParseUUIDPipe({
820862
exceptionFactory: (): Error => {
821863
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId);
@@ -825,6 +867,7 @@ export class EcosystemController {
825867
ecosystemId: string,
826868
@Param(
827869
'intentId',
870+
TrimStringParamPipe,
828871
new ParseUUIDPipe({
829872
exceptionFactory: (): Error => {
830873
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfIntentId);
@@ -871,6 +914,7 @@ export class EcosystemController {
871914
async deleteIntent(
872915
@Param(
873916
'ecosystemId',
917+
TrimStringParamPipe,
874918
new ParseUUIDPipe({
875919
exceptionFactory: (): Error => {
876920
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfEcosystemId);
@@ -880,6 +924,7 @@ export class EcosystemController {
880924
ecosystemId: string,
881925
@Param(
882926
'intentId',
927+
TrimStringParamPipe,
883928
new ParseUUIDPipe({
884929
exceptionFactory: (): Error => {
885930
throw new BadRequestException(ResponseMessages.ecosystem.error.invalidFormatOfIntentId);

apps/api-gateway/src/ecosystem/ecosystem.service.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
import { CreateEcosystemDto } from 'apps/ecosystem/dtos/create-ecosystem-dto';
1313
// eslint-disable-next-line camelcase
1414
import { user } from '@prisma/client';
15-
import { IUserRequest } from '@credebl/user-request/user-request.interface';
1615
import { CreateIntentDto } from 'apps/ecosystem/dtos/create-intent.dto';
1716
import { UpdateIntentDto } from 'apps/ecosystem/dtos/update-intent.dto';
1817
import { CreateIntentTemplateDto, UpdateIntentTemplateDto } from '../utilities/dtos/intent-template.dto';
@@ -143,9 +142,9 @@ export class EcosystemService {
143142
async updateIntentTemplate(
144143
id: string,
145144
updateIntentTemplateDto: UpdateIntentTemplateDto,
146-
user: IUserRequest
145+
user: user
147146
): Promise<object> {
148-
const payload = { id, ...updateIntentTemplateDto, user: { id: user.userId } };
147+
const payload = { id, ...updateIntentTemplateDto, user: { id: user.id } };
149148
return this.natsClient.sendNatsMessage(this.serviceProxy, 'update-intent-template', payload);
150149
}
151150

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
1-
import { ApiProperty } from '@nestjs/swagger';
21
import { IsNotEmpty, IsString } from 'class-validator';
32

3+
import { ApiProperty } from '@nestjs/swagger';
4+
import { Transform } from 'class-transformer';
5+
import { trim } from '@credebl/common/cast.helper';
6+
47
export class GetIntentTemplateByIntentAndOrgDto {
58
@ApiProperty({ example: 'WEB_CHECKIN', description: 'Intent name' })
6-
@IsNotEmpty()
9+
@Transform(({ value }) => trim(value))
10+
@IsNotEmpty({ message: 'intentName is required' })
711
@IsString()
812
intentName: string;
913

1014
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', description: 'Verifier Organization ID' })
11-
@IsNotEmpty()
15+
@Transform(({ value }) => trim(value))
16+
@IsNotEmpty({ message: 'verifierOrgId is required' })
1217
@IsString()
1318
verifierOrgId: string;
1419
}

apps/api-gateway/src/utilities/dtos/intent-template.dto.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
import { ApiProperty } from '@nestjs/swagger';
21
import { IsNotEmpty, IsOptional, IsUUID } from 'class-validator';
32

3+
import { ApiProperty } from '@nestjs/swagger';
4+
45
export class CreateIntentTemplateDto {
56
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', description: 'Organization ID' })
67
@IsOptional()
@@ -20,7 +21,7 @@ export class CreateIntentTemplateDto {
2021

2122
export class UpdateIntentTemplateDto {
2223
@ApiProperty({ example: '123e4567-e89b-12d3-a456-426614174000', description: 'Organization ID' })
23-
@IsNotEmpty()
24+
@IsOptional()
2425
@IsUUID()
2526
orgId: string;
2627

apps/ecosystem/repositories/ecosystem.repository.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,7 @@ export class EcosystemRepository {
640640
}
641641

642642
// eslint-disable-next-line camelcase
643-
async getIntentTemplateById(id: string): Promise<intent_templates> {
643+
async getIntentTemplateById(id: string): Promise<intent_templates | null> {
644644
try {
645645
const intentTemplate = await this.prisma.intent_templates.findUnique({
646646
where: { id },
@@ -650,10 +650,8 @@ export class EcosystemRepository {
650650
template: true
651651
}
652652
});
653-
if (!intentTemplate) {
654-
throw new NotFoundException('Intent template not found');
655-
}
656-
this.logger.log(`[getIntentTemplateById] - Intent template details ${id}`);
653+
654+
this.logger.log(`[getIntentTemplateById] fetched id ${id}`);
657655
return intentTemplate;
658656
} catch (error) {
659657
this.logger.error(`Error in getIntentTemplateById: ${error}`);
@@ -772,7 +770,9 @@ export class EcosystemRepository {
772770
async deleteIntentTemplate(id: string): Promise<intent_templates> {
773771
try {
774772
const intentTemplate = await this.prisma.intent_templates.delete({
775-
where: { id }
773+
where: {
774+
id
775+
}
776776
});
777777

778778
this.logger.log(`[deleteIntentTemplate] - Intent template deleted with id ${id}`);

apps/ecosystem/src/ecosystem.controller.ts

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -213,13 +213,13 @@ export class EcosystemController {
213213
}
214214

215215
@MessagePattern({ cmd: 'get-intent-templates-by-intent-id' })
216-
async getIntentTemplatesByIntentId(intentId: string): Promise<object[]> {
217-
return this.ecosystemService.getIntentTemplatesByIntentId(intentId);
216+
async getIntentTemplatesByIntentId(payload: { intentId: string }): Promise<object[]> {
217+
return this.ecosystemService.getIntentTemplatesByIntentId(payload.intentId);
218218
}
219219

220220
@MessagePattern({ cmd: 'get-intent-templates-by-org-id' })
221-
async getIntentTemplatesByOrgId(orgId: string): Promise<object[]> {
222-
return this.ecosystemService.getIntentTemplatesByOrgId(orgId);
221+
async getIntentTemplatesByOrgId(payload: { orgId: string }): Promise<object[]> {
222+
return this.ecosystemService.getIntentTemplatesByOrgId(payload.orgId);
223223
}
224224

225225
@MessagePattern({ cmd: 'get-all-intent-templates-by-query' })
@@ -254,10 +254,9 @@ export class EcosystemController {
254254
}
255255

256256
@MessagePattern({ cmd: 'delete-intent-template' })
257-
async deleteIntentTemplate(id: string): Promise<object> {
258-
return this.ecosystemService.deleteIntentTemplate(id);
257+
async deleteIntentTemplate(payload: { id: string }): Promise<object> {
258+
return this.ecosystemService.deleteIntentTemplate(payload.id);
259259
}
260-
261260
/**
262261
* Create a new intent
263262
*

apps/ecosystem/src/ecosystem.service.ts

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -579,6 +579,14 @@ export class EcosystemService {
579579

580580
async getIntentTemplatesByIntentId(intentId: string): Promise<object[]> {
581581
try {
582+
const intent = await this.ecosystemRepository.findIntentById(intentId);
583+
584+
if (!intent) {
585+
throw new RpcException({
586+
statusCode: HttpStatus.NOT_FOUND,
587+
message: ResponseMessages.ecosystem.error.intentNotFound
588+
});
589+
}
582590
return await this.ecosystemRepository.getIntentTemplates({ intentId });
583591
} catch (error) {
584592
const errorResponse = ErrorHandler.categorize(error, 'Failed to retrieve intent templates');
@@ -644,15 +652,50 @@ export class EcosystemService {
644652
data: { orgId: string; intentId: string; templateId: string; user: { id: string } }
645653
): Promise<object> {
646654
try {
647-
const { user, ...templateData } = data;
655+
const { user, orgId, intentId, templateId } = data;
656+
648657
if (!user?.id) {
649658
throw new RpcException({
650659
statusCode: HttpStatus.BAD_REQUEST,
651660
message: 'user is required'
652661
});
653662
}
663+
664+
if (!intentId || !templateId) {
665+
throw new RpcException({
666+
statusCode: HttpStatus.BAD_REQUEST,
667+
message: 'intentId and templateId are required'
668+
});
669+
}
670+
671+
const existing = await this.ecosystemRepository.getIntentTemplateById(id);
672+
673+
if (!existing) {
674+
throw new RpcException({
675+
statusCode: HttpStatus.NOT_FOUND,
676+
message: `Intent template not found for id ${id}`
677+
});
678+
}
679+
680+
const duplicate = await this.ecosystemRepository.findIntentTemplate({
681+
orgId,
682+
intentId,
683+
templateId
684+
});
685+
686+
if (duplicate && duplicate.id !== id) {
687+
const scope = orgId ? `org ${orgId}` : 'globally';
688+
689+
throw new RpcException({
690+
statusCode: HttpStatus.CONFLICT,
691+
message: `A template is already assigned to this intent for ${scope}.`
692+
});
693+
}
694+
654695
return await this.ecosystemRepository.updateIntentTemplate(id, {
655-
...templateData,
696+
orgId,
697+
intentId,
698+
templateId,
656699
lastChangedBy: user.id
657700
});
658701
} catch (error) {
@@ -685,11 +728,11 @@ export class EcosystemService {
685728
async createIntent(createIntentDto: CreateIntentDto): Promise<object> {
686729
try {
687730
const { name, description, ecosystemId, userId } = createIntentDto;
731+
const ecosystem = await this.ecosystemRepository.getEcosystemById(ecosystemId);
688732

689-
if (!name || !ecosystemId || !userId) {
690-
throw new BadRequestException('name, ecosystemId and userId are required');
733+
if (!ecosystem) {
734+
throw new NotFoundException(ResponseMessages.ecosystem.error.ecosystemNotFound);
691735
}
692-
693736
return this.ecosystemRepository.createIntent({
694737
name,
695738
description,
@@ -715,6 +758,12 @@ export class EcosystemService {
715758
intentId?: string
716759
): Promise<PaginatedResponse<object>> {
717760
try {
761+
const ecosystem = await this.ecosystemRepository.getEcosystemById(ecosystemId);
762+
763+
if (!ecosystem) {
764+
throw new NotFoundException(ResponseMessages.ecosystem.error.ecosystemNotFound);
765+
}
766+
718767
return await this.ecosystemRepository.getIntents(ecosystemId, pageDetail, intentId);
719768
} catch (error) {
720769
const errorResponse = ErrorHandler.categorize(error, 'Failed to retrieve intents');
@@ -739,10 +788,10 @@ export class EcosystemService {
739788
*/
740789
async updateIntent(updateIntentDto: UpdateIntentDto): Promise<object> {
741790
try {
742-
const { intentId, ecosystemId, userId, name, description } = updateIntentDto;
791+
const { userId, name, description } = updateIntentDto;
743792

744-
if (!intentId || !ecosystemId || !userId) {
745-
throw new BadRequestException('intentId, ecosystemId and userId are required');
793+
if (!userId) {
794+
throw new BadRequestException(ResponseMessages.ecosystem.error.userIdMissing);
746795
}
747796

748797
const intent = await this.ecosystemRepository.updateIntent({

0 commit comments

Comments
 (0)