Skip to content

Commit d9fe683

Browse files
authored
add diddoc class transformer (openwallet-foundation#108)
* expose default values in class-transformer * update diddoc classes to class-transformer * add class-transformer to docker build * update class-transformer to newest version Signed-off-by: Timo Glastra <[email protected]>
1 parent ecefcd1 commit d9fe683

35 files changed

+1452
-536
lines changed

Diff for: .gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ yarn-error.log
66
aries-framework-javascript-*.tgz
77
src/lib/__tests__/genesis-von.txn
88
coverage
9+
.DS_Store

Diff for: jest.config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ module.exports = {
1313
},
1414
collectCoverageFrom: ['src/lib/**/*.{js,jsx,tsx,ts}'],
1515
coveragePathIgnorePatterns: ['/node_modules/', '/__tests__/'],
16-
testTimeout: 30000,
16+
testTimeout: 45000,
1717
};

Diff for: package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
"await-poll": "^1.0.2",
2626
"bn.js": "^5.1.3",
2727
"buffer": "^6.0.2",
28-
"class-transformer": "^0.3.1",
28+
"class-transformer": "0.3.2",
2929
"class-validator": "^0.12.2",
3030
"debug": "^4.1.1",
3131
"events": "^3.2.0",

Diff for: src/lib/agent/__tests__/AgentConfig.test.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { getMockConnection } from '../../protocols/connections/ConnectionService.test';
2-
import { DidDoc, Service } from '../../protocols/connections/domain/DidDoc';
2+
import { DidDoc } from '../../protocols/connections/domain/did/DidDoc';
3+
import { IndyAgentService } from '../../protocols/connections/domain/did/service';
34
import { AgentConfig } from '../AgentConfig';
45

