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

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.115+0fab323a",
3
+ "version": "0.36.1-next.129+2a4f4386",
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.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",
31
+ "@sphereon/ssi-sdk-ext.did-utils": "0.36.1-next.129+2a4f4386",
32
+ "@sphereon/ssi-sdk-ext.identifier-resolution": "0.36.1-next.129+2a4f4386",
33
+ "@sphereon/ssi-sdk.agent-config": "0.36.1-next.129+2a4f4386",
34
+ "@sphereon/ssi-sdk.core": "0.36.1-next.129+2a4f4386",
35
+ "@sphereon/ssi-sdk.data-store-types": "0.36.1-next.129+2a4f4386",
36
+ "@sphereon/ssi-types": "0.36.1-next.129+2a4f4386",
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": "0fab323abf92edba332557800ab79493e3681e1f"
69
+ "gitHead": "2a4f43863a1544f53c63485daee40bcb2abbfa81"
70
70
  }
@@ -823,7 +823,7 @@ describe('Database entities tests', (): void => {
823
823
  }
824
824
 
825
825
  const issuerLocaleBrandingEntity: IssuerLocaleBrandingEntity = issuerLocaleBrandingEntityFrom(localeBranding)
826
- const fromDb: IssuerLocaleBrandingEntity = await dbConnection.getRepository(CredentialLocaleBrandingEntity).save(issuerLocaleBrandingEntity)
826
+ const fromDb: IssuerLocaleBrandingEntity = await dbConnection.getRepository(IssuerLocaleBrandingEntity).save(issuerLocaleBrandingEntity)
827
827
 
828
828
  expect(fromDb).toBeDefined()
829
829
  expect(fromDb?.alias).toEqual(localeBranding.alias)
@@ -1884,4 +1884,314 @@ describe('Issuance branding store tests', (): void => {
1884
1884
  expect(result?.localeBranding[0].background!.image!.alt).toBeUndefined()
1885
1885
  expect(result?.localeBranding[0].text!.color).toBeUndefined()
1886
1886
  })
1887
+
1888
+ // State-related tests
1889
+
1890
+ it('should populate state field when adding credential branding', async (): Promise<void> => {
1891
+ const credentialBranding: IBasicCredentialBranding = {
1892
+ issuerCorrelationId: 'issuerCorrelationId',
1893
+ vcHash: 'vcHash',
1894
+ localeBranding: [
1895
+ {
1896
+ alias: 'credentialTypeAlias',
1897
+ locale: 'en-US',
1898
+ },
1899
+ ],
1900
+ }
1901
+
1902
+ const result: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding)
1903
+
1904
+ expect(result).toBeDefined()
1905
+ expect(result.state).toBeDefined()
1906
+ expect(typeof result.state).toBe('string')
1907
+ expect(result.state.length).toBeGreaterThan(0)
1908
+ expect(result.localeBranding[0].state).toBeDefined()
1909
+ expect(typeof result.localeBranding[0].state).toBe('string')
1910
+ expect(result.localeBranding[0].state.length).toBeGreaterThan(0)
1911
+ })
1912
+
1913
+ it('should generate different states for different credential brandings', async (): Promise<void> => {
1914
+ const credentialBranding1: IBasicCredentialBranding = {
1915
+ issuerCorrelationId: 'issuerCorrelationId1',
1916
+ vcHash: 'vcHash1',
1917
+ localeBranding: [
1918
+ {
1919
+ alias: 'credentialTypeAlias1',
1920
+ locale: 'en-US',
1921
+ },
1922
+ ],
1923
+ }
1924
+
1925
+ const credentialBranding2: IBasicCredentialBranding = {
1926
+ issuerCorrelationId: 'issuerCorrelationId2',
1927
+ vcHash: 'vcHash2',
1928
+ localeBranding: [
1929
+ {
1930
+ alias: 'credentialTypeAlias2',
1931
+ locale: 'en-US',
1932
+ },
1933
+ ],
1934
+ }
1935
+
1936
+ const result1: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding1)
1937
+ const result2: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding2)
1938
+
1939
+ expect(result1.state).toBeDefined()
1940
+ expect(result2.state).toBeDefined()
1941
+ expect(result1.state).not.toEqual(result2.state)
1942
+ expect(result1.localeBranding[0].state).not.toEqual(result2.localeBranding[0].state)
1943
+ })
1944
+
1945
+ it('should keep same state when credential branding content does not change', async (): Promise<void> => {
1946
+ const credentialBranding: IBasicCredentialBranding = {
1947
+ issuerCorrelationId: 'issuerCorrelationId',
1948
+ vcHash: 'vcHash',
1949
+ localeBranding: [
1950
+ {
1951
+ alias: 'credentialTypeAlias',
1952
+ locale: 'en-US',
1953
+ description: 'Description',
1954
+ },
1955
+ ],
1956
+ }
1957
+
1958
+ const original: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding)
1959
+ const originalState: string = original.state
1960
+ const originalLocaleState: string = original.localeBranding[0].state
1961
+
1962
+ const retrieved: Array<ICredentialBranding> = await issuanceBrandingStore.getCredentialBranding({ filter: [{ id: original.id }] })
1963
+
1964
+ expect(retrieved).toBeDefined()
1965
+ expect(retrieved.length).toEqual(1)
1966
+ expect(retrieved[0].state).toEqual(originalState)
1967
+ expect(retrieved[0].localeBranding[0].state).toEqual(originalLocaleState)
1968
+ })
1969
+
1970
+ it('should filter credential branding by knownStates when all states match', async (): Promise<void> => {
1971
+ const credentialBranding: IBasicCredentialBranding = {
1972
+ issuerCorrelationId: 'issuerCorrelationId',
1973
+ vcHash: 'vcHash',
1974
+ localeBranding: [
1975
+ {
1976
+ alias: 'credentialTypeAlias',
1977
+ locale: 'en-US',
1978
+ },
1979
+ ],
1980
+ }
1981
+
1982
+ const saved: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding)
1983
+ const knownStates: Record<string, string> = {
1984
+ [saved.id]: saved.state,
1985
+ }
1986
+
1987
+ const result: Array<ICredentialBranding> = await issuanceBrandingStore.getCredentialBranding({ knownStates })
1988
+
1989
+ expect(result.length).toEqual(0)
1990
+ })
1991
+
1992
+ it('should return credential branding when knownStates differs', async (): Promise<void> => {
1993
+ const credentialBranding: IBasicCredentialBranding = {
1994
+ issuerCorrelationId: 'issuerCorrelationId',
1995
+ vcHash: 'vcHash',
1996
+ localeBranding: [
1997
+ {
1998
+ alias: 'credentialTypeAlias',
1999
+ locale: 'en-US',
2000
+ },
2001
+ ],
2002
+ }
2003
+
2004
+ const saved: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding)
2005
+ const knownStates: Record<string, string> = {
2006
+ [saved.id]: 'differentState',
2007
+ }
2008
+
2009
+ const result: Array<ICredentialBranding> = await issuanceBrandingStore.getCredentialBranding({ knownStates })
2010
+
2011
+ expect(result.length).toEqual(1)
2012
+ expect(result[0].id).toEqual(saved.id)
2013
+ })
2014
+
2015
+ it('should return credential branding when not in knownStates map', async (): Promise<void> => {
2016
+ const credentialBranding: IBasicCredentialBranding = {
2017
+ issuerCorrelationId: 'issuerCorrelationId',
2018
+ vcHash: 'vcHash',
2019
+ localeBranding: [
2020
+ {
2021
+ alias: 'credentialTypeAlias',
2022
+ locale: 'en-US',
2023
+ },
2024
+ ],
2025
+ }
2026
+
2027
+ const saved: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding)
2028
+ const knownStates: Record<string, string> = {}
2029
+
2030
+ const result: Array<ICredentialBranding> = await issuanceBrandingStore.getCredentialBranding({ knownStates })
2031
+
2032
+ expect(result.length).toEqual(1)
2033
+ expect(result[0].id).toEqual(saved.id)
2034
+ })
2035
+
2036
+ it('should filter multiple credential brandings correctly with knownStates', async (): Promise<void> => {
2037
+ const credentialBranding1: IBasicCredentialBranding = {
2038
+ issuerCorrelationId: 'issuerCorrelationId1',
2039
+ vcHash: 'vcHash1',
2040
+ localeBranding: [
2041
+ {
2042
+ alias: 'credentialTypeAlias1',
2043
+ locale: 'en-US',
2044
+ },
2045
+ ],
2046
+ }
2047
+
2048
+ const credentialBranding2: IBasicCredentialBranding = {
2049
+ issuerCorrelationId: 'issuerCorrelationId2',
2050
+ vcHash: 'vcHash2',
2051
+ localeBranding: [
2052
+ {
2053
+ alias: 'credentialTypeAlias2',
2054
+ locale: 'en-US',
2055
+ },
2056
+ ],
2057
+ }
2058
+
2059
+ const credentialBranding3: IBasicCredentialBranding = {
2060
+ issuerCorrelationId: 'issuerCorrelationId3',
2061
+ vcHash: 'vcHash3',
2062
+ localeBranding: [
2063
+ {
2064
+ alias: 'credentialTypeAlias3',
2065
+ locale: 'en-US',
2066
+ },
2067
+ ],
2068
+ }
2069
+
2070
+ const saved1: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding1)
2071
+ const saved2: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding2)
2072
+ const saved3: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding3)
2073
+
2074
+ const knownStates: Record<string, string> = {
2075
+ [saved1.id]: saved1.state,
2076
+ [saved2.id]: 'differentState',
2077
+ }
2078
+
2079
+ const result: Array<ICredentialBranding> = await issuanceBrandingStore.getCredentialBranding({ knownStates })
2080
+
2081
+ expect(result.length).toEqual(2)
2082
+ const resultIds: Array<string> = result.map((r: ICredentialBranding) => r.id)
2083
+ expect(resultIds).toContain(saved2.id)
2084
+ expect(resultIds).toContain(saved3.id)
2085
+ expect(resultIds).not.toContain(saved1.id)
2086
+ })
2087
+
2088
+ it('should add new locale branding with state field populated', async (): Promise<void> => {
2089
+ const credentialBranding: IBasicCredentialBranding = {
2090
+ issuerCorrelationId: 'issuerCorrelationId',
2091
+ vcHash: 'vcHash',
2092
+ localeBranding: [
2093
+ {
2094
+ alias: 'credentialTypeAlias',
2095
+ locale: 'en-US',
2096
+ },
2097
+ ],
2098
+ }
2099
+
2100
+ const saved: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding)
2101
+
2102
+ const addLocaleBrandingArgs: IAddCredentialLocaleBrandingArgs = {
2103
+ credentialBrandingId: saved.id,
2104
+ localeBranding: [
2105
+ {
2106
+ alias: 'credentialTypeAlias',
2107
+ locale: 'en-GB',
2108
+ },
2109
+ ],
2110
+ }
2111
+
2112
+ const result: ICredentialBranding = await issuanceBrandingStore.addCredentialLocaleBranding(addLocaleBrandingArgs)
2113
+
2114
+ expect(result.localeBranding.length).toEqual(2)
2115
+ expect(result.localeBranding[0].state).toBeDefined()
2116
+ expect(result.localeBranding[1].state).toBeDefined()
2117
+ expect(result.localeBranding[0].state).not.toEqual(result.localeBranding[1].state)
2118
+ })
2119
+
2120
+ it('should compute state based on locale branding claims', async (): Promise<void> => {
2121
+ const credentialBranding1: IBasicCredentialBranding = {
2122
+ issuerCorrelationId: 'issuerCorrelationId',
2123
+ vcHash: 'vcHash1',
2124
+ localeBranding: [
2125
+ {
2126
+ alias: 'credentialTypeAlias',
2127
+ locale: 'en-US',
2128
+ claims: [
2129
+ {
2130
+ key: 'name',
2131
+ name: 'Full Name',
2132
+ },
2133
+ ],
2134
+ },
2135
+ ],
2136
+ }
2137
+
2138
+ const credentialBranding2: IBasicCredentialBranding = {
2139
+ issuerCorrelationId: 'issuerCorrelationId',
2140
+ vcHash: 'vcHash2',
2141
+ localeBranding: [
2142
+ {
2143
+ alias: 'credentialTypeAlias',
2144
+ locale: 'en-US',
2145
+ claims: [
2146
+ {
2147
+ key: 'email',
2148
+ name: 'Email Address',
2149
+ },
2150
+ ],
2151
+ },
2152
+ ],
2153
+ }
2154
+
2155
+ const saved1: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding1)
2156
+ const saved2: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding2)
2157
+
2158
+ expect(saved1.localeBranding[0].state).toBeDefined()
2159
+ expect(saved2.localeBranding[0].state).toBeDefined()
2160
+ expect(saved1.localeBranding[0].state).not.toEqual(saved2.localeBranding[0].state)
2161
+ })
2162
+
2163
+ it('should compute deterministic state for same content', async (): Promise<void> => {
2164
+ const credentialBranding1: IBasicCredentialBranding = {
2165
+ issuerCorrelationId: 'issuerCorrelationId',
2166
+ vcHash: 'vcHash',
2167
+ localeBranding: [
2168
+ {
2169
+ alias: 'credentialTypeAlias',
2170
+ locale: 'en-US',
2171
+ description: 'Test description',
2172
+ },
2173
+ ],
2174
+ }
2175
+
2176
+ const saved1: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding1)
2177
+
2178
+ await issuanceBrandingStore.removeCredentialBranding({ filter: [{ id: saved1.id }] })
2179
+
2180
+ const credentialBranding2: IBasicCredentialBranding = {
2181
+ issuerCorrelationId: 'issuerCorrelationId',
2182
+ vcHash: 'vcHash',
2183
+ localeBranding: [
2184
+ {
2185
+ alias: 'credentialTypeAlias',
2186
+ locale: 'en-US',
2187
+ description: 'Test description',
2188
+ },
2189
+ ],
2190
+ }
2191
+
2192
+ const saved2: ICredentialBranding = await issuanceBrandingStore.addCredentialBranding(credentialBranding2)
2193
+
2194
+ expect(saved1.state).toEqual(saved2.state)
2195
+ expect(saved1.localeBranding[0].state).toEqual(saved2.localeBranding[0].state)
2196
+ })
1887
2197
  })
