@sphereon/ssi-sdk.data-store 0.36.1-next.11 → 0.36.1-next.115

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sphereon/ssi-sdk.data-store",
3
- "version": "0.36.1-next.11+262d209a",
3
+ "version": "0.36.1-next.115+0fab323a",
4
4
  "source": "src/index.ts",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -28,12 +28,12 @@
28
28
  "dependencies": {
29
29
  "@sphereon/kmp-mdoc-core": "0.2.0-SNAPSHOT.26",
30
30
  "@sphereon/pex": "5.0.0-unstable.28",
31
- "@sphereon/ssi-sdk-ext.did-utils": "0.36.1-next.11+262d209a",
32
- "@sphereon/ssi-sdk-ext.identifier-resolution": "0.36.1-next.11+262d209a",
33
- "@sphereon/ssi-sdk.agent-config": "0.36.1-next.11+262d209a",
34
- "@sphereon/ssi-sdk.core": "0.36.1-next.11+262d209a",
35
- "@sphereon/ssi-sdk.data-store-types": "0.36.1-next.11+262d209a",
36
- "@sphereon/ssi-types": "0.36.1-next.11+262d209a",
31
+ "@sphereon/ssi-sdk-ext.did-utils": "0.36.1-next.115+0fab323a",
32
+ "@sphereon/ssi-sdk-ext.identifier-resolution": "0.36.1-next.115+0fab323a",
33
+ "@sphereon/ssi-sdk.agent-config": "0.36.1-next.115+0fab323a",
34
+ "@sphereon/ssi-sdk.core": "0.36.1-next.115+0fab323a",
35
+ "@sphereon/ssi-sdk.data-store-types": "0.36.1-next.115+0fab323a",
36
+ "@sphereon/ssi-types": "0.36.1-next.115+0fab323a",
37
37
  "@veramo/core": "4.2.0",
38
38
  "@veramo/utils": "4.2.0",
39
39
  "blakejs": "^1.2.1",
@@ -66,5 +66,5 @@
66
66
  "PostgreSQL",
67
67
  "Contact Store"
68
68
  ],
69
- "gitHead": "262d209a803fecfba1ad5878724c4f6f91f86cec"
69
+ "gitHead": "0fab323abf92edba332557800ab79493e3681e1f"
70
70
  }
@@ -1,11 +1,5 @@
1
1
  import { DataSources } from '@sphereon/ssi-sdk.agent-config'
2
2
  import { defaultHasher } from '@sphereon/ssi-sdk.core'
