diff --git a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts index 51dc9b192..b8381e531 100644 --- a/apps/api-gateway/src/issuance/dtos/issuance.dto.ts +++ b/apps/api-gateway/src/issuance/dtos/issuance.dto.ts @@ -1,12 +1,109 @@ +/* eslint-disable @typescript-eslint/array-type */ -import { IsArray, IsNotEmpty, IsOptional, IsString, IsEmail, ArrayMaxSize, ValidateNested, ArrayMinSize, IsBoolean, IsDefined, MaxLength, IsEnum } from 'class-validator'; +import { IsArray, IsNotEmpty, IsOptional, IsString, IsEmail, ArrayMaxSize, ValidateNested, ArrayMinSize, IsBoolean, IsDefined, MaxLength, IsEnum, IsObject} from 'class-validator'; import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { Transform, Type } from 'class-transformer'; import { trim } from '@credebl/common/cast.helper'; import { SortValue } from '../../enum'; import { SortFields } from 'apps/connection/src/enum/connection.enum'; import { AutoAccept } from '@credebl/enum/enum'; +import { IssueCredentialType, JsonLdCredentialDetailCredentialStatusOptions, JsonLdCredentialDetailOptionsOptions, JsonObject } from '../interfaces'; +import { IsCredentialJsonLdContext, SingleOrArray } from '../utils/helper'; + +class Issuer { + @ApiProperty() + @IsNotEmpty({ message: 'id is required' }) + @Type(() => String) + id:string | { id?: string }; +} +class Credential { + @ApiProperty() + @IsNotEmpty({ message: 'context is required' }) + @IsCredentialJsonLdContext() + '@context': Array; + + @ApiProperty() + @IsNotEmpty({ message: 'type is required' }) + type: string[]; + + @ApiProperty() + @IsString({ message: 'id should be string' }) + @IsNotEmpty({ message: 'id is required' }) + @Type(() => String) + @IsOptional() + id?:string; + + + @ApiProperty() + @ValidateNested({ each: true }) + @Type(() => Issuer) + issuer:Issuer; + @ApiProperty() + @IsString({ message: 'issuance date should be string' }) + @IsNotEmpty({ message: 'issuance date is required' }) + @Type(() => String) + issuanceDate:string; + + @ApiProperty() + @IsString({ message: 'expiration date should be string' }) + @IsNotEmpty({ message: 'expiration date is required' }) + @Type(() => String) + @IsOptional() + expirationDate?:string; + + @ApiProperty() + @IsNotEmpty({ message: ' credential subject required' }) + credentialSubject: SingleOrArray; + [key: string]: unknown + + } + + export class JsonLdCredentialDetailCredentialStatus { + public constructor(options: JsonLdCredentialDetailCredentialStatusOptions) { + if (options) { + this.type = options.type; + } + } + @IsString() + public type!: string; + } + export class JsonLdCredentialDetailOptions { + public constructor(options: JsonLdCredentialDetailOptionsOptions) { + if (options) { + this.proofPurpose = options.proofPurpose; + this.created = options.created; + this.domain = options.domain; + this.challenge = options.challenge; + this.credentialStatus = options.credentialStatus; + this.proofType = options.proofType; + } + } + + @IsString() + @IsNotEmpty({ message: 'proof purpose is required' }) + public proofPurpose!: string; + + @IsString() + @IsOptional() + public created?: string; + + @IsString() + @IsOptional() + public domain?: string; + + @IsString() + @IsOptional() + public challenge?: string; + + @IsString() + @IsNotEmpty({ message: 'proof type is required' }) + public proofType!: string; + + @IsOptional() + @IsObject() + public credentialStatus?: JsonLdCredentialDetailCredentialStatus; + } class Attribute { @ApiProperty() @IsString({ message: 'Attribute name should be string' }) @@ -33,7 +130,8 @@ class CredentialsIssuanceDto { @IsNotEmpty({ message: 'Please provide valid credential definition id' }) @IsString({ message: 'credential definition id should be string' }) @Transform(({ value }) => value.trim()) - credentialDefinitionId: string; + @IsOptional() + credentialDefinitionId?: string; @ApiProperty({ example: 'string' }) @IsNotEmpty({ message: 'Please provide valid comment' }) @@ -86,6 +184,12 @@ class CredentialsIssuanceDto { }) autoAcceptCredential?: string; + @ApiProperty({ example: 'jsonld' }) + @IsNotEmpty({ message: 'Please provide credential type ' }) + @Transform(({ value }) => trim(value).toLocaleLowerCase()) + @IsOptional() + credentialType:IssueCredentialType; + orgId: string; } @@ -101,8 +205,29 @@ export class OOBIssueCredentialDto extends CredentialsIssuanceDto { @IsArray() @ValidateNested({ each: true }) @ArrayMinSize(1) + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid attributes' }) @Type(() => Attribute) - attributes: Attribute[]; + attributes?: Attribute[]; + + + @ApiProperty() + @IsNotEmpty({ message: 'Please provide valid credential' }) + @IsObject({ message: 'credential should be an object' }) + @Type(() => Credential) + @IsOptional() + @ValidateNested({ each: true }) + credential?:Credential; + + + @ApiProperty() + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid options' }) + @IsObject({ message: 'options should be an object' }) + @ValidateNested({ each: true }) + @Type(() => JsonLdCredentialDetailOptions) + options?:JsonLdCredentialDetailOptions; + } class CredentialOffer { @@ -111,7 +236,8 @@ class CredentialOffer { @IsArray({ message: 'Attributes should be an array' }) @ValidateNested({ each: true }) @Type(() => Attribute) - attributes: Attribute[]; + @IsOptional() + attributes?: Attribute[]; @ApiProperty({ example: 'testmail@xyz.com' }) @IsEmail({}, { message: 'Please provide a valid email' }) @@ -121,6 +247,22 @@ class CredentialOffer { @Transform(({ value }) => trim(value)) @Type(() => String) emailId: string; + + @IsNotEmpty({ message: 'Please provide valid credential' }) + @IsObject({ message: 'credential should be an object' }) + @Type(() => Credential) + @IsOptional() + @ValidateNested({ each: true }) + credential?:Credential; + + @ApiProperty() + @IsOptional() + @IsNotEmpty({ message: 'Please provide valid options' }) + @IsObject({ message: 'options should be an object' }) + @ValidateNested({ each: true }) + @Type(() => JsonLdCredentialDetailOptions) + options?:JsonLdCredentialDetailOptions; + } export class IssueCredentialDto extends OOBIssueCredentialDto { @@ -218,7 +360,39 @@ export class CredentialAttributes { } export class OOBCredentialDtoWithEmail { - @ApiProperty({ example: [{ 'emailId': 'abc@example.com', 'attributes': [{ 'value': 'string', 'name': 'string' }] }] }) + @ApiProperty({ example: [ + { + 'emailId': 'xyz@example.com', + 'credential': { + '@context': [ + 'https://www.w3.org/2018/credentials/v1', + 'https://www.w3.org/2018/credentials/examples/v1' + ], + 'type': [ + 'VerifiableCredential', + 'UniversityDegreeCredential' + ], + 'issuer': { + 'id': 'did:key:z6Mkn72LVp3mq1fWSefkSMh5V7qrmGfCV4KH3K6SoTM21ouM' + }, + 'issuanceDate': '2019-10-12T07:20:50.52Z', + 'credentialSubject': { + 'id': 'did:key:z6Mkn72LVp3mq1fWSefkSMh5V7qrmGfCV4KH3K6SoTM21ouM', + 'degree': { + 'type': 'BachelorDegree', + 'name': 'Bachelor of Science and Arts' + } + } + }, + 'options': { + 'proofType': 'Ed25519Signature2018', + 'proofPurpose': 'assertionMethod' + } + } + ] + + + }) @IsNotEmpty({ message: 'Please provide valid attributes' }) @IsArray({ message: 'attributes should be array' }) @ArrayMaxSize(Number(process.env.OOB_BATCH_SIZE), { message: `Limit reached (${process.env.OOB_BATCH_SIZE} credentials max). Easily handle larger batches via seamless CSV file uploads` }) @@ -229,8 +403,9 @@ export class OOBCredentialDtoWithEmail { @ApiProperty({ example: 'string' }) @IsNotEmpty({ message: 'Please provide valid credential definition id' }) @IsString({ message: 'credential definition id should be string' }) + @IsOptional() @Transform(({ value }) => value.trim()) - credentialDefinitionId: string; + credentialDefinitionId?: string; @ApiProperty({ example: 'string' }) @IsOptional() @@ -244,6 +419,12 @@ export class OOBCredentialDtoWithEmail { @IsString({ message: 'protocol version should be string' }) protocolVersion?: string; + @ApiProperty({ example: 'jsonld' }) + @IsNotEmpty({ message: 'Please provide credential type ' }) + @Transform(({ value }) => trim(value).toLocaleLowerCase()) + @IsOptional() + credentialType:IssueCredentialType; + imageUrl?: string; orgId: string; diff --git a/apps/api-gateway/src/issuance/interfaces/index.ts b/apps/api-gateway/src/issuance/interfaces/index.ts index 834a33b4d..e06d34539 100644 --- a/apps/api-gateway/src/issuance/interfaces/index.ts +++ b/apps/api-gateway/src/issuance/interfaces/index.ts @@ -1,3 +1,6 @@ +import { JsonLdCredentialDetailCredentialStatus } from '../dtos/issuance.dto'; +import { JsonValue } from '../utils/helper'; + export interface IUserRequestInterface { userId: string; email: string; @@ -75,3 +78,24 @@ export interface IIssuedCredentialSearchParams { searchByText: string; } +export enum IssueCredentialType { + JSONLD = 'jsonld', + INDY = 'indy' +} + +export interface JsonObject { + [property: string]: JsonValue + } + + export interface JsonLdCredentialDetailCredentialStatusOptions { + type: string + } + + export interface JsonLdCredentialDetailOptionsOptions { + proofPurpose: string + created?: string + domain?: string + challenge?: string + credentialStatus?: JsonLdCredentialDetailCredentialStatus + proofType: string + } \ No newline at end of file diff --git a/apps/api-gateway/src/issuance/issuance.controller.ts b/apps/api-gateway/src/issuance/issuance.controller.ts index 02956f16a..56c1c059c 100644 --- a/apps/api-gateway/src/issuance/issuance.controller.ts +++ b/apps/api-gateway/src/issuance/issuance.controller.ts @@ -1,3 +1,4 @@ +/* eslint-disable default-param-last */ /* eslint-disable no-param-reassign */ /* eslint-disable @typescript-eslint/no-explicit-any */ /* eslint-disable camelcase */ @@ -15,7 +16,9 @@ import { Header, UploadedFile, UseInterceptors, - Logger + Logger, + BadRequestException, + NotFoundException } from '@nestjs/common'; import { ApiTags, @@ -52,7 +55,7 @@ import { Roles } from '../authz/decorators/roles.decorator'; import { OrgRoles } from 'libs/org-roles/enums'; import { OrgRolesGuard } from '../authz/guards/org-roles.guard'; import { CustomExceptionFilter } from 'apps/api-gateway/common/exception-handler'; -import { FileExportResponse, IIssuedCredentialSearchParams, RequestPayload } from './interfaces'; +import { FileExportResponse, IIssuedCredentialSearchParams, IssueCredentialType, RequestPayload } from './interfaces'; import { AwsService } from '@credebl/aws'; import { FileInterceptor } from '@nestjs/platform-express'; import { v4 as uuidv4 } from 'uuid'; @@ -536,14 +539,30 @@ export class IssuanceController { @ApiBearerAuth() @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER) + @ApiQuery({ + name:'credentialType', + enum: IssueCredentialType + }) async outOfBandCredentialOffer( @User() user: IUserRequest, @Body() outOfBandCredentialDto: OOBCredentialDtoWithEmail, + @Query('credentialType') credentialType: IssueCredentialType = IssueCredentialType.INDY, @Param('orgId') orgId: string, @Res() res: Response ): Promise { outOfBandCredentialDto.orgId = orgId; + outOfBandCredentialDto.credentialType = credentialType; + const credOffer = outOfBandCredentialDto?.credentialOffer || []; + if (IssueCredentialType.INDY !== credentialType && IssueCredentialType.JSONLD !== credentialType) { + throw new NotFoundException(ResponseMessages.issuance.error.invalidCredentialType); +} + if (outOfBandCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.credential || 0 === Object.keys(offer?.credential).length))) { + throw new BadRequestException(ResponseMessages.issuance.error.credentialNotPresent); + } + if (outOfBandCredentialDto.credentialType === IssueCredentialType.JSONLD && credOffer.every(offer => (!offer?.options || 0 === Object.keys(offer?.options).length))) { + throw new BadRequestException(ResponseMessages.issuance.error.optionsNotPresent); + } const getCredentialDetails = await this.issueCredentialService.outOfBandCredentialOffer( user, outOfBandCredentialDto @@ -568,15 +587,21 @@ export class IssuanceController { summary: `Create out-of-band credential offer`, description: `Creates an out-of-band credential offer` }) + @ApiQuery({ + name:'credentialType', + enum: IssueCredentialType + }) @UseGuards(AuthGuard('jwt'), OrgRolesGuard) @Roles(OrgRoles.OWNER, OrgRoles.ADMIN, OrgRoles.ISSUER) @ApiResponse({ status: HttpStatus.CREATED, description: 'Success', type: ApiResponseDto }) async createOOBCredentialOffer( + @Query('credentialType') credentialType: IssueCredentialType = IssueCredentialType.INDY, @Param('orgId') orgId: string, @Body() issueCredentialDto: OOBIssueCredentialDto, @Res() res: Response ): Promise { issueCredentialDto.orgId = orgId; + issueCredentialDto.credentialType = credentialType; const getCredentialDetails = await this.issueCredentialService.sendCredentialOutOfBand(issueCredentialDto); const finalResponse: IResponseType = { statusCode: HttpStatus.CREATED, diff --git a/apps/api-gateway/src/issuance/issuance.service.ts b/apps/api-gateway/src/issuance/issuance.service.ts index 42b1f03ea..bd58cba92 100644 --- a/apps/api-gateway/src/issuance/issuance.service.ts +++ b/apps/api-gateway/src/issuance/issuance.service.ts @@ -4,7 +4,7 @@ import { ClientProxy } from '@nestjs/microservices'; import { BaseService } from 'libs/service/base.service'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { ClientDetails, FileParameter, IssuanceDto, IssueCredentialDto, OOBCredentialDtoWithEmail, OOBIssueCredentialDto, PreviewFileDetails } from './dtos/issuance.dto'; -import { FileExportResponse, IIssuedCredentialSearchParams, RequestPayload } from './interfaces'; +import { FileExportResponse, IIssuedCredentialSearchParams, IssueCredentialType, RequestPayload } from './interfaces'; import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; @Injectable() @@ -29,7 +29,14 @@ export class IssuanceService extends BaseService { sendCredentialOutOfBand(issueCredentialDto: OOBIssueCredentialDto): Promise<{ response: object; }> { - const payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential }; + let payload; + if (IssueCredentialType.INDY === issueCredentialDto.credentialType) { + payload = { attributes: issueCredentialDto.attributes, comment: issueCredentialDto.comment, credentialDefinitionId: issueCredentialDto.credentialDefinitionId, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType }; + } + if (IssueCredentialType.JSONLD === issueCredentialDto.credentialType) { + payload = { credential: issueCredentialDto.credential, options:issueCredentialDto.options, comment: issueCredentialDto.comment, orgId: issueCredentialDto.orgId, protocolVersion: issueCredentialDto.protocolVersion, goalCode: issueCredentialDto.goalCode, parentThreadId: issueCredentialDto.parentThreadId, willConfirm: issueCredentialDto.willConfirm, label: issueCredentialDto.label, autoAcceptCredential: issueCredentialDto.autoAcceptCredential, credentialType: issueCredentialDto.credentialType }; + } + return this.sendNats(this.issuanceProxy, 'send-credential-create-offer-oob', payload); } diff --git a/apps/api-gateway/src/issuance/utils/helper.ts b/apps/api-gateway/src/issuance/utils/helper.ts new file mode 100644 index 000000000..574e5eb69 --- /dev/null +++ b/apps/api-gateway/src/issuance/utils/helper.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/array-type */ +import { ValidateBy, ValidationOptions, buildMessage, isString, isURL } from 'class-validator'; +import { JsonObject } from '../interfaces'; +export type SingleOrArray = T | T[] +export type JsonValue = string | number | boolean | null | JsonObject | JsonArray +export type JsonArray = Array + + +export const isJsonObject = (value: unknown): value is JsonObject => value !== undefined && 'object' === typeof value && null !== value && !Array.isArray(value); +export const CREDENTIALS_CONTEXT_V1_URL = 'https://www.w3.org/2018/credentials/v1'; +export function IsCredentialJsonLdContext(validationOptions?: ValidationOptions): PropertyDecorator { + return ValidateBy( + { + name: 'IsCredentialJsonLdContext', + validator: { + validate: (value): boolean => { + if (!Array.isArray(value)) { return false; } + + // First item must be the verifiable credential context + if (value[0] !== CREDENTIALS_CONTEXT_V1_URL) { return false; } + + return value.every((v) => (isString(v) && isURL(v)) || isJsonObject(v)); + }, + defaultMessage: buildMessage( + (eachPrefix) => `${eachPrefix + }$property must be an array of strings or objects, where the first item is the verifiable credential context URL.`, + validationOptions + ) + } + }, + validationOptions + ); +} \ No newline at end of file diff --git a/apps/issuance/interfaces/issuance.interfaces.ts b/apps/issuance/interfaces/issuance.interfaces.ts index d7ccbff71..fdb743271 100644 --- a/apps/issuance/interfaces/issuance.interfaces.ts +++ b/apps/issuance/interfaces/issuance.interfaces.ts @@ -3,6 +3,7 @@ import { AutoAccept } from '@credebl/enum/enum'; import { IUserRequest } from '@credebl/user-request/user-request.interface'; import { organisation } from '@prisma/client'; import { IUserRequestInterface } from 'apps/agent-service/src/interface/agent-service.interface'; +import { IssueCredentialType } from 'apps/api-gateway/src/issuance/interfaces'; export interface IAttributes { attributeName: string; @@ -128,12 +129,22 @@ export interface ICredentialAttributesInterface { value: string; } +export interface ICredential{ + '@context':[]; + type: string[]; +} +export interface IOptions{ + proofType:string; + proofPurpose:string; +} export interface CredentialOffer { emailId: string; attributes: IAttributes[]; + credential?:ICredential; + options?:IOptions } export interface OutOfBandCredentialOfferPayload { - credentialDefinitionId: string; + credentialDefinitionId?: string; orgId: string; comment?: string; credentialOffer?: CredentialOffer[]; @@ -146,6 +157,7 @@ export interface OutOfBandCredentialOfferPayload { label?: string, imageUrl?: string, autoAcceptCredential?: string; + credentialType?:IssueCredentialType; } export interface OutOfBandCredentialOffer { diff --git a/apps/issuance/src/issuance.service.ts b/apps/issuance/src/issuance.service.ts index 1d0a1562d..0b4ff8ddd 100644 --- a/apps/issuance/src/issuance.service.ts +++ b/apps/issuance/src/issuance.service.ts @@ -29,7 +29,7 @@ import { Queue } from 'bull'; import { FileUploadStatus, FileUploadType } from 'apps/api-gateway/src/enum'; import { AwsService } from '@credebl/aws'; import { io } from 'socket.io-client'; -import { IIssuedCredentialSearchParams } from 'apps/api-gateway/src/issuance/interfaces'; +import { IIssuedCredentialSearchParams, IssueCredentialType } from 'apps/api-gateway/src/issuance/interfaces'; import { IIssuedCredential } from '@credebl/common/interfaces/issuance.interface'; import { OOBIssueCredentialDto } from 'apps/api-gateway/src/issuance/dtos/issuance.dto'; @@ -148,33 +148,35 @@ export class IssuanceService { async sendCredentialOutOfBand(payload: OOBIssueCredentialDto): Promise<{ response: object }> { try { - const { orgId, credentialDefinitionId, comment, attributes, protocolVersion } = payload; - - const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( - credentialDefinitionId - ); - - if (schemadetailsResponse?.attributes) { - const schemadetailsResponseError = []; - const attributesArray: IAttributes[] = JSON.parse(schemadetailsResponse.attributes); - - attributesArray.forEach((attribute) => { - if (attribute.attributeName && attribute.isRequired) { - - payload.attributes.map((attr) => { - if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { - schemadetailsResponseError.push( - `Attribute '${attribute.attributeName}' is required but has an empty value.` - ); - } - return true; - }); + + const { orgId, credentialDefinitionId, comment, attributes, protocolVersion, credential, options, credentialType} = payload; + if (credentialType === IssueCredentialType.INDY) { + const schemadetailsResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( + credentialDefinitionId + ); + + if (schemadetailsResponse?.attributes) { + const schemadetailsResponseError = []; + const attributesArray: IAttributes[] = JSON.parse(schemadetailsResponse.attributes); + + attributesArray.forEach((attribute) => { + if (attribute.attributeName && attribute.isRequired) { + + payload.attributes.map((attr) => { + if (attr.name === attribute.attributeName && attribute.isRequired && !attr.value) { + schemadetailsResponseError.push( + `Attribute '${attribute.attributeName}' is required but has an empty value.` + ); + } + return true; + }); + } + }); + if (0 < schemadetailsResponseError.length) { + throw new BadRequestException(schemadetailsResponseError); } - }); - if (0 < schemadetailsResponseError.length) { - throw new BadRequestException(schemadetailsResponseError); + } - } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); @@ -196,8 +198,10 @@ export class IssuanceService { if (!apiKey || null === apiKey || undefined === apiKey) { apiKey = await this._getOrgAgentApiKey(orgId); } + let issueData; + if (credentialType === IssueCredentialType.INDY) { - const issueData = { + issueData = { protocolVersion: protocolVersion || 'v1', credentialFormats: { indy: { @@ -214,6 +218,28 @@ export class IssuanceService { label: organisation?.name, comment: comment || '' }; + + } + + if (credentialType === IssueCredentialType.JSONLD) { + issueData = { + protocolVersion: protocolVersion || 'v2', + credentialFormats: { + jsonld: { + credential, + options + } + }, + autoAcceptCredential: payload.autoAcceptCredential || 'always', + goalCode: payload.goalCode || undefined, + parentThreadId: payload.parentThreadId || undefined, + willConfirm: payload.willConfirm || undefined, + imageUrl: organisation?.logoUrl || payload?.imageUrl || undefined, + label: organisation?.name, + comment: comment || '' + }; + } + const credentialCreateOfferDetails = await this._outOfBandCredentialOffer(issueData, url, apiKey); return credentialCreateOfferDetails; @@ -409,9 +435,13 @@ export class IssuanceService { orgId, protocolVersion, attributes, - emailId + emailId, + credentialType } = outOfBandCredential; + + if (IssueCredentialType.INDY === credentialType) { + const schemaResponse: SchemaDetails = await this.issuanceRepository.getCredentialDefinitionDetails( credentialDefinitionId ); @@ -461,6 +491,7 @@ const credefError = []; throw new BadRequestException(credefError); } } + } const agentDetails = await this.issuanceRepository.getAgentEndPoint(orgId); @@ -487,27 +518,49 @@ const credefError = []; const errors = []; const emailPromises = []; - + let outOfBandIssuancePayload; const sendEmailForCredentialOffer = async (iterator, emailId, index): Promise => { const iterationNo = index + 1; try { - const outOfBandIssuancePayload = { - protocolVersion: protocolVersion || 'v1', - credentialFormats: { - indy: { - // eslint-disable-next-line @typescript-eslint/no-unused-vars - attributes: (iterator.attributes || attributes).map(({ isRequired, ...rest }) => rest), - credentialDefinitionId - } - }, - autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', - comment, - goalCode: outOfBandCredential.goalCode || undefined, - parentThreadId: outOfBandCredential.parentThreadId || undefined, - willConfirm: outOfBandCredential.willConfirm || undefined, - label: organisation?.name, - imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl - }; + if (IssueCredentialType.INDY === credentialType) { + + outOfBandIssuancePayload = { + protocolVersion: protocolVersion || 'v1', + credentialFormats: { + indy: { + attributes: iterator.attributes || attributes, + credentialDefinitionId + } + }, + autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', + comment, + goalCode: outOfBandCredential.goalCode || undefined, + parentThreadId: outOfBandCredential.parentThreadId || undefined, + willConfirm: outOfBandCredential.willConfirm || undefined, + label: outOfBandCredential.label || undefined, + imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl + }; + } + + if (IssueCredentialType.JSONLD === credentialType) { + outOfBandIssuancePayload = { + protocolVersion:'v2', + credentialFormats: { + jsonld: { + credential: iterator.credential, + options: iterator.options + } + }, + autoAcceptCredential: outOfBandCredential.autoAcceptCredential || 'always', + comment, + goalCode: outOfBandCredential.goalCode || undefined, + parentThreadId: outOfBandCredential.parentThreadId || undefined, + willConfirm: outOfBandCredential.willConfirm || undefined, + label: outOfBandCredential.label || undefined, + imageUrl: organisation?.logoUrl || outOfBandCredential?.imageUrl + }; + } + this.logger.log(`outOfBandIssuancePayload ::: ${JSON.stringify(outOfBandIssuancePayload)}`); diff --git a/libs/common/src/response-messages/index.ts b/libs/common/src/response-messages/index.ts index 436d90506..e94b566c3 100644 --- a/libs/common/src/response-messages/index.ts +++ b/libs/common/src/response-messages/index.ts @@ -269,7 +269,10 @@ export const ResponseMessages = { emailIdNotPresent: 'EmailId is empty or not present', attributesNotPresent: 'Attributes are not present or not empty', unableToCreateOffer: 'Unable to create offer', - orgAgentTypeNotFound: 'Organization agent type not found' + orgAgentTypeNotFound: 'Organization agent type not found', + credentialNotPresent: 'credential is required', + optionsNotPresent:'options are required', + invalidCredentialType:'invalid credential type' } }, verification: {