@sphereon/ssi-sdk.data-store 0.19.1-next.2 → 0.19.1-next.24

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.
Files changed (68) hide show
  1. package/dist/digitalCredential/AbstractDigitalCredentialStore.d.ts +10 -0
  2. package/dist/digitalCredential/AbstractDigitalCredentialStore.d.ts.map +1 -0
  3. package/dist/digitalCredential/AbstractDigitalCredentialStore.js +7 -0
  4. package/dist/digitalCredential/AbstractDigitalCredentialStore.js.map +1 -0
  5. package/dist/digitalCredential/DigitalCredentialStore.d.ts +15 -0
  6. package/dist/digitalCredential/DigitalCredentialStore.d.ts.map +1 -0
  7. package/dist/digitalCredential/DigitalCredentialStore.js +118 -0
  8. package/dist/digitalCredential/DigitalCredentialStore.js.map +1 -0
  9. package/dist/entities/digitalCredential/DigitalCredentialEntity.d.ts +23 -0
  10. package/dist/entities/digitalCredential/DigitalCredentialEntity.d.ts.map +1 -0
  11. package/dist/entities/digitalCredential/DigitalCredentialEntity.js +93 -0
  12. package/dist/entities/digitalCredential/DigitalCredentialEntity.js.map +1 -0
  13. package/dist/index.d.ts +5 -2
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +10 -1
  16. package/dist/index.js.map +1 -1
  17. package/dist/migrations/generic/6-CreateDigitalCredential.d.ts +7 -0
  18. package/dist/migrations/generic/6-CreateDigitalCredential.d.ts.map +1 -0
  19. package/dist/migrations/generic/6-CreateDigitalCredential.js +78 -0
  20. package/dist/migrations/generic/6-CreateDigitalCredential.js.map +1 -0
  21. package/dist/migrations/generic/index.d.ts +2 -0
  22. package/dist/migrations/generic/index.d.ts.map +1 -1
  23. package/dist/migrations/generic/index.js +4 -1
  24. package/dist/migrations/generic/index.js.map +1 -1
  25. package/dist/migrations/index.d.ts +1 -1
  26. package/dist/migrations/index.d.ts.map +1 -1
  27. package/dist/migrations/index.js +2 -1
  28. package/dist/migrations/index.js.map +1 -1
  29. package/dist/migrations/postgres/1708525189001-CreateDigitalCredential.d.ts +7 -0
  30. package/dist/migrations/postgres/1708525189001-CreateDigitalCredential.d.ts.map +1 -0
  31. package/dist/migrations/postgres/1708525189001-CreateDigitalCredential.js +59 -0
  32. package/dist/migrations/postgres/1708525189001-CreateDigitalCredential.js.map +1 -0
  33. package/dist/migrations/sqlite/1708525189002-CreateDigitalCredential.d.ts +7 -0
  34. package/dist/migrations/sqlite/1708525189002-CreateDigitalCredential.d.ts.map +1 -0
  35. package/dist/migrations/sqlite/1708525189002-CreateDigitalCredential.js +50 -0
  36. package/dist/migrations/sqlite/1708525189002-CreateDigitalCredential.js.map +1 -0
  37. package/dist/types/digitalCredential/IAbstractDigitalCredentialStore.d.ts +42 -0
  38. package/dist/types/digitalCredential/IAbstractDigitalCredentialStore.d.ts.map +1 -0
  39. package/dist/types/digitalCredential/IAbstractDigitalCredentialStore.js +3 -0
  40. package/dist/types/digitalCredential/IAbstractDigitalCredentialStore.js.map +1 -0
  41. package/dist/types/digitalCredential/digitalCredential.d.ts +42 -0
  42. package/dist/types/digitalCredential/digitalCredential.d.ts.map +1 -0
  43. package/dist/types/digitalCredential/digitalCredential.js +28 -0
  44. package/dist/types/digitalCredential/digitalCredential.js.map +1 -0
  45. package/dist/utils/SortingUtils.d.ts +3 -0
  46. package/dist/utils/SortingUtils.d.ts.map +1 -0
  47. package/dist/utils/SortingUtils.js +18 -0
  48. package/dist/utils/SortingUtils.js.map +1 -0
  49. package/dist/utils/digitalCredential/MappingUtils.d.ts +7 -0
  50. package/dist/utils/digitalCredential/MappingUtils.d.ts.map +1 -0
  51. package/dist/utils/digitalCredential/MappingUtils.js +99 -0
  52. package/dist/utils/digitalCredential/MappingUtils.js.map +1 -0
  53. package/package.json +5 -4
  54. package/src/__tests__/digitalCredential.entities.test.ts +254 -0
  55. package/src/__tests__/digitalCredential.store.test.ts +294 -0
  56. package/src/digitalCredential/AbstractDigitalCredentialStore.ts +17 -0
  57. package/src/digitalCredential/DigitalCredentialStore.ts +127 -0
  58. package/src/entities/digitalCredential/DigitalCredentialEntity.ts +64 -0
  59. package/src/index.ts +9 -0
  60. package/src/migrations/generic/6-CreateDigitalCredential.ts +66 -0
  61. package/src/migrations/generic/index.ts +3 -0
  62. package/src/migrations/index.ts +1 -0
  63. package/src/migrations/postgres/1708525189001-CreateDigitalCredential.ts +44 -0
  64. package/src/migrations/sqlite/1708525189002-CreateDigitalCredential.ts +34 -0
  65. package/src/types/digitalCredential/IAbstractDigitalCredentialStore.ts +37 -0
  66. package/src/types/digitalCredential/digitalCredential.ts +46 -0
  67. package/src/utils/SortingUtils.ts +16 -0
  68. package/src/utils/digitalCredential/MappingUtils.ts +122 -0
