-
Notifications
You must be signed in to change notification settings - Fork 3
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
feat: persisting the database #158
base: develop
Are you sure you want to change the base?
Conversation
6368585
to
875ac35
Compare
apps/vc-api/Dockerfile
Outdated
EXPOSE 3000 | ||
|
||
COPY --from=builder /app/common/deploy /app | ||
COPY license-header.txt /app | ||
|
||
ENV DB_TYPE=SQLITE_INMEMORY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This variable is also set in .env
. Can we skip it here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The idea is to have this change backward compatible with previous deployments of the SSI. Now, they use an in-memory SQLite database. This line ensures that if a container is started without DB_TYPE set, it executes as before.
@@ -22,7 +22,12 @@ | |||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand", | |||
"test:e2e": "jest --config ./test/jest-e2e.json", | |||
"write-openapi": "ts-node ./scripts/write-open-api-json.ts && prettier -w docs/openapi.json", | |||
"update-openapi": "npm run write-openapi; git add docs/openapi.json" | |||
"update-openapi": "npm run write-openapi; git add docs/openapi.json", | |||
"migration:generate:pg": "export DB_TYPE=POSTGRES; npm run migration:run && cd src/migrations/pg && typeorm-ts-node-commonjs -d ../../config/ormconfig.ts migration:generate -p", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above - ideally to configure by .env
and have single migration:generate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, this was the first approach, but after exercising the creation of migrations a few times, I created two migration scripts in the package.json
because when generating migrations, it is necessary to execute both for Postgres and SQLite one after another. Editing .env
every time would be really annoying and prone to mistakes. :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is necessary to execute both for Postgres and SQLite
Could you give an example for such scenario?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Postgres and SQLite are different DB engines, so migration files are different for them. If we want SSI to be able to use any of them, we need to provide migrations for both of them that differ. So, when DB schema is extended during development, it is necessary to generate a new migration file for Postgres and SQLite. It is not possible to execute Postgres migration on SQLite SQLite migration on Postgres.
apps/vc-api/src/config/db.ts
Outdated
} | ||
}; | ||
|
||
export const typeOrmInMemoryModuleFactory = () => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can remove this now, since sqlite config is returned by typeOrmConfigFactory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is still used in tests and only there as they are executed with an in-memory database.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, why tests can't use typeOrmConfigFactory(config of sqlite datasource)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It was just simpler for me to keep this as a wrapper than mocking ConfigService
and injecting it in tests. Do you think it is worth the effort to do this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would look like this, what do you think?
TypeOrmModule.forRoot(
typeOrmConfigFactory({
get: (key): Record<string, unknown> => {
return { DB_TYPE: DB_TYPES.SQLITE_IN_MEMORY }[key];
}
} as unknown as ConfigService)
),
instead of this:
typeOrmInMemoryModuleFactory(),
} | ||
] | ||
}) | ||
.when('.DB_TYPE', { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why another clause of .when
? Can we add is
in previouse when.switch
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I needed to use this hack as setting common attributes for is: Joi.required().valid(DB_TYPES.SQLITE, DB_TYPES.POSTGRES)
in the switch is not possible. It triggers some inconsistent behavior of Joi
.
Maybe, repeating this twice would be more readable? What do you think?
DB_DROP_ON_START: Joi.boolean().default(false),
DB_SYNCHRONIZE: Joi.boolean().default(false),
DB_RUN_MIGRATIONS: Joi.boolean().default(true)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, it would look like this, but we would have repeated code:
.when('.DB_TYPE', {
switch: [
{
is: Joi.required().valid(DB_TYPES.SQLITE),
then: {
SQLITE_FILE: Joi.string().required(),
DB_DROP_ON_START: Joi.boolean().default(false),
DB_SYNCHRONIZE: Joi.boolean().default(false),
DB_RUN_MIGRATIONS: Joi.boolean().default(true)
}
},
{
is: Joi.required().valid(DB_TYPES.POSTGRES),
then: {
POSTGRES_DB_HOST: Joi.string().hostname().required(),
POSTGRES_DB_PORT: Joi.number().port().required(),
POSTGRES_DB_USER: Joi.string().required(),
POSTGRES_DB_PASSWORD: Joi.string().required(),
POSTGRES_DB_NAME: Joi.string().required(),
DB_DROP_ON_START: Joi.boolean().default(false),
DB_SYNCHRONIZE: Joi.boolean().default(false),
DB_RUN_MIGRATIONS: Joi.boolean().default(true)
}
}
]
})
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If these variables independent from DB_TYPE
can we validate them outside of when(DB_TYPE
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
They are dependent on the DB_TYPE
. They are required for Postgres and SQLite, but not for in-memory SQLite
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then may be add .with(DB_TYPE, DP_DROP_ON_START,..)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But .with
would require them to be defined and optional first, but in the case of in-memory SQLite, they are ignored. I think this may be confusing for someone who will make interpretations based on this schema.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
anyway .with
here would require keys to exist for DB_TYPE defined, not set to a given value
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good, some minor comments/questions
|
||
To create migrations, you will need to: | ||
|
||
1. [drop db schemas](#dropping-schemas) for both Postgres and SQLite |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@artursudnik why is this step necessary?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jrhender Because during development we usually execute the service with DB_SYNC_SCHEMA_ON_START=true
and before generating a new migration we need to recreate the schema to be in sync with existing migrations only. Then, TypeORM is able to calculate the delta between modified entities and the schema defined before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the explanation, I know that isn't the first time you've explained it to me 😅
Maybe we can add the explanation to the documentation.
1. [drop db schemas](#dropping-schemas) for both Postgres and SQLite | |
1. [drop db schemas](#dropping-schemas) for both Postgres and SQLite. This is useful because, if `DB_SYNC_SCHEMA_ON_START=true` is used during development, then the db schemas needs to be in sync with existing migrations only prior to generating new ones. |
README.md
Outdated
3. ensure your `.env` file contains valid settings for both Postgres and SQLite persisted in file | ||
4. [execute](#executing-existing-migrations) existing migrations | ||
5. [generate](#generating-new-migration-files) migration files | ||
6. validate migration files |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
By "validate migration files", did you mean that the files should be manually checked to make sure that they look correct?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jrhender Yes, they need to be at least reformatted because the formatting of generated files differs from what is set for the repo. I would also execute all migrations together with new ones and then execute tests.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks. By executing tests, I guess you meant manual tests of the API?
6. validate migration files | |
6. validate migration files. This should include executing all migrations (including new ones) and then executing manually tests of the API. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I am adding a suggestion to do this for both SQLite and Postgres
51e0e49
to
7fd2be8
Compare
7fd2be8
to
792c0a8
Compare
This PR adds:
docker-compose.dev.yaml
to start developing locally easier