Skip to content

Commit 788869f

Browse files
feat: ecosystem service and create ecosystem invitation API route (#1540)
* feat/add script to add platform admin keycloak and role Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/eslint issue Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/coderabbit comments Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * fix/changes to fetch value from db Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> * feat: ecosystem service and create ecosystem invitation API route Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: get all invitations api Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * refactor: removed userId parameter from send invitation and get invitation api Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * feat: create and get ecosystem api routes Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warning Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings and suggestions Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit warnings resolved Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * fix: coderabbit issues Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * resolved comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * resolve comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> * resolve comments on PR Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> --------- Signed-off-by: sujitaw <sujit.sutar@ayanworks.com> Signed-off-by: pranalidhanavade <pranali.dhanavade@ayanworks.com> Co-authored-by: sujitaw <sujit.sutar@ayanworks.com>
1 parent 2dc1fbe commit 788869f

24 files changed

Lines changed: 1822 additions & 23 deletions

File tree

.env.sample

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ AGENT_SERVICE_NKEY_SEED=xxxxxxxxxxxxx // Please provide Nkeys secret for agent s
118118
VERIFICATION_NKEY_SEED=xxxxxxxxxxxxx // Please provide Nkeys secret for verification service
119119
ISSUANCE_NKEY_SEED=xxxxxxxxxxxxx // Please provide Nkeys secret for issuance service
120120
CONNECTION_NKEY_SEED=xxxxxxxxxxxxx // Please provide Nkeys secret for connection service
121+
ECOSYSTEM_NKEY_SEED=xxxxxxxxxxxxx // Please provide Nkeys secret for ecosystem service
121122
CREDENTAILDEFINITION_NKEY_SEED=xxxxxxxxxxxxx // Please provide Nkeys secret for credential-definition service
122123
SCHEMA_NKEY_SEED=xxxxxxxxxxxxx // Please provide Nkeys secret for schema service
123124
UTILITIES_NKEY_SEED=xxxxxxxxxxxxx // Please provide Nkeys secret for utilities service

apps/api-gateway/src/app.module.ts

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,40 @@
1+
import { ClientsModule, Transport } from '@nestjs/microservices';
2+
import { CommonConstants, MICRO_SERVICE_NAME } from '@credebl/common/common.constant';
3+
import { ConditionalModule, ConfigModule } from '@nestjs/config';
14
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
5+
26
import { AgentController } from './agent/agent.controller';
37
import { AgentModule } from './agent-service/agent-service.module';
48
import { AppController } from './app.controller';
59
import { AppService } from './app.service';
610
import { AuthzMiddleware } from './authz/authz.middleware';
711
import { AuthzModule } from './authz/authz.module';
8-
import { ClientsModule, Transport } from '@nestjs/microservices';
9-
import { ConditionalModule, ConfigModule } from '@nestjs/config';
12+
import { CacheModule } from '@nestjs/cache-manager';
13+
import { CloudWalletModule } from './cloud-wallet/cloud-wallet.module';
14+
import { ConnectionModule } from './connection/connection.module';
15+
import { ContextModule } from '@credebl/context/contextModule';
1016
import { CredentialDefinitionModule } from './credential-definition/credential-definition.module';
17+
import { EcosystemModule } from './ecosystem/ecosystem.module';
1118
import { FidoModule } from './fido/fido.module';
19+
import { GeoLocationModule } from './geo-location/geo-location.module';
20+
import { GlobalConfigModule } from '@credebl/config/global-config.module';
1221
import { IssuanceModule } from './issuance/issuance.module';
22+
import { LoggerModule } from '@credebl/logger/logger.module';
23+
import { NotificationModule } from './notification/notification.module';
24+
import { Oid4vcIssuanceModule } from './oid4vc-issuance/oid4vc-issuance.module';
25+
import { Oid4vpModule } from './oid4vc-verification/oid4vc-verification.module';
1326
import { OrganizationModule } from './organization/organization.module';
27+
import { ConfigModule as PlatformConfig } from '@credebl/config/config.module';
1428
import { PlatformModule } from './platform/platform.module';
15-
import { VerificationModule } from './verification/verification.module';
1629
import { RevocationController } from './revocation/revocation.controller';
1730
import { RevocationModule } from './revocation/revocation.module';
1831
import { SchemaModule } from './schema/schema.module';
1932
import { UserModule } from './user/user.module';
20-
import { ConnectionModule } from './connection/connection.module';
21-
import { getNatsOptions } from '@credebl/common/nats.config';
22-
import { CacheModule } from '@nestjs/cache-manager';
23-
import { WebhookModule } from './webhook/webhook.module';
2433
import { UtilitiesModule } from './utilities/utilities.module';
25-
import { NotificationModule } from './notification/notification.module';
26-
import { GeoLocationModule } from './geo-location/geo-location.module';
27-
import { CommonConstants, MICRO_SERVICE_NAME } from '@credebl/common/common.constant';
28-
import { CloudWalletModule } from './cloud-wallet/cloud-wallet.module';
29-
import { ContextModule } from '@credebl/context/contextModule';
30-
import { LoggerModule } from '@credebl/logger/logger.module';
31-
import { GlobalConfigModule } from '@credebl/config/global-config.module';
32-
import { ConfigModule as PlatformConfig } from '@credebl/config/config.module';
33-
import { Oid4vcIssuanceModule } from './oid4vc-issuance/oid4vc-issuance.module';
34+
import { VerificationModule } from './verification/verification.module';
35+
import { WebhookModule } from './webhook/webhook.module';
3436
import { X509Module } from './x509/x509.module';
35-
import { Oid4vpModule } from './oid4vc-verification/oid4vc-verification.module';
37+
import { getNatsOptions } from '@credebl/common/nats.config';
3638
import { shouldLoadOidcModules } from '@credebl/common/common.utils';
3739

3840
@Module({
@@ -63,6 +65,7 @@ import { shouldLoadOidcModules } from '@credebl/common/common.utils';
6365
UtilitiesModule,
6466
WebhookModule,
6567
NotificationModule,
68+
EcosystemModule,
6669
GlobalConfigModule,
6770
CacheModule.register(),
6871
GeoLocationModule,
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
2+
3+
import { ApiProperty } from '@nestjs/swagger';
4+
import { Transform } from 'class-transformer';
5+
6+
export class CreateEcosystemInvitationDto {
7+
@ApiProperty({ example: 'awqx@gmail.com' })
8+
@IsEmail({}, { message: 'Please provide a valid email' })
9+
@IsNotEmpty({ message: 'Email is required' })
10+
@IsString({ message: 'Email should be a string' })
11+
@Transform(({ value }) => value?.trim())
12+
email: string;
13+
}
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
import {
2+
ApiBearerAuth,
3+
ApiForbiddenResponse,
4+
ApiOperation,
5+
ApiResponse,
6+
ApiTags,
7+
ApiUnauthorizedResponse
8+
} from '@nestjs/swagger';
9+
import { Body, Controller, Get, HttpStatus, Param, Post, Res, UseFilters, UseGuards } from '@nestjs/common';
10+
import { Response } from 'express';
11+
import { AuthGuard } from '@nestjs/passport';
12+
import { CustomExceptionFilter } from '@credebl/common/exception-handler';
13+
import { EcosystemService } from './ecosystem.service';
14+
import { ForbiddenErrorDto } from '../dtos/forbidden-error.dto';
15+
import { OrgRoles } from 'libs/org-roles/enums';
16+
import { ResponseMessages } from '@credebl/common/response-messages';
17+
import { Roles } from '../authz/decorators/roles.decorator';
18+
import { UnauthorizedErrorDto } from '../dtos/unauthorized-error.dto';
19+
import { CreateEcosystemInvitationDto } from './dtos/send-ecosystem-invitation';
20+
import { OrgRolesGuard } from '../authz/guards/org-roles.guard';
21+
import { user } from '@prisma/client';
22+
import { User } from '../authz/decorators/user.decorator';
23+
import { ApiResponseDto } from '../dtos/apiResponse.dto';
24+
import { CreateEcosystemDto } from 'apps/ecosystem/dtos/create-ecosystem-dto';
25+
import { IResponse } from '@credebl/common/interfaces/response.interface';
26+
27+
@UseFilters(CustomExceptionFilter)
28+
@Controller('ecosystem')
29+
@ApiTags('ecosystem')
30+
@ApiUnauthorizedResponse({
31+
description: 'Unauthorized',
32+
type: UnauthorizedErrorDto
33+
})
34+
@ApiForbiddenResponse({
35+
description: 'Forbidden',
36+
type: ForbiddenErrorDto
37+
})
38+
export class EcosystemController {
39+
constructor(private readonly ecosystemService: EcosystemService) {}
40+
41+
/**
42+
* Invitation to create ecosystem (platform admin)
43+
* @param createEcosystemInvitationDto
44+
* @returns Success message
45+
*/
46+
@Post('/invitations')
47+
@Roles(OrgRoles.PLATFORM_ADMIN)
48+
@ApiOperation({
49+
summary: 'Create ecosystem invitation (platform admin)',
50+
description: 'Invite a user to create an ecosystem'
51+
})
52+
@ApiResponse({
53+
status: HttpStatus.CREATED,
54+
description: 'Success',
55+
type: ApiResponseDto
56+
})
57+
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
58+
@ApiBearerAuth()
59+
async createInvitation(
60+
@Body() createEcosystemInvitationDto: CreateEcosystemInvitationDto,
61+
@User() reqUser: user,
62+
@Res() res: Response
63+
): Promise<Response> {
64+
await this.ecosystemService.inviteUserToCreateEcosystem(createEcosystemInvitationDto.email, reqUser.id);
65+
66+
return res.status(HttpStatus.CREATED).json({
67+
statusCode: HttpStatus.CREATED,
68+
message: ResponseMessages.ecosystem.success.createInvitation
69+
});
70+
}
71+
72+
/**
73+
* Get invitations sent by platform admin
74+
* @returns Invitation details
75+
*/
76+
77+
@Get('/invitations')
78+
@ApiOperation({
79+
summary: 'Get ecosystem invitations by user (platform admin)',
80+
description: 'Fetch all ecosystem invitations created by the logged-in user'
81+
})
82+
@ApiResponse({
83+
status: HttpStatus.OK,
84+
description: 'Invitations fetched successfully'
85+
})
86+
@Roles(OrgRoles.PLATFORM_ADMIN)
87+
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
88+
@ApiBearerAuth()
89+
async getInvitations(@User() reqUser: user, @Res() res: Response): Promise<Response> {
90+
const invitations = await this.ecosystemService.getInvitationsByUserId(reqUser.id);
91+
92+
return res.status(HttpStatus.OK).json({
93+
statusCode: HttpStatus.OK,
94+
message: ResponseMessages.ecosystem.success.fetch,
95+
data: invitations
96+
});
97+
}
98+
/**
99+
* Create new ecosystem
100+
* @param createEcosystemDto
101+
* @param orgId The ID of the organization
102+
* @returns Created ecosystem details
103+
*/
104+
@Post('/:orgId')
105+
@ApiOperation({
106+
summary: 'Create a new ecosystem',
107+
description: 'Create a new ecosystem'
108+
})
109+
@ApiResponse({
110+
status: HttpStatus.CREATED,
111+
description: 'Created',
112+
type: ApiResponseDto
113+
})
114+
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
115+
@ApiBearerAuth()
116+
@Roles(OrgRoles.OWNER)
117+
async createNewEcosystem(
118+
@Body() createEcosystemDto: CreateEcosystemDto,
119+
@Param('orgId') orgId: string,
120+
@User() user: user,
121+
@Res() res: Response
122+
): Promise<Response> {
123+
createEcosystemDto.orgId = orgId;
124+
createEcosystemDto.userId = user?.id;
125+
126+
const ecosystem = await this.ecosystemService.createEcosystem(createEcosystemDto);
127+
128+
const finalResponse: IResponse = {
129+
statusCode: HttpStatus.CREATED,
130+
message: ResponseMessages.ecosystem.success.create,
131+
data: ecosystem
132+
};
133+
134+
return res.status(HttpStatus.CREATED).json(finalResponse);
135+
}
136+
/**
137+
* Get all ecosystems (platform admin)
138+
* @returns All ecosystems from platform
139+
*/
140+
@Get()
141+
@ApiOperation({
142+
summary: 'Get all ecosystems (platform admin)',
143+
description: 'Fetch all ecosystems available on the platform'
144+
})
145+
@ApiResponse({
146+
status: HttpStatus.OK,
147+
description: 'Ecosystems fetched successfully'
148+
})
149+
@Roles(OrgRoles.PLATFORM_ADMIN)
150+
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
151+
@ApiBearerAuth()
152+
async getAllEcosystems(@User() reqUser: user, @Res() res: Response): Promise<Response> {
153+
const ecosystems = await this.ecosystemService.getAllEcosystems();
154+
155+
return res.status(HttpStatus.OK).json({
156+
statusCode: HttpStatus.OK,
157+
message: ResponseMessages.ecosystem.success.fetch,
158+
data: ecosystems
159+
});
160+
}
161+
162+
/**
163+
* Get specific ecosystem dashboard details
164+
* @param createEcosystemInvitationDto The details of the invitation
165+
* @param ecosystemId the ecosystem
166+
* @param orgId ID of the organization
167+
* @returns Details of specific ecosystem
168+
*/
169+
@Get('/:ecosystemId/:orgId/dashboard')
170+
@ApiOperation({
171+
summary: 'Get ecosystem dashboard details (platform admin and organization owner)',
172+
description: 'Fetch ecosystem dashboard data for a specific organization'
173+
})
174+
@ApiResponse({
175+
status: HttpStatus.OK,
176+
description: 'Ecosystem dashboard data fetched successfully'
177+
})
178+
@Roles(OrgRoles.PLATFORM_ADMIN, OrgRoles.OWNER, OrgRoles.ADMIN)
179+
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
180+
@ApiBearerAuth()
181+
async getEcosystemDashboard(
182+
@Param('ecosystemId') ecosystemId: string,
183+
@Param('orgId') orgId: string,
184+
@Res() res: Response
185+
): Promise<Response> {
186+
const dashboardData = await this.ecosystemService.getEcosystemDashboard(ecosystemId, orgId);
187+
188+
return res.status(HttpStatus.OK).json({
189+
statusCode: HttpStatus.OK,
190+
message: ResponseMessages.ecosystem.success.fetch,
191+
data: dashboardData
192+
});
193+
}
194+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ClientsModule, Transport } from '@nestjs/microservices';
2+
3+
import { CommonConstants } from '@credebl/common/common.constant';
4+
import { EcosystemController } from './ecosystem.controller';
5+
import { EcosystemService } from './ecosystem.service';
6+
import { Module } from '@nestjs/common';
7+
import { NATSClient } from '@credebl/common/NATSClient';
8+
import { getNatsOptions } from '@credebl/common/nats.config';
9+
10+
@Module({
11+
imports: [
12+
ClientsModule.register([
13+
{
14+
name: 'NATS_CLIENT',
15+
transport: Transport.NATS,
16+
options: getNatsOptions(CommonConstants.ECOSYSTEM_SERVICE, process.env.API_GATEWAY_NKEY_SEED)
17+
}
18+
])
19+
],
20+
controllers: [EcosystemController],
21+
providers: [EcosystemService, NATSClient]
22+
})
23+
export class EcosystemModule {}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import { NATSClient } from '@credebl/common/NATSClient';
2+
import { Injectable, Inject } from '@nestjs/common';
3+
import { ClientProxy } from '@nestjs/microservices';
4+
import { IEcosystem, IEcosystemDashboard, IEcosystemInvitations } from 'apps/ecosystem/interfaces/ecosystem.interfaces';
5+
import { CreateEcosystemDto } from 'apps/ecosystem/dtos/create-ecosystem-dto';
6+
7+
@Injectable()
8+
export class EcosystemService {
9+
constructor(
10+
@Inject('NATS_CLIENT') private readonly serviceProxy: ClientProxy,
11+
private readonly natsClient: NATSClient
12+
) {}
13+
14+
/**
15+
*
16+
* @param createEcosystemInvitationDto
17+
* @returns Ecosystem creation success
18+
*/
19+
async inviteUserToCreateEcosystem(email: string, platformAdminId: string): Promise<IEcosystemInvitations> {
20+
return this.natsClient.sendNatsMessage(this.serviceProxy, 'invite-user-for-ecosystem-creation', {
21+
email,
22+
platformAdminId
23+
});
24+
}
25+
/**
26+
*
27+
* @param userId
28+
* @returns Get invitations
29+
*/
30+
async getInvitationsByUserId(userId: string): Promise<IEcosystemInvitations[]> {
31+
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-ecosystem-invitations-by-user', { userId });
32+
}
33+
34+
/**
35+
*
36+
* @param createEcosystemDto
37+
* @returns Ecosystem creation success
38+
*/
39+
async createEcosystem(createEcosystemDto: CreateEcosystemDto): Promise<IEcosystem> {
40+
const payload = { createEcosystemDto };
41+
return this.natsClient.sendNatsMessage(this.serviceProxy, 'create-ecosystem', payload);
42+
}
43+
/**
44+
*
45+
* @param userId
46+
* @returns All ecosystems from platform
47+
*/
48+
async getAllEcosystems(): Promise<IEcosystem[]> {
49+
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-all-ecosystems', {});
50+
}
51+
/**
52+
*
53+
* @param ecosystemId
54+
* @param orgId
55+
* @returns Ecosystem details by ecosystemId
56+
*/
57+
async getEcosystemDashboard(ecosystemId: string, orgId: string): Promise<IEcosystemDashboard> {
58+
return this.natsClient.sendNatsMessage(this.serviceProxy, 'get-ecosystem-dashboard', { ecosystemId, orgId });
59+
}
60+
}

0 commit comments

Comments
 (0)