Skip to content

Commit a1c0335

Browse files
committed
fix: added support for nested attribute and updated builder function
Signed-off-by: Rinkal Bhojani <[email protected]>
1 parent ee5c472 commit a1c0335

File tree

6 files changed

+590
-320
lines changed

6 files changed

+590
-320
lines changed

apps/api-gateway/src/oid4vc-issuance/dtos/oid4vc-issuer-template.dto.ts

Lines changed: 161 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,40 +9,100 @@ import {
99
IsNotEmpty,
1010
IsArray,
1111
ValidateIf,
12-
IsEmpty
12+
IsEmpty,
13+
ArrayNotEmpty
1314
} from 'class-validator';
1415
import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath, PartialType } from '@nestjs/swagger';
1516
import { Type } from 'class-transformer';
16-
import { DisplayDto } from './oid4vc-issuer.dto';
1717
import { SignerOption } from '@prisma/client';
18+
import { CredentialFormat } from '@credebl/enum/enum';
19+
20+
class CredentialAttributeDisplayDto {
21+
@ApiPropertyOptional({ example: 'First Name' })
22+
@IsString()
23+
@IsNotEmpty()
24+
name: string;
25+
26+
@ApiPropertyOptional({ example: 'en' })
27+
@IsString()
28+
@IsOptional()
29+
locale?: string;
30+
}
31+
32+
// export class CredentialAttributeDto {
33+
// @ApiProperty({ required: false, description: 'Whether the attribute is mandatory' })
34+
// @IsOptional()
35+
// @IsBoolean()
36+
// mandatory?: boolean;
37+
38+
// // TODO: Check how do we handle claims with only path rpoperty like email, etc.
39+
// @ApiProperty({ description: 'Type of the attribute value (string, number, date, etc.)' })
40+
// @IsString()
41+
// value_type: string;
42+
43+
// @ApiProperty({
44+
// type: [String],
45+
// description:
46+
// 'Claims path pointer as per the draft 15 - https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID2.html#name-claims-path-pointer'
47+
// })
48+
// @IsArray()
49+
// @IsString({ each: true })
50+
// path: string[];
51+
52+
// @ApiProperty({ type: [CredentialAttributeDisplayDto], required: false, description: 'Localized display values' })
53+
// @IsOptional()
54+
// @ValidateNested({ each: true })
55+
// @Type(() => CredentialAttributeDisplayDto)
56+
// display?: CredentialAttributeDisplayDto[];
57+
// }
58+
59+
export enum AttributeType {
60+
STRING = 'string',
61+
NUMBER = 'number',
62+
BOOLEAN = 'boolean',
63+
DATE = 'date',
64+
OBJECT = 'object',
65+
ARRAY = 'array',
66+
IMAGE = 'image'
67+
}
1868

