@sphereon/ssi-sdk.data-store 0.24.0 → 0.24.1-next.42
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/contact/ContactStore.d.ts +3 -0
- package/dist/contact/ContactStore.d.ts.map +1 -1
- package/dist/contact/ContactStore.js +84 -48
- package/dist/contact/ContactStore.js.map +1 -1
- package/dist/entities/contact/BaseContactEntity.d.ts +2 -0
- package/dist/entities/contact/BaseContactEntity.d.ts.map +1 -1
- package/dist/entities/contact/BaseContactEntity.js +12 -3
- package/dist/entities/contact/BaseContactEntity.js.map +1 -1
- package/dist/entities/contact/ConnectionEntity.d.ts +2 -0
- package/dist/entities/contact/ConnectionEntity.d.ts.map +1 -1
- package/dist/entities/contact/ConnectionEntity.js +8 -0
- package/dist/entities/contact/ConnectionEntity.js.map +1 -1
- package/dist/entities/contact/ContactMetadataItemEntity.d.ts +14 -0
- package/dist/entities/contact/ContactMetadataItemEntity.d.ts.map +1 -0
- package/dist/entities/contact/ContactMetadataItemEntity.js +88 -0
- package/dist/entities/contact/ContactMetadataItemEntity.js.map +1 -0
- package/dist/entities/contact/CorrelationIdentifierEntity.d.ts +2 -0
- package/dist/entities/contact/CorrelationIdentifierEntity.d.ts.map +1 -1
- package/dist/entities/contact/CorrelationIdentifierEntity.js +8 -0
- package/dist/entities/contact/CorrelationIdentifierEntity.js.map +1 -1
- package/dist/entities/contact/DidAuthConfigEntity.d.ts +2 -0
- package/dist/entities/contact/DidAuthConfigEntity.d.ts.map +1 -1
- package/dist/entities/contact/DidAuthConfigEntity.js +8 -0
- package/dist/entities/contact/DidAuthConfigEntity.js.map +1 -1
- package/dist/entities/contact/ElectronicAddressEntity.d.ts +2 -0
- package/dist/entities/contact/ElectronicAddressEntity.d.ts.map +1 -1
- package/dist/entities/contact/ElectronicAddressEntity.js +8 -0
- package/dist/entities/contact/ElectronicAddressEntity.js.map +1 -1
- package/dist/entities/contact/IMetadataEntity.d.ts +8 -0
- package/dist/entities/contact/IMetadataEntity.d.ts.map +1 -0
- package/dist/entities/contact/IMetadataEntity.js +2 -0
- package/dist/entities/contact/IMetadataEntity.js.map +1 -0
- package/dist/entities/contact/IdentityEntity.d.ts +5 -2
- package/dist/entities/contact/IdentityEntity.d.ts.map +1 -1
- package/dist/entities/contact/IdentityEntity.js +13 -0
- package/dist/entities/contact/IdentityEntity.js.map +1 -1
- package/dist/entities/contact/IdentityMetadataItemEntity.d.ts +6 -2
- package/dist/entities/contact/IdentityMetadataItemEntity.d.ts.map +1 -1
- package/dist/entities/contact/IdentityMetadataItemEntity.js +19 -3
- package/dist/entities/contact/IdentityMetadataItemEntity.js.map +1 -1
- package/dist/entities/contact/NaturalPersonEntity.d.ts +2 -0
- package/dist/entities/contact/NaturalPersonEntity.d.ts.map +1 -1
- package/dist/entities/contact/NaturalPersonEntity.js +8 -0
- package/dist/entities/contact/NaturalPersonEntity.js.map +1 -1
- package/dist/entities/contact/OpenIdConfigEntity.d.ts +2 -0
- package/dist/entities/contact/OpenIdConfigEntity.d.ts.map +1 -1
- package/dist/entities/contact/OpenIdConfigEntity.js +8 -0
- package/dist/entities/contact/OpenIdConfigEntity.js.map +1 -1
- package/dist/entities/contact/OrganizationEntity.d.ts +2 -0
- package/dist/entities/contact/OrganizationEntity.d.ts.map +1 -1
- package/dist/entities/contact/OrganizationEntity.js +8 -0
- package/dist/entities/contact/OrganizationEntity.js.map +1 -1
- package/dist/entities/contact/PartyEntity.d.ts +2 -0
- package/dist/entities/contact/PartyEntity.d.ts.map +1 -1
- package/dist/entities/contact/PartyEntity.js +8 -0
- package/dist/entities/contact/PartyEntity.js.map +1 -1
- package/dist/entities/contact/PartyRelationshipEntity.d.ts +2 -0
- package/dist/entities/contact/PartyRelationshipEntity.d.ts.map +1 -1
- package/dist/entities/contact/PartyRelationshipEntity.js +8 -0
- package/dist/entities/contact/PartyRelationshipEntity.js.map +1 -1
- package/dist/entities/contact/PartyTypeEntity.js +4 -4
- package/dist/entities/contact/PartyTypeEntity.js.map +1 -1
- package/dist/entities/contact/PhysicalAddressEntity.d.ts +2 -0
- package/dist/entities/contact/PhysicalAddressEntity.d.ts.map +1 -1
- package/dist/entities/contact/PhysicalAddressEntity.js +8 -0
- package/dist/entities/contact/PhysicalAddressEntity.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/migrations/generic/8-CreateContacts.d.ts +7 -0
- package/dist/migrations/generic/8-CreateContacts.d.ts.map +1 -0
- package/dist/migrations/generic/8-CreateContacts.js +78 -0
- package/dist/migrations/generic/8-CreateContacts.js.map +1 -0
- package/dist/migrations/generic/9-CreateContacts.d.ts +7 -0
- package/dist/migrations/generic/9-CreateContacts.d.ts.map +1 -0
- package/dist/migrations/generic/9-CreateContacts.js +78 -0
- package/dist/migrations/generic/9-CreateContacts.js.map +1 -0
- package/dist/migrations/generic/index.d.ts.map +1 -1
- package/dist/migrations/generic/index.js +8 -1
- package/dist/migrations/generic/index.js.map +1 -1
- package/dist/migrations/postgres/1690925872592-CreateContacts.d.ts.map +1 -1
- package/dist/migrations/postgres/1690925872592-CreateContacts.js +40 -3
- package/dist/migrations/postgres/1690925872592-CreateContacts.js.map +1 -1
- package/dist/migrations/postgres/1710438363001-CreateContacts.d.ts +7 -0
- package/dist/migrations/postgres/1710438363001-CreateContacts.d.ts.map +1 -0
- package/dist/migrations/postgres/1710438363001-CreateContacts.js +63 -0
- package/dist/migrations/postgres/1710438363001-CreateContacts.js.map +1 -0
- package/dist/migrations/postgres/1715761125001-CreateContacts.d.ts +7 -0
- package/dist/migrations/postgres/1715761125001-CreateContacts.d.ts.map +1 -0
- package/dist/migrations/postgres/1715761125001-CreateContacts.js +74 -0
- package/dist/migrations/postgres/1715761125001-CreateContacts.js.map +1 -0
- package/dist/migrations/sqlite/1690925872693-CreateContacts.d.ts.map +1 -1
- package/dist/migrations/sqlite/1690925872693-CreateContacts.js +40 -3
- package/dist/migrations/sqlite/1690925872693-CreateContacts.js.map +1 -1
- package/dist/migrations/sqlite/1710438363002-CreateContacts.d.ts +7 -0
- package/dist/migrations/sqlite/1710438363002-CreateContacts.d.ts.map +1 -0
- package/dist/migrations/sqlite/1710438363002-CreateContacts.js +79 -0
- package/dist/migrations/sqlite/1710438363002-CreateContacts.js.map +1 -0
- package/dist/migrations/sqlite/1715761125002-CreateContacts.d.ts +7 -0
- package/dist/migrations/sqlite/1715761125002-CreateContacts.d.ts.map +1 -0
- package/dist/migrations/sqlite/1715761125002-CreateContacts.js +73 -0
- package/dist/migrations/sqlite/1715761125002-CreateContacts.js.map +1 -0
- package/dist/types/contact/contact.d.ts +51 -15
- package/dist/types/contact/contact.d.ts.map +1 -1
- package/dist/types/contact/contact.js +12 -7
- package/dist/types/contact/contact.js.map +1 -1
- package/dist/utils/contact/MappingUtils.d.ts +6 -4
- package/dist/utils/contact/MappingUtils.d.ts.map +1 -1
- package/dist/utils/contact/MappingUtils.js +128 -18
- package/dist/utils/contact/MappingUtils.js.map +1 -1
- package/package.json +4 -4
- package/src/__tests__/contact.entities.test.ts +129 -44
- package/src/__tests__/contact.store.test.ts +205 -29
- package/src/contact/ContactStore.ts +74 -30
- package/src/entities/contact/BaseContactEntity.ts +11 -0
- package/src/entities/contact/ConnectionEntity.ts +6 -0
- package/src/entities/contact/ContactMetadataItemEntity.ts +50 -0
- package/src/entities/contact/CorrelationIdentifierEntity.ts +6 -0
- package/src/entities/contact/DidAuthConfigEntity.ts +6 -0
- package/src/entities/contact/ElectronicAddressEntity.ts +6 -0
- package/src/entities/contact/IMetadataEntity.ts +7 -0
- package/src/entities/contact/IdentityEntity.ts +11 -2
- package/src/entities/contact/IdentityMetadataItemEntity.ts +16 -4
- package/src/entities/contact/NaturalPersonEntity.ts +6 -0
- package/src/entities/contact/OpenIdConfigEntity.ts +6 -0
- package/src/entities/contact/OrganizationEntity.ts +6 -0
- package/src/entities/contact/PartyEntity.ts +6 -0
- package/src/entities/contact/PartyRelationshipEntity.ts +6 -0
- package/src/entities/contact/PartyTypeEntity.ts +4 -4
- package/src/entities/contact/PhysicalAddressEntity.ts +6 -0
- package/src/index.ts +3 -0
- package/src/migrations/generic/8-CreateContacts.ts +66 -0
- package/src/migrations/generic/9-CreateContacts.ts +66 -0
- package/src/migrations/generic/index.ts +8 -1
- package/src/migrations/postgres/1690925872592-CreateContacts.ts +59 -5
- package/src/migrations/postgres/1710438363001-CreateContacts.ts +63 -0
- package/src/migrations/postgres/1715761125001-CreateContacts.ts +60 -0
- package/src/migrations/sqlite/1690925872693-CreateContacts.ts +70 -3
- package/src/migrations/sqlite/1710438363002-CreateContacts.ts +83 -0
- package/src/migrations/sqlite/1715761125002-CreateContacts.ts +59 -0
- package/src/types/contact/contact.ts +56 -15
- package/src/utils/contact/MappingUtils.ts +134 -18
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DataSource } from 'typeorm'
|
|
2
|
-
import { DataStoreContactEntities, DataStoreMigrations, PartyOrigin } from '../index'
|
|
2
|
+
import { DataStoreContactEntities, DataStoreMigrations, IdentityOrigin, MetadataItem, MetadataTypes, PartyOrigin } from '../index'
|
|
3
3
|
import { ContactStore } from '../contact/ContactStore'
|
|
4
4
|
import {
|
|
5
5
|
CorrelationIdentifierType,
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
GetPhysicalAddressesArgs,
|
|
11
11
|
GetRelationshipsArgs,
|
|
12
12
|
Identity,
|
|
13
|
-
|
|
13
|
+
CredentialRole,
|
|
14
14
|
NaturalPerson,
|
|
15
15
|
NonPersistedElectronicAddress,
|
|
16
16
|
NonPersistedIdentity,
|
|
@@ -49,6 +49,157 @@ describe('Contact store tests', (): void => {
|
|
|
49
49
|
await (await dbConnection).destroy()
|
|
50
50
|
})
|
|
51
51
|
|
|
52
|
+
it('should get party by contact metadata', async (): Promise<void> => {
|
|
53
|
+
const dateOfBirth = new Date(2016, 0, 5)
|
|
54
|
+
const party: NonPersistedParty = {
|
|
55
|
+
uri: 'example.com',
|
|
56
|
+
partyType: {
|
|
57
|
+
type: PartyTypeType.NATURAL_PERSON,
|
|
58
|
+
tenantId: '0605761c-4113-4ce5-a6b2-9cbae2f9d289',
|
|
59
|
+
name: 'example_name',
|
|
60
|
+
origin: PartyOrigin.EXTERNAL,
|
|
61
|
+
},
|
|
62
|
+
contact: {
|
|
63
|
+
firstName: 'example_first_name',
|
|
64
|
+
middleName: 'example_middle_name',
|
|
65
|
+
lastName: 'example_last_name',
|
|
66
|
+
metadata: [
|
|
67
|
+
{ label: 'grade', value: '5th' },
|
|
68
|
+
{ label: 'dateOfBirth', value: dateOfBirth },
|
|
69
|
+
] as Array<MetadataItem<MetadataTypes>>,
|
|
70
|
+
displayName: 'example_display_name',
|
|
71
|
+
},
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const savedParty: Party = await contactStore.addParty(party)
|
|
75
|
+
expect(savedParty).toBeDefined()
|
|
76
|
+
|
|
77
|
+
const singleResult: Party = await contactStore.getParty({ partyId: savedParty.id })
|
|
78
|
+
expect(singleResult).toBeDefined()
|
|
79
|
+
|
|
80
|
+
const args: GetPartiesArgs = {
|
|
81
|
+
filter: [{ contact: { metadata: { label: 'dateOfBirth', value: dateOfBirth } } }],
|
|
82
|
+
}
|
|
83
|
+
const result: Array<Party> = await contactStore.getParties(args)
|
|
84
|
+
expect(result).toBeDefined()
|
|
85
|
+
expect(result.length).toEqual(1)
|
|
86
|
+
expect(result[0]).toBeDefined()
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
it('should get a party by identity metadata', async (): Promise<void> => {
|
|
90
|
+
const party: NonPersistedParty = {
|
|
91
|
+
uri: 'example.com',
|
|
92
|
+
partyType: {
|
|
93
|
+
type: PartyTypeType.NATURAL_PERSON,
|
|
94
|
+
tenantId: '0605761c-4113-4ce5-a6b2-9cbae2f9d289',
|
|
95
|
+
name: 'example_name',
|
|
96
|
+
origin: PartyOrigin.EXTERNAL,
|
|
97
|
+
},
|
|
98
|
+
contact: {
|
|
99
|
+
firstName: 'example_first_name',
|
|
100
|
+
middleName: 'example_middle_name',
|
|
101
|
+
lastName: 'example_last_name',
|
|
102
|
+
displayName: 'example_display_name',
|
|
103
|
+
},
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const savedParty: Party = await contactStore.addParty(party)
|
|
107
|
+
expect(savedParty).toBeDefined()
|
|
108
|
+
|
|
109
|
+
const identity: NonPersistedIdentity = {
|
|
110
|
+
alias: 'test_alias',
|
|
111
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
112
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
113
|
+
identifier: {
|
|
114
|
+
type: CorrelationIdentifierType.DID,
|
|
115
|
+
correlationId: 'example_did',
|
|
116
|
+
},
|
|
117
|
+
metadata: [
|
|
118
|
+
{
|
|
119
|
+
label: 'label1',
|
|
120
|
+
value: 'example_value',
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
label: 'label2',
|
|
124
|
+
value: 'example_value',
|
|
125
|
+
},
|
|
126
|
+
],
|
|
127
|
+
}
|
|
128
|
+
const savedIdentity: Identity = await contactStore.addIdentity({ partyId: savedParty.id, identity: identity })
|
|
129
|
+
expect(savedIdentity).toBeDefined()
|
|
130
|
+
|
|
131
|
+
const args: GetPartiesArgs = {
|
|
132
|
+
filter: [{ identities: { metadata: { label: 'label1', value: 'example_value' } } }],
|
|
133
|
+
}
|
|
134
|
+
const result: Array<Party> = await contactStore.getParties(args)
|
|
135
|
+
expect(result).toBeDefined()
|
|
136
|
+
expect(result.length).toEqual(1)
|
|
137
|
+
expect(result[0]).toBeDefined()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
it('should get party by both contact and identity metadata', async (): Promise<void> => {
|
|
141
|
+
const example_date = new Date(2016, 0, 5)
|
|
142
|
+
const party: NonPersistedParty = {
|
|
143
|
+
uri: 'example.com',
|
|
144
|
+
partyType: {
|
|
145
|
+
type: PartyTypeType.NATURAL_PERSON,
|
|
146
|
+
tenantId: '0605761c-4113-4ce5-a6b2-9cbae2f9d289',
|
|
147
|
+
name: 'example_name',
|
|
148
|
+
origin: PartyOrigin.EXTERNAL,
|
|
149
|
+
},
|
|
150
|
+
contact: {
|
|
151
|
+
firstName: 'example_first_name',
|
|
152
|
+
middleName: 'example_middle_name',
|
|
153
|
+
lastName: 'example_last_name',
|
|
154
|
+
displayName: 'example_display_name',
|
|
155
|
+
metadata: [
|
|
156
|
+
{ label: 'label1', value: 'example_value' },
|
|
157
|
+
{ label: 'label2', value: example_date },
|
|
158
|
+
] as Array<MetadataItem<MetadataTypes>>,
|
|
159
|
+
},
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const savedParty: Party = await contactStore.addParty(party)
|
|
163
|
+
expect(savedParty).toBeDefined()
|
|
164
|
+
|
|
165
|
+
const identity: NonPersistedIdentity = {
|
|
166
|
+
alias: 'test_alias',
|
|
167
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
168
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
169
|
+
identifier: {
|
|
170
|
+
type: CorrelationIdentifierType.DID,
|
|
171
|
+
correlationId: 'example_did',
|
|
172
|
+
},
|
|
173
|
+
metadata: [
|
|
174
|
+
{
|
|
175
|
+
label: 'label3',
|
|
176
|
+
value: 'example_value',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
label: 'label4',
|
|
180
|
+
value: 'example_value',
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
}
|
|
184
|
+
const savedIdentity: Identity = await contactStore.addIdentity({ partyId: savedParty.id, identity: identity })
|
|
185
|
+
expect(savedIdentity).toBeDefined()
|
|
186
|
+
|
|
187
|
+
const args: GetPartiesArgs = {
|
|
188
|
+
filter: [
|
|
189
|
+
{
|
|
190
|
+
contact: { metadata: { label: 'label2', value: example_date } },
|
|
191
|
+
identities: { metadata: { label: 'label3', value: 'example_value' } },
|
|
192
|
+
},
|
|
193
|
+
],
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const result: Array<Party> = await contactStore.getParties(args)
|
|
197
|
+
|
|
198
|
+
expect(result).toBeDefined()
|
|
199
|
+
expect(result.length).toEqual(1)
|
|
200
|
+
expect(result[0]).toBeDefined()
|
|
201
|
+
})
|
|
202
|
+
|
|
52
203
|
it('should get party by id', async (): Promise<void> => {
|
|
53
204
|
const party: NonPersistedParty = {
|
|
54
205
|
uri: 'example.com',
|
|
@@ -189,7 +340,8 @@ describe('Contact store tests', (): void => {
|
|
|
189
340
|
identities: [
|
|
190
341
|
{
|
|
191
342
|
alias: 'test_alias1',
|
|
192
|
-
|
|
343
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
344
|
+
roles: [CredentialRole.ISSUER],
|
|
193
345
|
identifier: {
|
|
194
346
|
type: CorrelationIdentifierType.DID,
|
|
195
347
|
correlationId: 'example_did1',
|
|
@@ -197,7 +349,8 @@ describe('Contact store tests', (): void => {
|
|
|
197
349
|
},
|
|
198
350
|
{
|
|
199
351
|
alias: 'test_alias2',
|
|
200
|
-
|
|
352
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
353
|
+
roles: [CredentialRole.VERIFIER],
|
|
201
354
|
identifier: {
|
|
202
355
|
type: CorrelationIdentifierType.DID,
|
|
203
356
|
correlationId: 'example_did2',
|
|
@@ -205,7 +358,8 @@ describe('Contact store tests', (): void => {
|
|
|
205
358
|
},
|
|
206
359
|
{
|
|
207
360
|
alias: 'test_alias3',
|
|
208
|
-
|
|
361
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
362
|
+
roles: [CredentialRole.HOLDER],
|
|
209
363
|
identifier: {
|
|
210
364
|
type: CorrelationIdentifierType.DID,
|
|
211
365
|
correlationId: 'example_did3',
|
|
@@ -413,7 +567,8 @@ describe('Contact store tests', (): void => {
|
|
|
413
567
|
identities: [
|
|
414
568
|
{
|
|
415
569
|
alias: 'test_alias1',
|
|
416
|
-
|
|
570
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
571
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
417
572
|
identifier: {
|
|
418
573
|
type: CorrelationIdentifierType.DID,
|
|
419
574
|
correlationId: 'example_did1',
|
|
@@ -421,7 +576,8 @@ describe('Contact store tests', (): void => {
|
|
|
421
576
|
},
|
|
422
577
|
{
|
|
423
578
|
alias: 'test_alias2',
|
|
424
|
-
|
|
579
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
580
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
425
581
|
identifier: {
|
|
426
582
|
type: CorrelationIdentifierType.DID,
|
|
427
583
|
correlationId: 'example_did2',
|
|
@@ -457,7 +613,8 @@ describe('Contact store tests', (): void => {
|
|
|
457
613
|
identities: [
|
|
458
614
|
{
|
|
459
615
|
alias: 'test_alias1',
|
|
460
|
-
|
|
616
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
617
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
461
618
|
identifier: {
|
|
462
619
|
type: CorrelationIdentifierType.URL,
|
|
463
620
|
correlationId: 'example_did1',
|
|
@@ -465,7 +622,8 @@ describe('Contact store tests', (): void => {
|
|
|
465
622
|
},
|
|
466
623
|
{
|
|
467
624
|
alias: 'test_alias2',
|
|
468
|
-
|
|
625
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
626
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
469
627
|
identifier: {
|
|
470
628
|
type: CorrelationIdentifierType.DID,
|
|
471
629
|
correlationId: 'example_did2',
|
|
@@ -498,7 +656,8 @@ describe('Contact store tests', (): void => {
|
|
|
498
656
|
|
|
499
657
|
const identity1: NonPersistedIdentity = {
|
|
500
658
|
alias: 'test_alias1',
|
|
501
|
-
|
|
659
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
660
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
502
661
|
identifier: {
|
|
503
662
|
type: CorrelationIdentifierType.DID,
|
|
504
663
|
correlationId: 'example_did1',
|
|
@@ -509,7 +668,8 @@ describe('Contact store tests', (): void => {
|
|
|
509
668
|
|
|
510
669
|
const identity2: NonPersistedIdentity = {
|
|
511
670
|
alias: 'test_alias2',
|
|
512
|
-
|
|
671
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
672
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
513
673
|
identifier: {
|
|
514
674
|
type: CorrelationIdentifierType.DID,
|
|
515
675
|
correlationId: 'example_did2',
|
|
@@ -589,7 +749,8 @@ describe('Contact store tests', (): void => {
|
|
|
589
749
|
|
|
590
750
|
const identity: NonPersistedIdentity = {
|
|
591
751
|
alias: 'test_alias',
|
|
592
|
-
|
|
752
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
753
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
593
754
|
identifier: {
|
|
594
755
|
type: CorrelationIdentifierType.DID,
|
|
595
756
|
correlationId: 'example_did',
|
|
@@ -624,7 +785,8 @@ describe('Contact store tests', (): void => {
|
|
|
624
785
|
|
|
625
786
|
const identity: NonPersistedIdentity = {
|
|
626
787
|
alias: 'test_alias',
|
|
627
|
-
|
|
788
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
789
|
+
roles: [CredentialRole.HOLDER],
|
|
628
790
|
identifier: {
|
|
629
791
|
type: CorrelationIdentifierType.DID,
|
|
630
792
|
correlationId: 'example_did',
|
|
@@ -665,7 +827,8 @@ describe('Contact store tests', (): void => {
|
|
|
665
827
|
|
|
666
828
|
const identity1: NonPersistedIdentity = {
|
|
667
829
|
alias: 'test_alias1',
|
|
668
|
-
|
|
830
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
831
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
669
832
|
identifier: {
|
|
670
833
|
type: CorrelationIdentifierType.DID,
|
|
671
834
|
correlationId: 'example_did1',
|
|
@@ -676,7 +839,8 @@ describe('Contact store tests', (): void => {
|
|
|
676
839
|
|
|
677
840
|
const identity2: NonPersistedIdentity = {
|
|
678
841
|
alias: 'test_alias2',
|
|
679
|
-
|
|
842
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
843
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
680
844
|
identifier: {
|
|
681
845
|
type: CorrelationIdentifierType.DID,
|
|
682
846
|
correlationId: 'example_did2',
|
|
@@ -715,7 +879,8 @@ describe('Contact store tests', (): void => {
|
|
|
715
879
|
|
|
716
880
|
const identity1: NonPersistedIdentity = {
|
|
717
881
|
alias: 'test_alias1',
|
|
718
|
-
|
|
882
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
883
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
719
884
|
identifier: {
|
|
720
885
|
type: CorrelationIdentifierType.DID,
|
|
721
886
|
correlationId: 'example_did1',
|
|
@@ -726,7 +891,8 @@ describe('Contact store tests', (): void => {
|
|
|
726
891
|
|
|
727
892
|
const identity2: NonPersistedIdentity = {
|
|
728
893
|
alias: 'test_alias2',
|
|
729
|
-
|
|
894
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
895
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
730
896
|
identifier: {
|
|
731
897
|
type: CorrelationIdentifierType.DID,
|
|
732
898
|
correlationId: 'example_did2',
|
|
@@ -762,7 +928,8 @@ describe('Contact store tests', (): void => {
|
|
|
762
928
|
const alias = 'test_alias1'
|
|
763
929
|
const identity1: NonPersistedIdentity = {
|
|
764
930
|
alias,
|
|
765
|
-
|
|
931
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
932
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
766
933
|
identifier: {
|
|
767
934
|
type: CorrelationIdentifierType.DID,
|
|
768
935
|
correlationId: 'example_did1',
|
|
@@ -773,7 +940,8 @@ describe('Contact store tests', (): void => {
|
|
|
773
940
|
|
|
774
941
|
const identity2: NonPersistedIdentity = {
|
|
775
942
|
alias: 'test_alias2',
|
|
776
|
-
|
|
943
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
944
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
777
945
|
identifier: {
|
|
778
946
|
type: CorrelationIdentifierType.DID,
|
|
779
947
|
correlationId: 'example_did2',
|
|
@@ -813,7 +981,8 @@ describe('Contact store tests', (): void => {
|
|
|
813
981
|
const alias = 'test_alias1'
|
|
814
982
|
const identity1: NonPersistedIdentity = {
|
|
815
983
|
alias,
|
|
816
|
-
|
|
984
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
985
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
817
986
|
identifier: {
|
|
818
987
|
type: CorrelationIdentifierType.DID,
|
|
819
988
|
correlationId: 'example_did1',
|
|
@@ -833,7 +1002,7 @@ describe('Contact store tests', (): void => {
|
|
|
833
1002
|
expect(savedIdentity1).toBeDefined()
|
|
834
1003
|
|
|
835
1004
|
const args: GetIdentitiesArgs = {
|
|
836
|
-
filter: [{ metadata: { label: 'label1' } }],
|
|
1005
|
+
filter: [{ metadata: { label: 'label1', value: 'example_value' } }],
|
|
837
1006
|
}
|
|
838
1007
|
|
|
839
1008
|
const result: Array<Identity> = await contactStore.getIdentities(args)
|
|
@@ -863,7 +1032,8 @@ describe('Contact store tests', (): void => {
|
|
|
863
1032
|
|
|
864
1033
|
const identity: NonPersistedIdentity = {
|
|
865
1034
|
alias: 'test_alias',
|
|
866
|
-
|
|
1035
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
1036
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
867
1037
|
identifier: {
|
|
868
1038
|
type: CorrelationIdentifierType.DID,
|
|
869
1039
|
correlationId: 'example_did',
|
|
@@ -904,7 +1074,8 @@ describe('Contact store tests', (): void => {
|
|
|
904
1074
|
const correlationId = 'missing_connection_example'
|
|
905
1075
|
const identity: NonPersistedIdentity = {
|
|
906
1076
|
alias: correlationId,
|
|
907
|
-
|
|
1077
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
1078
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
908
1079
|
identifier: {
|
|
909
1080
|
type: CorrelationIdentifierType.URL,
|
|
910
1081
|
correlationId,
|
|
@@ -938,7 +1109,8 @@ describe('Contact store tests', (): void => {
|
|
|
938
1109
|
const correlationId = 'missing_connection_example'
|
|
939
1110
|
const identity: NonPersistedIdentity = {
|
|
940
1111
|
alias: correlationId,
|
|
941
|
-
|
|
1112
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
1113
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER, CredentialRole.HOLDER],
|
|
942
1114
|
identifier: {
|
|
943
1115
|
type: CorrelationIdentifierType.DID,
|
|
944
1116
|
correlationId,
|
|
@@ -973,7 +1145,8 @@ describe('Contact store tests', (): void => {
|
|
|
973
1145
|
|
|
974
1146
|
const identity: NonPersistedIdentity = {
|
|
975
1147
|
alias: 'example_did',
|
|
976
|
-
|
|
1148
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
1149
|
+
roles: [CredentialRole.ISSUER, CredentialRole.VERIFIER],
|
|
977
1150
|
identifier: {
|
|
978
1151
|
type: CorrelationIdentifierType.DID,
|
|
979
1152
|
correlationId: 'example_did',
|
|
@@ -1008,7 +1181,8 @@ describe('Contact store tests', (): void => {
|
|
|
1008
1181
|
identities: [
|
|
1009
1182
|
{
|
|
1010
1183
|
alias: 'test_alias1',
|
|
1011
|
-
|
|
1184
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
1185
|
+
roles: [CredentialRole.VERIFIER],
|
|
1012
1186
|
identifier: {
|
|
1013
1187
|
type: CorrelationIdentifierType.DID,
|
|
1014
1188
|
correlationId: 'example_did1',
|
|
@@ -1016,7 +1190,8 @@ describe('Contact store tests', (): void => {
|
|
|
1016
1190
|
},
|
|
1017
1191
|
{
|
|
1018
1192
|
alias: 'test_alias2',
|
|
1019
|
-
|
|
1193
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
1194
|
+
roles: [CredentialRole.ISSUER],
|
|
1020
1195
|
identifier: {
|
|
1021
1196
|
type: CorrelationIdentifierType.DID,
|
|
1022
1197
|
correlationId: 'example_did2',
|
|
@@ -1024,7 +1199,8 @@ describe('Contact store tests', (): void => {
|
|
|
1024
1199
|
},
|
|
1025
1200
|
{
|
|
1026
1201
|
alias: 'test_alias3',
|
|
1027
|
-
|
|
1202
|
+
origin: IdentityOrigin.EXTERNAL,
|
|
1203
|
+
roles: [CredentialRole.HOLDER],
|
|
1028
1204
|
identifier: {
|
|
1029
1205
|
type: CorrelationIdentifierType.DID,
|
|
1030
1206
|
correlationId: 'example_did3',
|
|
@@ -1038,7 +1214,7 @@ describe('Contact store tests', (): void => {
|
|
|
1038
1214
|
|
|
1039
1215
|
expect(result.roles).toBeDefined()
|
|
1040
1216
|
expect(result.roles.length).toEqual(3)
|
|
1041
|
-
expect(result.roles).toEqual([
|
|
1217
|
+
expect(result.roles).toEqual([CredentialRole.VERIFIER, CredentialRole.ISSUER, CredentialRole.HOLDER])
|
|
1042
1218
|
})
|
|
1043
1219
|
|
|
1044
1220
|
it('should add relationship', async (): Promise<void> => {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OrPromise } from '@sphereon/ssi-types'
|
|
2
|
-
import { DataSource, In, Repository } from 'typeorm'
|
|
2
|
+
import { BaseEntity, DataSource, FindOptionsWhere, In, Repository } from 'typeorm'
|
|
3
3
|
import Debug from 'debug'
|
|
4
4
|
import { AbstractContactStore } from './AbstractContactStore'
|
|
5
5
|
import { PartyEntity } from '../entities/contact/PartyEntity'
|
|
@@ -54,6 +54,8 @@ import {
|
|
|
54
54
|
GetRelationshipArgs,
|
|
55
55
|
GetRelationshipsArgs,
|
|
56
56
|
Identity,
|
|
57
|
+
MetadataItem,
|
|
58
|
+
MetadataTypes,
|
|
57
59
|
NonPersistedConnectionConfig,
|
|
58
60
|
NonPersistedContact,
|
|
59
61
|
Party,
|
|
@@ -99,21 +101,16 @@ export class ContactStore extends AbstractContactStore {
|
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
getParties = async (args?: GetPartiesArgs): Promise<Array<Party>> => {
|
|
102
|
-
debug(
|
|
104
|
+
debug('getParties()', args)
|
|
103
105
|
const { filter } = args ?? {}
|
|
104
|
-
const partyRepository
|
|
105
|
-
const
|
|
106
|
-
|
|
107
|
-
})
|
|
106
|
+
const partyRepository = (await this.dbConnection).getRepository(PartyEntity)
|
|
107
|
+
const filterConditions = this.buildFilters(filter)
|
|
108
|
+
const initialResult = await partyRepository.find({ select: ['id'], where: filterConditions })
|
|
108
109
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
id: In(initialResult.map((party: PartyEntity) => party.id)),
|
|
112
|
-
},
|
|
113
|
-
})
|
|
110
|
+
// Fetch the complete entities based on the initial result IDs
|
|
111
|
+
const result = await partyRepository.find({ where: { id: In(initialResult.map((party) => party.id)) } })
|
|
114
112
|
debug(`getParties() resulted in ${result.length} parties`)
|
|
115
|
-
|
|
116
|
-
return result.map((party: PartyEntity) => partyFrom(party))
|
|
113
|
+
return result.map(partyFrom)
|
|
117
114
|
}
|
|
118
115
|
|
|
119
116
|
addParty = async (args: AddPartyArgs): Promise<Party> => {
|
|
@@ -211,18 +208,12 @@ export class ContactStore extends AbstractContactStore {
|
|
|
211
208
|
|
|
212
209
|
getIdentities = async (args?: GetIdentitiesArgs): Promise<Array<Identity>> => {
|
|
213
210
|
const { filter } = args ?? {}
|
|
214
|
-
const identityRepository
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
})
|
|
218
|
-
|
|
219
|
-
const result: Array<IdentityEntity> = await identityRepository.find({
|
|
220
|
-
where: {
|
|
221
|
-
id: In(initialResult.map((identity: IdentityEntity) => identity.id)),
|
|
222
|
-
},
|
|
223
|
-
})
|
|
211
|
+
const identityRepository = (await this.dbConnection).getRepository(IdentityEntity)
|
|
212
|
+
const filterConditions = this.buildFilters(filter)
|
|
213
|
+
const initialResult = await identityRepository.find({ select: ['id'], where: filterConditions })
|
|
224
214
|
|
|
225
|
-
|
|
215
|
+
const result = await identityRepository.find({ where: { id: In(initialResult.map((identity) => identity.id)) } })
|
|
216
|
+
return result.map(identityFrom)
|
|
226
217
|
}
|
|
227
218
|
|
|
228
219
|
addIdentity = async (args: AddIdentityArgs): Promise<Identity> => {
|
|
@@ -622,7 +613,7 @@ export class ContactStore extends AbstractContactStore {
|
|
|
622
613
|
await physicalAddressRepository.delete(physicalAddressId)
|
|
623
614
|
}
|
|
624
615
|
|
|
625
|
-
private hasCorrectConnectionConfig(type: ConnectionType, config: NonPersistedConnectionConfig): boolean {
|
|
616
|
+
private hasCorrectConnectionConfig = (type: ConnectionType, config: NonPersistedConnectionConfig): boolean => {
|
|
626
617
|
switch (type) {
|
|
627
618
|
case ConnectionType.OPENID_CONNECT:
|
|
628
619
|
return isOpenIdConfig(config)
|
|
@@ -633,7 +624,7 @@ export class ContactStore extends AbstractContactStore {
|
|
|
633
624
|
}
|
|
634
625
|
}
|
|
635
626
|
|
|
636
|
-
private hasCorrectPartyType(type: PartyTypeType, contact: NonPersistedContact): boolean {
|
|
627
|
+
private hasCorrectPartyType = (type: PartyTypeType, contact: NonPersistedContact): boolean => {
|
|
637
628
|
switch (type) {
|
|
638
629
|
case PartyTypeType.NATURAL_PERSON:
|
|
639
630
|
return isNaturalPerson(contact)
|
|
@@ -644,7 +635,7 @@ export class ContactStore extends AbstractContactStore {
|
|
|
644
635
|
}
|
|
645
636
|
}
|
|
646
637
|
|
|
647
|
-
private async
|
|
638
|
+
private deleteIdentities = async (identities: Array<IdentityEntity>): Promise<void> => {
|
|
648
639
|
debug('Removing identities', identities)
|
|
649
640
|
|
|
650
641
|
const connection: DataSource = await this.dbConnection
|
|
@@ -680,7 +671,7 @@ export class ContactStore extends AbstractContactStore {
|
|
|
680
671
|
})
|
|
681
672
|
}
|
|
682
673
|
|
|
683
|
-
private async
|
|
674
|
+
private deleteElectronicAddresses = async (electronicAddresses: Array<ElectronicAddressEntity>): Promise<void> => {
|
|
684
675
|
debug('Removing electronic addresses', electronicAddresses)
|
|
685
676
|
|
|
686
677
|
const electronicAddressRepository: Repository<ElectronicAddressEntity> = (await this.dbConnection).getRepository(ElectronicAddressEntity)
|
|
@@ -691,7 +682,7 @@ export class ContactStore extends AbstractContactStore {
|
|
|
691
682
|
})
|
|
692
683
|
}
|
|
693
684
|
|
|
694
|
-
private async
|
|
685
|
+
private deletePhysicalAddresses = async (physicalAddresses: Array<PhysicalAddressEntity>): Promise<void> => {
|
|
695
686
|
debug('Removing physical addresses', physicalAddresses)
|
|
696
687
|
|
|
697
688
|
const physicalAddressRepository: Repository<PhysicalAddressEntity> = (await this.dbConnection).getRepository(PhysicalAddressEntity)
|
|
@@ -702,7 +693,7 @@ export class ContactStore extends AbstractContactStore {
|
|
|
702
693
|
})
|
|
703
694
|
}
|
|
704
695
|
|
|
705
|
-
private async
|
|
696
|
+
private assertRelationshipSides = async (leftId: string, rightId: string): Promise<void> => {
|
|
706
697
|
const partyRepository: Repository<PartyEntity> = (await this.dbConnection).getRepository(PartyEntity)
|
|
707
698
|
const leftParty: PartyEntity | null = await partyRepository.findOne({
|
|
708
699
|
where: { id: leftId },
|
|
@@ -720,4 +711,57 @@ export class ContactStore extends AbstractContactStore {
|
|
|
720
711
|
return Promise.reject(Error(`No party found for right side of the relationship, party id: ${rightId}`))
|
|
721
712
|
}
|
|
722
713
|
}
|
|
714
|
+
|
|
715
|
+
private buildFilters = <T extends BaseEntity>(filter?: Array<Record<string, any>>): Array<FindOptionsWhere<T>> | FindOptionsWhere<T> => {
|
|
716
|
+
if (!filter) return {}
|
|
717
|
+
|
|
718
|
+
return filter.map((condition) => this.processCondition(condition))
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
private processCondition = (condition: Record<string, any>): Record<string, any> => {
|
|
722
|
+
const conditionObject: Record<string, any> = {}
|
|
723
|
+
|
|
724
|
+
Object.keys(condition).forEach((key) => {
|
|
725
|
+
const value = condition[key]
|
|
726
|
+
|
|
727
|
+
if (key === 'metadata' && value) {
|
|
728
|
+
conditionObject[key] = this.buildMetadataCondition(value)
|
|
729
|
+
} else if (typeof value === 'object' && value !== null) {
|
|
730
|
+
conditionObject[key] = this.processCondition(value)
|
|
731
|
+
} else {
|
|
732
|
+
conditionObject[key] = value
|
|
733
|
+
}
|
|
734
|
+
})
|
|
735
|
+
|
|
736
|
+
return conditionObject
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
private buildMetadataCondition = <T extends MetadataItem<MetadataTypes>>(metadata: Partial<T>): FindOptionsWhere<IMetadataEntity> => {
|
|
740
|
+
const metadataCondition: FindOptionsWhere<any> = {
|
|
741
|
+
label: metadata.label,
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
switch (typeof metadata.value) {
|
|
745
|
+
case 'string':
|
|
746
|
+
metadataCondition.stringValue = metadata.value as string
|
|
747
|
+
break
|
|
748
|
+
case 'number':
|
|
749
|
+
metadataCondition.numberValue = metadata.value as number
|
|
750
|
+
break
|
|
751
|
+
case 'boolean':
|
|
752
|
+
metadataCondition.boolValue = metadata.value as boolean
|
|
753
|
+
break
|
|
754
|
+
case 'object':
|
|
755
|
+
if (metadata.value instanceof Date) {
|
|
756
|
+
metadataCondition.dateValue = metadata.value as Date
|
|
757
|
+
} else {
|
|
758
|
+
// For now, we only support / implement not-primitive type Date in the entity
|
|
759
|
+
throw new Error(`Unsupported object type: ${Object.prototype.toString.call(metadata.value).slice(8, -1)} for value ${metadata.value}`) // slice to extract type from string [object String]
|
|
760
|
+
}
|
|
761
|
+
break
|
|
762
|
+
default:
|
|
763
|
+
throw new Error(`Unsupported value type: ${typeof metadata.value}`)
|
|
764
|
+
}
|
|
765
|
+
return metadataCondition
|
|
766
|
+
}
|
|
723
767
|
}
|
|
@@ -5,12 +5,14 @@ import {
|
|
|
5
5
|
CreateDateColumn,
|
|
6
6
|
Entity,
|
|
7
7
|
JoinColumn,
|
|
8
|
+
OneToMany,
|
|
8
9
|
OneToOne,
|
|
9
10
|
PrimaryGeneratedColumn,
|
|
10
11
|
TableInheritance,
|
|
11
12
|
UpdateDateColumn,
|
|
12
13
|
} from 'typeorm'
|
|
13
14
|
import { PartyEntity } from './PartyEntity'
|
|
15
|
+
import { ContactMetadataItemEntity } from './ContactMetadataItemEntity'
|
|
14
16
|
|
|
15
17
|
@Entity('BaseContact')
|
|
16
18
|
@TableInheritance({ column: { type: 'varchar', name: 'type' } })
|
|
@@ -30,6 +32,15 @@ export abstract class BaseContactEntity extends BaseEntity {
|
|
|
30
32
|
@JoinColumn({ name: 'party_id' })
|
|
31
33
|
party!: PartyEntity
|
|
32
34
|
|
|
35
|
+
@OneToMany(() => ContactMetadataItemEntity, (metadata: ContactMetadataItemEntity) => metadata.contact, {
|
|
36
|
+
cascade: true,
|
|
37
|
+
onDelete: 'CASCADE',
|
|
38
|
+
eager: true,
|
|
39
|
+
nullable: false,
|
|
40
|
+
})
|
|
41
|
+
@JoinColumn({ name: 'metadata_id' })
|
|
42
|
+
metadata!: Array<ContactMetadataItemEntity>
|
|
43
|
+
|
|
33
44
|
// By default, @UpdateDateColumn in TypeORM updates the timestamp only when the entity's top-level properties change.
|
|
34
45
|
@BeforeInsert()
|
|
35
46
|
@BeforeUpdate()
|
|
@@ -13,6 +13,12 @@ export class ConnectionEntity extends BaseEntity {
|
|
|
13
13
|
@Column('simple-enum', { name: 'type', enum: ConnectionType, nullable: false })
|
|
14
14
|
type!: ConnectionType
|
|
15
15
|
|
|
16
|
+
@Column({name: 'tenant_id', nullable: true})
|
|
17
|
+
tenantId?: string
|
|
18
|
+
|
|
19
|
+
@Column({name: 'owner_id', nullable: true})
|
|
20
|
+
ownerId?: string
|
|
21
|
+
|
|
16
22
|
@OneToOne(() => BaseConfigEntity, (config: OpenIdConfigEntity | DidAuthConfigEntity) => config.connection, {
|
|
17
23
|
cascade: true,
|
|
18
24
|
onDelete: 'CASCADE',
|