Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(data-store): support credentials with undefined issuanceDate #1274

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 19 additions & 1 deletion __tests__/initial.migration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import {
} from '../packages/data-store/src'
import { KeyManager } from '../packages/key-manager/src'
import { DIDManager } from '../packages/did-manager/src'
import { CredentialPlugin } from "../packages/credential-w3c/src";
import { FakeDidProvider, FakeDidResolver } from '../packages/test-utils/src'

import { DataSource, DataSourceOptions } from 'typeorm'
Expand Down Expand Up @@ -135,12 +136,13 @@ describe('database initial migration tests', () => {
new DataStore(dbConnection),
new DataStoreORM(dbConnection),
new DIDComm(),
new CredentialPlugin()
],
})
return true
})
afterAll(async () => {
await (await dbConnection).close()
await (await dbConnection).destroy()
fs.unlinkSync(databaseFile)
})

Expand Down Expand Up @@ -260,6 +262,22 @@ describe('database initial migration tests', () => {
const msg = await agent.unpackDIDCommMessage(packed)
expect(msg.message.body).toEqual({ hello: 'world' })
})

it('saves credential with undefined issuanceDate', async () => {
const issuer = await agent.didManagerCreate({ provider: 'did:key' })
const cred = await agent.createVerifiableCredential({
credential: {
issuer: issuer.did,
credentialSubject: {
name: 'Alice',
},
issuanceDate: undefined,
},
proofFormat: 'jwt',
})
const stored = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential: cred })
expect(stored).toBeDefined()
})
})
}
})
2 changes: 1 addition & 1 deletion __tests__/localAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {
const tearDown = async (): Promise<boolean> => {
try {
await (await dbConnection).dropDatabase()
await (await dbConnection).close()
await (await dbConnection).destroy()
} catch (e) {
// nop
}
Expand Down
1 change: 1 addition & 0 deletions __tests__/localMemoryStoreAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ const setup = async (options?: IAgentOptions): Promise<boolean> => {

const tearDown = async (): Promise<boolean> => {
try {
await dbConnection?.dropDatabase()
await dbConnection?.destroy()
} catch (e) {
// nop
Expand Down
2 changes: 1 addition & 1 deletion __tests__/restAgent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ const tearDown = async (): Promise<boolean> => {
await new Promise((resolve, reject) => restServer.close(resolve))
try {
await (await dbConnection).dropDatabase()
await (await dbConnection).close()
await (await dbConnection).destroy()
} catch (e) {
// nop
}
Expand Down
138 changes: 109 additions & 29 deletions __tests__/shared/saveClaims.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import {
TAgent,
} from '../../packages/core-types/src'
import { ISelectiveDisclosure } from '../../packages/selective-disclosure/src'
import { beforeAll } from '@jest/globals'

type ConfiguredAgent = TAgent<
IDIDManager & ICredentialIssuer & IDataStoreORM & IDataStore & IMessageHandler & ISelectiveDisclosure
Expand All @@ -28,13 +29,9 @@ export default (testContext: {
beforeAll(async () => {
await testContext.setup()
agent = testContext.getAgent()
})
afterAll(testContext.tearDown)

it('should create identifier', async () => {
identifier = await agent.didManagerCreate({ kms: 'local' })
expect(identifier).toHaveProperty('did')
})
afterAll(testContext.tearDown)

it('should create verifiable credentials', async () => {
// Looping these in a map/forEach throws SQL UNIQUE CONSTRAINT errors
Expand Down Expand Up @@ -123,59 +120,56 @@ export default (testContext: {
{ column: 'value', value: ['math', 'art'] },
],
order: [{ column: 'issuanceDate', direction: 'DESC' }],
take: 1
take: 1,
})
expect(credentials).toHaveLength(1)
})
it('should be able to limit credentials when searching and sorting', async () => {
const credentials = await agent.dataStoreORMGetVerifiableCredentials({
where: [
{ column: 'type', value: ['VerifiableCredential'] },
],
where: [{ column: 'type', value: ['VerifiableCredential'] }],
order: [{ column: 'issuanceDate', direction: 'DESC' }],
take: 1,
skip: 1
skip: 1,
})
expect(credentials).toHaveLength(1)
})

it('should be able to limit credentials when sorting', async () => {
const credentialsAllDesc = await agent.dataStoreORMGetVerifiableCredentials({
order: [{ column: 'issuanceDate', direction: 'DESC' }]
order: [{ column: 'issuanceDate', direction: 'DESC' }],
})

const credentialsAllAsc = await agent.dataStoreORMGetVerifiableCredentials({
order: [{ column: 'issuanceDate', direction: 'ASC' }]
order: [{ column: 'issuanceDate', direction: 'ASC' }],
})

const credentialsIdAllDesc = await agent.dataStoreORMGetVerifiableCredentials({
order: [{ column: 'id', direction: 'DESC' }]
order: [{ column: 'id', direction: 'DESC' }],
})

const credentialsIdAllAsc = await agent.dataStoreORMGetVerifiableCredentials({
order: [{ column: 'id', direction: 'ASC' }]
order: [{ column: 'id', direction: 'ASC' }],
})


const credentials1 = await agent.dataStoreORMGetVerifiableCredentials({
order: [{ column: 'issuanceDate', direction: 'DESC' }],
take: 1,
skip: 0
skip: 0,
})
const credentials2 = await agent.dataStoreORMGetVerifiableCredentials({
order: [{ column: 'issuanceDate', direction: 'DESC' }],
take: 1,
skip: 1
skip: 1,
})
const credentials3 = await agent.dataStoreORMGetVerifiableCredentials({
order: [{ column: 'issuanceDate', direction: 'ASC' }],
take: 2,
skip: 0
skip: 0,
})
const credentials4 = await agent.dataStoreORMGetVerifiableCredentials({
order: [{ column: 'issuanceDate', direction: 'ASC' }],
take: 2,
skip: 1
skip: 1,
})

expect(credentialsAllDesc).toHaveLength(3)
Expand All @@ -190,18 +184,30 @@ export default (testContext: {
expect(credentialsIdAllDesc[1].verifiableCredential.id).toEqual('b')
expect(credentialsIdAllDesc[2].verifiableCredential.id).toEqual('a')

expect(credentialsAllDesc[0].verifiableCredential.issuanceDate).toEqual(credentials1[0].verifiableCredential.issuanceDate)
expect(credentialsAllDesc[1].verifiableCredential.issuanceDate).toEqual(credentials2[0].verifiableCredential.issuanceDate)

expect(credentialsAllDesc[0].verifiableCredential.issuanceDate).toEqual(credentialsAllAsc[2].verifiableCredential.issuanceDate)
expect(credentialsAllDesc[1].verifiableCredential.issuanceDate).toEqual(credentialsAllAsc[1].verifiableCredential.issuanceDate)
expect(credentialsAllDesc[2].verifiableCredential.issuanceDate).toEqual(credentialsAllAsc[0].verifiableCredential.issuanceDate)
expect(credentialsAllDesc[0].verifiableCredential.issuanceDate).toEqual(
credentials1[0].verifiableCredential.issuanceDate,
)
expect(credentialsAllDesc[1].verifiableCredential.issuanceDate).toEqual(
credentials2[0].verifiableCredential.issuanceDate,
)

expect(credentialsAllDesc[0].verifiableCredential.issuanceDate).toEqual(
credentialsAllAsc[2].verifiableCredential.issuanceDate,
)
expect(credentialsAllDesc[1].verifiableCredential.issuanceDate).toEqual(
credentialsAllAsc[1].verifiableCredential.issuanceDate,
)
expect(credentialsAllDesc[2].verifiableCredential.issuanceDate).toEqual(
credentialsAllAsc[0].verifiableCredential.issuanceDate,
)

expect(new Date(credentials1[0].verifiableCredential.issuanceDate).getTime())
.toBeGreaterThan(new Date(credentials2[0].verifiableCredential.issuanceDate).getTime())
expect(new Date(credentials1[0].verifiableCredential.issuanceDate).getTime()).toBeGreaterThan(
new Date(credentials2[0].verifiableCredential.issuanceDate).getTime(),
)

expect(new Date(credentials4[0].verifiableCredential.issuanceDate).getTime())
.toBeGreaterThan(new Date(credentials3[0].verifiableCredential.issuanceDate).getTime())
expect(new Date(credentials4[0].verifiableCredential.issuanceDate).getTime()).toBeGreaterThan(
new Date(credentials3[0].verifiableCredential.issuanceDate).getTime(),
)
})

it('should be able to delete credential', async () => {
Expand All @@ -216,4 +222,78 @@ export default (testContext: {
expect(credentials2).toHaveLength(2)
})
})

describe('credential queries', () => {
let agent: ConfiguredAgent

beforeAll(async () => {
await testContext.setup()
agent = testContext.getAgent()
})

it('should query by type and issuer', async () => {
const issuer = await agent.didManagerCreate({ kms: 'local' })
const cred1 = await agent.createVerifiableCredential({
credential: {
issuer: { id: issuer.did },
type: ['Test123'],
credentialSubject:{
hello: 'world',
}
},
proofFormat: 'jwt',
})
const credentialHash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential: cred1 })

const found = await agent.dataStoreORMGetVerifiableCredentials({
where: [
{ column: 'type', value: ['VerifiableCredential,Test123'] },
{ column: 'issuer', value: [issuer.did] },
],
})
expect(found).toHaveLength(1)
expect(found[0].hash).toEqual(credentialHash)
})

it('should query by type and issuer with orderby', async () => {
const issuer = await agent.didManagerCreate({ kms: 'local' })

const cred1 = await agent.createVerifiableCredential({
credential: {
issuer: { id: issuer.did },
type: ['Test321'],
credentialSubject:{
first: true,
},
issuanceDate: undefined // intentionally use a nullish looking value here
},
proofFormat: 'jwt',
})
const cred1Hash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential: cred1 })

const cred2 = await agent.createVerifiableCredential({
credential: {
issuer: { id: issuer.did },
type: ['Test321'],
credentialSubject:{
first: false,
},
issuanceDate: '2000-01-01T00:00:00Z',
},
proofFormat: 'jwt',
})
const cred2Hash = await agent.dataStoreSaveVerifiableCredential({ verifiableCredential: cred2 })

const found = await agent.dataStoreORMGetVerifiableCredentials({
where: [
{ column: 'type', value: ['VerifiableCredential,Test321'] },
{ column: 'issuer', value: [issuer.did] },
],
order: [{ column: 'issuanceDate', direction: 'DESC' }],
take: 1
})
expect(found).toHaveLength(1)
expect(found[0].hash).toEqual(cred2Hash)
})
})
}
5 changes: 3 additions & 2 deletions packages/data-store/src/entities/claim.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ export class Claim extends BaseEntity {
// @ts-ignore
credential: Relation<Credential>

@Column()
// The VC data model does not allow credentials without an issuance date, but some credentials from the wild may
@Column({ nullable: true })
// @ts-ignore
issuanceDate: Date
issuanceDate?: Date

@Column({ nullable: true })
expirationDate?: Date
Expand Down
8 changes: 4 additions & 4 deletions packages/data-store/src/entities/credential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import { asArray, computeEntryHash, extractIssuer } from '@veramo/utils'
/**
* Represents some common properties of a Verifiable Credential that are stored in a TypeORM database for querying.
*
* @see {@link @veramo/core-types#IDataStoreORM.dataStoreORMGetVerifiableCredentials | dataStoreORMGetVerifiableCredentials}
* for the interface defining how this can be queried.
* @see {@link @veramo/core-types#IDataStoreORM.dataStoreORMGetVerifiableCredentials | dataStoreORMGetVerifiableCredentials} for the interface defining how this can be queried.
*
* @see {@link @veramo/data-store#DataStoreORM | DataStoreORM} for the implementation of the query interface.
*
Expand Down Expand Up @@ -65,9 +64,10 @@ export class Credential extends BaseEntity {
@Column({ nullable: true })
id?: string

@Column()
// The VC data model does not allow credentials without an issuance date, but some credentials from the wild may
@Column({ nullable: true })
// @ts-ignore
issuanceDate: Date
issuanceDate?: Date

@Column({ nullable: true })
expirationDate?: Date
Expand Down
Loading