56
describe('AgentConfig', () => {
@@ -15,7 +16,12 @@ describe('AgentConfig', () => {
1516
agentConfig.establishInbound({
1617
verkey: 'test',
1718
connection: getMockConnection({
18-
theirDidDoc: new DidDoc('test', [], [], [new Service('test', endpoint, [], [], 1, 'type')]),
19+
theirDidDoc: new DidDoc({
20+
id: 'test',
21+
publicKey: [],
22+
authentication: [],
23+
service: [new IndyAgentService({ id: `test;indy`, serviceEndpoint: endpoint, recipientKeys: [] })],
24+
}),
1925
}),
2026
});
2127

Diff for: src/lib/helpers.ts

-34
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { Transform } from 'class-transformer';
21
import { validateOrReject } from 'class-validator';
32

43
import { ConnectionInvitationMessage } from './protocols/connections/messages/ConnectionInvitationMessage';
@@ -41,36 +40,3 @@ export function encodeInvitationToUrl(
4140

4241
return invitationUrl;
4342
}
44-
45-
/**
46-
* Provide a default value for a parameter when using class-transformer
47-
*
48-
* Class transfomer doesn't use the default value of a property when transforming an
49-
* object using `plainToClass`. This decorator allows to set a default value when no value is
50-
* present during transformation.
51-
*
52-
* @param defaultValue the default value to use when there is no value present during transformation
53-
* @see https://github.com/typestack/class-transformer/issues/129#issuecomment-425843700
54-
*
55-
* @example
56-
* import { plainToClass } from 'class-transformer'
57-
*
58-
* class Test {
59-
* // doesn't work
60-
* myProp = true;
61-
*
62-
* // does work
63-
* @Default(true)
64-
* myDefaultProp: boolean;
65-
* }
66-
*
67-
* plainToClass(Test, {})
68-
* // results in
69-
* {
70-
* "myProp": undefined,
71-
* "myDefaultProp": true
72-
* }
73-
*/
74-
export function Default<T>(defaultValue: T) {
75-
return Transform((value: T | null | undefined) => (value !== null && value !== undefined ? value : defaultValue));
76-
}

Diff for: src/lib/protocols/connections/ConnectionService.test.ts

+55-27
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ import { ConnectionState } from './domain/ConnectionState';
99
import { InitConfig } from '../../types';
1010
import { ConnectionRole } from './domain/ConnectionRole';
1111
import { ConnectionInvitationMessage } from './messages/ConnectionInvitationMessage';
12-
1312
import { Repository } from '../../storage/Repository';
14-
import { DidDoc, Service } from './domain/DidDoc';
1513
import { Connection } from './domain/Connection';
1614
import { signData, unpackAndVerifySignatureDecorator } from '../../decorators/signature/SignatureDecoratorUtils';
1715
import { InboundMessageContext } from '../../agent/models/InboundMessageContext';
@@ -21,6 +19,9 @@ import { ConnectionRequestMessage } from './messages/ConnectionRequestMessage';
2119
import { TrustPingMessage } from '../trustping/TrustPingMessage';
2220
import { AckMessage, AckStatus } from './messages/AckMessage';
2321
import { JsonTransformer } from '../../utils/JsonTransformer';
22+
import { DidDoc } from './domain/did/DidDoc';
23+
import { IndyAgentService } from './domain/did/service';
24+
2425
jest.mock('./../../storage/Repository');
2526
const ConnectionRepository = <jest.Mock<Repository<ConnectionRecord>>>(<unknown>Repository);
2627

@@ -30,20 +31,29 @@ export function getMockConnection({
3031
id = 'test',
3132
did = 'test-did',
3233
verkey = 'key-1',
33-
didDoc = new DidDoc(did, [], [], [new Service(`${did};indy`, 'https://endpoint.com', [verkey], [], 0, 'IndyAgent')]),
34+
didDoc = new DidDoc({
35+
id: did,
36+
publicKey: [],
37+
authentication: [],
38+
service: [
39+
new IndyAgentService({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', recipientKeys: [verkey] }),
40+
],
41+
}),
3442
tags = {},
3543
invitation = new ConnectionInvitationMessage({
3644
label: 'test',
3745
recipientKeys: [verkey],
3846
serviceEndpoint: 'https:endpoint.com/msg',
3947
}),
4048
theirDid = 'their-did',
41-
theirDidDoc = new DidDoc(
42-
theirDid,
43-
[],
44-
[],
45-
[new Service(`${did};indy`, 'https://endpoint.com', [verkey], [], 0, 'IndyAgent')]
46-
),
49+
theirDidDoc = new DidDoc({
50+
id: theirDid,
51+
publicKey: [],
52+
authentication: [],
53+
service: [
54+
new IndyAgentService({ id: `${did};indy`, serviceEndpoint: 'https://endpoint.com', recipientKeys: [verkey] }),
55+
],
56+
}),
4757
}: Partial<ConnectionStorageProps> = {}) {
4858
return new ConnectionRecord({
4959
did,
@@ -290,12 +300,18 @@ describe('ConnectionService', () => {
290300

291301
const theirDid = 'their-did';
292302
const theirVerkey = 'their-verkey';
293-
const theirDidDoc = new DidDoc(
294-
theirDid,
295-
[],
296-
[],
297-
[new Service(`${theirDid};indy`, 'https://endpoint.com', [theirVerkey], [], 0, 'IndyAgent')]
298-
);
303+
const theirDidDoc = new DidDoc({
304+
id: theirDid,
305+
publicKey: [],
306+
authentication: [],
307+
service: [
308+
new IndyAgentService({
309+
id: `${theirDid};indy`,
310+
serviceEndpoint: 'https://endpoint.com',
311+
recipientKeys: [theirVerkey],
312+
}),
313+
],
314+
});
299315

300316
const connectionRequest = new ConnectionRequestMessage({
301317
did: theirDid,
@@ -477,12 +493,18 @@ describe('ConnectionService', () => {
477493

478494
const otherPartyConnection = new Connection({
479495
did: theirDid,
480-
didDoc: new DidDoc(
481-
theirDid,
482-
[],
483-
[],
484-
[new Service(`${did};indy`, 'https://endpoint.com', [theirVerkey], [], 0, 'IndyAgent')]
485-
),
496+
didDoc: new DidDoc({
497+
id: theirDid,
498+
publicKey: [],
499+
authentication: [],
500+
service: [
501+
new IndyAgentService({
502+
id: `${did};indy`,
503+
serviceEndpoint: 'https://endpoint.com',
504+
recipientKeys: [theirVerkey],
505+
}),
506+
],
507+
}),
486508
});
487509

488510
const plainConnection = JsonTransformer.toJSON(otherPartyConnection);
@@ -537,12 +559,18 @@ describe('ConnectionService', () => {
537559

538560
const otherPartyConnection = new Connection({
539561
did: theirDid,
540-
didDoc: new DidDoc(
541-
theirDid,
542-
[],
543-
[],
544-
[new Service(`${did};indy`, 'https://endpoint.com', [theirVerkey], [], 0, 'IndyAgent')]
545-
),
562+
didDoc: new DidDoc({
563+
id: theirDid,
564+
publicKey: [],
565+
authentication: [],
566+
service: [
567+
new IndyAgentService({
568+
id: `${did};indy`,
569+
serviceEndpoint: 'https://endpoint.com',
570+
recipientKeys: [theirVerkey],
571+
}),
572+
],
573+
}),
546574
});
547575
const plainConnection = JsonTransformer.toJSON(otherPartyConnection);
548576
const connectionSig = await signData(plainConnection, wallet, theirVerkey);

Diff for: src/lib/protocols/connections/ConnectionService.ts

+35-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ import { validateOrReject } from 'class-validator';
33

44
import { AgentConfig } from '../../agent/AgentConfig';
55
import { ConnectionState } from './domain/ConnectionState';
6-
import { DidDoc, Service, PublicKey, PublicKeyType, Authentication } from './domain/DidDoc';
76
import { ConnectionRecord, ConnectionTags } from '../../storage/ConnectionRecord';
87
import { Repository } from '../../storage/Repository';
98
import { Wallet } from '../../wallet/Wallet';
@@ -18,6 +17,10 @@ import { ConnectionRole } from './domain/ConnectionRole';
1817
import { TrustPingMessage } from '../trustping/TrustPingMessage';
1918
import { JsonTransformer } from '../../utils/JsonTransformer';
2019
import { AgentMessage } from '../../agent/AgentMessage';
20+
import { Ed25119Sig2018 } from './domain/did/publicKey';
21+
import { IndyAgentService } from './domain/did/service';
22+
import { DidDoc } from './domain/did/DidDoc';
23+
import { authenticationTypes, ReferencedAuthentication } from './domain/did/authentication';
2124

2225
export enum ConnectionEventType {
2326
StateChanged = 'stateChanged',
@@ -64,11 +67,12 @@ export class ConnectionService extends EventEmitter {
6467
});
6568

6669
const { didDoc } = connectionRecord;
70+
const [service] = didDoc.getServicesByClassType(IndyAgentService);
6771
const invitation = new ConnectionInvitationMessage({
6872
label: this.config.label,
69-
recipientKeys: didDoc.service[0].recipientKeys,
70-
serviceEndpoint: didDoc.service[0].serviceEndpoint,
71-
routingKeys: didDoc.service[0].routingKeys,
73+
recipientKeys: service.recipientKeys,
74+
serviceEndpoint: service.serviceEndpoint,
75+
routingKeys: service.routingKeys,
7276
});
7377

7478
connectionRecord.invitation = invitation;
@@ -249,6 +253,7 @@ export class ConnectionService extends EventEmitter {
249253
const connectionJson = await unpackAndVerifySignatureDecorator(message.connectionSig, this.wallet);
250254

251255
const connection = JsonTransformer.fromJSON(connectionJson, Connection);
256+
// TODO: throw framework error stating the connection object is invalid
252257
await validateOrReject(connection);
253258

254259
// Per the Connection RFC we must check if the key used to sign the connection~sig is the same key
@@ -345,18 +350,31 @@ export class ConnectionService extends EventEmitter {
345350
autoAcceptConnection?: boolean;
346351
tags?: ConnectionTags;
347352
}): Promise<ConnectionRecord> {
348-
const [did, verkey] = await this.wallet.createDid();
349-
const publicKey = new PublicKey(`${did}#1`, PublicKeyType.ED25519_SIG_2018, did, verkey);
350-
const service = new Service(
351-
`${did};indy`,
352-
this.config.getEndpoint(),
353-
[verkey],
354-
this.config.getRoutingKeys(),
355-
0,
356-
'IndyAgent'
357-
);
358-
const auth = new Authentication(publicKey);
359-
const didDoc = new DidDoc(did, [auth], [publicKey], [service]);
353+
const [did, verkey] = await this.wallet.createDid({ method_name: 'sov' });
354+
355+
const publicKey = new Ed25119Sig2018({
356+
id: `${did}#1`,
357+
controller: did,
358+
publicKeyBase58: verkey,
359+
});
360+
361+
const service = new IndyAgentService({
362+
id: `${did};indy`,
363+
serviceEndpoint: this.config.getEndpoint(),
364+
recipientKeys: [verkey],
365+
routingKeys: this.config.getRoutingKeys(),
366+
});
367+
368+
// TODO: abstract the second paramater for ReferencedAuthentication away. This can be
369+
// inferred from the publicKey class instance
370+
const auth = new ReferencedAuthentication(publicKey, authenticationTypes[publicKey.type]);
371+
372+
const didDoc = new DidDoc({
373+
id: did,
374+
authentication: [auth],
375+
service: [service],
376+
publicKey: [publicKey],
377+
});
360378

361379
const connectionRecord = new ConnectionRecord({
362380
did,
@@ -377,7 +395,7 @@ export class ConnectionService extends EventEmitter {
377395
return connectionRecord;
378396
}
379397

380-
public async getConnections() {
398+
public getConnections() {
381399
return this.connectionRepository.findAll();
382400
}
383401

Diff for: src/lib/protocols/connections/domain/Connection.ts

+5-11
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
1-
import { IsString } from 'class-validator';
2-
import { Expose, Transform } from 'class-transformer';
1+
import { IsString, ValidateNested } from 'class-validator';
2+
import { Expose, Type } from 'class-transformer';
33

4-
import { DidDoc } from './DidDoc';
4+
import { DidDoc } from './did/DidDoc';
55

66
export interface ConnectionOptions {
77
did: string;
@@ -21,13 +21,7 @@ export class Connection {
2121
public did!: string;
2222

2323
@Expose({ name: 'DIDDoc' })
24-
// TODO: add type for DidDoc
25-
// When we add the @Type json object DidDoc parameter will be cast to DidDoc class instance
26-
// However the DidDoc class is not yet decorated using class-transformer
27-
// meaning it will give errors because the class will be invalid.
28-
// for now the DidDoc class is however correctly cast from class instance to json
29-
// @Type(() => DidDoc)
30-
// This way we also don't need the custom transformer
31-
@Transform((value: DidDoc | undefined) => (value?.toJSON ? value.toJSON() : value), { toPlainOnly: true })
24+
@Type(() => DidDoc)
25+
@ValidateNested()
3226
public didDoc?: DidDoc;
3327
}

0 commit comments

Comments
 (0)