@@ -0,0 +1,127 @@
1
+ import { AbstractDigitalCredentialStore } from './AbstractDigitalCredentialStore'
2
+ import {
3
+ AddCredentialArgs,
4
+ GetCredentialArgs,
5
+ GetCredentialsArgs,
6
+ GetCredentialsResponse,
7
+ RemoveCredentialArgs,
8
+ UpdateCredentialStateArgs,
9
+ } from '../types/digitalCredential/IAbstractDigitalCredentialStore'
10
+ import { OrPromise } from '@sphereon/ssi-types'
11
+ import { DataSource, FindOptionsOrder, Repository } from 'typeorm'
12
+ import Debug from 'debug'
13
+ import { DigitalCredentialEntity } from '../entities/digitalCredential/DigitalCredentialEntity'
14
+ import { nonPersistedDigitalCredentialEntityFromAddArgs } from '../utils/digitalCredential/MappingUtils'
15
+ import { FindOptionsWhere } from 'typeorm/find-options/FindOptionsWhere'
16
+ import { CredentialStateType, DigitalCredential, NonPersistedDigitalCredential } from '../types/digitalCredential/digitalCredential'
17
+ import { parseAndValidateOrderOptions } from '../utils/SortingUtils'
18
+
19
+ const debug: Debug.Debugger = Debug('sphereon:ssi-sdk:credential-store')
20
+
21
+ export class DigitalCredentialStore extends AbstractDigitalCredentialStore {
22
+ private readonly dbConnection: OrPromise<DataSource>
23
+
24
+ constructor(dbConnection: OrPromise<DataSource>) {
25
+ super()
26
+ this.dbConnection = dbConnection
27
+ }
28
+
29
+ addCredential = async (args: AddCredentialArgs): Promise<DigitalCredentialEntity> => {
30
+ debug('Adding credential', args)
31
+ const digitalCredentialEntityRepository: Repository<DigitalCredentialEntity> = (await this.dbConnection).getRepository(DigitalCredentialEntity)
32
+ const credentialEntity: NonPersistedDigitalCredential = nonPersistedDigitalCredentialEntityFromAddArgs(args)
33
+ const createdResult: DigitalCredentialEntity = await digitalCredentialEntityRepository.save(credentialEntity)
34
+ return Promise.resolve(createdResult)
35
+ }
36
+
37
+ getCredential = async (args: GetCredentialArgs): Promise<DigitalCredentialEntity> => {
38
+ const result: DigitalCredentialEntity | null = await (await this.dbConnection).getRepository(DigitalCredentialEntity).findOne({
39
+ where: args,
40
+ })
41
+
42
+ if (!result) {
43
+ return Promise.reject(Error(`No credential found for arg: ${args.toString()}`))
44
+ }
45
+ return result
46
+ }
47
+
48
+ getCredentials = async (args?: GetCredentialsArgs): Promise<GetCredentialsResponse> => {
49
+ const { filter = {}, offset, limit, order = 'id.asc' } = args ?? {}
50
+ const sortOptions: FindOptionsOrder<DigitalCredentialEntity> =
51
+ order && typeof order === 'string'
52
+ ? parseAndValidateOrderOptions<DigitalCredentialEntity>(order)
53
+ : <FindOptionsOrder<DigitalCredentialEntity>>order
54
+ const [result, total] = await (await this.dbConnection).getRepository(DigitalCredentialEntity).findAndCount({
55
+ where: filter,
56
+ skip: offset,
57
+ take: limit,
58
+ order: sortOptions,
59
+ })
60
+ return {
61
+ data: result,
62
+ total,
63
+ }
64
+ }
65
+
66
+ removeCredential = async (args: RemoveCredentialArgs): Promise<boolean> => {
67
+ if (!args) {
68
+ return false
69
+ }
70
+
71
+ let query: FindOptionsWhere<DigitalCredentialEntity> = {}
72
+
73
+ if ('id' in args) {
74
+ query.id = args.id
75
+ } else if ('hash' in args) {
76
+ query.hash = args.hash
77
+ } else {
78
+ return false
79
+ }
80
+ try {
81
+ const connection = await this.dbConnection
82
+ const result = await connection.getRepository(DigitalCredentialEntity).delete(query)
83
+ return result.affected === 1
84
+ } catch (error) {
85
+ console.error('Error removing digital credential:', error)
86
+ return false
87
+ }
88
+ }
89
+
90
+ updateCredentialState = async (args: UpdateCredentialStateArgs): Promise<DigitalCredentialEntity> => {
91
+ const credentialRepository: Repository<DigitalCredentialEntity> = (await this.dbConnection).getRepository(DigitalCredentialEntity)
92
+ const whereClause: Record<string, any> = {}
93
+ if ('id' in args) {
94
+ whereClause.id = args.id
95
+ } else if ('hash' in args) {
96
+ whereClause.hash = args.hash
97
+ } else {
98
+ throw new Error('No id or hash param is provided.')
99
+ }
100
+ if (!args.verifiedState) {
101
+ throw new Error('No verifiedState param is provided.')
102
+ }
103
+ if (args.verifiedState === CredentialStateType.REVOKED && !args.revokedAt) {
104
+ throw new Error('No revokedAt param is provided.')
105
+ }
106
+ if (args.verifiedState !== CredentialStateType.REVOKED && !args.verifiedAt) {
107
+ throw new Error('No verifiedAt param is provided.')
108
+ }
109
+ const credential: DigitalCredentialEntity | null = await credentialRepository.findOne({
110
+ where: whereClause,
111
+ })
112
+
113
+ if (!credential) {
114
+ return Promise.reject(Error(`No credential found for args: ${whereClause}`))
115
+ }
116
+ const updatedCredential: DigitalCredential = {
117
+ ...credential,
118
+ ...(args.verifiedState !== CredentialStateType.REVOKED && { verifiedAt: args.verifiedAt }),
119
+ ...(args.verifiedState === CredentialStateType.REVOKED && { revokedAt: args.revokedAt }),
120
+ lastUpdatedAt: new Date(),
121
+ verifiedState: args.verifiedState,
122
+ }
123
+ debug('Updating credential', credential)
124
+ const updatedResult: DigitalCredentialEntity = await credentialRepository.save(updatedCredential, { transaction: true })
125
+ return updatedResult
126
+ }
127
+ }
@@ -0,0 +1,64 @@
1
+ import { BaseEntity, Column, CreateDateColumn, Entity, PrimaryGeneratedColumn, UpdateDateColumn } from 'typeorm'
2
+ import {
3
+ CredentialCorrelationType,
4
+ CredentialDocumentFormat,
5
+ CredentialStateType,
6
+ DocumentType,
7
+ } from '../../types/digitalCredential/digitalCredential'
8
+
9
+ @Entity('DigitalCredential')
10
+ export class DigitalCredentialEntity extends BaseEntity {
11
+ @PrimaryGeneratedColumn('uuid')
12
+ id!: string
13
+
14
+ @Column('simple-enum', { name: 'document_type', enum: DocumentType, nullable: false })
15
+ documentType!: DocumentType
16
+
17
+ @Column('simple-enum', { name: 'document_format', enum: CredentialDocumentFormat, nullable: false })
18
+ documentFormat!: CredentialDocumentFormat
19
+
20
+ @Column('text', { name: 'raw_document', nullable: false })
21
+ rawDocument!: string
22
+
23
+ @Column('text', { name: 'uniform_document', nullable: false })
24
+ uniformDocument!: string
25
+
26
+ @Column('text', { name: 'hash', nullable: false, unique: true })
27
+ hash!: string
28
+
29
+ @Column('simple-enum', { name: 'issuer_correlation_type', enum: CredentialCorrelationType, nullable: false })
30
+ issuerCorrelationType!: CredentialCorrelationType
31
+
32
+ @Column('simple-enum', { name: 'subject_correlation_type', enum: CredentialCorrelationType, nullable: true })
33
+ subjectCorrelationType?: CredentialCorrelationType
34
+
35
+ @Column('text', { name: 'issuer_correlation_id', nullable: false })
36
+ issuerCorrelationId!: string
37
+
38
+ @Column('text', { name: 'subject_correlation_id', nullable: true })
39
+ subjectCorrelationId?: string
40
+
41
+ @Column('simple-enum', { name: 'verified_state', enum: CredentialStateType, nullable: true })
42
+ verifiedState?: CredentialStateType
43
+
44
+ @Column('text', { name: 'tenant_id', nullable: true })
45
+ tenantId?: string
46
+
47
+ @CreateDateColumn({ name: 'created_at', nullable: false })
48
+ createdAt!: Date
49
+
50
+ @UpdateDateColumn({ name: 'last_updated_at', nullable: false })
51
+ lastUpdatedAt!: Date
52
+
53
+ @Column('date', { name: 'valid_until', nullable: true })
54
+ validUntil?: Date
55
+
56
+ @Column('date', { name: 'valid_from', nullable: true })
57
+ validFrom?: Date
58
+
59
+ @Column('date', { name: 'verified_at', nullable: true })
60
+ verifiedAt?: Date
61
+
62
+ @Column('date', { name: 'revoked_at', nullable: true })
63
+ revokedAt?: Date
64
+ }
package/src/index.ts CHANGED
@@ -31,6 +31,8 @@ export { AbstractIssuanceBrandingStore } from './issuanceBranding/AbstractIssuan
31
31
  export { IssuanceBrandingStore } from './issuanceBranding/IssuanceBrandingStore'
