Skip to content

Commit 6f96c17

Browse files
Rework project architecture
1 parent ac5fda8 commit 6f96c17

33 files changed

+872
-0
lines changed

.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# APP_CONFIG__DB__URL=postgresql+asyncpg://user:password@localhost:5432/db_name
2+
APP_CONFIG__DB__NAME=db_name
3+
APP_CONFIG__DB__PASSWORD=password
4+
APP_CONFIG__DB__USER=user
5+
APP_CONFIG__DB__HOST=localhost
6+
APP_CONFIG__DB__PORT=5432
7+
APP_CONFIG__DB__DRIVER=postgresql+asyncpg

.env-template

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
APP_CONFIG__DB__URL=postgresql+asyncpg://user:password@localhost:5432/db_name
2+
APP_CONFIG__DB__ECHO=1
3+
APP_CONFIG__DB__ECHO_POOL=False
4+
APP_CONFIG__DB__POOL_SIZE=50
5+
APP_CONFIG__DB__MAX_OVERFLOW=10

alembic.ini

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# A generic, single database configuration.
2+
3+
[alembic]
4+
# path to migration scripts.
5+
# Use forward slashes (/) also on windows to provide an os agnostic path
6+
script_location = migrations
7+
8+
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
9+
# Uncomment the line below if you want the files to be prepended with date and time
10+
file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
11+
12+
# sys.path path, will be prepended to sys.path if present.
13+
# defaults to the current working directory.
14+
prepend_sys_path = .
15+
16+
# timezone to use when rendering the date within the migration file
17+
# as well as the filename.
18+
# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library.
19+
# Any required deps can installed by adding `migrations[tz]` to the pip requirements
20+
# string value is passed to ZoneInfo()
21+
# leave blank for localtime
22+
# timezone =
23+
24+
# max length of characters to apply to the "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 migrations.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 = newline
51+
#
52+
# Use os.pathsep. Default configuration used for new projects.
53+
version_path_separator = os
54+
55+
# set to 'true' to search source files recursively
56+
# in each "version_locations" directory
57+
# new in Alembic version 1.10
58+
# recursive_version_locations = false
59+
60+
# the output encoding used when revision files
61+
# are written from script.py.mako
62+
# output_encoding = utf-8
63+
64+
sqlalchemy.url = driver://user:pass@localhost/dbname
65+
66+
67+
[post_write_hooks]
68+
# post_write_hooks defines scripts or Python functions that are run
69+
# on newly generated revision scripts. See the documentation for further
70+
# detail and examples
71+
72+
# format using "black" - use the console_scripts runner, against the "black" entrypoint
73+
# hooks = black
74+
# black.type = console_scripts
75+
# black.entrypoint = black
76+
# black.options = -l 79 REVISION_SCRIPT_FILENAME
77+
78+
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
79+
hooks = ruff
80+
ruff.type = exec
81+
# %(here)s/.venv/bin/ruff
82+
ruff.executable = %(here)s/.venv/bin/ruff
83+
ruff.options = check --fix REVISION_SCRIPT_FILENAME
84+
85+
;hooks = pre_commit
86+
;pre_commit.type = console_scripts
87+
;pre_commit.entrypoint = pre-commit
88+
;pre_commit.options = run --files REVISION_SCRIPT_FILENAME
89+
90+
# Logging configuration
91+
[loggers]
92+
keys = root,sqlalchemy,alembic
93+
94+
[handlers]
95+
keys = console
96+
97+
[formatters]
98+
keys = generic
99+
100+
[logger_root]
101+
level = WARNING
102+
handlers = console
103+
qualname =
104+
105+
[logger_sqlalchemy]
106+
level = WARNING
107+
handlers =
108+
qualname = sqlalchemy.engine
109+
110+
[logger_alembic]
111+
level = INFO
112+
handlers =
113+
qualname = alembic
114+
115+
[handler_console]
116+
class = StreamHandler
117+
args = (sys.stderr,)
118+
level = NOTSET
119+
formatter = generic
120+
121+
[formatter_generic]
122+
format = %(levelname)-5.5s [%(name)s] %(message)s
123+
datefmt = %H:%M:%S

app/__init__.py

Whitespace-only changes.

app/api/__init__.py

Whitespace-only changes.

app/api/v1/__init__.py

Whitespace-only changes.

app/api/v1/endpoints/__init__.py

Whitespace-only changes.

app/api/v1/endpoints/auth.py

Whitespace-only changes.

app/api/v1/endpoints/users.py

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
from __future__ import annotations
2+
3+
from typing import TYPE_CHECKING
4+
5+
from fastapi import (
6+
APIRouter,
7+
)
8+
9+
from app.core.schemas import UserCreateS, UserPutS, UserS
10+
from app.core.schemas.user import UserPatchS
11+
from app.exceptions.users import raise_user_already_exists_exp, raise_user_not_found_exp
12+
from app.repository import UserService
13+
from app.utils.types import SessionDep
14+
15+
if TYPE_CHECKING:
16+
from typing import Any
17+
router = APIRouter(tags=["Users"], prefix="/users")
18+
19+
20+
@router.get("", response_model=list[UserS])
21+
async def get_all_users(
22+
session: SessionDep,
23+
) -> Any:
24+
users = await UserService.get_all(session=session)
25+
return users
26+
27+
28+
@router.post("", response_model=UserS)
29+
async def create_user(
30+
session: SessionDep,
31+
user_create: UserCreateS,
32+
) -> Any:
33+
user = await UserService.create(
34+
session=session,
35+
user_create=user_create,
36+
)
37+
if user is None:
38+
raise_user_already_exists_exp()
39+
40+
return user
41+
42+
43+
@router.get("/{user_id}", response_model=UserS)
44+
async def get_user(
45+
user_id: int,
46+
session: SessionDep,
47+
) -> Any:
48+
user = await UserService.get(session=session, user_id=user_id)
49+
if user is None:
50+
raise_user_not_found_exp()
51+
return user
52+
53+
54+
@router.put("/{user_id}", response_model=UserS)
55+
async def update_user_partial(
56+
session: SessionDep,
57+
user_id: int,
58+
user_put: UserPutS,
59+
) -> Any:
60+
updated_user = await UserService.update(
61+
session=session, user_id=user_id, user_update=user_put
62+
)
63+
if updated_user is None:
64+
raise_user_not_found_exp()
65+
return updated_user
66+
67+
68+
@router.patch("/{user_id}", response_model=UserS)
69+
async def update_user(
70+
session: SessionDep,
71+
user_id: int,
72+
user_patch: UserPatchS,
73+
) -> Any:
74+
updated_user = await UserService.update(
75+
session=session,
76+
user_id=user_id,
77+
user_update=user_patch,
78+
)
79+
if updated_user is None:
80+
raise_user_not_found_exp()
81+
return updated_user
82+
83+
84+
@router.delete("/{user_id}", response_model=UserS)
85+
async def delete_user(
86+
session: SessionDep,
87+
user_id: int,
88+
) -> Any:
89+
deleted_user = await UserService.delete(session=session, user_id=user_id)
90+
if deleted_user is None:
91+
raise_user_not_found_exp()
92+
return deleted_user

app/api/v1/routes.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from fastapi import APIRouter
2+
3+
from app.api.v1.endpoints.users import router as user_router
4+
5+
api_v1_router = APIRouter(prefix="/v1")
6+
7+
router_list = [
8+
user_router,
9+
]
10+
11+
for r in router_list:
12+
api_v1_router.include_router(r)

0 commit comments

Comments
 (0)