@@ -109,9 +109,21 @@ export class ContactStore extends AbstractContactStore {
109
109
  const initialResult = await partyRepository.find({ select: ['id'], where: filterConditions })
110
110
 
111
111
  // Fetch the complete entities based on the initial result IDs
112
- const result = await partyRepository.find({ where: { id: In(initialResult.map((party) => party.id)) } })
112
+ const result = await partyRepository.find({
113
+ where: { id: In(initialResult.map((party) => party.id)) },
114
+ relations: ['contact'], // Explicit load prevents eager loading issues
115
+ })
113
116
  debug(`getParties() resulted in ${result.length} parties`)
114
- return result.map(partyFrom)
117
+ return result
118
+ .filter((party) => {
119
+ // Do not crash fetching the entire contacts list over one missing contact relation
120
+ if (!party.contact) {
121
+ console.warn(`party ${party.id} does not have an associated contact`)
122
+ return false
123
+ }
124
+ return true
125
+ })
126
+ .map(partyFrom)
115
127
  }
116
128
 
117
129
  addParty = async (args: AddPartyArgs): Promise<Party> => {
@@ -12,7 +12,8 @@ import {
12
12
  PrimaryGeneratedColumn,
13
13
  UpdateDateColumn,
14
14
  } from 'typeorm'
15
- import { CredentialLocaleBrandingEntity } from './CredentialLocaleBrandingEntity'
15
+ import { CredentialLocaleBrandingEntity, computeCredentialLocaleBrandingState } from './CredentialLocaleBrandingEntity'
16
+ import { computeCompactHash } from '../../utils/issuanceBranding/HashUtils'
16
17
 
17
18
  @Entity('CredentialBranding')
18
19
  @Index('IDX_CredentialBrandingEntity_vcHash', ['vcHash'])
@@ -29,6 +30,9 @@ export class CredentialBrandingEntity extends BaseEntity {
29
30
  @IsNotEmpty({ message: 'Blank issuerCorrelationIds are not allowed' })
30
31
  issuerCorrelationId!: string
31
32
 
33
+ @Column('varchar', { name: 'state', length: 255, nullable: false })
34
+ state!: string
35
+
32
36
  @OneToMany(
33
37
  () => CredentialLocaleBrandingEntity,
34
38
  (credentialLocaleBrandingEntity: CredentialLocaleBrandingEntity) => credentialLocaleBrandingEntity.credentialBranding,
@@ -55,6 +59,14 @@ export class CredentialBrandingEntity extends BaseEntity {
55
59
  this.lastUpdatedAt = new Date()
56
60
  }
57
61
 
62
+ @BeforeInsert()
63
+ @BeforeUpdate()
64
+ setState(): void {
65
+ if (this.localeBranding && Array.isArray(this.localeBranding)) {
66
+ this.state = this.computeState()
67
+ }
68
+ }
69
+
58
70
  @BeforeInsert()
59
71
  @BeforeUpdate()
60
72
  async validate(): Promise<undefined> {
@@ -64,4 +76,35 @@ export class CredentialBrandingEntity extends BaseEntity {
64
76
  }
65
77
  return
66
78
  }
79
+
80
+ private computeState(): string {
81
+ const localeStates: Array<{ locale: string; alias: string; id: string; state: string }> = (this.localeBranding ?? []).map(
82
+ (localeBranding: CredentialLocaleBrandingEntity) => ({
83
+ locale: localeBranding.locale ?? '',
84
+ alias: localeBranding.alias ?? '',
85
+ id: localeBranding.id ?? '',
86
+ state: computeCredentialLocaleBrandingState(localeBranding),
87
+ }),
88
+ )
89
+
90
+ localeStates.sort((first, second) => {
91
+ const localeCompare: number = first.locale.localeCompare(second.locale)
92
+ if (localeCompare !== 0) {
93
+ return localeCompare
94
+ }
95
+ const aliasCompare: number = first.alias.localeCompare(second.alias)
96
+ if (aliasCompare !== 0) {
97
+ return aliasCompare
98
+ }
99
+ return first.id.localeCompare(second.id)
100
+ })
101
+
102
+ const payload = {
103
+ issuerCorrelationId: this.issuerCorrelationId,
104
+ vcHash: this.vcHash,
105
+ localeBranding: localeStates.map((entry) => entry.state),
106
+ }
107
+
108
+ return computeCompactHash(JSON.stringify(payload))
109
+ }
67
110
  }
@@ -1,4 +1,5 @@
1
- import { ChildEntity, Column, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm'
1
+ import { BeforeInsert, BeforeUpdate, ChildEntity, Column, Index, JoinColumn, ManyToOne, OneToMany } from 'typeorm'
2
+ import { computeCompactHash } from '../../utils/issuanceBranding/HashUtils'
2
3
  import { BaseLocaleBrandingEntity } from './BaseLocaleBrandingEntity'
3
4
  import { CredentialBrandingEntity } from './CredentialBrandingEntity'
4
5
  import { CredentialClaimsEntity } from './CredentialClaimsEntity'
@@ -23,4 +24,66 @@ export class CredentialLocaleBrandingEntity extends BaseLocaleBrandingEntity {
23
24
 
24
25
  @Column('uuid', { name: 'credentialBrandingId', nullable: false })
25
26
  credentialBrandingId!: string
27
+
28
+ @Column('varchar', { name: 'state', length: 255, nullable: false })
29
+ state!: string
30
+
31
+ @BeforeInsert()
32
+ @BeforeUpdate()
33
+ setState(): void {
34
+ this.state = computeCredentialLocaleBrandingState(this)
35
+ }
36
+ }
37
+
38
+ export const computeCredentialLocaleBrandingState = (localeBranding: CredentialLocaleBrandingEntity): string => {
39
+ const sortedClaims: Array<{ key: string; name: string }> = (localeBranding.claims ?? [])
40
+ .map((claim: CredentialClaimsEntity) => ({ key: claim.key, name: claim.name }))
41
+ .sort((first: { key: string }, second: { key: string }) => first.key.localeCompare(second.key))
42
+
43
+ const payload = {
44
+ alias: localeBranding.alias ?? null,
45
+ locale: localeBranding.locale ?? null,
46
+ description: localeBranding.description ?? null,
47
+ logo: localeBranding.logo
48
+ ? {
49
+ uri: localeBranding.logo.uri ?? null,
50
+ dataUri: localeBranding.logo.dataUri ?? null,
51
+ mediaType: localeBranding.logo.mediaType ?? null,
52
+ alt: localeBranding.logo.alt ?? null,
53
+ dimensions: localeBranding.logo.dimensions
54
+ ? {
55
+ width: localeBranding.logo.dimensions.width,
56
+ height: localeBranding.logo.dimensions.height,
57
+ }
58
+ : null,
59
+ }
60
+ : null,
61
+ background: localeBranding.background
62
+ ? {
63
+ color: localeBranding.background.color ?? null,
64
+ image: localeBranding.background.image
65
+ ? {
66
+ uri: localeBranding.background.image.uri ?? null,
67
+ dataUri: localeBranding.background.image.dataUri ?? null,
68
+ mediaType: localeBranding.background.image.mediaType ?? null,
69
+ alt: localeBranding.background.image.alt ?? null,
70
+ dimensions: localeBranding.background.image.dimensions
71
+ ? {
72
+ width: localeBranding.background.image.dimensions.width,
73
+ height: localeBranding.background.image.dimensions.height,
74
+ }
75
+ : null,
76
+ }
77
+ : null,
78
+ }
79
+ : null,
80
+ text: localeBranding.text
81
+ ? {
82
+ color: localeBranding.text.color ?? null,
83
+ }
84
+ : null,
85
+ claims: sortedClaims,
86
+ }
87
+
88
+ return computeCompactHash(JSON.stringify(payload))
26
89
  }
@@ -1,5 +1,6 @@
1
1
  import { Validate } from 'class-validator'
2
- import { ChildEntity, Column, Index, JoinColumn, ManyToOne } from 'typeorm'
2
+ import { BeforeInsert, BeforeUpdate, ChildEntity, Column, Index, JoinColumn, ManyToOne } from 'typeorm'
3
+ import { computeCompactHash } from '../../utils/issuanceBranding/HashUtils'
3
4
  import { IsNonEmptyStringConstraint } from '../validators'
4
5
  import { BaseLocaleBrandingEntity } from './BaseLocaleBrandingEntity'
5
6
  import { IssuerBrandingEntity } from './IssuerBrandingEntity'
@@ -30,4 +31,65 @@ export class IssuerLocaleBrandingEntity extends BaseLocaleBrandingEntity {
30
31
 
31
32
  @Column('text', { name: 'issuerBrandingId', nullable: false })
32
33
  issuerBrandingId!: string
34
+
35
+ @Column('varchar', { name: 'state', length: 255, nullable: false })
36
+ state!: string
37
+
38
+ @BeforeInsert()
39
+ @BeforeUpdate()
40
+ setState(): void {
41
+ this.state = this.computeState()
42
+ }
43
+
44
+ private computeState(): string {
45
+ const payload = {
46
+ alias: this.alias ?? null,
47
+ locale: this.locale ?? null,
48
+ description: this.description ?? null,
49
+ clientUri: this.clientUri ?? null,
50
+ tosUri: this.tosUri ?? null,
51
+ policyUri: this.policyUri ?? null,
52
+ contacts: this.contacts ?? null,
53
+ logo: this.logo
54
+ ? {
55
+ uri: this.logo.uri ?? null,
56
+ dataUri: this.logo.dataUri ?? null,
57
+ mediaType: this.logo.mediaType ?? null,
58
+ alt: this.logo.alt ?? null,
59
+ dimensions: this.logo.dimensions
60
+ ? {
61
+ width: this.logo.dimensions.width,
62
+ height: this.logo.dimensions.height,
63
+ }
64
+ : null,
65
+ }
66
+ : null,
67
+ background: this.background
68
+ ? {
69
+ color: this.background.color ?? null,
70
+ image: this.background.image
71
+ ? {
72
+ uri: this.background.image.uri ?? null,
73
+ dataUri: this.background.image.dataUri ?? null,
74
+ mediaType: this.background.image.mediaType ?? null,
75
+ alt: this.background.image.alt ?? null,
76
+ dimensions: this.background.image.dimensions
77
+ ? {
78
+ width: this.background.image.dimensions.width,
79
+ height: this.background.image.dimensions.height,
80
+ }
81
+ : null,
82
+ }
83
+ : null,
84
+ }
85
+ : null,
86
+ text: this.text
87
+ ? {
88
+ color: this.text.color ?? null,
89
+ }
90
+ : null,
91
+ }
92
+
93
+ return computeCompactHash(JSON.stringify(payload))
94
+ }
33
95
  }