Skip to content

Commit ecf1d0b

Browse files
author
ColeMeikle
committed
#377 Setup database migrations (Cole)
1 parent ab90095 commit ecf1d0b

File tree

12 files changed

+522
-48
lines changed

12 files changed

+522
-48
lines changed

Dockerfile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ FROM python:3.11-slim
33
WORKDIR /app
44
COPY requirements.txt /app/
55
COPY /gs/backend /app/
6+
RUN apt-get update && apt-get install -y gcc build-essential
67
RUN pip install -r requirements.txt
78
EXPOSE 8000
89

9-
CMD ["fastapi", "run", "gs/backend/main.py"]
10+
#CMD ["fastapi", "run", "gs/backend/main.py"]
11+
CMD ["uvicorn", "gs.backend.main:app", "--host", "0.0.0.0", "--port", "8000"]

alembic.ini

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
# A generic, single database configuration.
2+
3+
[alembic]
4+
# path to migration scripts
5+
script_location = migrations
6+
7+
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
8+
# Uncomment the line below if you want the files to be prepended with date and time
9+
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
10+
# for all available tokens
11+
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
12+
13+
# sys.path path, will be prepended to sys.path if present.
14+
# defaults to the current working directory.
15+
prepend_sys_path = .
16+
17+
# timezone to use when rendering the date within the migration file
18+
# as well as the filename.
19+
# If specified, requires the python-dateutil library that can be
20+
# installed by adding `alembic[tz]` to the pip requirements
21+
# string value is passed to dateutil.tz.gettz()
22+
# leave blank for localtime
23+
# timezone =
24+
25+
# max length of characters to apply to the
26+
# "slug" field
27+
# truncate_slug_length = 40
28+
29+
# set to 'true' to run the environment during
30+
# the 'revision' command, regardless of autogenerate
31+
# revision_environment = false
32+
33+
# set to 'true' to allow .pyc and .pyo files without
34+
# a source .py file to be detected as revisions in the
35+
# versions/ directory
36+
# sourceless = false
37+
38+
# version location specification; This defaults
39+
# to migrations/versions. When using multiple version
40+
# directories, initial revisions must be specified with --version-path.
41+
# The path separator used here should be the separator specified by "version_path_separator" below.
42+
# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
43+
44+
# version path separator; As mentioned above, this is the character used to split
45+
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
46+
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
47+
# Valid values for version_path_separator are:
48+
#
49+
# version_path_separator = :
50+
# version_path_separator = ;
51+
# version_path_separator = space
52+
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
53+
54+
# set to 'true' to search source files recursively
55+
# in each "version_locations" directory
56+
# new in Alembic version 1.10
57+
# recursive_version_locations = false
58+
59+
# the output encoding used when revision files
60+
# are written from script.py.mako
61+
# output_encoding = utf-8
62+
63+
sqlalchemy.url = driver://user:pass@localhost/dbname
64+
65+
66+
[post_write_hooks]
67+
# post_write_hooks defines scripts or Python functions that are run
68+
# on newly generated revision scripts. See the documentation for further
69+
# detail and examples
70+
71+
# format using "black" - use the console_scripts runner, against the "black" entrypoint
72+
# hooks = black
73+
# black.type = console_scripts
74+
# black.entrypoint = black
75+
# black.options = -l 79 REVISION_SCRIPT_FILENAME
76+
77+
# Logging configuration
78+
[loggers]
79+
keys = root,sqlalchemy,alembic
80+
81+
[handlers]
82+
keys = console
83+
84+
[formatters]
85+
keys = generic
86+
87+
[logger_root]
88+
level = WARN
89+
handlers = console
90+
qualname =
91+
92+
[logger_sqlalchemy]
93+
level = WARN
94+
handlers =
95+
qualname = sqlalchemy.engine
96+
97+
[logger_alembic]
98+
level = INFO
99+
handlers =
100+
qualname = alembic
101+
102+
[handler_console]
103+
class = StreamHandler
104+
args = (sys.stderr,)
105+
level = NOTSET
106+
formatter = generic
107+
108+
[formatter_generic]
109+
format = %(levelname)-5.5s [%(name)s] %(message)s
110+
datefmt = %H:%M:%S

gs/backend/data/database/engine.py

Lines changed: 5 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22
from sqlmodel import Session, create_engine, text
33

44
from gs.backend.config.config import DATABASE_CONNECTION_STRING
5-
from gs.backend.data.tables.aro_user_tables import ARO_USER_SCHEMA_METADATA, ARO_USER_SCHEMA_NAME
6-
from gs.backend.data.tables.main_tables import MAIN_SCHEMA_METADATA, MAIN_SCHEMA_NAME
7-
from gs.backend.data.tables.transactional_tables import TRANSACTIONAL_SCHEMA_METADATA, TRANSACTIONAL_SCHEMA_NAME
5+
from gs.backend.data.tables.aro_user_tables import ARO_USER_SCHEMA_NAME
6+
from gs.backend.data.tables.main_tables import MAIN_SCHEMA_NAME
7+
from gs.backend.data.tables.transactional_tables import TRANSACTIONAL_SCHEMA_NAME
88

99

1010
def get_db_engine() -> Engine:
@@ -43,26 +43,11 @@ def _create_schemas(session: Session) -> None:
4343
connection.commit()
4444

