Skip to content

Commit 6cb8d27

Browse files
authored
feat: native SQLite and Postgres database support based on Drizzle ORM (openwallet-foundation#2324)
Signed-off-by: Timo Glastra <timo@animo.id>
1 parent 2fd3c03 commit 6cb8d27

File tree

334 files changed

+19716
-3084
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

334 files changed

+19716
-3084
lines changed

.changeset/kind-bobcats-camp.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@credo-ts/drizzle-storage": minor
3+
---
4+
5+
feat: add support for new SQLite and PostgreSQL storage based on Drizzle.
6+
7+
The Drizzle Storage Module is an additional storage implementation for Credo which natively integrates with PostgreSQL and SQLite. It can be combined with Aries Askar as the KMS.
8+
9+
The Drizzle Storage Module does not introduce any breaking chnages to how the storage APIs works in Credo, and for new projects you only have to configure the Drizzle module to connect to your database.

.github/workflows/continuous-integration.yml

Lines changed: 81 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
# Each shard runs a set of the tests
6060
# Make sure to UPDATE THE TEST command with the total length of
6161
# the shards if you change this!!
62-
shard: [1, 2]
62+
shard: [1, 2, 3]
6363

6464
steps:
6565
- uses: actions/checkout@v5
@@ -88,14 +88,21 @@ jobs:
8888
- name: Install dependencies
8989
run: pnpm install --frozen-lockfile
9090

91+
# Issue with node 22 starting 22.18
92+
# https://github.com/nodejs/node/issues/59364
9193
- name: Run tests
92-
run: pnpm test:unit --coverage --forceExit --shard=${{ matrix.shard }}/2
94+
run: NODE_OPTIONS="$NODE_OPTIONS --no-experimental-strip-types" pnpm test:unit --coverage --forceExit --shard=${{ matrix.shard }}/3
95+
if: matrix.node-version == 22
96+
97+
- name: Run tests
98+
run: pnpm test:unit --coverage --forceExit --shard=${{ matrix.shard }}/3
99+
if: matrix.node-version != 22
93100

94101
# Upload coverage for shard
95102
- run: mv coverage/coverage-final.json coverage/${{ matrix.shard }}.json
96103
- uses: actions/upload-artifact@v4
97104
with:
98-
name: coverage-artifacts-${{ matrix.node-version }}
105+
name: coverage-artifacts-${{ matrix.node-version }}-unit-${{ matrix.shard }}
99106
path: coverage/${{ matrix.shard }}.json
100107
overwrite: true
101108

@@ -141,26 +148,94 @@ jobs:
141148
- name: Install dependencies
142149
run: pnpm install --frozen-lockfile
143150

151+
# Issue with node 22 starting 22.18
152+
# https://github.com/nodejs/node/issues/59364
153+
- name: Run tests
154+
run: NODE_OPTIONS="$NODE_OPTIONS --no-experimental-strip-types" pnpm test:e2e --coverage --forceExit
155+
if: matrix.node-version == 22
156+
144157
- name: Run tests
145158
run: pnpm test:e2e --coverage --forceExit
159+
if: matrix.node-version != 22
146160

147161
# Upload coverage for e2e
148162
- run: mv coverage/coverage-final.json coverage/e2e.json
149163
- uses: actions/upload-artifact@v4
150164
with:
151-
name: coverage-artifacts-${{ matrix.node-version }}
165+
name: coverage-artifacts-${{ matrix.node-version }}-e2e
152166
path: coverage/e2e.json
153167
overwrite: true
154168

169+
drizzle-tests:
170+
runs-on: ubuntu-24.04
171+
name: Drizzle Tests
172+
if: github.event_name != 'pull_request_review' || github.event.pull_request.head.ref == 'changeset-release/main'
173+
174+
strategy:
175+
fail-fast: false
176+
matrix:
177+
node-version: [20, 22]
178+
179+
steps:
180+
- uses: actions/checkout@v4
181+
182+
# setup dependencies
183+
- name: Setup services
184+
run: docker compose up -d
185+
186+
- name: Setup NodeJS
187+
id: setup-node
188+
uses: actions/setup-node@v4
189+
with:
190+
node-version: ${{ matrix.node-version }}
191+
192+
- uses: pnpm/action-setup@v4
193+
194+
# See https://github.com/actions/setup-node/issues/641#issuecomment-1358859686
195+
- name: pnpm cache path
196+
id: pnpm-cache-path
197+
run: |
198+
echo "STORE_PATH=$(pnpm store path)" >> $GITHUB_OUTPUT
199+
200+
- name: pnpm cache
201+
uses: actions/cache@v3
202+
with:
203+
path: ${{ steps.pnpm-cache-path.outputs.STORE_PATH }}
204+
key: ${{ runner.os }}-${{ steps.setup-node.outputs.node-version }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
205+
restore-keys: |
206+
${{ runner.os }}-${{ steps.setup-node.outputs.node-version }}-pnpm-store-
207+
208+
- name: Install dependencies
209+
run: pnpm install --frozen-lockfile
210+
211+
# Issue with node 22 starting 22.18
212+
# https://github.com/nodejs/node/issues/59364
213+
- name: Run tests
214+
run: NODE_OPTIONS="$NODE_OPTIONS --no-experimental-strip-types" pnpm test:drizzle --coverage --forceExit
215+
if: matrix.node-version == 22
216+
217+
- name: Run tests
218+
run: pnpm test:drizzle --coverage --forceExit
219+
if: matrix.node-version != 22
220+
221+
# Upload coverage for e2e
222+
- run: mv coverage/coverage-final.json coverage/drizzle.json
223+
- uses: actions/upload-artifact@v4
224+
with:
225+
name: coverage-artifacts-${{ matrix.node-version }}-drizzle
226+
path: coverage/drizzle.json
227+
overwrite: true
228+
155229
# Upload all the coverage reports
156230
report-coverage:
157231
runs-on: ubuntu-24.04
158-
needs: [e2e-tests, unit-tests]
232+
needs: [e2e-tests, unit-tests, drizzle-tests]
159233
steps:
160234
- uses: actions/download-artifact@v5
161235
with:
162-
name: coverage-artifacts-20
236+
pattern: coverage-artifacts-20-*
163237
path: coverage
238+
merge-multiple: true
164239

165240
- uses: codecov/codecov-action@v5
166241
with:

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,6 @@ coverage
1111
logs.txt
1212
logs/
1313
.pnpm-store
14-
ngrok.auth.yml
14+
ngrok.auth.yml
15+
cli-build
16+
*.db

jest.config.base.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,13 @@ const config: Config.InitialOptions = {
66
// NOTE: overridden in e2e test. Make sure to
77
// update that match as well when changing this one
88
testMatch: ['**/?(*.)test.ts'],
9-
moduleNameMapper: {
10-
'@credo-ts/(.+)': ['<rootDir>/../../packages/$1/src', '<rootDir>/../packages/$1/src', '<rootDir>/packages/$1/src'],
11-
},
129
transform: {
13-
'\\.tsx?$': [
10+
'\\.(t|j)sx?$': [
1411
'ts-jest',
1512
{
16-
isolatedModules: true,
13+
tsconfig: {
14+
isolatedModules: true,
15+
},
1716
},
1817
],
1918
},

jest.config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import base from './jest.config.base'
55
const config: Config.InitialOptions = {
66
...base,
77
roots: ['<rootDir>'],
8+
verbose: true,
89
coverageReporters: ['text-summary', 'lcov', 'json'],
910
coveragePathIgnorePatterns: ['/build/', '/node_modules/', '/__tests__/', 'tests'],
1011
coverageDirectory: '<rootDir>/coverage/',

package.json

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,9 @@
1515
"style:fix": "biome check --write --unsafe",
1616
"clean": "pnpm -r --parallel run clean",
1717
"build": "pnpm -r --parallel run build",
18-
"test:unit": "jest --testPathIgnorePatterns 'e2e.test.ts$'",
19-
"test:e2e": "jest --testMatch '**/?(*.)e2e.test.ts'",
18+
"test:unit": "pnpm test --testPathIgnorePatterns e2e.test.ts",
19+
"test:e2e": "pnpm test --testPathIgnorePatterns drizzle --testMatch '**/?(*.)e2e.test.ts'",
20+
"test:drizzle": "pnpm test --testMatch '**/*drizzle*.test.ts'",
2021
"test": "jest",
2122
"validate": "pnpm types:check && pnpm style:check",
2223
"run-mediator": "ts-node ./samples/mediator.ts",
@@ -28,22 +29,38 @@
2829
"@babel/preset-env": "^7.27.2",
2930
"@biomejs/biome": "^1.9.4",
3031
"@changesets/cli": "^2.29.4",
32+
"@credo-ts/action-menu": "workspace:*",
33+
"@credo-ts/anoncreds": "workspace:*",
34+
"@credo-ts/askar": "workspace:*",
35+
"@credo-ts/cheqd": "workspace:*",
36+
"@credo-ts/core": "workspace:*",
37+
"@credo-ts/didcomm": "workspace:*",
38+
"@credo-ts/drizzle-storage": "workspace:*",
39+
"@credo-ts/drpc": "workspace:*",
40+
"@credo-ts/indy-sdk-to-askar-migration": "workspace:*",
41+
"@credo-ts/indy-vdr": "workspace:*",
42+
"@credo-ts/node": "workspace:*",
43+
"@credo-ts/openid4vc": "workspace:*",
44+
"@credo-ts/question-answer": "workspace:*",
45+
"@credo-ts/react-native": "workspace:*",
46+
"@credo-ts/redis-cache": "workspace:*",
47+
"@credo-ts/tenants": "workspace:*",
3148
"@jest/types": "^29.6.3",
3249
"@openwallet-foundation/askar-nodejs": "catalog:",
3350
"@types/cors": "^2.8.19",
3451
"@types/express": "catalog:",
35-
"@types/jest": "^29.5.14",
52+
"@types/jest": "^30.0.0",
3653
"@types/node": "catalog:",
3754
"@types/supertest": "^6.0.3",
3855
"@types/varint": "^6.0.3",
3956
"@types/ws": "catalog:",
4057
"cors": "^2.8.5",
4158
"express": "catalog:",
42-
"jest": "^29.7.0",
59+
"jest": "^30.0.3",
4360
"nock": "catalog:",
4461
"rxjs": "catalog:",
45-
"supertest": "^7.1.1",
46-
"ts-jest": "^29.3.4",
62+
"supertest": "^7.1.3",
63+
"ts-jest": "^29.4.0",
4764
"ts-node": "catalog:",
4865
"tsyringe": "catalog:",
4966
"typescript": "catalog:",

packages/action-menu/src/services/__tests__/ActionMenuService.test.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ describe('ActionMenuService', () => {
9494
],
9595
},
9696
})
97-
).rejects.toThrowError('Action Menu contains duplicated options')
97+
).rejects.toThrow('Action Menu contains duplicated options')
9898
})
9999