1969
export class CredentialAttributeDto {
70+
@ApiProperty({ description: 'Unique key for this attribute (e.g., full_name, org.iso.23220.photoID.1.birth_date)' })
71+
@IsString()
72+
key: string;
73+
2074
@ApiProperty({ required: false, description: 'Whether the attribute is mandatory' })
2175
@IsOptional()
2276
@IsBoolean()
2377
mandatory?: boolean;
2478

2579
// TODO: Check how do we handle claims with only path rpoperty like email, etc.
26-
@ApiProperty({ description: 'Type of the attribute value (string, number, date, etc.)' })
27-
@IsString()
80+
@ApiProperty({ enum: AttributeType, description: 'Type of the attribute value (string, number, date, etc.)' })
81+
@IsEnum(AttributeType)
2882
value_type: string;
2983

84+
@ApiProperty({ description: 'Whether this attribute should be disclosed (for SD-JWT)' })
85+
@IsOptional()
86+
@IsBoolean()
87+
disclose?: boolean;
88+
89+
@ApiProperty({ type: [CredentialAttributeDisplayDto], required: false, description: 'Localized display values' })
90+
@IsOptional()
91+
@ValidateNested({ each: true })
92+
@Type(() => CredentialAttributeDisplayDto)
93+
display?: CredentialAttributeDisplayDto[];
94+
3095
@ApiProperty({
31-
type: [String],
32-
description:
33-
'Claims path pointer as per the draft 15 - https://openid.net/specs/openid-4-verifiable-credential-issuance-1_0-ID2.html#name-claims-path-pointer'
96+
description: 'Nested attributes if type is object or array',
97+
required: false,
98+
type: () => [CredentialAttributeDto]
3499
})
35-
@IsArray()
36-
@IsString({ each: true })
37-
path: string[];
38-
39-
@ApiProperty({ type: [DisplayDto], required: false, description: 'Localized display values' })
40100
@IsOptional()
101+
@IsArray()
41102
@ValidateNested({ each: true })
42-
@Type(() => DisplayDto)
43-
display?: DisplayDto[];
103+
@Type(() => CredentialAttributeDto)
104+
children?: CredentialAttributeDto[];
44105
}
45-
46106
class LogoDto {
47107
@ApiPropertyOptional({
48108
example: 'https://upload.wikimedia.org/wikipedia/commons/2/2f/ABC-2021-LOGO.svg'
@@ -84,6 +144,23 @@ class CredentialDisplayDto {
84144
@ValidateNested()
85145
@Type(() => LogoDto)
86146
logo?: LogoDto;
147+
148+
@ApiPropertyOptional({ example: '#12107c' })
149+
@IsString()
150+
@IsOptional()
151+
background_color?: string;
152+
153+
@ApiPropertyOptional({ example: '#FFFFFF' })
154+
@IsString()
155+
@IsOptional()
156+
text_color?: string;
157+
158+
@ApiPropertyOptional({ example: { uri: 'https://upload.wikimedia.org/wikipedia/commons/2/2f/ABC-2021-LOGO.svg' } })
159+
@IsObject()
160+
@IsOptional()
161+
background_image?: {
162+
uri: string;
163+
};
87164
}
88165

89166
export class AppearanceDto {
@@ -115,7 +192,55 @@ export class AppearanceDto {
115192
display: CredentialDisplayDto[];
116193
}
117194

118-
@ApiExtraModels(CredentialAttributeDto)
195+
export class MdocNamespaceDto {
196+
@ApiProperty({ description: 'Namespace key (e.g., org.iso.23220.photoID.1)' })
197+
@IsString()
198+
namespace: string;
199+
200+
@ApiProperty({ type: () => [CredentialAttributeDto] })
201+
@IsArray()
202+
@ArrayNotEmpty()
203+
@ValidateNested({ each: true })
204+
@Type(() => CredentialAttributeDto)
205+
attributes: CredentialAttributeDto[];
206+
}
207+
export class MdocTemplateDto {
208+
@ApiProperty({
209+
description: 'Document type (required when format is "mso_mdoc"; must NOT be provided when format is "vc+sd-jwt")',
210+
example: 'org.iso.23220.photoID.1'
211+
})
212+
//@ValidateIf((o: CreateCredentialTemplateDto) => 'mso_mdoc' === o.format)
213+
@IsString()
214+
doctype: string;
215+
216+
@ApiProperty({ type: () => [MdocNamespaceDto] })
217+
@IsArray()
218+
@ArrayNotEmpty()
219+
@ValidateNested({ each: true })
220+
@Type(() => MdocNamespaceDto)
221+
namespaces: MdocNamespaceDto[];
222+
}
223+
224+
export class SdJwtTemplateDto {
225+
@ApiProperty({
226+
description:
227+
'Verifiable Credential Type (required when format is "vc+sd-jwt"; must NOT be provided when format is "mso_mdoc")',
228+
example: 'BirthCertificateCredential-sdjwt'
229+
})
230+
// @ValidateIf((o: CreateCredentialTemplateDto) => 'vc+sd-jwt' === o.format)
231+
@IsString()
232+
vct: string;
233+
234+
@ApiProperty({
235+
type: 'array',
236+
items: { $ref: getSchemaPath(CredentialAttributeDto) },
237+
description: 'Attributes included in the credential template'
238+
})
239+
@IsArray()
240+
attributes: CredentialAttributeDto[];
241+
}
242+
243+
@ApiExtraModels(CredentialAttributeDto, SdJwtTemplateDto, MdocTemplateDto)
119244
export class CreateCredentialTemplateDto {
120245
@ApiProperty({ description: 'Template name' })
121246
@IsString()
@@ -134,47 +259,37 @@ export class CreateCredentialTemplateDto {
134259
@IsEnum(SignerOption)
135260
signerOption!: SignerOption;
136261

137-
@ApiProperty({ enum: ['mso_mdoc', 'vc+sd-jwt'], description: 'Credential format type' })
138-
@IsEnum(['mso_mdoc', 'vc+sd-jwt'])
139-
format: 'mso_mdoc' | 'vc+sd-jwt';
140-
141-
@ApiPropertyOptional({
142-
description: 'Document type (required when format is "mso_mdoc"; must NOT be provided when format is "vc+sd-jwt")',
143-
example: 'org.iso.23220.photoID.1'
144-
})
145-
@ValidateIf((o: CreateCredentialTemplateDto) => 'mso_mdoc' === o.format)
146-
@IsString()
147-
doctype?: string;
262+
@ApiProperty({ enum: CredentialFormat, description: 'Credential format type' })
263+
@IsEnum(CredentialFormat)
264+
format: CredentialFormat;
148265

149-
@ValidateIf((o: CreateCredentialTemplateDto) => 'vc+sd-jwt' === o.format)
266+
@ValidateIf((o: CreateCredentialTemplateDto) => CredentialFormat.SdJwtVc === o.format)
150267
@IsEmpty({ message: 'doctype must not be provided when format is "vc+sd-jwt"' })
151268
readonly _doctypeAbsentGuard?: unknown;
152269

153-
@ApiPropertyOptional({
154-
description:
155-
'Verifiable Credential Type (required when format is "vc+sd-jwt"; must NOT be provided when format is "mso_mdoc")',
156-
example: 'BirthCertificateCredential-sdjwt'
157-
})
158-
@ValidateIf((o: CreateCredentialTemplateDto) => 'vc+sd-jwt' === o.format)
159-
@IsString()
160-
vct?: string;
161-
162-
@ValidateIf((o: CreateCredentialTemplateDto) => 'mso_mdoc' === o.format)
270+
@ValidateIf((o: CreateCredentialTemplateDto) => CredentialFormat.Mdoc === o.format)
163271
@IsEmpty({ message: 'vct must not be provided when format is "mso_mdoc"' })
164272
readonly _vctAbsentGuard?: unknown;
165273

274+
@ApiProperty({
275+
type: Object,
276+
oneOf: [{ $ref: getSchemaPath(SdJwtTemplateDto) }, { $ref: getSchemaPath(MdocTemplateDto) }],
277+
description: 'Credential template definition (depends on credentialFormat)'
278+
})
279+
@ValidateNested()
280+
@Type(({ object }) => {
281+
if (object.format === CredentialFormat.Mdoc) {
282+
return MdocTemplateDto;
283+
} else if (object.format === CredentialFormat.SdJwtVc) {
284+
return SdJwtTemplateDto;
285+
}
286+
})
287+
template: SdJwtTemplateDto | MdocTemplateDto;
288+
166289
@ApiProperty({ default: false, description: 'Indicates whether credentials can be revoked' })
167290
@IsBoolean()
168291
canBeRevoked = false;
169292

170-
@ApiProperty({
171-
type: 'array',
172-
items: { $ref: getSchemaPath(CredentialAttributeDto) },
173-
description: 'Attributes included in the credential template'
174-
})
175-
@IsArray()
176-
attributes: CredentialAttributeDto[];
177-
178293
@ApiProperty({
179294
type: Object,
180295
required: false,

0 commit comments

Comments
 (0)