32
32
  export { StatusListStore } from './statusList/StatusListStore'
33
33
  import { AuditEventEntity, auditEventEntityFrom } from './entities/eventLogger/AuditEventEntity'
34
+ import { DigitalCredentialEntity } from './entities/digitalCredential/DigitalCredentialEntity'
35
+ import { digitalCredentialFrom, digitalCredentialsFrom, nonPersistedDigitalCredentialEntityFromAddArgs } from './utils/digitalCredential/MappingUtils'
34
36
  export { AbstractEventLoggerStore } from './eventLogger/AbstractEventLoggerStore'
35
37
  export { EventLoggerStore } from './eventLogger/EventLoggerStore'
36
38
  export {
@@ -77,12 +79,15 @@ export const DataStoreStatusListEntities = [StatusListEntity, StatusListEntryEnt
77
79
 
78
80
  export const DataStoreEventLoggerEntities = [AuditEventEntity]
79
81
 
82
+ export const DataStoreDigitalCredentialEntities = [DigitalCredentialEntity]
83
+
80
84
  // All entities combined if a party wants to enable them all at once
81
85
  export const DataStoreEntities = [
82
86
  ...DataStoreContactEntities,
83
87
  ...DataStoreIssuanceBrandingEntities,
84
88
  ...DataStoreStatusListEntities,
85
89
  ...DataStoreEventLoggerEntities,
90
+ ...DataStoreDigitalCredentialEntities,
86
91
  ]
87
92
 
88
93
  export {
@@ -119,4 +124,8 @@ export {
119
124
  StatusListEntryEntity,
120
125
  AuditEventEntity,
121
126
  auditEventEntityFrom,
127
+ DigitalCredentialEntity,
128
+ digitalCredentialFrom,
129
+ digitalCredentialsFrom,
130
+ nonPersistedDigitalCredentialEntityFromAddArgs,
122
131
  }
@@ -0,0 +1,66 @@
1
+ import { DatabaseType, MigrationInterface, QueryRunner } from 'typeorm'
2
+ import Debug, { Debugger } from 'debug'
3
+ import { CreateDigitalCredential1708525189001 } from '../postgres/1708525189001-CreateDigitalCredential'
4
+ import { CreateDigitalCredential1708525189002 } from '../sqlite/1708525189002-CreateDigitalCredential'
5
+
6
+ const debug: Debugger = Debug('sphereon:ssi-sdk:migrations')
7
+
8
+ export class CreateDigitalCredential1708525189000 implements MigrationInterface {
9
+ name: string = 'CreateDigitalCredential1708525189000'
10
+
11
+ public async up(queryRunner: QueryRunner): Promise<void> {
12
+ debug('migration: creating DigitalCredential tables')
13
+ const dbType: DatabaseType = queryRunner.connection.driver.options.type
14
+
15
+ switch (dbType) {
16
+ case 'postgres': {
17
+ debug('using postgres migration file for DigitalCredential')
18
+ const mig: CreateDigitalCredential1708525189001 = new CreateDigitalCredential1708525189001()
19
+ await mig.up(queryRunner)
20
+ debug('Postgres Migration statements for DigitalCredential executed')
21
+ return
22
+ }
23
+ case 'sqlite':
24
+ case 'expo':
25
+ case 'react-native': {
26
+ debug('using sqlite/react-native migration file for DigitalCredential')
27
+ const mig: CreateDigitalCredential1708525189002 = new CreateDigitalCredential1708525189002()
28
+ await mig.up(queryRunner)
29
+ debug('SQLite Migration statements for DigitalCredential executed')
30
+ return
31
+ }
32
+ default:
33
+ return Promise.reject(
34
+ `Migrations are currently only supported for sqlite, react-native, expo, and postgres for UniformCredential. 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 DigitalCredential tables')
41
+ const dbType: DatabaseType = queryRunner.connection.driver.options.type
42
+
43
+ switch (dbType) {
44
+ case 'postgres': {
45
+ debug('using postgres migration file for DigitalCredential')
46
+ const mig: CreateDigitalCredential1708525189001 = new CreateDigitalCredential1708525189001()
47
+ await mig.down(queryRunner)
48
+ debug('Postgres Migration statements for DigitalCredential reverted')
49
+ return
50
+ }
51
+ case 'sqlite':
52
+ case 'expo':
53
+ case 'react-native': {
54
+ debug('using sqlite/react-native migration file for DigitalCredential')
55
+ const mig: CreateDigitalCredential1708525189002 = new CreateDigitalCredential1708525189002()
56
+ await mig.down(queryRunner)
57
+ debug('SQLite Migration statements for DigitalCredential reverted')
58
+ return
59
+ }
60
+ default:
61
+ return Promise.reject(
62
+ `Migrations are currently only supported for sqlite, react-native, expo, and postgres for DigitalCredential. 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 { CreateIssuanceBranding1659463079429 } from './2-CreateIssuanceBranding'
3
3
  import { CreateContacts1690925872318 } from './3-CreateContacts'
4
4
  import { CreateStatusList1693866470000 } from './4-CreateStatusList'
5
5
  import { CreateAuditEvents1701635835330 } from './5-CreateAuditEvents'
6
+ import { CreateDigitalCredential1708525189000 } from './6-CreateDigitalCredential'
6
7
 
7
8
  /**
8
9
  * The migrations array that SHOULD be used when initializing a TypeORM database connection.
@@ -17,6 +18,7 @@ export const DataStoreContactMigrations = [CreateContacts1659463079429, CreateCo
17
18
  export const DataStoreIssuanceBrandingMigrations = [CreateIssuanceBranding1659463079429]
18
19
  export const DataStoreStatusListMigrations = [CreateStatusList1693866470000]
19
20
  export const DataStoreEventLoggerMigrations = [CreateAuditEvents1701635835330]
21
+ export const DataStoreDigitalCredentialMigrations = [CreateDigitalCredential1708525189000]
20
22
 
21
23
  // All migrations together
22
24
  export const DataStoreMigrations = [
@@ -24,4 +26,5 @@ export const DataStoreMigrations = [
24
26
  ...DataStoreIssuanceBrandingMigrations,
25
27
  ...DataStoreStatusListMigrations,
26
28
  ...DataStoreEventLoggerMigrations,
29
+ ...DataStoreDigitalCredentialMigrations,
27
30
  ]
@@ -4,4 +4,5 @@ export {
4
4
  DataStoreContactMigrations,
5
5
  DataStoreIssuanceBrandingMigrations,
6
6
  DataStoreStatusListMigrations,
7
+ DataStoreDigitalCredentialMigrations,
7
8
  } from './generic'
@@ -0,0 +1,44 @@
1
+ import { MigrationInterface, QueryRunner } from 'typeorm'
2
+
3
+ export class CreateDigitalCredential1708525189001 implements MigrationInterface {
4
+ name = 'CreateDigitalCredential1708525189001'
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(`CREATE TYPE "digital_document_type" AS ENUM('VC', 'VP', 'C', 'P')`)
8
+ await queryRunner.query(`CREATE TYPE "digital_credential_document_format" AS ENUM('JSON_LD', 'JWT', 'SD_JWT', 'MDOC')`)
9
+ await queryRunner.query(`CREATE TYPE "digital_credential_correlation_type" AS ENUM('DID')`)
10
+ await queryRunner.query(`CREATE TYPE "digital_credential_state_type" AS ENUM('REVOKED', 'VERIFIED', 'EXPIRED')`)
11
+
12
+ await queryRunner.query(`
13
+ CREATE TABLE "DigitalCredential" (
14
+ "id" uuid NOT NULL DEFAULT uuid_generate_v4(),
15
+ "document_type" "digital_document_type" NOT NULL,
16
+ "document_format" "digital_credential_document_format" NOT NULL,
17
+ "raw_document" text NOT NULL,
18
+ "uniform_document" text NOT NULL,
19
+ "hash" text NOT NULL UNIQUE,
20
+ "issuer_correlation_type" "digital_credential_correlation_type" NOT NULL,
21
+ "subject_correlation_type" "digital_credential_correlation_type",
22
+ "issuer_correlation_id" text NOT NULL,
23
+ "subject_correlation_id" text,
24
+ "verified_state" "digital_credential_state_type",
25
+ "tenant_id" text,
26
+ "created_at" TIMESTAMP NOT NULL DEFAULT now(),
27
+ "last_updated_at" TIMESTAMP NOT NULL DEFAULT now(),
28
+ "valid_from" DATE,
29
+ "valid_until" DATE,
30
+ "verified_at" DATE,
31
+ "revoked_at" DATE,
32
+ PRIMARY KEY ("id")
33
+ )
34
+ `)
35
+ }
36
+
37
+ public async down(queryRunner: QueryRunner): Promise<void> {
38
+ await queryRunner.query(`DROP TABLE "DigitalCredential"`)
39
+ await queryRunner.query(`DROP TYPE "digital_credential_state_type"`)
40
+ await queryRunner.query(`DROP TYPE "digital_credential_correlation_type"`)
41
+ await queryRunner.query(`DROP TYPE "digital_credential_document_format"`)
42
+ await queryRunner.query(`DROP TYPE "digital_document_type"`)
43
+ }
44
+ }
@@ -0,0 +1,34 @@
1
+ import { MigrationInterface, QueryRunner } from 'typeorm'
2
+
3
+ export class CreateDigitalCredential1708525189002 implements MigrationInterface {
4
+ name = 'CreateDigitalCredential1708525189002'
5
+
6
+ public async up(queryRunner: QueryRunner): Promise<void> {
7
+ await queryRunner.query(`
8
+ CREATE TABLE "DigitalCredential" (
9
+ "id" varchar PRIMARY KEY NOT NULL,
10
+ "document_type" varchar CHECK( "document_type" IN ('VC', 'VP', 'C', 'P') ) NOT NULL,
11
+ "document_format" varchar CHECK( "document_format" IN ('JSON_LD', 'JWT', 'SD_JWT', 'MDOC') ) NOT NULL,
12
+ "raw_document" text NOT NULL,
13
+ "uniform_document" text NOT NULL,
14
+ "hash" text NOT NULL UNIQUE,
15
+ "issuer_correlation_type" varchar CHECK( "issuer_correlation_type" IN ('DID') ) NOT NULL,
16
+ "subject_correlation_type" varchar CHECK( "subject_correlation_type" IN ('DID') ),
17
+ "issuer_correlation_id" text NOT NULL,
18
+ "subject_correlation_id" text,
19
+ "verified_state" varchar CHECK( "verified_state" IN ('REVOKED', 'VERIFIED', 'EXPIRED') ),
20
+ "tenant_id" text,
21
+ "created_at" datetime NOT NULL DEFAULT (datetime('now')),
22
+ "last_updated_at" datetime NOT NULL DEFAULT (datetime('now')),
23
+ "valid_from" datetime,
24
+ "valid_until" datetime,
25
+ "verified_at" datetime,
26
+ "revoked_at" datetime
27
+ )
28
+ `)
29
+ }
30
+
31
+ public async down(queryRunner: QueryRunner): Promise<void> {
32
+ await queryRunner.query(`DROP TABLE "DigitalCredential"`)
33
+ }
34
+ }
@@ -0,0 +1,37 @@
1
+ import { CredentialCorrelationType, CredentialStateType, DigitalCredential } from './digitalCredential'
2
+ import { Hasher } from '@sphereon/ssi-types'
3
+ import { FindOptionsOrder } from 'typeorm'
4
+ import { DigitalCredentialEntity } from '../../entities/digitalCredential/DigitalCredentialEntity'
5
+
6
+ export type GetCredentialArgs = { id: string } | { hash: string }
7
+
8
+ export type FindDigitalCredentialArgs = Array<Partial<DigitalCredential>>
9
+
10
+ export type GetCredentialsArgs = {
11
+ filter?: FindDigitalCredentialArgs
12
+ offset?: number
13
+ limit?: number
14
+ order?: string | FindOptionsOrder<DigitalCredentialEntity>
15
+ }
16
+
17
+ export type GetCredentialsResponse = {
18
+ data: Array<DigitalCredential>
19
+ total: number
20
+ }
21
+
22
+ export type AddCredentialArgs = {
23
+ rawDocument: string
24
+ issuerCorrelationType: CredentialCorrelationType
25
+ subjectCorrelationType?: CredentialCorrelationType
26
+ issuerCorrelationId: string
27
+ subjectCorrelationId?: string
28
+ tenantId?: string
29
+ state?: CredentialStateType
30
+ verifiedAt?: Date
31
+ revokedAt?: Date
32
+ opts?: { maxTimeSkewInMS?: number; hasher?: Hasher }
33
+ }
34
+
35
+ export type UpdateCredentialStateArgs = GetCredentialArgs & { verifiedState: CredentialStateType; verifiedAt?: Date; revokedAt?: Date }
36
+
37
+ export type RemoveCredentialArgs = GetCredentialArgs
@@ -0,0 +1,46 @@
1
+ export type NonPersistedDigitalCredential = Omit<DigitalCredential, 'id'>
2
+
3
+ export type DigitalCredential = {
4
+ id: string
5
+ documentType: DocumentType
6
+ documentFormat: CredentialDocumentFormat
7
+ rawDocument: string
8
+ uniformDocument: string
9
+ hash: string
10
+ issuerCorrelationType: CredentialCorrelationType
11
+ subjectCorrelationType?: CredentialCorrelationType
12
+ issuerCorrelationId: string
13
+ subjectCorrelationId?: string
14
+ verifiedState?: CredentialStateType
15
+ tenantId?: string
16
+ createdAt: Date
17
+ lastUpdatedAt: Date
18
+ validUntil?: Date
19
+ validFrom?: Date
20
+ verifiedAt?: Date
21
+ revokedAt?: Date
22
+ }
23
+
24
+ export enum DocumentType {
25
+ VC = 'VC',
26
+ VP = 'VP',
27
+ P = 'P',
28
+ C = 'C',
29
+ }
30
+
31
+ export enum CredentialDocumentFormat {
32
+ JSON_LD = 'JSON_LD',
33
+ JWT = 'JWT',
34
+ SD_JWT = 'SD_JWT',
35
+ MDOC = 'MDOC',
36
+ }
37
+
38
+ export enum CredentialCorrelationType {
39
+ DID = 'DID',
40
+ }
41
+
42
+ export enum CredentialStateType {
43
+ REVOKED = 'REVOKED',
44
+ VERIFIED = 'VERIFIED',
45
+ EXPIRED = 'EXPIRED',
46
+ }
@@ -0,0 +1,16 @@
1
+ import { BaseEntity, FindOptionsOrder } from 'typeorm'
2
+
3
+ export const parseAndValidateOrderOptions = <T extends BaseEntity>(order: string): FindOptionsOrder<T> => {
4
+ const orderPairs = order.split(',').map((pair) => pair.trim().split('.'))
5
+ const orderOptions: FindOptionsOrder<T> = {}
6
+
7
+ orderPairs.forEach(([field, direction]) => {
8
+ const dir = direction.toUpperCase()
9
+ if (dir !== 'ASC' && dir !== 'DESC') {
10
+ throw new Error(`Invalid order direction: '${direction}'. Must be 'asc' or 'desc'.`)
11
+ }
12
+ ;(orderOptions as any)[field] = dir
13
+ })
14
+
15
+ return orderOptions
16
+ }
@@ -0,0 +1,122 @@
1
+ import { AddCredentialArgs } from '../../types/digitalCredential/IAbstractDigitalCredentialStore'
2
+ import { DigitalCredentialEntity } from '../../entities/digitalCredential/DigitalCredentialEntity'
3
+ import {
4
+ CredentialMapper,
5
+ decodeSdJwtVc,
6
+ DocumentFormat,
7
+ IVerifiableCredential,
8
+ IVerifiablePresentation,
9
+ OriginalVerifiableCredential,
10
+ OriginalVerifiablePresentation,
11
+ SdJwtDecodedVerifiableCredentialPayload,
12
+ } from '@sphereon/ssi-types'
13
+ import {
14
+ CredentialDocumentFormat,
15
+ DocumentType,
16
+ DigitalCredential,
17
+ NonPersistedDigitalCredential,
18
+ } from '../../types/digitalCredential/digitalCredential'
19
+ import { computeEntryHash } from '@veramo/utils'
20
+
21
+ function determineDocumentType(raw: string): DocumentType {
22
+ const rawDocument = parseRawDocument(raw)
23
+ if (!rawDocument) {
24
+ throw new Error(`Couldn't parse the credential: ${raw}`)
25
+ }
26
+
27
+ const hasProof = CredentialMapper.hasProof(rawDocument)
28
+ const isCredential = CredentialMapper.isCredential(rawDocument)
29
+ const isPresentation = CredentialMapper.isPresentation(rawDocument)
30
+
31
+ if (isCredential) {
32
+ return hasProof ? DocumentType.VC : DocumentType.C
33
+ } else if (isPresentation) {
34
+ return hasProof ? DocumentType.VP : DocumentType.P
35
+ }
36
+ throw new Error(`Couldn't determine the type of the credential: ${raw}`)
37
+ }
38
+
39
+ function parseRawDocument(raw: string): OriginalVerifiableCredential | OriginalVerifiablePresentation {
40
+ if (CredentialMapper.isJwtEncoded(raw) || CredentialMapper.isSdJwtEncoded(raw)) {
41
+ return raw
42
+ }
43
+ try {
44
+ return JSON.parse(raw)
45
+ } catch (e) {
46
+ throw new Error(`Can't parse the raw credential: ${raw}`)
47
+ }
48
+ }
49
+
50
+ function determineCredentialDocumentFormat(documentFormat: DocumentFormat): CredentialDocumentFormat {
51
+ switch (documentFormat) {
52
+ case DocumentFormat.JSONLD:
53
+ return CredentialDocumentFormat.JSON_LD
54
+ case DocumentFormat.JWT:
55
+ return CredentialDocumentFormat.JWT
56
+ case DocumentFormat.SD_JWT_VC:
57
+ return CredentialDocumentFormat.SD_JWT
58
+ default:
59
+ throw new Error(`Not supported document format: ${documentFormat}`)
60
+ }
61
+ }
62
+
63
+ function getValidUntil(uniformDocument: IVerifiableCredential | IVerifiablePresentation | SdJwtDecodedVerifiableCredentialPayload): Date | undefined {
64
+ if ('expirationDate' in uniformDocument) {
65
+ return new Date(uniformDocument.expirationDate)
66
+ } else if ('validUntil' in uniformDocument) {
67
+ return new Date(uniformDocument.validUntil)
68
+ } else if ('exp' in uniformDocument) {
69
+ return new Date(uniformDocument.exp)
70
+ }
71
+ return undefined
72
+ }
73
+
74
+ function getValidFrom(uniformDocument: IVerifiableCredential | IVerifiablePresentation | SdJwtDecodedVerifiableCredentialPayload): Date | undefined {
75
+ if ('issuanceDate' in uniformDocument) {
76
+ return new Date(uniformDocument.issuanceDate)
77
+ } else if ('validFrom' in uniformDocument) {
78
+ return new Date(uniformDocument['validFrom'])
79
+ } else if ('nbf' in uniformDocument) {
80
+ return new Date(uniformDocument['nbf'] * 1000)
81
+ } else if ('iat' in uniformDocument) {
82
+ return new Date(uniformDocument['iat'] * 1000)
83
+ }
84
+ return undefined
85
+ }
86
+
87
+ export const nonPersistedDigitalCredentialEntityFromAddArgs = (addCredentialArgs: AddCredentialArgs): NonPersistedDigitalCredential => {
88
+ const documentType: DocumentType = determineDocumentType(addCredentialArgs.rawDocument)
89
+ const documentFormat: DocumentFormat = CredentialMapper.detectDocumentType(addCredentialArgs.rawDocument)
90
+ if (documentFormat === DocumentFormat.SD_JWT_VC && !addCredentialArgs.opts?.hasher) {
91
+ throw new Error('No hasher function is provided for SD_JWT credential.')
92
+ }
93
+ const uniformDocument =
94
+ documentFormat === DocumentFormat.SD_JWT_VC
95
+ ? decodeSdJwtVc(addCredentialArgs.rawDocument, addCredentialArgs.opts!.hasher!).decodedPayload
96
+ : documentType === DocumentType.VC || documentType === DocumentType.C
97
+ ? CredentialMapper.toUniformCredential(addCredentialArgs.rawDocument)
98
+ : CredentialMapper.toUniformPresentation(addCredentialArgs.rawDocument)
99
+ const validFrom: Date | undefined = getValidFrom(uniformDocument)
100
+ const validUntil: Date | undefined = getValidUntil(uniformDocument)
101
+ return {
102
+ ...addCredentialArgs,
103
+ documentType,
104
+ documentFormat: determineCredentialDocumentFormat(documentFormat),
105
+ createdAt: new Date(),
106
+ hash: computeEntryHash(addCredentialArgs.rawDocument),
107
+ uniformDocument: JSON.stringify(uniformDocument),
108
+ validFrom,
109
+ validUntil,
110
+ lastUpdatedAt: new Date(),
111
+ }
112
+ }
113
+
114
+ export const digitalCredentialFrom = (credentialEntity: DigitalCredentialEntity): DigitalCredential => {
115
+ return {
116
+ ...credentialEntity,
117
+ }
118
+ }
119
+
120
+ export const digitalCredentialsFrom = (credentialEntities: Array<DigitalCredentialEntity>): DigitalCredential[] => {
121
+ return credentialEntities.map((credentialEntity) => digitalCredentialFrom(credentialEntity))
122
+ }