FastAPI-fastkit 1.0.1__py3-none-any.whl → 1.0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- fastapi_fastkit/__init__.py +10 -1
- fastapi_fastkit/backend/main.py +482 -482
- fastapi_fastkit/backend/transducer.py +106 -106
- fastapi_fastkit/cli.py +664 -631
- fastapi_fastkit/core/settings.py +155 -155
- fastapi_fastkit/fastapi_project_template/README.md +90 -90
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/.env-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/.gitignore-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/README.md-tpl +107 -107
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/requirements.txt-tpl +36 -36
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/scripts/format.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/scripts/lint.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/scripts/test.sh-tpl +6 -6
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/setup.cfg-tpl +14 -14
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/setup.py-tpl +25 -25
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/api.py-tpl +9 -9
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/api/routes/items.py-tpl +57 -57
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/core/config.py-tpl +64 -64
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/crud/items.py-tpl +21 -21
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/main.py-tpl +26 -26
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/mocks/mock_items.json-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/src/schemas/items.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/tests/conftest.py-tpl +42 -42
- fastapi_fastkit/fastapi_project_template/fastapi-async-crud/tests/test_items.py-tpl +48 -48
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/.env-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/.gitignore-tpl +191 -191
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/README.md-tpl +156 -156
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/requirements.txt-tpl +36 -36
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/scripts/format.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/scripts/lint.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/scripts/test.sh-tpl +6 -6
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/setup.cfg-tpl +12 -12
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/setup.py-tpl +27 -27
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/.DS_Store +0 -0
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/api.py-tpl +9 -9
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/api/routes/items.py-tpl +172 -172
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/core/config.py-tpl +64 -64
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/crud/items.py-tpl +21 -21
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/helper/exceptions.py-tpl +103 -103
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/helper/pagination.py-tpl +72 -72
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/main.py-tpl +39 -39
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/mocks/mock_items.json-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/base.py-tpl +49 -49
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/schemas/items.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/src/utils/documents.py-tpl +20 -20
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/tests/conftest.py-tpl +42 -42
- fastapi_fastkit/fastapi_project_template/fastapi-custom-response/tests/test_items.py-tpl +61 -61
- fastapi_fastkit/fastapi_project_template/fastapi-default/README.md-tpl +101 -101
- fastapi_fastkit/fastapi_project_template/fastapi-default/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-default/setup.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-default/src/mocks/mock_items.json-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-default/tests/conftest.py-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-default/tests/test_items.py-tpl +44 -44
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/.env-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/.gitignore-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/Dockerfile-tpl +23 -23
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/README.md-tpl +119 -119
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/requirements.txt-tpl +36 -36
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/scripts/format.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/scripts/lint.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/scripts/test.sh-tpl +6 -6
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/setup.cfg-tpl +13 -13
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/setup.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/api.py-tpl +6 -6
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/api/routes/items.py-tpl +54 -54
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/core/config.py-tpl +64 -64
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/crud/items.py-tpl +15 -15
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/main.py-tpl +26 -26
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/mocks/mock_items.json-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/src/schemas/items.py-tpl +19 -19
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/conftest.py-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-dockerized/tests/test_items.py-tpl +44 -44
- fastapi_fastkit/fastapi_project_template/fastapi-empty/.env-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-empty/setup.py-tpl +22 -22
- fastapi_fastkit/fastapi_project_template/fastapi-empty/src/core/config.py-tpl +75 -75
- fastapi_fastkit/fastapi_project_template/fastapi-empty/src/main.py-tpl +36 -36
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/.env-tpl +9 -9
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/.gitignore-tpl +30 -30
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/Dockerfile-tpl +23 -23
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/README.md-tpl +144 -144
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/alembic.ini-tpl +119 -119
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/docker-compose.yml-tpl +44 -44
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/requirements.txt-tpl +41 -41
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/format.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/lint.sh-tpl +5 -5
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/pre-start.sh-tpl +16 -16
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/run-server.sh-tpl +8 -8
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/scripts/test.sh-tpl +10 -10
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/setup.cfg-tpl +13 -13
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/setup.py-tpl +25 -25
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/README-tpl +1 -1
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/env.py-tpl +85 -85
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/script.py.mako-tpl +26 -26
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/alembic/versions/bedcdc35b64a_first_alembic.py-tpl +37 -37
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/api.py-tpl +11 -11
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/deps.py-tpl +19 -19
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/api/routes/items.py-tpl +109 -109
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/config.py-tpl +88 -88
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/core/db.py-tpl +53 -53
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/crud/items.py-tpl +102 -102
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/main.py-tpl +45 -45
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/schemas/items.py-tpl +54 -54
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/utils/backend_pre_start.py-tpl +39 -39
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/utils/init_data.py-tpl +23 -23
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/src/utils/tests_pre_start.py-tpl +39 -39
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/conftest.py-tpl +63 -63
- fastapi_fastkit/fastapi_project_template/fastapi-psql-orm/tests/test_items.py-tpl +44 -44
- fastapi_fastkit/fastapi_project_template/modules/api/routes/new_route.py-tpl +64 -64
- fastapi_fastkit/fastapi_project_template/modules/crud/new_route.py-tpl +29 -29
- fastapi_fastkit/fastapi_project_template/modules/schemas/new_route.py-tpl +23 -23
- fastapi_fastkit/logs/fastkit_debug_20250702_161423.log +21 -0
- fastapi_fastkit/logs/fastkit_debug_20250702_162706.log +20 -0
- fastapi_fastkit/logs/fastkit_debug_20250702_163010.log +21 -0
- fastapi_fastkit/logs/fastkit_debug_20250702_163526.log +21 -0
- fastapi_fastkit/logs/fastkit_debug_20250702_164053.log +21 -0
- fastapi_fastkit/logs/fastkit_debug_20250702_165220.log +21 -0
- fastapi_fastkit/logs/fastkit_debug_20250702_165647.log +21 -0
- fastapi_fastkit/utils/logging.py +112 -1
- fastapi_fastkit/utils/main.py +140 -129
- {fastapi_fastkit-1.0.1.dist-info → fastapi_fastkit-1.0.2.dist-info}/METADATA +13 -2
- {fastapi_fastkit-1.0.1.dist-info → fastapi_fastkit-1.0.2.dist-info}/RECORD +127 -119
- {fastapi_fastkit-1.0.1.dist-info → fastapi_fastkit-1.0.2.dist-info}/licenses/LICENSE +21 -21
- {fastapi_fastkit-1.0.1.dist-info → fastapi_fastkit-1.0.2.dist-info}/WHEEL +0 -0
- {fastapi_fastkit-1.0.1.dist-info → fastapi_fastkit-1.0.2.dist-info}/entry_points.txt +0 -0
|
@@ -1,64 +1,64 @@
|
|
|
1
|
-
# --------------------------------------------------------------------------
|
|
2
|
-
# Configuration module
|
|
3
|
-
# --------------------------------------------------------------------------
|
|
4
|
-
import secrets
|
|
5
|
-
import warnings
|
|
6
|
-
from typing import Annotated, Any, Literal
|
|
7
|
-
|
|
8
|
-
from pydantic import AnyUrl, BeforeValidator, computed_field, model_validator
|
|
9
|
-
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
10
|
-
from typing_extensions import Self
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def parse_cors(v: Any) -> list[str] | str:
|
|
14
|
-
if isinstance(v, str) and not v.startswith("["):
|
|
15
|
-
return [i.strip() for i in v.split(",")]
|
|
16
|
-
elif isinstance(v, list | str):
|
|
17
|
-
return v
|
|
18
|
-
raise ValueError(v)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class Settings(BaseSettings):
|
|
22
|
-
model_config = SettingsConfigDict(
|
|
23
|
-
env_file=".env",
|
|
24
|
-
env_ignore_empty=True,
|
|
25
|
-
extra="ignore",
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
SECRET_KEY: str = secrets.token_urlsafe(32)
|
|
29
|
-
ENVIRONMENT: Literal["development", "production"] = "development"
|
|
30
|
-
|
|
31
|
-
CLIENT_ORIGIN: str = ""
|
|
32
|
-
|
|
33
|
-
BACKEND_CORS_ORIGINS: Annotated[list[AnyUrl] | str, BeforeValidator(parse_cors)] = (
|
|
34
|
-
[]
|
|
35
|
-
)
|
|
36
|
-
|
|
37
|
-
@computed_field # type: ignore[prop-decorator]
|
|
38
|
-
@property
|
|
39
|
-
def all_cors_origins(self) -> list[str]:
|
|
40
|
-
return [str(origin).rstrip("/") for origin in self.BACKEND_CORS_ORIGINS] + [
|
|
41
|
-
self.CLIENT_ORIGIN
|
|
42
|
-
]
|
|
43
|
-
|
|
44
|
-
PROJECT_NAME: str = "<project_name>"
|
|
45
|
-
|
|
46
|
-
def _check_default_secret(self, var_name: str, value: str | None) -> None:
|
|
47
|
-
if value == "changethis":
|
|
48
|
-
message = (
|
|
49
|
-
f'The value of {var_name} is "changethis", '
|
|
50
|
-
"for security, please change it, at least for deployments."
|
|
51
|
-
)
|
|
52
|
-
if self.ENVIRONMENT == "development":
|
|
53
|
-
warnings.warn(message, stacklevel=1)
|
|
54
|
-
else:
|
|
55
|
-
raise ValueError(message)
|
|
56
|
-
|
|
57
|
-
@model_validator(mode="after")
|
|
58
|
-
def _enforce_non_default_secrets(self) -> Self:
|
|
59
|
-
self._check_default_secret("SECRET_KEY", self.SECRET_KEY)
|
|
60
|
-
|
|
61
|
-
return self
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
settings = Settings() # type: ignore
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Configuration module
|
|
3
|
+
# --------------------------------------------------------------------------
|
|
4
|
+
import secrets
|
|
5
|
+
import warnings
|
|
6
|
+
from typing import Annotated, Any, Literal
|
|
7
|
+
|
|
8
|
+
from pydantic import AnyUrl, BeforeValidator, computed_field, model_validator
|
|
9
|
+
from pydantic_settings import BaseSettings, SettingsConfigDict
|
|
10
|
+
from typing_extensions import Self
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def parse_cors(v: Any) -> list[str] | str:
|
|
14
|
+
if isinstance(v, str) and not v.startswith("["):
|
|
15
|
+
return [i.strip() for i in v.split(",")]
|
|
16
|
+
elif isinstance(v, list | str):
|
|
17
|
+
return v
|
|
18
|
+
raise ValueError(v)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Settings(BaseSettings):
|
|
22
|
+
model_config = SettingsConfigDict(
|
|
23
|
+
env_file=".env",
|
|
24
|
+
env_ignore_empty=True,
|
|
25
|
+
extra="ignore",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
SECRET_KEY: str = secrets.token_urlsafe(32)
|
|
29
|
+
ENVIRONMENT: Literal["development", "production"] = "development"
|
|
30
|
+
|
|
31
|
+
CLIENT_ORIGIN: str = ""
|
|
32
|
+
|
|
33
|
+
BACKEND_CORS_ORIGINS: Annotated[list[AnyUrl] | str, BeforeValidator(parse_cors)] = (
|
|
34
|
+
[]
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
@computed_field # type: ignore[prop-decorator]
|
|
38
|
+
@property
|
|
39
|
+
def all_cors_origins(self) -> list[str]:
|
|
40
|
+
return [str(origin).rstrip("/") for origin in self.BACKEND_CORS_ORIGINS] + [
|
|
41
|
+
self.CLIENT_ORIGIN
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
PROJECT_NAME: str = "<project_name>"
|
|
45
|
+
|
|
46
|
+
def _check_default_secret(self, var_name: str, value: str | None) -> None:
|
|
47
|
+
if value == "changethis":
|
|
48
|
+
message = (
|
|
49
|
+
f'The value of {var_name} is "changethis", '
|
|
50
|
+
"for security, please change it, at least for deployments."
|
|
51
|
+
)
|
|
52
|
+
if self.ENVIRONMENT == "development":
|
|
53
|
+
warnings.warn(message, stacklevel=1)
|
|
54
|
+
else:
|
|
55
|
+
raise ValueError(message)
|
|
56
|
+
|
|
57
|
+
@model_validator(mode="after")
|
|
58
|
+
def _enforce_non_default_secrets(self) -> Self:
|
|
59
|
+
self._check_default_secret("SECRET_KEY", self.SECRET_KEY)
|
|
60
|
+
|
|
61
|
+
return self
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
settings = Settings() # type: ignore
|
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
# --------------------------------------------------------------------------
|
|
2
|
-
# Item CRUD method module
|
|
3
|
-
# --------------------------------------------------------------------------
|
|
4
|
-
import json
|
|
5
|
-
import os
|
|
6
|
-
from typing import List
|
|
7
|
-
|
|
8
|
-
import aiofiles # type: ignore
|
|
9
|
-
|
|
10
|
-
MOCK_FILE = os.path.join(os.path.dirname(__file__), "../mocks/mock_items.json")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
async def read_items():
|
|
14
|
-
async with aiofiles.open(MOCK_FILE, mode="r") as file:
|
|
15
|
-
contents = await file.read()
|
|
16
|
-
return json.loads(contents)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
async def write_items(items: List[dict]):
|
|
20
|
-
async with aiofiles.open(MOCK_FILE, mode="w") as file:
|
|
21
|
-
await file.write(json.dumps(items, indent=2))
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Item CRUD method module
|
|
3
|
+
# --------------------------------------------------------------------------
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from typing import List
|
|
7
|
+
|
|
8
|
+
import aiofiles # type: ignore
|
|
9
|
+
|
|
10
|
+
MOCK_FILE = os.path.join(os.path.dirname(__file__), "../mocks/mock_items.json")
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
async def read_items():
|
|
14
|
+
async with aiofiles.open(MOCK_FILE, mode="r") as file:
|
|
15
|
+
contents = await file.read()
|
|
16
|
+
return json.loads(contents)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
async def write_items(items: List[dict]):
|
|
20
|
+
async with aiofiles.open(MOCK_FILE, mode="w") as file:
|
|
21
|
+
await file.write(json.dumps(items, indent=2))
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
# --------------------------------------------------------------------------
|
|
2
|
-
# Main server application module
|
|
3
|
-
# --------------------------------------------------------------------------
|
|
4
|
-
from contextlib import asynccontextmanager
|
|
5
|
-
|
|
6
|
-
from fastapi import FastAPI
|
|
7
|
-
from starlette.middleware.cors import CORSMiddleware
|
|
8
|
-
|
|
9
|
-
from src.api.api import api_router
|
|
10
|
-
from src.core.config import settings
|
|
11
|
-
|
|
12
|
-
app = FastAPI(
|
|
13
|
-
title=settings.PROJECT_NAME,
|
|
14
|
-
)
|
|
15
|
-
|
|
16
|
-
if settings.all_cors_origins:
|
|
17
|
-
app.add_middleware(
|
|
18
|
-
CORSMiddleware,
|
|
19
|
-
allow_origins=settings.all_cors_origins,
|
|
20
|
-
allow_credentials=True,
|
|
21
|
-
allow_methods=["*"],
|
|
22
|
-
allow_headers=["*"],
|
|
23
|
-
)
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
app.include_router(api_router)
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Main server application module
|
|
3
|
+
# --------------------------------------------------------------------------
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
5
|
+
|
|
6
|
+
from fastapi import FastAPI
|
|
7
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
8
|
+
|
|
9
|
+
from src.api.api import api_router
|
|
10
|
+
from src.core.config import settings
|
|
11
|
+
|
|
12
|
+
app = FastAPI(
|
|
13
|
+
title=settings.PROJECT_NAME,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
if settings.all_cors_origins:
|
|
17
|
+
app.add_middleware(
|
|
18
|
+
CORSMiddleware,
|
|
19
|
+
allow_origins=settings.all_cors_origins,
|
|
20
|
+
allow_credentials=True,
|
|
21
|
+
allow_methods=["*"],
|
|
22
|
+
allow_headers=["*"],
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
app.include_router(api_router)
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
[
|
|
2
|
-
{
|
|
3
|
-
"id": 1,
|
|
4
|
-
"name": "Test Item 1",
|
|
5
|
-
"price": 20.0,
|
|
6
|
-
"category": "test"
|
|
7
|
-
}
|
|
8
|
-
]
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"id": 1,
|
|
4
|
+
"name": "Test Item 1",
|
|
5
|
+
"price": 20.0,
|
|
6
|
+
"category": "test"
|
|
7
|
+
}
|
|
8
|
+
]
|
|
@@ -1,22 +1,22 @@
|
|
|
1
|
-
# --------------------------------------------------------------------------
|
|
2
|
-
# Item schema module
|
|
3
|
-
# --------------------------------------------------------------------------
|
|
4
|
-
from typing import Optional
|
|
5
|
-
|
|
6
|
-
from pydantic import BaseModel, ConfigDict
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class ItemBase(BaseModel):
|
|
10
|
-
name: str
|
|
11
|
-
description: Optional[str] = None
|
|
12
|
-
price: float
|
|
13
|
-
category: str
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ItemCreate(ItemBase):
|
|
17
|
-
pass
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
class ItemResponse(ItemBase):
|
|
21
|
-
id: int
|
|
22
|
-
model_config = ConfigDict(from_attributes=True)
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Item schema module
|
|
3
|
+
# --------------------------------------------------------------------------
|
|
4
|
+
from typing import Optional
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, ConfigDict
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ItemBase(BaseModel):
|
|
10
|
+
name: str
|
|
11
|
+
description: Optional[str] = None
|
|
12
|
+
price: float
|
|
13
|
+
category: str
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ItemCreate(ItemBase):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class ItemResponse(ItemBase):
|
|
21
|
+
id: int
|
|
22
|
+
model_config = ConfigDict(from_attributes=True)
|
|
@@ -1,42 +1,42 @@
|
|
|
1
|
-
# --------------------------------------------------------------------------
|
|
2
|
-
# pytest runtime configuration module
|
|
3
|
-
# --------------------------------------------------------------------------
|
|
4
|
-
import asyncio
|
|
5
|
-
import os
|
|
6
|
-
import typing
|
|
7
|
-
from pathlib import Path
|
|
8
|
-
|
|
9
|
-
import pytest
|
|
10
|
-
import pytest_asyncio
|
|
11
|
-
from httpx import ASGITransport, AsyncClient
|
|
12
|
-
|
|
13
|
-
from src.main import app
|
|
14
|
-
|
|
15
|
-
MOCK_PATH = os.path.join(os.path.dirname(__file__), "../src/mocks/mock_items.json")
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@pytest.fixture(scope="session")
|
|
19
|
-
def event_loop(request) -> typing.Generator:
|
|
20
|
-
loop = asyncio.get_event_loop()
|
|
21
|
-
yield loop
|
|
22
|
-
loop.close()
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
@pytest_asyncio.fixture(scope="function", autouse=True)
|
|
26
|
-
def mock_data_reset():
|
|
27
|
-
# Back up original data into memory
|
|
28
|
-
mock_path = Path(__file__).parent.parent / "src/mocks/mock_items.json"
|
|
29
|
-
original_data = mock_path.read_text()
|
|
30
|
-
|
|
31
|
-
yield
|
|
32
|
-
|
|
33
|
-
# After test, restore original data
|
|
34
|
-
mock_path.write_text(original_data)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
@pytest_asyncio.fixture(scope="module")
|
|
38
|
-
async def client():
|
|
39
|
-
async with AsyncClient(
|
|
40
|
-
transport=ASGITransport(app=app), base_url="http://test"
|
|
41
|
-
) as ac:
|
|
42
|
-
yield ac
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# pytest runtime configuration module
|
|
3
|
+
# --------------------------------------------------------------------------
|
|
4
|
+
import asyncio
|
|
5
|
+
import os
|
|
6
|
+
import typing
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import pytest
|
|
10
|
+
import pytest_asyncio
|
|
11
|
+
from httpx import ASGITransport, AsyncClient
|
|
12
|
+
|
|
13
|
+
from src.main import app
|
|
14
|
+
|
|
15
|
+
MOCK_PATH = os.path.join(os.path.dirname(__file__), "../src/mocks/mock_items.json")
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@pytest.fixture(scope="session")
|
|
19
|
+
def event_loop(request) -> typing.Generator:
|
|
20
|
+
loop = asyncio.get_event_loop()
|
|
21
|
+
yield loop
|
|
22
|
+
loop.close()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@pytest_asyncio.fixture(scope="function", autouse=True)
|
|
26
|
+
def mock_data_reset():
|
|
27
|
+
# Back up original data into memory
|
|
28
|
+
mock_path = Path(__file__).parent.parent / "src/mocks/mock_items.json"
|
|
29
|
+
original_data = mock_path.read_text()
|
|
30
|
+
|
|
31
|
+
yield
|
|
32
|
+
|
|
33
|
+
# After test, restore original data
|
|
34
|
+
mock_path.write_text(original_data)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@pytest_asyncio.fixture(scope="module")
|
|
38
|
+
async def client():
|
|
39
|
+
async with AsyncClient(
|
|
40
|
+
transport=ASGITransport(app=app), base_url="http://test"
|
|
41
|
+
) as ac:
|
|
42
|
+
yield ac
|
|
@@ -1,48 +1,48 @@
|
|
|
1
|
-
# --------------------------------------------------------------------------
|
|
2
|
-
# Item endpoint testcases (Async version)
|
|
3
|
-
# --------------------------------------------------------------------------
|
|
4
|
-
import pytest
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
@pytest.mark.asyncio
|
|
8
|
-
async def test_read_all_items(client):
|
|
9
|
-
response = await client.get("/items")
|
|
10
|
-
assert response.status_code == 200
|
|
11
|
-
assert len(response.json()) == 1
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
@pytest.mark.asyncio
|
|
15
|
-
async def test_read_item_success(client):
|
|
16
|
-
response = await client.get("/items/1")
|
|
17
|
-
assert response.status_code == 200
|
|
18
|
-
assert response.json()["name"] == "Test Item 1"
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@pytest.mark.asyncio
|
|
22
|
-
async def test_read_item_not_found(client):
|
|
23
|
-
response = await client.get("/items/999")
|
|
24
|
-
assert response.status_code == 404
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
@pytest.mark.asyncio
|
|
28
|
-
async def test_create_item(client):
|
|
29
|
-
new_item = {"name": "New Item", "price": 30.0, "category": "new"}
|
|
30
|
-
response = await client.post("/items", json=new_item)
|
|
31
|
-
assert response.status_code == 201
|
|
32
|
-
assert response.json()["id"] == 2
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
@pytest.mark.asyncio
|
|
36
|
-
async def test_update_item(client):
|
|
37
|
-
updated_data = {"name": "Updated Item", "price": 15.0, "category": "updated"}
|
|
38
|
-
response = await client.put("/items/1", json=updated_data)
|
|
39
|
-
assert response.status_code == 200
|
|
40
|
-
assert response.json()["name"] == "Updated Item"
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@pytest.mark.asyncio
|
|
44
|
-
async def test_delete_item(client):
|
|
45
|
-
response = await client.delete("/items/1")
|
|
46
|
-
assert response.status_code == 200
|
|
47
|
-
response = await client.get("/items/1")
|
|
48
|
-
assert response.status_code == 404
|
|
1
|
+
# --------------------------------------------------------------------------
|
|
2
|
+
# Item endpoint testcases (Async version)
|
|
3
|
+
# --------------------------------------------------------------------------
|
|
4
|
+
import pytest
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
@pytest.mark.asyncio
|
|
8
|
+
async def test_read_all_items(client):
|
|
9
|
+
response = await client.get("/items")
|
|
10
|
+
assert response.status_code == 200
|
|
11
|
+
assert len(response.json()) == 1
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@pytest.mark.asyncio
|
|
15
|
+
async def test_read_item_success(client):
|
|
16
|
+
response = await client.get("/items/1")
|
|
17
|
+
assert response.status_code == 200
|
|
18
|
+
assert response.json()["name"] == "Test Item 1"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
@pytest.mark.asyncio
|
|
22
|
+
async def test_read_item_not_found(client):
|
|
23
|
+
response = await client.get("/items/999")
|
|
24
|
+
assert response.status_code == 404
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
@pytest.mark.asyncio
|
|
28
|
+
async def test_create_item(client):
|
|
29
|
+
new_item = {"name": "New Item", "price": 30.0, "category": "new"}
|
|
30
|
+
response = await client.post("/items", json=new_item)
|
|
31
|
+
assert response.status_code == 201
|
|
32
|
+
assert response.json()["id"] == 2
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@pytest.mark.asyncio
|
|
36
|
+
async def test_update_item(client):
|
|
37
|
+
updated_data = {"name": "Updated Item", "price": 15.0, "category": "updated"}
|
|
38
|
+
response = await client.put("/items/1", json=updated_data)
|
|
39
|
+
assert response.status_code == 200
|
|
40
|
+
assert response.json()["name"] == "Updated Item"
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@pytest.mark.asyncio
|
|
44
|
+
async def test_delete_item(client):
|
|
45
|
+
response = await client.delete("/items/1")
|
|
46
|
+
assert response.status_code == 200
|
|
47
|
+
response = await client.get("/items/1")
|
|
48
|
+
assert response.status_code == 404
|
|
@@ -1 +1 @@
|
|
|
1
|
-
SECRET_KEY=changethis
|
|
1
|
+
SECRET_KEY=changethis
|