3
- import { CredentialRole, IVerifiablePresentation } from '@sphereon/ssi-types'
4
- import { DataSource } from 'typeorm'
5
- import { afterEach, beforeEach, describe, expect, it } from 'vitest'
6
- import { DigitalCredentialStore } from '../digitalCredential/DigitalCredentialStore'
7
- import { DataStoreDigitalCredentialEntities } from '../index'
8
- import { DataStoreDigitalCredentialMigrations } from '../migrations'
9
3
  import {
10
4
  AddCredentialArgs,
11
5
  CredentialCorrelationType,
@@ -16,6 +10,12 @@ import {
16
10
  GetCredentialsArgs,
17
11
  GetCredentialsResponse,
18
12
  } from '@sphereon/ssi-sdk.data-store-types'
13
+ import { CredentialRole, IVerifiablePresentation } from '@sphereon/ssi-types'
14
+ import { DataSource } from 'typeorm'
15
+ import { afterEach, beforeEach, describe, expect, it } from 'vitest'
16
+ import { DigitalCredentialStore } from '../digitalCredential/DigitalCredentialStore'
17
+ import { DataStoreDigitalCredentialEntities } from '../index'
18
+ import { DataStoreDigitalCredentialMigrations } from '../migrations'
19
19
 
20
20
  describe('Database entities tests', (): void => {
21
21
  let dbConnection: DataSource
@@ -334,7 +334,7 @@ describe('Database entities tests', (): void => {
334
334
  expect(result).toEqual(false)
335
335
  })
336
336
 
337
- it('should update stored digital credential', async (): Promise<void> => {
337
+ it('should update stored digital credential state', async (): Promise<void> => {
338
338
  const rawCredential: string =
339
339
  'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw'
340
340
  const digitalCredential: AddCredentialArgs = {
@@ -359,6 +359,121 @@ describe('Database entities tests', (): void => {
359
359
  expect(result.verifiedState).toEqual(CredentialStateType.VERIFIED)
360
360
  })
361
361
 
362
+ // Add these test cases to digitalCredential.store.test.ts after the existing update tests
363
+
364
+ it('should update digital credential fields', async (): Promise<void> => {
365
+ const rawCredential: string =
366
+ 'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw'
367
+ const digitalCredential: AddCredentialArgs = {
368
+ rawDocument: rawCredential,
369
+ kmsKeyRef: 'testRef',
370
+ identifierMethod: 'did',
371
+ issuerCorrelationType: CredentialCorrelationType.DID,
372
+ subjectCorrelationType: CredentialCorrelationType.DID,
373
+ issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
374
+ subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
375
+ credentialRole: CredentialRole.VERIFIER,
376
+ tenantId: 'urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj',
377
+ }
378
+
379
+ const savedDigitalCredential: DigitalCredential = await digitalCredentialStore.addCredential(digitalCredential)
380
+
381
+ const result = await digitalCredentialStore.updateCredential({
382
+ id: savedDigitalCredential.id,
383
+ kmsKeyRef: 'newTestRef',
384
+ tenantId: 'urn:uuid:new-tenant-id',
385
+ linkedVpId: 'vp-123',
386
+ })
387
+
388
+ expect(result.kmsKeyRef).toEqual('newTestRef')
389
+ expect(result.tenantId).toEqual('urn:uuid:new-tenant-id')
390
+ expect(result.linkedVpId).toEqual('vp-123')
391
+ expect(result.id).toEqual(savedDigitalCredential.id)
392
+ expect(result.hash).toEqual(savedDigitalCredential.hash)
393
+ expect(result.createdAt).toEqual(savedDigitalCredential.createdAt)
394
+ expect(result.lastUpdatedAt.getTime()).toBeGreaterThan(savedDigitalCredential.lastUpdatedAt.getTime())
395
+ })
396
+
397
+ it('should update digital credential by hash', async (): Promise<void> => {
398
+ const rawCredential: string =
399
+ 'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw'
400
+ const digitalCredential: AddCredentialArgs = {
401
+ rawDocument: rawCredential,
402
+ kmsKeyRef: 'testRef',
403
+ identifierMethod: 'did',
404
+ issuerCorrelationType: CredentialCorrelationType.DID,
405
+ subjectCorrelationType: CredentialCorrelationType.DID,
406
+ issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
407
+ subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
408
+ credentialRole: CredentialRole.VERIFIER,
409
+ tenantId: 'urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj',
410
+ }
411
+
412
+ const savedDigitalCredential: DigitalCredential = await digitalCredentialStore.addCredential(digitalCredential)
413
+
414
+ const result = await digitalCredentialStore.updateCredential({
415
+ hash: savedDigitalCredential.hash,
416
+ rpCorrelationId: 'did:example:rp',
417
+ rpCorrelationType: CredentialCorrelationType.DID,
418
+ })
419
+
420
+ expect(result.rpCorrelationId).toEqual('did:example:rp')
421
+ expect(result.rpCorrelationType).toEqual(CredentialCorrelationType.DID)
422
+ expect(result.hash).toEqual(savedDigitalCredential.hash)
423
+ })
424
+
425
+ it('should throw error when updating credential without id or hash', async (): Promise<void> => {
426
+ await expect(
427
+ digitalCredentialStore.updateCredential({
428
+ kmsKeyRef: 'newRef',
429
+ } as any),
430
+ ).rejects.toThrowError('No id or hash param is provided.')
431
+ })
432
+
433
+ it('should throw error when updating non-existent credential', async (): Promise<void> => {
434
+ await expect(
435
+ digitalCredentialStore.updateCredential({
436
+ id: 'non-existent-id',
437
+ kmsKeyRef: 'newRef',
438
+ }),
439
+ ).rejects.toThrowError('No credential found for args: {"id":"non-existent-id"}')
440
+ })
441
+
442
+ it('should preserve immutable fields when updating credential', async (): Promise<void> => {
443
+ const rawCredential: string =
444
+ 'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw'
445
+ const digitalCredential: AddCredentialArgs = {
446
+ rawDocument: rawCredential,
447
+ kmsKeyRef: 'testRef',
448
+ identifierMethod: 'did',
449
+ issuerCorrelationType: CredentialCorrelationType.DID,
450
+ subjectCorrelationType: CredentialCorrelationType.DID,
451
+ issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
452
+ subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
453
+ credentialRole: CredentialRole.VERIFIER,
454
+ tenantId: 'urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj',
455
+ }
456
+
457
+ const savedDigitalCredential: DigitalCredential = await digitalCredentialStore.addCredential(digitalCredential)
458
+ const originalId = savedDigitalCredential.id
459
+ const originalHash = savedDigitalCredential.hash
460
+ const originalCreatedAt = savedDigitalCredential.createdAt
461
+
462
+ // Try to update with different id, hash, and createdAt - these should be ignored
463
+ const result = await digitalCredentialStore.updateCredential({
464
+ id: savedDigitalCredential.id,
465
+ kmsKeyRef: 'updatedRef',
466
+ hash: 'should-be-ignored' as any,
467
+ })
468
+
469
+ // Verify immutable fields remain unchanged
470
+ expect(result.id).toEqual(originalId)
471
+ expect(result.hash).toEqual(originalHash)
472
+ expect(result.createdAt).toEqual(originalCreatedAt)
473
+ // Verify mutable field was updated
474
+ expect(result.kmsKeyRef).toEqual('updatedRef')
475
+ })
476
+
362
477
  it('should throw exception on updating stored digital credential to revoked', async (): Promise<void> => {
363
478
  const rawCredential: string =
364
479
  'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw'
@@ -409,4 +524,231 @@ describe('Database entities tests', (): void => {
409
524
  expect(result.verifiedState).toEqual(CredentialStateType.REVOKED)
410
525
  expect(result.revokedAt).toEqual(currentDate)
411
526
  })
527
+
528
+ it('should filter credentials by linkedVpUntil date range', async (): Promise<void> => {
529
+ const baseCredential: AddCredentialArgs = {
530
+ rawDocument:
531
+ 'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw',
532
+ kmsKeyRef: 'testRef',
533
+ identifierMethod: 'did',
534
+ issuerCorrelationType: CredentialCorrelationType.DID,
535
+ subjectCorrelationType: CredentialCorrelationType.DID,
536
+ issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
537
+ subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
538
+ credentialRole: CredentialRole.VERIFIER,
539
+ tenantId: 'urn:uuid:nnag4b43-1e7a-98f8-a32c-a48dbc5b10mj',
540
+ }
541
+
542
+ // Create credentials with different linkedVpUntil dates
543
+ const credential1: DigitalCredential = await digitalCredentialStore.addCredential(baseCredential)
544
+ const date1 = new Date('2024-01-15')
545
+ await digitalCredentialStore.updateCredential({
546
+ id: credential1.id,
547
+ linkedVpId: 'vp-1',
548
+ linkedVpUntil: date1,
549
+ })
550
+
551
+ const credential2Args = {
552
+ ...baseCredential,
553
+ rawDocument: baseCredential.rawDocument + '1', // Slightly different to get different hash
554
+ }
555
+ const credential2: DigitalCredential = await digitalCredentialStore.addCredential(credential2Args)
556
+ const date2 = new Date('2024-06-15')
557
+ await digitalCredentialStore.updateCredential({
558
+ id: credential2.id,
559
+ linkedVpId: 'vp-2',
560
+ linkedVpUntil: date2,
561
+ })
562
+
563
+ const credential3Args = {
564
+ ...baseCredential,
565
+ rawDocument: baseCredential.rawDocument + '2', // Different again
566
+ }
567
+ const credential3: DigitalCredential = await digitalCredentialStore.addCredential(credential3Args)
568
+ const date3 = new Date('2024-12-31')
569
+ await digitalCredentialStore.updateCredential({
570
+ id: credential3.id,
571
+ linkedVpId: 'vp-3',
572
+ linkedVpUntil: date3,
573
+ })
574
+
575
+ // Credential without linkedVpUntil
576
+ const credential4Args = {
577
+ ...baseCredential,
578
+ rawDocument: baseCredential.rawDocument + '3',
579
+ }
580
+ await digitalCredentialStore.addCredential(credential4Args)
581
+
582
+ // Test: Get credentials expiring on or before mid-year (should get credential1 and credential2)
583
+ const filterDate1 = new Date('2024-06-15')
584
+ const result1: GetCredentialsResponse = await digitalCredentialStore.getCredentials({
585
+ filter: [{ linkedVpUntil: filterDate1 }],
586
+ })
587
+ expect(result1.total).toEqual(2)
588
+ expect(result1.data.some((c) => c.linkedVpId === 'vp-1')).toBe(true)
589
+ expect(result1.data.some((c) => c.linkedVpId === 'vp-2')).toBe(true)
590
+
591
+ // Test: Get credentials expiring on or before end of January (should only get credential1)
592
+ const filterDate2 = new Date('2024-01-31')
593
+ const result2: GetCredentialsResponse = await digitalCredentialStore.getCredentials({
594
+ filter: [{ linkedVpUntil: filterDate2 }],
595
+ })
596
+ expect(result2.total).toEqual(1)
597
+ expect(result2.data[0].linkedVpId).toEqual('vp-1')
598
+
599
+ // Test: Get credentials expiring on or before end of year (should get all 3 with linkedVpUntil)
600
+ const filterDate3 = new Date('2024-12-31')
601
+ const result3: GetCredentialsResponse = await digitalCredentialStore.getCredentials({
602
+ filter: [{ linkedVpUntil: filterDate3 }],
603
+ })
604
+ expect(result3.total).toEqual(3)
605
+ expect(result3.data.some((c) => c.linkedVpId === 'vp-1')).toBe(true)
606
+ expect(result3.data.some((c) => c.linkedVpId === 'vp-2')).toBe(true)
607
+ expect(result3.data.some((c) => c.linkedVpId === 'vp-3')).toBe(true)
608
+
609
+ // Test: Get credentials expiring before the earliest date (should get none)
610
+ const filterDate4 = new Date('2024-01-01')
611
+ const result4: GetCredentialsResponse = await digitalCredentialStore.getCredentials({
612
+ filter: [{ linkedVpUntil: filterDate4 }],
613
+ })
614
+ expect(result4.total).toEqual(0)
615
+ })
616
+
617
+ it('should filter credentials by linkedVpUntil combined with other filters', async (): Promise<void> => {
618
+ const tenant1 = 'urn:uuid:tenant-1'
619
+ const tenant2 = 'urn:uuid:tenant-2'
620
+
621
+ const baseCredential: AddCredentialArgs = {
622
+ rawDocument:
623
+ 'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw',
624
+ kmsKeyRef: 'testRef',
625
+ identifierMethod: 'did',
626
+ issuerCorrelationType: CredentialCorrelationType.DID,
627
+ subjectCorrelationType: CredentialCorrelationType.DID,
628
+ issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
629
+ subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
630
+ credentialRole: CredentialRole.VERIFIER,
631
+ }
632
+
633
+ // Tenant 1 credentials
634
+ const credential1: DigitalCredential = await digitalCredentialStore.addCredential({ ...baseCredential, tenantId: tenant1 })
635
+ await digitalCredentialStore.updateCredential({
636
+ id: credential1.id,
637
+ linkedVpId: 'vp-1',
638
+ linkedVpUntil: new Date('2024-03-01'),
639
+ })
640
+
641
+ const credential2Args = { ...baseCredential, tenantId: tenant1, rawDocument: baseCredential.rawDocument + '1' }
642
+ const credential2: DigitalCredential = await digitalCredentialStore.addCredential(credential2Args)
643
+ await digitalCredentialStore.updateCredential({
644
+ id: credential2.id,
645
+ linkedVpId: 'vp-2',
646
+ linkedVpUntil: new Date('2024-08-01'),
647
+ })
648
+
649
+ // Tenant 2 credentials
650
+ const credential3Args = { ...baseCredential, tenantId: tenant2, rawDocument: baseCredential.rawDocument + '2' }
651
+ const credential3: DigitalCredential = await digitalCredentialStore.addCredential(credential3Args)
652
+ await digitalCredentialStore.updateCredential({
653
+ id: credential3.id,
654
+ linkedVpId: 'vp-3',
655
+ linkedVpUntil: new Date('2024-03-01'),
656
+ })
657
+
658
+ const credential4Args = { ...baseCredential, tenantId: tenant2, rawDocument: baseCredential.rawDocument + '3' }
659
+ const credential4: DigitalCredential = await digitalCredentialStore.addCredential(credential4Args)
660
+ await digitalCredentialStore.updateCredential({
661
+ id: credential4.id,
662
+ linkedVpId: 'vp-4',
663
+ linkedVpUntil: new Date('2024-08-01'),
664
+ })
665
+
666
+ // Test: Filter by tenant and linkedVpUntil
667
+ const filterDate = new Date('2024-06-01')
668
+ const result: GetCredentialsResponse = await digitalCredentialStore.getCredentials({
669
+ filter: [
670
+ {
671
+ tenantId: tenant1,
672
+ linkedVpUntil: filterDate,
673
+ },
674
+ ],
675
+ })
676
+
677
+ // Should only get tenant1's credential that expires before June (credential1)
678
+ expect(result.total).toEqual(1)
679
+ expect(result.data[0].linkedVpId).toEqual('vp-1')
680
+ expect(result.data[0].tenantId).toEqual(tenant1)
681
+ })
682
+
683
+ it('should handle empty results when filtering by linkedVpUntil with no matches', async (): Promise<void> => {
684
+ const baseCredential: AddCredentialArgs = {
685
+ rawDocument:
686
+ 'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw',
687
+ kmsKeyRef: 'testRef',
688
+ identifierMethod: 'did',
689
+ issuerCorrelationType: CredentialCorrelationType.DID,
690
+ subjectCorrelationType: CredentialCorrelationType.DID,
691
+ issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
692
+ subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
693
+ credentialRole: CredentialRole.VERIFIER,
694
+ tenantId: 'urn:uuid:test-tenant',
695
+ }
696
+
697
+ // Create credential with linkedVpUntil in the future
698
+ const credential: DigitalCredential = await digitalCredentialStore.addCredential(baseCredential)
699
+ await digitalCredentialStore.updateCredential({
700
+ id: credential.id,
701
+ linkedVpId: 'vp-future',
702
+ linkedVpUntil: new Date('2025-12-31'),
703
+ })
704
+
705
+ // Filter by date in the past - should return no results
706
+ const filterDate = new Date('2024-01-01')
707
+ const result: GetCredentialsResponse = await digitalCredentialStore.getCredentials({
708
+ filter: [{ linkedVpUntil: filterDate }],
709
+ })
710
+
711
+ expect(result.total).toEqual(0)
712
+ expect(result.data.length).toEqual(0)
713
+ })
714
+
715
+ it('should handle credentials with null linkedVpUntil when filtering', async (): Promise<void> => {
716
+ const baseCredential: AddCredentialArgs = {
717
+ rawDocument:
718
+ 'eyJraWQiOiJkaWQ6a2V5Ono2TWtyaGt5M3B1c20yNk1laUZhWFUzbjJuZWtyYW13RlVtZ0dyZUdHa0RWNnpRaiN6Nk1rcmhreTNwdXNtMjZNZWlGYVhVM24ybmVrcmFtd0ZVbWdHcmVHR2tEVjZ6UWoiLCJhbGciOiJFZERTQSIsInR5cCI6IkpXVCJ9.eyJ2YyI6eyJAY29udGV4dCI6WyJodHRwczovL3d3dy53My5vcmcvMjAxOC9jcmVkZW50aWFscy92MSIsImh0dHBzOi8vc3BoZXJlb24tb3BlbnNvdXJjZS5naXRodWIuaW8vc3NpLW1vYmlsZS13YWxsZXQvY29udGV4dC9zcGhlcmVvbi13YWxsZXQtaWRlbnRpdHktdjEuanNvbmxkIl0sInR5cGUiOlsiVmVyaWZpYWJsZUNyZWRlbnRpYWwiLCJTcGhlcmVvbldhbGxldElkZW50aXR5Q3JlZGVudGlhbCJdLCJjcmVkZW50aWFsU3ViamVjdCI6eyJmaXJzdE5hbWUiOiJTIiwibGFzdE5hbWUiOiJLIiwiZW1haWxBZGRyZXNzIjoic0BrIn19LCJzdWIiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJqdGkiOiJ1cm46dXVpZDpkZGE3YmYyNC04ZTdhLTQxZjgtYjY2Yy1hNDhkYmM1YjEwZmEiLCJuYmYiOjE3MDg0NDA4MDgsImlzcyI6ImRpZDprZXk6ejZNa3Joa3kzcHVzbTI2TWVpRmFYVTNuMm5la3JhbXdGVW1nR3JlR0drRFY2elFqIn0.G0M84XVAxSmzGY-NQuB9NBofNrINSn6lvxW6761Vlq6ypvYgtc2xNdpiRmw8ryVNfnpzrr4Z5cB1RlrC05rJAw',
719
+ kmsKeyRef: 'testRef',
720
+ identifierMethod: 'did',
721
+ issuerCorrelationType: CredentialCorrelationType.DID,
722
+ subjectCorrelationType: CredentialCorrelationType.DID,
723
+ issuerCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
724
+ subjectCorrelationId: 'did:key:z6Mkrhky3pusm26MeiFaXU3n2nekramwFUmgGreGGkDV6zQj',
725
+ credentialRole: CredentialRole.VERIFIER,
726
+ tenantId: 'urn:uuid:test-tenant',
727
+ }
728
+
729
+ // Credential with linkedVpUntil
730
+ const credential1: DigitalCredential = await digitalCredentialStore.addCredential(baseCredential)
731
+ await digitalCredentialStore.updateCredential({
732
+ id: credential1.id,
733
+ linkedVpId: 'vp-with-expiry',
734
+ linkedVpUntil: new Date('2024-06-01'),
735
+ })
736
+
737
+ // Credential without linkedVpUntil (null)
738
+ const credential2Args = { ...baseCredential, rawDocument: baseCredential.rawDocument + '1' }
739
+ const credential2: DigitalCredential = await digitalCredentialStore.addCredential(credential2Args)
740
+ await digitalCredentialStore.updateCredential({
741
+ id: credential2.id,
742
+ linkedVpId: 'vp-no-expiry',
743
+ })
744
+
745
+ // Filter should only return the credential with linkedVpUntil
746
+ const filterDate = new Date('2024-12-31')
747
+ const result: GetCredentialsResponse = await digitalCredentialStore.getCredentials({
748
+ filter: [{ linkedVpUntil: filterDate }],
749
+ })
750
+
751
+ expect(result.total).toEqual(1)
752
+ expect(result.data[0].linkedVpId).toEqual('vp-with-expiry')
753
+ })
412
754
  })
@@ -8,13 +8,20 @@ import {
8
8
  GetCredentialsResponse,
9
9
  NonPersistedDigitalCredential,
10
10
  RemoveCredentialArgs,
11
+ UpdateCredentialArgs,
11
12
  UpdateCredentialStateArgs,
12
13
  } from '@sphereon/ssi-sdk.data-store-types'
13
14
  import { CredentialRole, OrPromise } from '@sphereon/ssi-types'
14
15
  import Debug from 'debug'
15
- import { DataSource, type FindOptionsOrder, type FindOptionsWhere, Repository } from 'typeorm'
16
+ import { DataSource, type FindOptionsOrder, type FindOptionsWhere, LessThanOrEqual, Repository } from 'typeorm'
16
17
 
17
- import { digitalCredentialFrom, digitalCredentialsFrom, nonPersistedDigitalCredentialEntityFromAddArgs } from '../../src'
18
+ import {
19
+ digitalCredentialFrom,
20
+ digitalCredentialsFrom,
21
+ nonPersistedDigitalCredentialEntityFromAddArgs,
22
+ persistedDigitalCredentialEntityFromStateArgs,
23
+ persistedDigitalCredentialEntityFromUpdateArgs,
24
+ } from '../../src'
18
25
  import { DigitalCredentialEntity } from '../entities/digitalCredential/DigitalCredentialEntity'
19
26
  import { parseAndValidateOrderOptions } from '../utils/SortingUtils'
20
27
 
@@ -60,8 +67,28 @@ export class DigitalCredentialStore extends AbstractDigitalCredentialStore {
60
67
  ? parseAndValidateOrderOptions<DigitalCredentialEntity>(order)
61
68
  : <FindOptionsOrder<DigitalCredentialEntity>>order
62
69
  const dcRepo = await this.getRepository()
70
+
71
+ // Process filter to convert linkedVpUntil dates into LessThanOrEqual operators
72
+ const processLinkedVpUntil = (filterItem: Partial<DigitalCredential>): FindOptionsWhere<DigitalCredentialEntity> => {
73
+ const processed = { ...filterItem } as FindOptionsWhere<DigitalCredentialEntity>
74
+ if (filterItem.linkedVpUntil) {
75
+ processed.linkedVpUntil = LessThanOrEqual(filterItem.linkedVpUntil)
76
+ }
77
+ return processed
78
+ }
79
+
80
+ let whereClause: FindOptionsWhere<DigitalCredentialEntity> | FindOptionsWhere<DigitalCredentialEntity>[]
81
+
82
+ if (Array.isArray(filter) && filter.length > 0) {
83
+ whereClause = filter.map(processLinkedVpUntil)
84
+ } else if (Object.keys(filter).length > 0) {
85
+ whereClause = processLinkedVpUntil(filter as Partial<DigitalCredential>)
86
+ } else {
87
+ whereClause = filter as FindOptionsWhere<DigitalCredentialEntity>
88
+ }
89
+
63
90
  const [result, total] = await dcRepo.findAndCount({
64
- where: filter,
91
+ where: whereClause,
65
92
  skip: offset,
66
93
  take: limit,
67
94
  order: sortOptions,
@@ -72,6 +99,41 @@ export class DigitalCredentialStore extends AbstractDigitalCredentialStore {
72
99
  }
73
100
  }
74
101
 
102
+ updateCredential = async (args: UpdateCredentialArgs): Promise<DigitalCredential> => {
103
+ const dcRepo = await this.getRepository()
104
+ const whereClause: Record<string, any> = {}
105
+
106
+ if ('id' in args) {
107
+ whereClause.id = args.id
108
+ } else if ('hash' in args) {
109
+ whereClause.hash = args.hash
110
+ } else {
111
+ return Promise.reject(Error('No id or hash param is provided.'))
112
+ }
113
+
114
+ const credential: DigitalCredentialEntity | null = await dcRepo.findOne({
115
+ where: whereClause,
116
+ })
117
+
118
+ if (!credential) {
119
+ return Promise.reject(Error(`No credential found for args: ${JSON.stringify(whereClause)}`))
120
+ }
121
+
122
+ // Extract updates by removing the identifier fields
123
+ const updates = Object.fromEntries(Object.entries(args).filter(([key]) => key !== 'id' && key !== 'hash')) as Partial<DigitalCredential>
124
+
125
+ const entityToSave = persistedDigitalCredentialEntityFromUpdateArgs(credential, updates)
126
+
127
+ const validationError = this.assertValidDigitalCredential(entityToSave)
128
+ if (validationError) {
129
+ return Promise.reject(validationError)
130
+ }
131
+
132
+ debug('Updating credential', entityToSave)
133
+ const updatedResult = await dcRepo.save(entityToSave as any, { transaction: true })
134
+ return digitalCredentialFrom(updatedResult)
135
+ }
136
+
75
137
  removeCredential = async (args: RemoveCredentialArgs): Promise<boolean> => {
76
138
  if (!args) {
77
139
  return false
@@ -147,20 +209,16 @@ export class DigitalCredentialStore extends AbstractDigitalCredentialStore {
147
209
  if (!credential) {
148
210
  return Promise.reject(Error(`No credential found for args: ${JSON.stringify(whereClause)}`))
149
211
  }
150
- const updatedCredential: DigitalCredential = {
151
- ...credential,
152
- ...(args.verifiedState !== CredentialStateType.REVOKED && { verifiedAt: args.verifiedAt }),
153
- ...(args.verifiedState === CredentialStateType.REVOKED && { revokedAt: args.revokedAt }),
154
- identifierMethod: credential.identifierMethod,
155
- lastUpdatedAt: new Date(),
156
- verifiedState: args.verifiedState,
157
- }
158
- debug('Updating credential', credential)
159
- const updatedResult: DigitalCredentialEntity = await credentialRepository.save(updatedCredential, { transaction: true })
212
+
213
+ // Create entity with state updates applied
214
+ const entityToSave = persistedDigitalCredentialEntityFromStateArgs(credential, args)
215
+
216
+ debug('Updating credential state', entityToSave)
217
+ const updatedResult: DigitalCredentialEntity = await credentialRepository.save(entityToSave as any, { transaction: true })
160
218
  return digitalCredentialFrom(updatedResult)
161
219
  }
162
220
 
163
- private assertValidDigitalCredential(credentialEntity: NonPersistedDigitalCredential): Error | undefined {
221
+ private assertValidDigitalCredential(credentialEntity: NonPersistedDigitalCredential | DigitalCredentialEntity): Error | undefined {
164
222
  const { kmsKeyRef, identifierMethod, credentialRole, isIssuerSigned } = credentialEntity
165
223
 
166
224
  const isRoleInvalid = credentialRole === CredentialRole.ISSUER || (credentialRole === CredentialRole.HOLDER && !isIssuerSigned)
@@ -76,6 +76,15 @@ export class DigitalCredentialEntity extends BaseEntity implements DigitalCreden
76
76
  @Column('text', { name: 'tenant_id', nullable: true })
77
77
  tenantId?: string
78
78
 
79
+ @Column('text', { name: 'linked_vp_id', nullable: true })
80
+ linkedVpId?: string
81
+
82
+ @Column({ name: 'linked_vp_from', nullable: true, type: typeOrmDateTime() })
83
+ linkedVpFrom?: Date
84
+
85
+ @Column({ name: 'linked_vp_until', nullable: true, type: typeOrmDateTime() })
86
+ linkedVpUntil?: Date
87
+
79
88
  @CreateDateColumn({ name: 'created_at', nullable: false, type: typeOrmDateTime() })
80
89
  createdAt!: Date
81
90
 
@@ -0,0 +1,66 @@
1
+ import Debug, { Debugger } from 'debug'
2
+ import { DatabaseType, MigrationInterface, QueryRunner } from 'typeorm'
3
+ import { AddLinkedVpFields1763387280001 } from '../postgres/1763387280001-AddLinkedVpFields'
4
+ import { AddLinkedVpFields1763387280002 } from '../sqlite/1763387280002-AddLinkedVpFields'
5
+
6
+ const debug: Debugger = Debug('sphereon:ssi-sdk:migrations')
7
+
8
+ export class AddLinkedVpFields1763387280000 implements MigrationInterface {
9
+ name: string = 'AddLinkedVpFields1763387280000'
10
+
11
+ public async up(queryRunner: QueryRunner): Promise<void> {
12
+ debug('migration: adding linked VP fields to DigitalCredential table')
13
+ const dbType: DatabaseType = queryRunner.connection.driver.options.type
14
+
15
+ switch (dbType) {
16
+ case 'postgres': {
17
+ debug('using postgres migration file for AddLinkedVpFields')
18
+ const mig: AddLinkedVpFields1763387280001 = new AddLinkedVpFields1763387280001()
19
+ await mig.up(queryRunner)
20
+ debug('Postgres migration statements for AddLinkedVpFields executed')
21
+ return
22
+ }
23
+ case 'sqlite':
24
+ case 'expo':
25
+ case 'react-native': {
26
+ debug('using sqlite/react-native migration file for AddLinkedVpFields')
27
+ const mig: AddLinkedVpFields1763387280002 = new AddLinkedVpFields1763387280002()
28
+ await mig.up(queryRunner)
29
+ debug('SQLite migration statements for AddLinkedVpFields executed')
30
+ return
31
+ }
32
+ default:
33
+ return Promise.reject(
34
+ `Migrations are currently only supported for sqlite, react-native, expo, and postgres for AddLinkedVpFields. Was ${dbType}. Please run your database without migrations and with 'migrationsRun: false' and 'synchronize: true' for now`,
35
+ )
36
+ }
37
+ }
38
+
39
+ public async down(queryRunner: QueryRunner): Promise<void> {
40
+ debug('migration: reverting linked VP fields from DigitalCredential table')
41
+ const dbType: DatabaseType = queryRunner.connection.driver.options.type
42
+
43
+ switch (dbType) {
44
+ case 'postgres': {
45
+ debug('using postgres migration file for AddLinkedVpFields')
46
+ const mig: AddLinkedVpFields1763387280001 = new AddLinkedVpFields1763387280001()
47
+ await mig.down(queryRunner)
48
+ debug('Postgres migration statements for AddLinkedVpFields reverted')
49
+ return
50
+ }
51
+ case 'sqlite':
52
+ case 'expo':
53
+ case 'react-native': {
54
+ debug('using sqlite/react-native migration file for AddLinkedVpFields')
55
+ const mig: AddLinkedVpFields1763387280002 = new AddLinkedVpFields1763387280002()
56
+ await mig.down(queryRunner)
57
+ debug('SQLite migration statements for AddLinkedVpFields reverted')
58
+ return
59
+ }
60
+ default:
61
+ return Promise.reject(
62
+ `Migrations are currently only supported for sqlite, react-native, expo, and postgres for AddLinkedVpFields. Was ${dbType}. Please run your database without migrations and with 'migrationsRun: false' and 'synchronize: true' for now`,
63
+ )
64
+ }
65
+ }
66
+ }
@@ -3,6 +3,7 @@ import { CreatePresentationDefinitions1716533767523 } from './10-CreatePresentat
3
3
  import { FixCredentialClaimsReferencesUuid1741895822987 } from './11-FixCredentialClaimsReferenceUuid'
4
4
  import { AddBitstringStatusListEnum1741895823000, CreateBitstringStatusList1741895823000 } from './12-CreateBitstringStatusList'
5
5
  import { CreateDcqlQueryItem1726617600000 } from './13-CreateDcqlQueryItem'
6
+ import { AddLinkedVpFields1763387280000 } from './14-AddLinkedVpFields'
6
7
  import { CreateIssuanceBranding1659463079429 } from './2-CreateIssuanceBranding'
7
8
  import { CreateContacts1690925872318 } from './3-CreateContacts'
8
9
  import { CreateStatusList1693866470000 } from './4-CreateStatusList'
@@ -34,7 +35,7 @@ export const DataStoreStatusListMigrations = [
34
35
  CreateBitstringStatusList1741895823000,
35
36
  ]
36
37
  export const DataStoreEventLoggerMigrations = [CreateAuditEvents1701635835330]
37
- export const DataStoreDigitalCredentialMigrations = [CreateDigitalCredential1708525189000]
38
+ export const DataStoreDigitalCredentialMigrations = [CreateDigitalCredential1708525189000, AddLinkedVpFields1763387280000]
38
39
  export const DataStoreMachineStateMigrations = [CreateMachineStateStore1708098041262]
39
40
  export const DataStorePresentationDefinitionMigrations = [CreatePresentationDefinitions1716533767523, CreateDcqlQueryItem1726617600000]
40
41