Skip to content
This repository was archived by the owner on Feb 19, 2023. It is now read-only.

Commit 64d12a3

Browse files
authored
Add default models and Alembic (#12)
* [#9] feat: add default models, alembic, example bot/package; infra: tweak GHA * [#9] fix: missing env vars * [#9] chore: update packages and pg_client
1 parent b261a98 commit 64d12a3

File tree

21 files changed

+393
-27
lines changed

21 files changed

+393
-27
lines changed

.env_example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,7 @@ DOMAIN_LIST=
2222
# Redis
2323
REDIS_HOST=redis
2424
REDIS_PORT=6379
25+
26+
# Misc
27+
CLIENT_ID=
28+
CLIENT_SECRET=

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
working-directory: ./backend
3535
run: |
3636
pip install black==22.6.0
37-
black --check --config=pyproject.toml .
37+
black --check .
3838
3939
isort:
4040
runs-on: ubuntu-20.04
@@ -50,7 +50,7 @@ jobs:
5050
working-directory: ./backend
5151
run: |
5252
pip install isort==5.10.1
53-
isort --settings-path=pyproject.toml src
53+
isort src
5454
5555
create-release:
5656
needs: [linter, black, isort]

Makefile

Whitespace-only changes.

backend/Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ RUN apk add --no-cache \
1515
gcc \
1616
libffi-dev \
1717
g++ \
18+
git \
1819
musl-dev &&\
1920
pip3 install --upgrade pip &&\
2021
rm -rf /tmp/*

backend/alembic.ini

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

backend/migrations/README

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Generic single-database configuration with an async dbapi.

backend/migrations/env.py

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import asyncio
2+
import pathlib
3+
import sys
4+
from logging.config import fileConfig
5+
6+
from alembic import context
7+
from sqlalchemy import create_engine
8+
from sqlalchemy.engine import Connection
9+
from sqlalchemy.ext.asyncio import AsyncEngine
10+
from sqlmodel import SQLModel
11+
12+
13+
sys.path.append(str(pathlib.Path(__file__).resolve().parents[1]))
14+
15+
from src.db.utils import get_db_url # noqa
16+
from src.models import * # noqa
17+
18+
19+
POSTGRES_URL = get_db_url()
20+
21+
# this is the Alembic Config object, which provides
22+
# access to the values within the .ini file in use.
23+
config = context.config
24+
25+
# Interpret the config file for Python logging.
26+
# This line sets up loggers basically.
27+
if config.config_file_name is not None:
28+
fileConfig(config.config_file_name)
29+
30+
# add your model's MetaData object here
31+
# for 'autogenerate' support
32+
# from myapp import mymodel
33+
# target_metadata = mymodel.Base.metadata
34+
target_metadata = SQLModel.metadata
35+
36+
# other values from the config, defined by the needs of env.py,
37+
# can be acquired:
38+
# my_important_option = config.get_main_option("my_important_option")
39+
# ... etc.
40+
41+
42+
def run_migrations_offline() -> None:
43+
"""Run migrations in 'offline' mode.
44+
45+
This configures the context with just a URL
46+
and not an Engine, though an Engine is acceptable
47+
here as well. By skipping the Engine creation
48+
we don't even need a DBAPI to be available.
49+
50+
Calls to context.execute() here emit the given string to the
51+
script output.
52+
53+
"""
54+
context.configure(
55+
url=POSTGRES_URL,
56+
target_metadata=target_metadata,
57+
literal_binds=True,
58+
dialect_opts={"paramstyle": "named"},
59+
)
60+
61+
with context.begin_transaction():
62+
context.run_migrations()
63+
64+
65+
def do_run_migrations(connection: Connection) -> None:
66+
context.configure(connection=connection, target_metadata=target_metadata)
67+
68+
with context.begin_transaction():
69+
context.run_migrations()
70+
71+
72+
async def run_migrations_online() -> None:
73+
"""Run migrations in 'online' mode.
74+
75+
In this scenario we need to create an Engine
76+
and associate a connection with the context.
77+
78+
"""
79+
# connectable = AsyncEngine(
80+
# engine_from_config(
81+
# config.get_section(config.config_ini_section),
82+
# prefix="sqlalchemy.",
83+
# poolclass=pool.NullPool,
84+
# future=True,
85+
# )
86+
# )
87+
connectable = AsyncEngine(create_engine(POSTGRES_URL, echo=True, future=True))
88+
89+
async with connectable.connect() as connection:
90+
await connection.run_sync(do_run_migrations)
91+
92+
await connectable.dispose()
93+
94+
95+
if context.is_offline_mode():
96+
run_migrations_offline()
97+
else:
98+
asyncio.run(run_migrations_online())

backend/migrations/script.py.mako

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
"""${message}
2+
3+
Revision ID: ${up_revision}
4+
Revises: ${down_revision | comma,n}
5+
Create Date: ${create_date}
6+
7+
"""
8+
from alembic import op
9+
import sqlalchemy as sa
10+
import sqlmodel
11+
${imports if imports else ""}
12+
13+
# revision identifiers, used by Alembic.
14+
revision = ${repr(up_revision)}
15+
down_revision = ${repr(down_revision)}
16+
branch_labels = ${repr(branch_labels)}
17+
depends_on = ${repr(depends_on)}
18+
19+
20+
def upgrade() -> None:
21+
${upgrades if upgrades else "pass"}
22+
23+
24+
def downgrade() -> None:
25+
${downgrades if downgrades else "pass"}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
"""init
2+
3+
Revision ID: d7effbf2730a
4+
Revises:
5+
Create Date: 2022-09-03 07:30:33.398069
6+
7+
"""
8+
import sqlalchemy as sa
9+
import sqlmodel
10+
from alembic import op
11+
12+
13+
# revision identifiers, used by Alembic.
14+
revision = "d7effbf2730a"
15+
down_revision = None
16+
branch_labels = None
17+
depends_on = None
18+
19+
20+
def upgrade() -> None:
21+
# ### commands auto generated by Alembic - please adjust! ###
22+
op.create_table(
23+
"meme",
24+
sa.Column("updated_at", sa.DateTime(timezone=True), nullable=True),
25+
sa.Column("created_at", sa.DateTime(timezone=True), nullable=True),
26+
sa.Column("timestamp", sa.DateTime(timezone=True), nullable=False),
27+
sa.Column("submission_id", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
28+
sa.Column("submission_url", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
29+
sa.Column("submission_title", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
30+
sa.Column("permalink", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
31+
sa.Column("author", sqlmodel.sql.sqltypes.AutoString(), nullable=False),
32+
sa.Column("id", sqlmodel.sql.sqltypes.GUID(), nullable=False),
33+
sa.PrimaryKeyConstraint("id"),
34+
)
35+
op.create_index(op.f("ix_meme_id"), "meme", ["id"], unique=False)
36+
# ### end Alembic commands ###
37+
38+
39+
def downgrade() -> None:
40+
# ### commands auto generated by Alembic - please adjust! ###
41+
op.drop_index(op.f("ix_meme_id"), table_name="meme")
42+
op.drop_table("meme")
43+
# ### end Alembic commands ###

backend/requirements/base.txt

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
# api
2-
fastapi==0.81.0
3-
uvicorn==0.18.3
1+
2+
# utils
3+
aioredis==2.0.1
4+
alembic==1.8.1
45

56
# database
67
asyncpg==0.26.0
7-
sqlmodel==0.0.7
8-
psycopg2-binary==2.9.3
98

10-
# utils
11-
aioredis==2.0.1
9+
# api
10+
fastapi==0.81.0
1211
fastapi-limiter==0.1.4
12+
psycopg2-binary==2.9.3
13+
git+https://github.com/nickatnight/[email protected]#egg=r-dogecoin-bot
14+
sqlmodel==0.0.8
15+
uvicorn==0.18.3

0 commit comments

Comments
 (0)