Skip to content

Commit

Permalink
feat: multi-did creation implementation for an organization (#667)
Browse files Browse the repository at this point in the history
* feat: multi did support

Signed-off-by: tipusinghaw <[email protected]>

* refactor: added validation

Signed-off-by: tipusinghaw <[email protected]>

* refactor: added logic for pervious primary DID

Signed-off-by: tipusinghaw <[email protected]>

* refactor: changed primaryDid and added size to did veriable

Signed-off-by: tipusinghaw <[email protected]>

* fix: resloved commits

Signed-off-by: tipusinghaw <[email protected]>

* fix: updated the import url

Signed-off-by: tipusinghaw <[email protected]>

---------

Signed-off-by: tipusinghaw <[email protected]>
  • Loading branch information
tipusinghaw committed Apr 23, 2024
1 parent 7508edf commit 70c5743
Show file tree
Hide file tree
Showing 17 changed files with 462 additions and 20 deletions.
37 changes: 34 additions & 3 deletions apps/agent-service/src/agent-service.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -836,8 +836,9 @@ export class AgentServiceService {
* @param payload
* @returns did and didDocument
*/
async createDid(payload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise<object> {
async createDid(createDidPayload: IDidCreate, orgId: string, user: IUserRequestInterface): Promise<object> {
try {
const {isPrimaryDid} = createDidPayload;
const agentDetails = await this.agentServiceRepository.getOrgAgentDetails(orgId);

const getApiKey = await this.getOrgAgentApiKey(orgId);
Expand All @@ -848,8 +849,38 @@ export class AgentServiceService {
} else if (getOrgAgentType.agent === OrgAgentType.SHARED) {
url = `${agentDetails.agentEndPoint}${CommonConstants.URL_SHAGENT_CREATE_DID}${agentDetails.tenantId}`;
}
const didDetails = await this.commonService.httpPost(url, payload, { headers: { authorization: getApiKey } });
return didDetails;

delete createDidPayload.isPrimaryDid;

const didDetails = await this.commonService.httpPost(url, createDidPayload, { headers: { authorization: getApiKey } });

if (!didDetails) {
throw new InternalServerErrorException(ResponseMessages.agent.error.createDid, {
cause: new Error(),
description: ResponseMessages.errorMessages.serverError
});
}
const createdDidDetails = {
orgId,
did: didDetails.did,
didDocument: didDetails.didDocument,
isPrimaryDid,
orgAgentId: agentDetails.id,
userId: user.id
};
const storeDidDetails = await this.agentServiceRepository.storeDidDetails(createdDidDetails);

if (!storeDidDetails) {
throw new InternalServerErrorException(ResponseMessages.agent.error.storeDid, {
cause: new Error(),
description: ResponseMessages.errorMessages.serverError
});
}
if (isPrimaryDid && storeDidDetails.did) {
await this.agentServiceRepository.setPrimaryDid(storeDidDetails.did, orgId);
}

return storeDidDetails;
} catch (error) {
this.logger.error(`error in create did : ${JSON.stringify(error)}`);

Expand Down
10 changes: 10 additions & 0 deletions apps/agent-service/src/interface/agent-service.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ export interface IDidCreate {
did?: string;
role?: string;
endorserDid?: string;
isPrimaryDid?: boolean;
}
export interface ITenantSchema {
tenantId?: string;
Expand Down Expand Up @@ -183,6 +184,15 @@ export interface IStoreOrgAgentDetails {
agentType?: string;
}

export interface IStoreDidDetails {
orgId: string;
isPrimaryDid?: boolean;
did: string;
didDocument?: string;
userId: string;
orgAgentId: string;
}

export interface IStoreOrgAgent {
id?: string;
clientSocketId?: string;
Expand Down
56 changes: 54 additions & 2 deletions apps/agent-service/src/repositories/agent-service.repository.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { PrismaService } from '@credebl/prisma-service';
import { Injectable, Logger } from '@nestjs/common';
// eslint-disable-next-line camelcase
import { ledgerConfig, ledgers, org_agents, org_agents_type, organisation, platform_config, user } from '@prisma/client';
import { ICreateOrgAgent, IOrgAgent, IOrgAgentsResponse, IOrgLedgers, IStoreAgent, IStoreOrgAgentDetails } from '../interface/agent-service.interface';
import { ledgerConfig, ledgers, org_agents, org_agents_type, org_dids, organisation, platform_config, user } from '@prisma/client';
import { ICreateOrgAgent, IOrgAgent, IOrgAgentsResponse, IOrgLedgers, IStoreAgent, IStoreDidDetails, IStoreOrgAgentDetails } from '../interface/agent-service.interface';
import { AgentType } from '@credebl/enum/enum';

@Injectable()
Expand Down Expand Up @@ -155,6 +155,58 @@ export class AgentServiceRepository {
}
}

/**
* Store DID details
* @param storeDidDetails
* @returns did details
*/
// eslint-disable-next-line camelcase
async storeDidDetails(storeDidDetails: IStoreDidDetails): Promise<org_dids> {
try {
const {orgId, did, didDocument, isPrimaryDid, userId, orgAgentId} = storeDidDetails;

return this.prisma.org_dids.create({
data: {
orgId,
did,
didDocument,
isPrimaryDid,
createdBy: userId,
lastChangedBy: userId,
orgAgentId
}
});
} catch (error) {
this.logger.error(`[storeDidDetails] - Store DID details: ${JSON.stringify(error)}`);
throw error;
}
}


/**
* Set primary DID
* @param did
* @returns did details
*/
// eslint-disable-next-line camelcase
async setPrimaryDid(isPrimaryDid:string, orgId:string): Promise<org_agents> {
try {
return await this.prisma.org_agents.update({
where: {
orgId
},
data: {
orgDid: isPrimaryDid
}
});

} catch (error) {
this.logger.error(`[setprimaryDid] - Update DID details: ${JSON.stringify(error)}`);
throw error;
}
}


/**
* Get agent details
* @param orgId
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,13 @@ export class AgentController {
* @param orgId
* @returns did
*/
@Post('/orgs/:orgId/agents/createDid')
@Post('/orgs/:orgId/agents/did')
@ApiOperation({
summary: 'Create did',
description: 'Create did'
summary: 'Create new did',
description: 'Create new did for an organization'
})
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
@Roles(OrgRoles.OWNER, OrgRoles.ADMIN)
@Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER)
@ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto })
async createDid(
@Param('orgId') orgId: string,
Expand Down
7 changes: 6 additions & 1 deletion apps/api-gateway/src/agent-service/dto/create-did.dto.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { trim } from '@credebl/common/cast.helper';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import { MaxLength, IsString, Matches, IsNotEmpty, IsOptional } from 'class-validator';
import { MaxLength, IsString, Matches, IsNotEmpty, IsOptional, IsBoolean } from 'class-validator';

export class CreateDidDto {

Expand Down Expand Up @@ -75,4 +75,9 @@ export class CreateDidDto {
@Transform(({ value }) => trim(value))
@IsString({ message: 'endorser did must be in string format.' })
endorserDid?: string;

@ApiProperty({example: false})
@ApiPropertyOptional()
@IsBoolean({ message: 'isPrimaryDid did must be true or false.' })
isPrimaryDid: boolean = false;
}
22 changes: 22 additions & 0 deletions apps/api-gateway/src/organization/dtos/set-primary-did.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { ApiExtraModels, ApiPropertyOptional } from '@nestjs/swagger';
import { IsNotEmpty, IsString} from 'class-validator';

import { Transform } from 'class-transformer';
import { trim } from '@credebl/common/cast.helper';

@ApiExtraModels()
export class PrimaryDid {

@ApiPropertyOptional()
@Transform(({ value }) => trim(value))
@IsNotEmpty({ message: 'Did is required.' })
@IsString({ message: 'Did must be in string format.' })
did: string;

@ApiPropertyOptional()
@Transform(({ value }) => trim(value))
@IsNotEmpty({ message: 'Id is required.' })
@IsString({ message: 'Id must be in string format.' })
id: string;

}
44 changes: 44 additions & 0 deletions apps/api-gateway/src/organization/organization.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { ClientCredentialsDto } from './dtos/client-credentials.dto';
import { PaginationDto } from '@credebl/common/dtos/pagination.dto';
import { validate as isValidUUID } from 'uuid';
import { UserAccessGuard } from '../authz/guards/user-access-guard';
import { PrimaryDid } from './dtos/set-primary-did.dto';

@UseFilters(CustomExceptionFilter)
@Controller('orgs')
Expand Down Expand Up @@ -318,6 +319,29 @@ export class OrganizationController {

return res.status(HttpStatus.OK).json(finalResponse);
}

/**
* @returns DID list of organization
*/

@Get('/:orgId/dids')
@Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER)
@ApiBearerAuth()
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
@ApiResponse({ status: HttpStatus.OK, description: 'Success', type: ApiResponseDto })
@ApiOperation({ summary: 'Fetch organization DIDs', description: 'Get all DIDs from organization' })

async getAllDidByOrgId(@Param('orgId') orgId: string, @Res() res: Response): Promise<Response> {
const users = await this.organizationService.getDidList(orgId);
const finalResponse: IResponse = {
statusCode: HttpStatus.OK,
message: ResponseMessages.organisation.success.orgDids,
data: users
};

return res.status(HttpStatus.OK).json(finalResponse);
}

/**
* @returns organization details
*/
Expand All @@ -341,6 +365,26 @@ export class OrganizationController {
return res.status(HttpStatus.CREATED).json(finalResponse);
}


/**
* @returns success message
*/

@Post('/:orgId/primary-did')
@Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER, OrgRoles.VERIFIER, OrgRoles.MEMBER)
@ApiOperation({ summary: 'Set primary DID', description: 'Set primary DID for an organization' })
@ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto })
@UseGuards(AuthGuard('jwt'), OrgRolesGuard)
@ApiBearerAuth()
async setPrimaryDid(@Param('orgId') orgId: string, @Body() primaryDidPayload: PrimaryDid, @Res() res: Response): Promise<Response> {
const orgData = await this.organizationService.setPrimaryDid(primaryDidPayload, orgId);
const finalResponse: IResponse = {
statusCode: HttpStatus.CREATED,
message: ResponseMessages.organisation.success.primaryDid,
data: orgData
};
return res.status(HttpStatus.CREATED).json(finalResponse);
}
/**
*
* @param orgId
Expand Down
21 changes: 20 additions & 1 deletion apps/api-gateway/src/organization/organization.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,14 @@ import { BulkSendInvitationDto } from './dtos/send-invitation.dto';
import { UpdateUserRolesDto } from './dtos/update-user-roles.dto';
import { UpdateOrganizationDto } from './dtos/update-organization-dto';
import { organisation } from '@prisma/client';
import { IGetOrgById, IGetOrganization } from 'apps/organization/interfaces/organization.interface';
import { IDidList, IGetOrgById, IGetOrganization } from 'apps/organization/interfaces/organization.interface';
import { IOrgUsers } from 'apps/user/interfaces/user.interface';
import { IOrgCredentials, IOrganization, IOrganizationInvitations, IOrganizationDashboard } from '@credebl/common/interfaces/organization.interface';
import { ClientCredentialsDto } from './dtos/client-credentials.dto';
import { IAccessTokenData } from '@credebl/common/interfaces/interface';
import { PaginationDto } from '@credebl/common/dtos/pagination.dto';
import { IClientRoles } from '@credebl/client-registration/interfaces/client.interface';
import { PrimaryDid } from './dtos/set-primary-did.dto';

@Injectable()
export class OrganizationService extends BaseService {
Expand All @@ -31,6 +32,17 @@ export class OrganizationService extends BaseService {
return this.sendNatsMessage(this.serviceProxy, 'create-organization', payload);
}

/**
*
* @param primaryDidPayload
* @returns Set Primary Did for organization
*/
async setPrimaryDid(primaryDidPayload: PrimaryDid, orgId:string): Promise<organisation> {
const {did, id} = primaryDidPayload;
const payload = { did, orgId, id};
return this.sendNatsMessage(this.serviceProxy, 'set-primary-did', payload);
}

/**
*
* @param orgId
Expand Down Expand Up @@ -174,6 +186,13 @@ export class OrganizationService extends BaseService {
return this.sendNatsMessage(this.serviceProxy, 'fetch-organization-user', payload);
}

async getDidList(
orgId: string
): Promise<IDidList[]> {
const payload = { orgId };
return this.sendNatsMessage(this.serviceProxy, 'fetch-organization-dids', payload);
}

async getOrgPofile(
orgId: string
): Promise<organisation> {
Expand Down
28 changes: 28 additions & 0 deletions apps/organization/interfaces/organization.interface.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Prisma } from "@prisma/client";

export interface IUserOrgRoles {
id: string
userId: string
Expand Down Expand Up @@ -141,4 +143,30 @@ export interface Payload {
pageNumber: number;
pageSize: number;
search: string;
}

export interface IDidList {
id: string;
createDateTime: Date;
did: string;
lastChangedDateTime: Date;
isPrimaryDid: boolean;
}

export interface IPrimaryDid {
orgId: string,
did: string
}

export interface IDidDetails {
id: string;
createDateTime: Date;
createdBy: string;
lastChangedDateTime: Date;
lastChangedBy: string;
orgId: string;
isPrimaryDid: boolean;
did: string;
didDocument: Prisma.JsonValue;
orgAgentId: string;
}
Loading

0 comments on commit 70c5743

Please sign in to comment.