Skip to content

Commit f146548

Browse files
committed
feat: updated the consent notice APIs
Signed-off-by: Tipu_Singh <tipu.singh@ayanworks.com>
1 parent 382a06f commit f146548

10 files changed

Lines changed: 107 additions & 98 deletions

File tree

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -258,12 +258,8 @@ export class EcosystemService {
258258
return this.natsClient.sendNatsMessage(this.serviceProxy, 'create-intent-notice', payload);
259259
}
260260

261-
async getIntentNoticeById(id: string): Promise<object> {
262-
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-intent-notice', { id });
263-
}
264-
265-
async getIntentNotices(intentId?: string): Promise<object[]> {
266-
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-intent-notices', { intentId });
261+
async getIntentNotices(id?: string, intentId?: string): Promise<object[]> {
262+
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-intent-notices', { id, intentId });
267263
}
268264

269265
async getIntentNoticesByEcosystemId(

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

Lines changed: 14 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -639,12 +639,24 @@ export class IntentController {
639639
@UseGuards(AuthGuard('jwt'), EcosystemRolesGuard)
640640
@ApiOperation({
641641
summary: 'Get intent notices',
642-
description: 'Retrieves all intent notices, optionally filtered by intentId.'
642+
description: 'Retrieves intent notices. Filter by notice id or intentId (both optional).'
643643
})
644+
@ApiQuery({ name: 'id', required: false, type: String, description: 'Filter by notice PK UUID (optional)' })
644645
@ApiQuery({ name: 'intentId', required: false, type: String, description: 'Filter by intent UUID (optional)' })
645646
@ApiResponse({ status: HttpStatus.OK, description: 'Intent notices fetched successfully', type: ApiResponseDto })
646647
async getIntentNotices(
647648
@Res() res: Response,
649+
@Query(
650+
'id',
651+
new ParseUUIDPipe({
652+
version: '4',
653+
optional: true,
654+
exceptionFactory: (): Error => {
655+
throw new BadRequestException('Invalid notice ID');
656+
}
657+
})
658+
)
659+
id?: string,
648660
@Query(
649661
'intentId',
650662
new ParseUUIDPipe({
@@ -657,7 +669,7 @@ export class IntentController {
657669
)
658670
intentId?: string
659671
): Promise<Response> {
660-
const result = await this.ecosystemService.getIntentNotices(intentId);
672+
const result = await this.ecosystemService.getIntentNotices(id, intentId);
661673
const finalResponse: IResponse = {
662674
statusCode: HttpStatus.OK,
663675
message: ResponseMessages.intentNotice.success.fetchAll,
@@ -718,34 +730,6 @@ export class IntentController {
718730
});
719731
}
720732

721-
@Get('/notice/:id')
722-
@ApiBearerAuth()
723-
@Roles(OrgRoles.ECOSYSTEM_LEAD, OrgRoles.ECOSYSTEM_MEMBER)
724-
@UseGuards(AuthGuard('jwt'), EcosystemRolesGuard)
725-
@ApiOperation({ summary: 'Get intent notice by ID', description: 'Retrieves a specific intent notice by its ID.' })
726-
@ApiResponse({ status: HttpStatus.OK, description: 'Intent notice fetched successfully', type: ApiResponseDto })
727-
async getIntentNoticeById(
728-
@Param(
729-
'id',
730-
TrimStringParamPipe,
731-
new ParseUUIDPipe({
732-
exceptionFactory: (): Error => {
733-
throw new BadRequestException('Invalid notice ID');
734-
}
735-
})
736-
)
737-
id: string,
738-
@Res() res: Response
739-
): Promise<Response> {
740-
const result = await this.ecosystemService.getIntentNoticeById(id);
741-
const finalResponse: IResponse = {
742-
statusCode: HttpStatus.OK,
743-
message: ResponseMessages.intentNotice.success.fetch,
744-
data: result
745-
};
746-
return res.status(HttpStatus.OK).json(finalResponse);
747-
}
748-
749733
@Put('/notice/:id')
750734
@ApiBearerAuth()
751735
@Roles(OrgRoles.ECOSYSTEM_LEAD)

apps/api-gateway/src/oid4vc-verification/oid4vc-verification.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -319,7 +319,7 @@ export class Oid4vcVerificationController {
319319
description: 'Verification presentation created successfully.',
320320
type: ApiResponseDto
321321
})
322-
@ApiQuery({ name: 'ecosystemId', required: false })
322+
@ApiQuery({ name: 'ecosystemId', required: true })
323323
@ApiBearerAuth()
324324
@Roles(OrgRoles.OWNER)
325325
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)

apps/api-gateway/src/oid4vc-verification/oid4vc-verification.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ export class Oid4vcVerificationService {
2626
verifierId: string,
2727
createIntentDto: CreateIntentBasedVerificationDto,
2828
userDetails: user,
29-
ecosystemId?: string
29+
ecosystemId: string
3030
): Promise<object> {
3131
const { intent, responseMode, requestSigner, expectedOrigins } = createIntentDto;
3232
const payload = {

apps/ecosystem/repositories/ecosystem.repository.ts

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -811,11 +811,15 @@ export class EcosystemRepository {
811811
}
812812

813813
// eslint-disable-next-line camelcase
814-
async getIntentTemplateByIntentAndOrg(intentName: string, verifierOrgId: string): Promise<intent_templates | null> {
814+
async getIntentTemplateByIntentAndOrg(
815+
intentName: string,
816+
verifierOrgId: string,
817+
ecosystemId?: string
818+
): Promise<intent_templates | null> {
815819
try {
816820
const template = await this.prisma.intent_templates.findFirst({
817821
where: {
818-
intent: { is: { name: intentName } },
822+
intent: { is: { name: intentName, ...(ecosystemId && { ecosystemId }) } },
819823
OR: [{ orgId: verifierOrgId }, { orgId: null }]
820824
},
821825
select: {
@@ -1711,21 +1715,18 @@ export class EcosystemRepository {
17111715
}
17121716
}
17131717

1714-
async getIntentNoticeById(id: string): Promise<object | null> {
1715-
try {
1716-
return await this.prisma.intent_notices.findFirst({
1717-
where: { id }
1718-
});
1719-
} catch (error) {
1720-
this.logger.error(`getIntentNoticeById error: ${error}`);
1721-
throw error;
1722-
}
1723-
}
1724-
1725-
async getIntentNotices(intentId?: string): Promise<object[]> {
1718+
async getIntentNotices(id?: string, intentId?: string): Promise<object[]> {
17261719
try {
1720+
const where = {
1721+
...(id && { id }),
1722+
...(intentId && { intentId })
1723+
};
17271724
return await this.prisma.intent_notices.findMany({
1728-
where: intentId ? { intentId } : {},
1725+
where,
1726+
include: {
1727+
intent: { select: { id: true, name: true, ecosystemId: true } },
1728+
organisation: { select: { name: true, description: true } }
1729+
},
17291730
orderBy: { createDateTime: 'desc' }
17301731
});
17311732
} catch (error) {
@@ -1751,7 +1752,10 @@ export class EcosystemRepository {
17511752
const [data, totalCount] = await this.prisma.$transaction([
17521753
this.prisma.intent_notices.findMany({
17531754
where,
1754-
include: { intent: { select: { id: true, name: true, ecosystemId: true } } },
1755+
include: {
1756+
intent: { select: { id: true, name: true, ecosystemId: true } },
1757+
organisation: { select: { name: true, description: true } }
1758+
},
17551759
orderBy: { createDateTime: 'desc' },
17561760
skip: (pageNumber - 1) * pageSize,
17571761
take: pageSize

apps/ecosystem/src/ecosystem.controller.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,13 @@ export class EcosystemController {
238238
async getIntentTemplateByIntentAndOrg(payload: {
239239
intentName: string;
240240
verifierOrgId: string;
241+
ecosystemId?: string;
241242
}): Promise<object | null> {
242-
return this.ecosystemService.getIntentTemplateByIntentAndOrg(payload.intentName, payload.verifierOrgId);
243+
return this.ecosystemService.getIntentTemplateByIntentAndOrg(
244+
payload.intentName,
245+
payload.verifierOrgId,
246+
payload.ecosystemId
247+
);
243248
}
244249

245250
@MessagePattern({ cmd: 'update-intent-template' })
@@ -370,14 +375,9 @@ export class EcosystemController {
370375
);
371376
}
372377

373-
@MessagePattern({ cmd: 'get-intent-notice' })
374-
async getIntentNoticeById(payload: { id: string }): Promise<object> {
375-
return this.ecosystemService.getIntentNoticeById(payload.id);
376-
}
377-
378378
@MessagePattern({ cmd: 'get-intent-notices' })
379-
async getIntentNotices(payload: { intentId?: string }): Promise<object[]> {
380-
return this.ecosystemService.getIntentNotices(payload.intentId);
379+
async getIntentNotices(payload: { id?: string; intentId?: string }): Promise<object[]> {
380+
return this.ecosystemService.getIntentNotices(payload.id, payload.intentId);
381381
}
382382

383383
@MessagePattern({ cmd: 'get-intent-notices-by-ecosystem' })
Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,31 @@
1-
// export function isOrgExistForUser(userDetails: any, orgId: string): boolean {
2-
// const userOrgRoles = userDetails.userOrgRoles as { orgId: string | null }[];
3-
// return userOrgRoles.some((role) => role.orgId === orgId);
4-
// }
1+
import { HttpStatus } from '@nestjs/common';
2+
import { RpcException } from '@nestjs/microservices';
3+
4+
export async function validateNoticeUrl(noticeUrl: string): Promise<void> {
5+
if (!noticeUrl || !noticeUrl.trim()) {
6+
throw new RpcException({
7+
statusCode: HttpStatus.BAD_REQUEST,
8+
message: 'noticeUrl must not be empty.'
9+
});
10+
}
11+
try {
12+
const controller = new AbortController();
13+
const timeout = setTimeout(() => controller.abort(), 5000);
14+
const response = await fetch(noticeUrl, { method: 'HEAD', signal: controller.signal });
15+
clearTimeout(timeout);
16+
if (!response.ok) {
17+
throw new RpcException({
18+
statusCode: HttpStatus.BAD_REQUEST,
19+
message: `noticeUrl is not reachable (HTTP ${response.status}).`
20+
});
21+
}
22+
} catch (err) {
23+
if (err instanceof RpcException) {
24+
throw err;
25+
}
26+
throw new RpcException({
27+
statusCode: HttpStatus.BAD_REQUEST,
28+
message: `noticeUrl could not be resolved: ${err?.message ?? 'unreachable'}`
29+
});
30+
}
31+
}

apps/ecosystem/src/ecosystem.service.ts

Lines changed: 27 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
import { ClientProxy, RpcException } from '@nestjs/microservices';
1717
import { ClientRegistrationService } from '@credebl/client-registration';
1818
import { EcosystemRepository } from 'apps/ecosystem/repositories/ecosystem.repository';
19+
import { validateNoticeUrl } from './ecosystem.helper';
1920
import { CreateEcosystemInviteTemplate } from '../templates/create-ecosystem.templates';
2021
import { EmailDto } from '@credebl/common/dtos/email.dto';
2122
import { InviteMemberToEcosystem } from '../templates/invite-member-template';
@@ -728,9 +729,17 @@ export class EcosystemService {
728729
}
729730
}
730731

731-
async getIntentTemplateByIntentAndOrg(intentName: string, verifierOrgId: string): Promise<object | null> {
732+
async getIntentTemplateByIntentAndOrg(
733+
intentName: string,
734+
verifierOrgId: string,
735+
ecosystemId?: string
736+
): Promise<object | null> {
732737
try {
733-
const intentTemplate = await this.ecosystemRepository.getIntentTemplateByIntentAndOrg(intentName, verifierOrgId);
738+
const intentTemplate = await this.ecosystemRepository.getIntentTemplateByIntentAndOrg(
739+
intentName,
740+
verifierOrgId,
741+
ecosystemId
742+
);
734743
if (!intentTemplate) {
735744
this.logger.log(
736745
`[getIntentTemplateByIntentAndOrg] - No template found for intent ${intentName} and org ${verifierOrgId}`
@@ -1020,19 +1029,28 @@ export class EcosystemService {
10201029

10211030
async createIntentNotice(intentId: string, noticeUrl: string, userDetails: user, orgId?: string): Promise<object> {
10221031
try {
1023-
// if (orgId) {
1024-
// if (!isOrgExistForUser(userDetails, orgId)) {
1025-
// throw new RpcException({ statusCode: HttpStatus.FORBIDDEN, message: 'Provided orgId does not belong to the user.' });
1026-
// }
1027-
// }
1032+
await validateNoticeUrl(noticeUrl);
1033+
10281034
const intent = await this.ecosystemRepository.findIntentById(intentId);
1035+
10291036
if (!intent) {
10301037
throw new RpcException({
10311038
statusCode: HttpStatus.NOT_FOUND,
10321039
message: ResponseMessages.intentNotice.error.intentNotFound
10331040
});
10341041
}
10351042
await this.validateEcosystemLead(userDetails.id, intent['ecosystemId']);
1043+
1044+
if (orgId) {
1045+
const orgEcosystemMembership = await this.ecosystemRepository.getEcosystemOrg(intent['ecosystemId'], orgId);
1046+
if (!orgEcosystemMembership) {
1047+
throw new RpcException({
1048+
statusCode: HttpStatus.FORBIDDEN,
1049+
message: 'The provided orgId is not a member or lead of this ecosystem.'
1050+
});
1051+
}
1052+
}
1053+
10361054
const isAlreadyExists = await this.ecosystemRepository.intentNoticeExists(intentId, orgId ?? null);
10371055
if (isAlreadyExists) {
10381056
const slotLabel = orgId ? `orgId ${orgId}` : 'no orgId';
@@ -1052,29 +1070,9 @@ export class EcosystemService {
10521070
}
10531071
}
10541072

1055-
async getIntentNoticeById(id: string): Promise<object> {
1056-
try {
1057-
const record = await this.ecosystemRepository.getIntentNoticeById(id);
1058-
if (!record) {
1059-
throw new RpcException({
1060-
statusCode: HttpStatus.NOT_FOUND,
1061-
message: ResponseMessages.intentNotice.error.notFound
1062-
});
1063-
}
1064-
return record;
1065-
} catch (error) {
1066-
const errorResponse = ErrorHandler.categorize(error, ResponseMessages.intentNotice.error.notFound);
1067-
this.logger.error(
1068-
`[getIntentNoticeById] ${errorResponse.statusCode}: ${errorResponse.message}`,
1069-
ErrorHandler.format(error)
1070-
);
1071-
throw new RpcException(errorResponse);
1072-
}
1073-
}
1074-
1075-
async getIntentNotices(intentId?: string): Promise<object[]> {
1073+
async getIntentNotices(id?: string, intentId?: string): Promise<object[]> {
10761074
try {
1077-
const records = await this.ecosystemRepository.getIntentNotices(intentId);
1075+
const records = await this.ecosystemRepository.getIntentNotices(id, intentId);
10781076
if (!records || 0 === records.length) {
10791077
throw new RpcException({
10801078
statusCode: HttpStatus.NOT_FOUND,

apps/oid4vc-verification/src/oid4vc-verification.controller.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ export class Oid4vpVerificationController {
113113
requestSigner: IRequestSigner;
114114
userDetails: user;
115115
expectedOrigins?: string[];
116-
ecosystemId?: string;
116+
ecosystemId: string;
117117
}): Promise<object> {
118118
const { orgId, verifierId, intent, responseMode, requestSigner, expectedOrigins, userDetails, ecosystemId } =
119119
payload;

apps/oid4vc-verification/src/oid4vc-verification.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ export class Oid4vpVerificationService extends BaseService {
314314
requestSigner: IRequestSigner,
315315
userDetails: user,
316316
expectedOrigins?: string[],
317-
ecosystemId?: string
317+
ecosystemId?: string // kept optional here so existing callers don't break
318318
): Promise<object> {
319319
this.logger.debug(
320320
`[createIntentBasedVerificationPresentation] called for orgId=${orgId}, verifierId=${verifierId}, intent=${intent}, user=${userDetails?.id ?? 'unknown'}`
@@ -340,7 +340,7 @@ export class Oid4vpVerificationService extends BaseService {
340340
const templateData = await this.natsClient.sendNatsMessage(
341341
this.oid4vpVerificationServiceProxy,
342342
'get-intent-template-by-intent-and-org',
343-
{ intentName: intent, verifierOrgId: orgId }
343+
{ intentName: intent, verifierOrgId: orgId, ecosystemId }
344344
);
345345

346346
if (!templateData) {

0 commit comments

Comments
 (0)