4545

46-
def _create_tables(session: Session) -> None:
47-
"""
48-
Creates the tables.
49-
50-
:warning: This assumes the relevant schemas were already created
51-
52-
:param session: The session for which to create the schemas
53-
"""
54-
metadatas = [MAIN_SCHEMA_METADATA, ARO_USER_SCHEMA_METADATA, TRANSACTIONAL_SCHEMA_METADATA]
55-
connection = session.connection()
56-
for metadata in metadatas:
57-
metadata.create_all(connection)
58-
connection.commit()
59-
60-
6146
def setup_database(session: Session) -> None:
6247
"""
63-
Creates the schemas and tables for the session.
48+
Creates the schemas for the session.
49+
Table creation is now handled by Alembic migrations
6450
6551
:param session: The session for which to create the schemas
6652
"""
6753
_create_schemas(session)
68-
_create_tables(session)

gs/backend/data/database/utils.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
def to_foreign_key_value(
2+
schema_name: str,
23
foreign_table_name: str,
34
foreign_column_name: str = "id",
45
) -> str:
@@ -17,5 +18,5 @@ def to_foreign_key_value(
1718
1819
:return: The foreign key value
1920
"""
20-
foreign_key_value = f"{foreign_table_name}.{foreign_column_name}"
21+
foreign_key_value = f"{schema_name}.{foreign_table_name}.{foreign_column_name}"
2122
return foreign_key_value

gs/backend/data/tables/aro_user_tables.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from pydantic import EmailStr
77
from sqlalchemy import Enum
88
from sqlalchemy.dialects.postgresql import UUID as DB_UUID
9-
from sqlalchemy.schema import Column, ForeignKey, MetaData
9+
from sqlalchemy.schema import Column, ForeignKey
1010
from sqlmodel import Field
1111

1212
from gs.backend.config.data_config import (
@@ -20,7 +20,6 @@
2020

2121
# Schema information
2222
ARO_USER_SCHEMA_NAME: Final[str] = "aro_users"
23-
ARO_USER_SCHEMA_METADATA: Final[MetaData] = MetaData(ARO_USER_SCHEMA_NAME)
2423

2524
# Table names in database
2625
ARO_USER_TABLE_NAME: Final[str] = "users_data"
@@ -54,8 +53,8 @@ class AROUsers(BaseSQLModel, table=True):
5453
phone_number: str
5554

5655
# table information
57-
metadata = ARO_USER_SCHEMA_METADATA
5856
__tablename__ = ARO_USER_TABLE_NAME
57+
__table_args__ = {"schema": ARO_USER_SCHEMA_NAME}
5958

6059

6160
class AROUserLogin(BaseSQLModel, table=True):
@@ -81,8 +80,8 @@ class AROUserLogin(BaseSQLModel, table=True):
8180
user_data_id: UUID = Column(DB_UUID, ForeignKey(AROUsers.id)) # type: ignore
8281
email_verification_token: str = Field(min_length=1, max_length=200)
8382

84-
metadata = ARO_USER_SCHEMA_METADATA
8583
__tablename__ = ARO_USER_LOGIN
84+
__table_args__ = {"schema": ARO_USER_SCHEMA_NAME}
8685

8786

8887
class AROUserAuthToken(BaseSQLModel, table=True):
@@ -104,5 +103,5 @@ class AROUserAuthToken(BaseSQLModel, table=True):
104103
expiry: datetime = Field()
105104
auth_type: AROAuthToken = Field(sa_column=Column(Enum(AROAuthToken, name="auth_type"), nullable=False))
106105

107-
metadata = ARO_USER_SCHEMA_METADATA
108106
__tablename__ = ARO_AUTH_TOKEN
107+
__table_args__ = {"schema": ARO_USER_SCHEMA_NAME}

gs/backend/data/tables/main_tables.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
from typing import Final, TypeAlias
22

33
from sqlalchemy import Integer
4-
from sqlalchemy.schema import MetaData
54
from sqlmodel import Field
65

76
from gs.backend.data.tables.base_model import BaseSQLModel
87

98
# Schema information
109
MAIN_SCHEMA_NAME: Final[str] = "main"
11-
MAIN_SCHEMA_METADATA: Final[MetaData] = MetaData(MAIN_SCHEMA_NAME)
1210

1311
# Table names in database
1412
MAIN_COMMAND_TABLE_NAME: Final[str] = "commands"
@@ -35,8 +33,8 @@ class MainCommand(BaseSQLModel, table=True):
3533
total_size: int = Field(gt=0)
3634

3735
# table information
38-
metadata = MAIN_SCHEMA_METADATA
3936
__tablename__ = MAIN_COMMAND_TABLE_NAME
37+
__table_args__ = {"schema": MAIN_SCHEMA_NAME}
4038

4139

4240
class MainTelemetry(BaseSQLModel, table=True):
@@ -54,5 +52,5 @@ class MainTelemetry(BaseSQLModel, table=True):
5452
total_size: int = Field(gt=0)
5553

5654
# table information
57-
metadata = MAIN_SCHEMA_METADATA
5855
__tablename__ = MAIN_TELEMETRY_TABLE_NAME
56+
__table_args__ = {"schema": MAIN_SCHEMA_NAME}

0 commit comments

Comments
 (0)