Skip to content

Commit

Permalink
feat: mongo transactions (#198)
Browse files Browse the repository at this point in the history
* fix: update invitation to include entityId on submission approval

* feat: transactional mongo session for submission approval/rejection and invitation creation

* feat: mongodb replica set docker compose

* fix: update entity detail url

* ci: mongo replica set

* test: mongodb rollbacks

* chore: remove placeholder email

* refactor: rename GenericError
  • Loading branch information
niall-shaw authored Jan 26, 2024
1 parent bf009dd commit 3412883
Show file tree
Hide file tree
Showing 22 changed files with 469 additions and 122 deletions.
5 changes: 5 additions & 0 deletions .docker/mongodb/initdb.d/user-create.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
db.createUser({
user: process.env.MONGO_INITDB_ROOT_USERNAME,
pwd: process.env.MONGO_INITDB_ROOT_PASSWORD,
roles: [{ role: 'readWrite', db: process.env.MONGO_INITDB_DATABASE }],
})
15 changes: 15 additions & 0 deletions .docker/mongodb/local.file.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
10VGkBqCI6gYMv00OJo4Ygd0c04ChooR2aGBesfrDt5HEXODjRkDSD/zATaNuyXT
LT9Uwf+kYS1v0wbYgjYcuvg1cnW1eFT9N13km6D2dttT76WyR2/Oke5lnjaliEzc
uKkmaWC/Flx0ZE6jsggk22SMJElb8u72lNbVfwmJXOjF4HNUhrPAnWNTWhM9J8RF
z5A1VbqF3TAIegQeNDgKBUZB7e9wJbtNXQVRnqdImNN0USYxTIVkGwNRLbEjCYRu
H46wvn1VJIxqvGntjqczlI1YQmtECzVegr+mwoJ8bA3cbKPjbkXl4WegpvwdF8HF
4Y/+lvVTxCjHcX4p4VDc9HLJnp9vZFpRLrgp6fTiKZ0knyN+Md5el1QyRnKGufCH
gv/dG1zw9/AY7ykIrGOkqcoZH9XR5OzXZtl0enkwMSOoO7b+WGXXzw2VL2+DsrjB
dgjaPQwvXRTLeu+lDIgIPF2Z1rEV4Fg8d9GnvnkLK2AytTZSW/vbWSZteM89tRj0
DIIGEqtSKiAzTzETuVDsavFZmmAB2e0VcVM9+emR0f37nwBZ3W6+97C6jbeHrAfC
XeDwIFD4AntSrv5vcT0+PylcNHpdbnrVVlKgsxIbb54pi1ZfCUewVff9E2ym2BNX
QghRa1OQz4l/VliR9eBtZp3HaRgQgrG+4n/R8PbZbYfSe1UxWRuOk950qMyzeGU/
DdPujFt++fajFkXuyhWcT0ytSgM4MrOTBXeeeMkbZNrn/TsvlxLqE64tgsRfsP8a
4/y2jnciddPhZT/B2lU5rt18kaD7Imm5AC0aBh7iNkiWHL544frfs+qHh3t3C43+
jmyKoDy8BK9dBKZgL+VSBSflv7o96z/zOet7amtrKwGLODsEWkwoEMjKfr8cmLyh
hJfI0ARMD/M3zjj1EXfrEI3LgJui/RTJ/BhEAA==
14 changes: 14 additions & 0 deletions .docker/mongodb/scripts/init-replica-set.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
var cfg = {
_id: 'rs0',
protocolVersion: 1,
version: 1,
members: [
{
_id: 0,
host: 'mongo:27017',
priority: 2,
},
],
}
rs.initiate(cfg, { force: true })
rs.status()
7 changes: 7 additions & 0 deletions .docker/mongodb/scripts/mongo-setup.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

echo "Waiting for startup.."
sleep 5
echo mongosetup.sh time now: $(date +"%T")
mongosh --host mongo:27017 -u ${MONGO_INITDB_ROOT_USERNAME} -p ${MONGO_INITDB_ROOT_PASSWORD} < $(dirname "$0")/init-replica-set.js
echo "done"
11 changes: 6 additions & 5 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@ jobs:
AUTH_JWT_SECRET_KEY: abcdefgh123456
AUTH_ADMIN_PASSWORD_HASH: $2b$10$9ScWBY1SuA0a2nZbjvAOWeshe1XqchHKeI18NgWSs.TS4zbZsds3C
FRONTEND_URL: http://localhost:3001
services:
mongo:
image: mongo:7.0
ports:
- 4000:27017

concurrency:
group: ${{ github.workflow }}-test-${{ github.ref }}
Expand All @@ -55,6 +50,12 @@ jobs:
with:
node-version: 20
cache: yarn
- name: Start MongoDB (with replica set)
uses: supercharge/[email protected]
with:
mongodb-version: 7.0
mongodb-replica-set: rs0
mongodb-port: 4000
- run: yarn global add node-gyp
- run: yarn install --frozen-lockfile
- run: yarn build
Expand Down
57 changes: 47 additions & 10 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ services:
SMTP_USER: user
SMTP_PASSWORD: password
AUTH_JWT_SECRET_KEY: authjwtsecretkey
AUTH_ADMIN_PASSWORD_HASH: passwordhash
AUTH_ADMIN_PASSWORD_HASH: $$2b$$10$$Jo5knrTTpteyyyBN1aCh3.JThMmLxtX33Djl4H8rprAG1UCUOYIRm
FRONTEND_URL: http://localhost:3001
SKIP_INITIAL_DATA_LOAD: true
LOGGER_LOG_LEVEL: debug
Expand All @@ -32,24 +32,60 @@ services:
depends_on:
mongo:
condition: service_healthy

mongo:
image: mongo:7.0
container_name: mongo
ports:
- 27017:27017
image: mongo:7.0
entrypoint:
- bash
- -c
- |
cp /auth/local.file.key /auth/file.key
chmod 400 /auth/file.key
chown 999:999 /auth/file.key
exec docker-entrypoint.sh $$@
command:
[
"mongod",
"--port",
"27017",
"--keyFile",
"/auth/file.key",
"--replSet",
"rs0",
"--bind_ip_all"
]
environment:
MONGO_INITDB_ROOT_USERNAME: mongo
MONGO_INITDB_ROOT_PASSWORD: mongo
MONGO_INITDB_DATABASE: trust-registry
expose:
- 27017
ports:
- 27017:27017
networks:
- ssi-trust-registry
volumes:
- ./.docker/mongodb/initdb.d/:/docker-entrypoint-initdb.d/
- ./.docker/mongodb/local.file.key:/auth/local.file.key
healthcheck:
test: echo 'db.runCommand("ping").ok' | mongosh localhost:27017/test --quiet
interval: 15s
timeout: 5s
start_period: 5s
test: echo 'rs.status().ok' | mongosh admin --port 27017 -u $${MONGO_INITDB_ROOT_USERNAME} -p $${MONGO_INITDB_ROOT_PASSWORD} --quiet | grep 1
interval: 30s
start_period: 60s
mongo-replica-setup:
container_name: mongo-setup
image: mongo:7.0
restart: on-failure
networks:
- ssi-trust-registry
volumes:
- mongo:/data/db
- ./.docker/mongodb/scripts:/scripts
entrypoint: [ "bash", "/scripts/mongo-setup.sh" ]
environment:
MONGO_INITDB_ROOT_USERNAME: mongo
MONGO_INITDB_ROOT_PASSWORD: mongo
depends_on:
- mongo
mongo-express:
image: mongo-express:latest
container_name: mongo-express
Expand Down Expand Up @@ -88,8 +124,9 @@ services:
condition: service_healthy

volumes:
mongo:
mongo-data:
smtp4dev-data:


networks:
ssi-trust-registry:
2 changes: 1 addition & 1 deletion packages/backend/.env.example
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PORT=3000
URL=http://localhost
FRONTEND_URL=http://localhost:3001
DB_CONNECTION_STRING=mongodb://localhost:4000
DB_CONNECTION_STRING=mongodb://localhost:4000?directConnection=true
DB_NAME=test_registry
LOGGER_LOG_LEVEL=debug
LOGGER_OUTPUT_FORMAT=json
Expand Down
4 changes: 4 additions & 0 deletions packages/backend/src/database.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export async function connect(config: DbConfig) {
return database
}

export async function createSession() {
return client.startSession()
}

export function close() {
return client.close()
}
2 changes: 1 addition & 1 deletion packages/backend/src/email/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ async function sendInvitationEmail(
invitation: Invitation,
) {
const { submitApiUrl, submitUiUrl } = getSubmitUrls(invitation)
logger.info(`Sending submission approved email to: `, invitation.emailAddress)
logger.info(`Sending invitation email to: `, invitation.emailAddress)
await emailClient.sendMailFromTemplate(
invitation.emailAddress,
'SSI Trust Registry - Invitation',
Expand Down
10 changes: 7 additions & 3 deletions packages/backend/src/entity/mongoRepository.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import partial from 'lodash.partial'
import { Collection, Db } from 'mongodb'
import { ClientSession, Collection, Db } from 'mongodb'
import { Entity } from '@ssi-trust-registry/common'
import { createLogger } from '../logger'
import { EntityRepository } from './service'
Expand Down Expand Up @@ -44,11 +44,15 @@ async function findByDid(collection: Collection, did: string) {
return result && Entity.parse(result)
}

async function addEntity(collection: Collection, entity: Entity) {
async function addEntity(
collection: Collection,
entity: Entity,
session?: ClientSession,
) {
const entityData = {
...entity,
}
await collection.insertOne(entityData)
await collection.insertOne(entityData, { session })
return entity
}

Expand Down
5 changes: 3 additions & 2 deletions packages/backend/src/entity/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { v4 as uuidv4 } from 'uuid'
import { Entity, EntityDto } from '@ssi-trust-registry/common'
import { createLogger } from '../logger'
import { ValidationService } from './validationService'
import { ClientSession } from 'mongodb'

const logger = createLogger(__filename)
extendZodWithOpenApi(z)
Expand All @@ -28,8 +29,8 @@ export interface EntityRepository {
getAllEntities: () => Promise<Entity[]>
findById: (id: string) => Promise<Entity | null>
findByDid: (did: string) => Promise<Entity | null>
addEntity: (entity: Entity) => Promise<Entity>
updateEntity: (entity: Entity) => Promise<Entity>
addEntity: (entity: Entity, session?: ClientSession) => Promise<Entity>
updateEntity: (entity: Entity, session?: ClientSession) => Promise<Entity>
}

export const EntityImportDto = EntityDto.extend({
Expand Down
Loading

0 comments on commit 3412883

Please sign in to comment.