Skip to content
Open
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
10 changes: 6 additions & 4 deletions application/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "backend",
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "yarn prisma generate && esbuild src/index.ts --bundle --platform=node --outfile=dist/index.js --external:sharp --external:@prisma/client --external:express --external:cors --loader:.html=text --loader:.css=text --define:process.env.ESBUILD=true",
"build": "yarn prisma:generate && esbuild src/index.ts --bundle --platform=node --outfile=dist/index.js --external:sharp --external:@prisma/client --external:express --external:cors --loader:.html=text --loader:.css=text --define:process.env.ESBUILD=true",
"start": "node dist/index.js",
"type-check": "tsc -b",
"lint": "eslint .",
Expand All @@ -11,13 +11,14 @@
"tsoa:spec": "tsoa spec",
"tsoa:routes": "tsoa routes",
"build-docs": "yarn run tsoa:routes && yarn run tsoa:spec && tsx scripts/replace-refs.ts",
"prisma:generate": "yarn prisma generate",
"prisma:seed": "yarn prisma db seed",
"prisma:test": "yarn ts-node -T scripts/runTestSeed.ts",
"prisma:wipe": "yarn ts-node -T scripts/runWipeDB.ts",
"prisma:stresstest": "yarn prisma:wipe && yarn prisma:seed && vite-node scripts/stressTestSeed.ts",
"prisma:migrate": "yarn prisma migrate dev",
"console": "ts-node -T -r ./prisma/console.ts"
"prisma:migrate": "yarn prisma migrate dev && yarn prisma:erd:check",
"console": "ts-node -T -r ./prisma/console.ts",
"prisma:erd:check": "git diff --exit-code prisma/dbml/schema.dbml > /dev/null 2>&1 || (printf '\\n⚠️ schema.dbml changed — remember to paste the new DBML into the dbdiagram.io project so the interactive ERD stays in sync\\n dbdiagram.io project: https://dbdiagram.io/d/CTRL-ERD-6a7554fa35ee2e87b04f19e0\\n\\n')",
"prisma:generate": "yarn prisma generate && yarn prisma:erd:check"
},
"prisma": {
"seed": "tsx prisma/seed/seed.ts"
Expand Down Expand Up @@ -64,6 +65,7 @@
"jest-mock-extended": "^3.0.7",
"nodemailer-mock": "^2.0.6",
"prisma": "^5.20.0",
"prisma-dbml-generator": "^0.12.0",
"prisma-json-types-generator": "^3.1.1",
"supertest": "^7.0.0",
"ts-jest": "^29.1.5",
Expand Down
268 changes: 268 additions & 0 deletions application/backend/prisma/dbml/schema.dbml
Original file line number Diff line number Diff line change
@@ -0,0 +1,268 @@
//// ------------------------------------------------------
//// THIS FILE WAS AUTOMATICALLY GENERATED (DO NOT MODIFY)
//// ------------------------------------------------------

Project "CTRL" {
database_type: 'PostgreSQL'
Note: ''
}

Table User {
id Int [pk, increment]
firstName String [not null, note: '@encrypted']
middleName String [note: '@encrypted']
lastName String [not null, note: '@encrypted']
email String [unique, not null, note: '@encrypted']
emailHash String [unique, note: '@encryption:hash(email)']
password String [not null]
role Role [not null, default: 'Participant']
adminOfStudies Study [not null]
organisations Organisation [not null]
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime [not null]
agreedTermsAt DateTime
profiles ParticipantProfile [not null]
auditLogs AuditLog [not null]
otpTokens OTPToken [not null]
passwordResetTokens PasswordResetToken [not null]
lockedUntil DateTime
retriesRemaining Int [not null, default: 10]
deleted Boolean [not null, default: false]
}

Table ParticipantProfile {
id Int [pk, increment]
individualId String [unique, not null]
firstName String [not null, note: '@encrypted']
firstNameHash String [note: '@encryption:hash(firstName)']
middleName String [note: '@encrypted']
lastName String [not null, note: '@encrypted']
lastNameHash String [note: '@encryption:hash(lastName)']
dob String [not null, note: '@encrypted']
dobHash String [note: '@encryption:hash(dob)']
mobile String [not null, note: '@encrypted']
addressLine String [not null, note: '@encrypted']
suburb String [not null, note: '@encrypted']
state StateTerritory [not null]
postcode String [not null, note: '@encrypted']
participantType ParticipantType [not null]
nextOfKin AlternativeContact
preferredContact ContactMethod [not null]
studies StudyParticipant [not null]
familyId Int [increment, not null]
userId Int
user User
surveys SurveyVersionAnswers [not null]
}

Table StudyParticipant {
participantProfile ParticipantProfile [not null]
participantProfileId Int [not null]
study Study [not null]
studyId Int [not null]
participantNumber Int [not null, default: 0]
participantId String
externalId String
deleted Boolean [not null, default: false]

indexes {
(participantProfileId, studyId) [pk]
}
}

Table AlternativeContact {
id Int [pk, increment]
firstName String [not null, note: '@encrypted']
middleName String [note: '@encrypted']
lastName String [not null, note: '@encrypted']
mobile String [note: '@encrypted']
email String [not null, note: '@encrypted']
ParticipantProfile ParticipantProfile [not null]
participantProfileId Int [unique, not null]
}

Table Organisation {
id Int [pk, increment]
name String [unique, not null]
primaryColour String
secondaryColour String
elsaToken String [note: '@encrypted']
logo Bytes
tcLink String [not null, default: 'https://garvan-data-science-platform.github.io/ctrl-docs/docs/terms-and-conditions']
newsLink String
users User [not null]
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime [not null]
}

Table Study {
id Int [pk, increment]
name String [not null]
description String
logo Bytes
redcapURL String [note: '@encrypted']
redcapToken String [note: '@encrypted']
inviteEmailSubject String [not null, default: 'Invitation to CTRL - Dynamic Consent Platform']
inviteEmailText String [not null, default: 'You have been invited to register with CTRL dynamic consent platform.']
contactUsEmail String
surveys SurveyVersion [not null]
profiles StudyParticipant [not null]
invites Invite [not null]
admins User [not null]
deleted Boolean [not null, default: false]
}

Table SurveyVersion {
id Int [pk, increment]
versionNumber Int [not null]
data Json [not null, note: '![PrismaJson.SurveyVersionData]']
status SurveyVersionStatus [not null, default: 'DRAFT']
surveyVersionAnswers SurveyVersionAnswers [not null]
study Study [not null]
studyId Int [not null]
createdAt DateTime [default: `now()`, not null]
updatedAt DateTime [not null]
publishedAt DateTime

indexes {
(studyId, versionNumber) [unique]
}
}

Table SurveyVersionAnswers {
id Int [pk, increment]
version SurveyVersion [not null]
versionId Int [not null]
profile ParticipantProfile [not null]
profileId Int [not null]
answers Json [not null, note: '![PrismaJson.SurveyAnswerData]']
derived String
}

Table Invite {
id String [pk]
email String [not null, note: '@encrypted']
emailHash String [note: '@encryption:hash(email)']
status InviteStatus [not null, default: 'PENDING']
studyId Int [not null]
study Study [not null]
expiresAt DateTime [not null]
createdAt DateTime [default: `now()`, not null]
sentAt DateTime
prefill String [note: '@encrypted']

indexes {
(studyId, emailHash) [unique]
}
}

Table PasswordResetToken {
id Int [pk, increment]
token String [unique, not null]
userId Int [not null]
user User [not null]
expiresAt DateTime [not null]
used Boolean [not null, default: false]
}

Table AuditLog {
id Int [pk, increment]
resource String [not null]
operation AuditLogOperation [not null]
success Boolean [not null, default: true]
timestamp DateTime [default: `now()`, not null]
userId Int
user User
meta Json [not null]
requestBody String [note: '@encrypted']
}

Table OTPToken {
id String [pk]
userId Int [not null]
user User [not null]
code String [not null]
expiresAt DateTime [not null]
}

Table StudyToUser {
adminofstudiesId Int [ref: > Study.id]
adminsId Int [ref: > User.id]
}

Table OrganisationToUser {
organisationsId Int [ref: > Organisation.id]
usersId Int [ref: > User.id]
}

Enum ParticipantType {
STANDARD
GUARDIAN
DEPENDENT_AGE
DEPENDENT_OTHER
}

Enum Role {
OperatorAdmin
Participant
OrganisationAdmin
StudyAdmin
}

Enum StateTerritory {
ACT
NSW
NT
QLD
SA
TAS
VIC
WA
}

Enum ContactMethod {
EMAIL
MOBILE
MAIL
}

Enum SurveyVersionStatus {
DRAFT
PUBLISHED
}

Enum InviteStatus {
PENDING
ACCEPTED
EXPIRED
REVOKED
FAILED_TO_SEND
}

Enum AuditLogOperation {
CREATE
UPDATE
DELETE
}

Ref: ParticipantProfile.userId > User.id

Ref: StudyParticipant.participantProfileId > ParticipantProfile.id [delete: Cascade]

Ref: StudyParticipant.studyId > Study.id [delete: Cascade]

Ref: AlternativeContact.participantProfileId - ParticipantProfile.id [delete: Cascade]

Ref: SurveyVersion.studyId > Study.id [delete: Cascade]

Ref: SurveyVersionAnswers.versionId > SurveyVersion.id [delete: Cascade]

Ref: SurveyVersionAnswers.profileId > ParticipantProfile.id [delete: Cascade]

Ref: Invite.studyId > Study.id [delete: Cascade]

Ref: PasswordResetToken.userId > User.id [delete: Cascade]

Ref: AuditLog.userId > User.id [delete: Restrict]

Ref: OTPToken.userId > User.id [delete: Cascade]
9 changes: 9 additions & 0 deletions application/backend/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ generator client {
previewFeatures = ["omitApi"]
}

generator dbml {
provider = "prisma-dbml-generator"
output = "./dbml"
outputName = "schema.dbml"
projectName = "CTRL"
projectDatabaseType = "PostgreSQL"
includeRelationFields = "true"
}

generator json {
provider = "prisma-json-types-generator"
}
Expand Down
Loading
Loading