100100
it('no previous menu: emits a menu with title, description and options', async () => {
@@ -241,7 +241,7 @@ describe('ActionMenuService', () => {
241241
actionMenuRecord: mockRecord,
242242
performedAction: { name: 'fake' },
243243
})
244-
).rejects.toThrowError('Selection does not match valid actions')
244+
).rejects.toThrow('Selection does not match valid actions')
245245
})
246246

247247
it('throws an error when state is not preparing-selection', async () => {
@@ -254,9 +254,7 @@ describe('ActionMenuService', () => {
254254
actionMenuRecord: mockRecord,
255255
performedAction: { name: 'opt1' },
256256
})
257-
).rejects.toThrowError(
258-
`Action Menu record is in invalid state ${state}. Valid states are: preparing-selection.`
259-
)
257+
).rejects.toThrow(`Action Menu record is in invalid state ${state}. Valid states are: preparing-selection.`)
260258
}
261259
})
262260

@@ -696,9 +694,7 @@ describe('ActionMenuService', () => {
696694

697695
mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord))
698696

699-
expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError(
700-
'Selection does not match valid actions'
701-
)
697+
expect(actionMenuService.processPerform(messageContext)).rejects.toThrow('Selection does not match valid actions')
702698

703699
expect(actionMenuRepository.update).not.toHaveBeenCalled()
704700
expect(actionMenuRepository.save).not.toHaveBeenCalled()
@@ -721,7 +717,7 @@ describe('ActionMenuService', () => {
721717

722718
mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(null))
723719

724-
expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError(
720+
expect(actionMenuService.processPerform(messageContext)).rejects.toThrow(
725721
`No Action Menu found with thread id ${mockPerformMessage.threadId}`
726722
)
727723

@@ -747,7 +743,7 @@ describe('ActionMenuService', () => {
747743
mockRecord.state = ActionMenuState.Done
748744
mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord))
749745

750-
expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError(
746+
expect(actionMenuService.processPerform(messageContext)).rejects.toThrow(
751747
`Action Menu record is in invalid state ${mockRecord.state}. Valid states are: ${ActionMenuState.AwaitingSelection}.`
752748
)
753749

@@ -773,7 +769,7 @@ describe('ActionMenuService', () => {
773769
mockRecord.state = ActionMenuState.Null
774770
mockFunction(actionMenuRepository.findSingleByQuery).mockReturnValue(Promise.resolve(mockRecord))
775771

776-
expect(actionMenuService.processPerform(messageContext)).rejects.toThrowError(
772+
expect(actionMenuService.processPerform(messageContext)).rejects.toThrow(
777773
new ActionMenuProblemReportError('Action Menu has been cleared by the responder', {
778774
problemCode: ActionMenuProblemReportReason.Timeout,
779775
})

packages/anoncreds/src/anoncreds-rs/__tests__/AnonCredsRsHolderService.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,7 @@ describe('AnonCredsRsHolderService', () => {
361361
proofRequest,
362362
attributeReferent: 'name',
363363
})
364-
).rejects.toThrowError()
364+
).rejects.toThrow()
365365
})
366366

367367
test('referent with single restriction', async () => {
@@ -513,7 +513,7 @@ describe('AnonCredsRsHolderService', () => {
513513
findByIdMock.mockResolvedValueOnce(null).mockResolvedValueOnce(record)
514514
getByCredentialIdMock.mockRejectedValueOnce(new Error())
515515

516-
await expect(anonCredsHolderService.getCredential(agentContext, { id: 'myCredentialId' })).rejects.toThrowError()
516+
await expect(anonCredsHolderService.getCredential(agentContext, { id: 'myCredentialId' })).rejects.toThrow()
517517

518518
const credentialInfo = await anonCredsHolderService.getCredential(agentContext, { id: 'myCredentialId' })
519519

packages/anoncreds/src/protocols/credentials/v1/__tests__/V1CredentialProtocolProposeOffer.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -296,7 +296,7 @@ describe('V1CredentialProtocolProposeOffer', () => {
296296
}),
297297
})
298298

299-
await expect(credentialProtocol.createOffer(agentContext, offerOptions)).rejects.toThrowError(
299+
await expect(credentialProtocol.createOffer(agentContext, offerOptions)).rejects.toThrow(
300300
'Missing required credential preview from indy format service'
301301
)
302302
})

packages/anoncreds/src/protocols/proofs/v1/__tests__/v1-indy-proofs.e2e.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -482,7 +482,7 @@ describe('Present Proof', () => {
482482
},
483483
},
484484
})
485-
).rejects.toThrowError('The proof request contains duplicate predicates and attributes: age')
485+
).rejects.toThrow('The proof request contains duplicate predicates and attributes: age')
486486
})
487487

488488
test('Faber starts with proof request to Alice but gets Problem Reported', async () => {

0 commit comments

Comments
 (0)