diff --git a/.env.template b/.env.template index 2945876..eee4f22 100644 --- a/.env.template +++ b/.env.template @@ -32,6 +32,7 @@ DB_PORT=5432 DB_NAME=addressbook DB_USER=addressbook DB_PASSWORD=addressbook +DB_FLYWAY_PATH=/usr/local/bin/flyway # Server UVICORN_HOST=0.0.0.0 diff --git a/app/lib/settings.py b/app/lib/settings.py index 8b6036e..e7c7ed9 100644 --- a/app/lib/settings.py +++ b/app/lib/settings.py @@ -81,6 +81,21 @@ class DatabaseSettings(BaseEnvSettings): NAME: str = "db-name" USER: str = "db-user" PASSWORD: str = "db-password" + FLYWAY_PATH: str = "/usr/local/bin/flyway" + + +class TestingSettings(BaseEnvSettings): + class Config: + env_prefix = "TESTS_" + case_sensitive = True + + DB_HOST: str = "localhost" + DB_PORT: int = 5432 + DB_NAME: str = "test_db-name" + DB_USER: str = "db-user" + DB_PASSWORD: str = "db-password" + DROP_DATABASE_BEFORE_TESTS = True + DROP_DATABASE_AFTER_TESTS = True class ServerSettings(BaseEnvSettings): @@ -108,3 +123,4 @@ app = AppSettings.parse_obj({}) db = DatabaseSettings.parse_obj({}) openapi = OpenAPISettings.parse_obj({}) server = ServerSettings.parse_obj({}) +testing = TestingSettings.parse_obj({}) diff --git a/migrate.py b/migrate.py new file mode 100644 index 0000000..03e6ecf --- /dev/null +++ b/migrate.py @@ -0,0 +1,44 @@ +import subprocess +from dataclasses import dataclass + +from app.lib import settings + + +@dataclass +class DatabaseConfig: + HOST: str + PORT: int + NAME: str + USER: str + PASSWORD: str + + +def migrate(conf: DatabaseConfig) -> None: + args = [ + settings.db.FLYWAY_PATH, + "migrate", + f"-url=jdbc:postgresql://{conf.HOST}:{conf.PORT}/{conf.NAME}", + f"-user={conf.USER}", + f"-password={conf.PASSWORD}", + "-configFiles=migrations/flyway.conf", + ] + + subprocess.run( + args=args, + encoding="utf-8", + ) + + +def main() -> None: + conf = DatabaseConfig( + HOST=settings.db.HOST, + PORT=settings.db.PORT, + NAME=settings.db.NAME, + USER=settings.db.USER, + PASSWORD=settings.db.PASSWORD, + ) + migrate(conf) + + +if __name__ == "__main__": + main() diff --git a/migrations/0001-initial.sql b/migrations/0001-initial.sql deleted file mode 100644 index c2b17fc..0000000 --- a/migrations/0001-initial.sql +++ /dev/null @@ -1,29 +0,0 @@ -CREATE TABLE cities -( - id uuid DEFAULT gen_random_uuid() NOT NULL, - name varchar(50) NOT NULL, - postal_code varchar(10), - created_at timestamp WITH TIME ZONE DEFAULT NOW() NOT NULL, - updated_at timestamp WITH TIME ZONE DEFAULT NOW() NOT NULL, - sa_orm_sentinel uuid -); - -CREATE UNIQUE INDEX cities_id_uindex - ON cities (id); - -CREATE TABLE users -( - id uuid DEFAULT gen_random_uuid() NOT NULL, - first_name varchar(50), - last_name varchar(50), - city_id uuid - CONSTRAINT users_cities_id_fk - REFERENCES cities (), - created_at timestamp WITH TIME ZONE DEFAULT NOW() NOT NULL, - updated_at timestamp WITH TIME ZONE DEFAULT NOW() NOT NULL, - sa_orm_sentinel uuid -); - -CREATE UNIQUE INDEX users_id_uindex - ON users (id); - diff --git a/migrations/V0000__initial.sql b/migrations/V0000__initial.sql new file mode 100644 index 0000000..e69de29 diff --git a/migrations/V0001__create_cities.sql b/migrations/V0001__create_cities.sql new file mode 100644 index 0000000..7c6a983 --- /dev/null +++ b/migrations/V0001__create_cities.sql @@ -0,0 +1,12 @@ +CREATE TABLE IF NOT EXISTS cities +( + id uuid DEFAULT gen_random_uuid() NOT NULL, + name varchar(50) NOT NULL, + postal_code varchar(10), + created_at timestamp WITH TIME ZONE DEFAULT NOW() NOT NULL, + updated_at timestamp WITH TIME ZONE DEFAULT NOW() NOT NULL, + sa_orm_sentinel uuid +); + +CREATE UNIQUE INDEX IF NOT EXISTS cities_id_uindex + ON cities (id); diff --git a/migrations/V0002__create_users.sql b/migrations/V0002__create_users.sql new file mode 100644 index 0000000..a03e5dc --- /dev/null +++ b/migrations/V0002__create_users.sql @@ -0,0 +1,15 @@ +CREATE TABLE IF NOT EXISTS users +( + id uuid DEFAULT gen_random_uuid() NOT NULL, + first_name varchar(50), + last_name varchar(50), + city_id uuid + CONSTRAINT users_cities_id_fk + REFERENCES cities (id), + created_at timestamp WITH TIME ZONE DEFAULT NOW() NOT NULL, + updated_at timestamp WITH TIME ZONE DEFAULT NOW() NOT NULL, + sa_orm_sentinel uuid +); + +CREATE UNIQUE INDEX IF NOT EXISTS users_id_uindex + ON users (id); diff --git a/migrations/flyway.conf b/migrations/flyway.conf new file mode 100644 index 0000000..899903e --- /dev/null +++ b/migrations/flyway.conf @@ -0,0 +1,53 @@ +flyway.connectRetries=0 +# The SQL statements to run to initialize a new database connection immediately after opening it. (default: none) +flyway.initSql=set client_min_messages to warning; + + +flyway.locations=filesystem:migrations + + +# Comma-separated list of directories containing JDBC drivers and Java-based migrations. +# (default: /jars) +# flyway.jarDirs= + +flyway.sqlMigrationPrefix=V +flyway.repeatableSqlMigrationPrefix=R +flyway.sqlMigrationSeparator=__ +flyway.sqlMigrationSuffixes=sql +flyway.placeholderReplacement=true + + +# Placeholders to replace in Sql migrations +# flyway.placeholders.user= +# flyway.placeholders.my_other_placeholder= + +flyway.placeholderPrefix=${ +flyway.placeholderSuffix=} + +flyway.validateOnMigrate=false +flyway.cleanOnValidationError=false +flyway.cleanDisabled=true +flyway.baselineVersion=0000 + +flyway.baselineOnMigrate=false + +flyway.outOfOrder=true +flyway.validateMigrationNaming=true +# This allows you to tie in custom code and logic to the Flyway lifecycle notifications (default: empty). +# Set this to a comma-separated list of fully qualified class names of org.flywaydb.core.api.callback.Callback +# implementations. +# flyway.callbacks= + +# If set to true, default built-in callbacks (sql) are skipped and only custom callback as +# defined by 'flyway.callbacks' are used. (default: false) +# flyway.skipDefaultCallbacks= + +flyway.ignoreMissingMigrations=false +flyway.ignoreIgnoredMigrations=false +flyway.ignorePendingMigrations=false +flyway.ignoreFutureMigrations=true + +flyway.mixed=false +flyway.group=false + +flyway.installedBy=