Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
- Added more features
  • Loading branch information
python3-dev committed Sep 7, 2024
1 parent 46f1817 commit 93f3f44
Show file tree
Hide file tree
Showing 37 changed files with 1,752 additions and 274 deletions.
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DB_URI=""
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,6 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

.ruff_cache
.vscode
Binary file added cms.sqlite
Binary file not shown.
30 changes: 19 additions & 11 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,40 @@
"""FastAPI server."""

from fastapi import FastAPI, Request, status
import textwrap

from fastapi import FastAPI, status
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import ORJSONResponse

from src.core.config.api import APIConfig

app = FastAPI(
rosa = FastAPI(
title=APIConfig.TITLE,
version=APIConfig.VERSION,
description=APIConfig.DESCRIPTION,
summary=APIConfig.SUMMARY,
description=textwrap.dedent(APIConfig.DESCRIPTION),
default_response_class=ORJSONResponse,
docs_url=f"/{APIConfig.VERSION}/docs",
redoc_url=f"/{APIConfig.VERSION}/redoc",
openapi_url=f"/{APIConfig.VERSION}/openapi.json",
root_path=f"/{APIConfig.VERSION}",
redirect_slashes=True,
)

app.add_middleware(
rosa.add_middleware(
middleware_class=CORSMiddleware,
allow_origins=APIConfig.CORS_ORIGINS,
)


@app.get(
f"/{APIConfig.VERSION}/",
@rosa.get(
"/",
status_code=status.HTTP_200_OK,
response_class=ORJSONResponse,
)
def home(request: Request) -> ORJSONResponse:
def home() -> ORJSONResponse:
"""Home route."""
return ORJSONResponse(content={"content": "successful response."})


from src.cms.api import article, author, category

rosa.include_router(router=article)
rosa.include_router(router=author)
rosa.include_router(router=category)
543 changes: 314 additions & 229 deletions poetry.lock

Large diffs are not rendered by default.

46 changes: 19 additions & 27 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,25 +5,30 @@ description = "Rosa is a simple and fast content management system focussed on m
authors = ["Pratheesh Prakash <pratheeshraniprakash@gmail.com>"]
license = "GPL-3.0-or-later"
readme = "README.md"
package-mode = false

[tool.poetry.dependencies]
python = "^3.10"
fastapi = "^0.110.3"
uvicorn = { extras = ["standard"], version = "^0.29.0" }
orjson = "^3.10.2"
pydantic-settings = "^2.2.1"
email-validator = "^2.1.1"
python = "^3.12"
fastapi = "^0.112.1"
uvicorn = { extras = ["standard"], version = "0.30.6" }
orjson = "^3.10.7"
pydantic-settings = "^2.4.0"
email-validator = "^2.2.0"
httpx = "^0.27.0"
python-multipart = "^0.0.9"
itsdangerous = "^2.2.0"
pyyaml = "^6.0.1"
strawberry-graphql = { extras = ["fastapi"], version = "^0.227.2" }
pytest = "^8.2.0"
sqlalchemy = "^2.0.29"
ruff = "^0.4.2"
pyyaml = "^6.0.2"
strawberry-graphql = "^0.237.3"
pytest = "^8.3.2"
sqlalchemy = {extras = ["asyncio"], version = "2.0.32"}
ruff = "^0.6.1"
refurb = "^2.0.0"
pydocstyle = "^6.3.0"
isort = "^5.13.2"
asyncio = "^3.4.3"
aiosqlite = "^0.20.0"
asyncpg = "^0.29.0"
pytest-asyncio = "^0.23.8"


[build-system]
Expand All @@ -42,6 +47,7 @@ warn_required_dynamic_aliases = true
warn_untyped_fields = false

[tool.ruff]
src = ["src"]
target-version = "py312"
line-length = 100
exclude = [
Expand All @@ -59,6 +65,7 @@ exclude = [
".pytest_cache",
".pytype",
".ruff_cache",
".env.example",
".svn",
".tox",
".venv",
Expand All @@ -83,22 +90,7 @@ line-ending = "auto"

[tool.ruff.lint]

select = [
"E",
"F",
"C90",
"W",
"I",
"D",
"UP",
"ERA",
"PD",
"PL",
"TRY",
"NPY",
"FURB",
"RUF",
]
select = ["ALL"]

ignore = ["UP038", "D104"]

Expand Down
1 change: 1 addition & 0 deletions src/authentication/models/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .user import User
46 changes: 46 additions & 0 deletions src/authentication/models/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
"""User model."""

from sqlalchemy import ForeignKey
from sqlalchemy.orm import Mapped, mapped_column
from sqlalchemy.types import INTEGER, TEXT
from src.core.database.db import Base


class User(Base):
"""User schema."""

__tablename__: str = "user"

id: Mapped[int] = mapped_column(
name="id",
primary_key=True,
autoincrement="auto",
type_=INTEGER,
)
name: Mapped[str] = mapped_column(
name="username",
type_=TEXT,
nullable=False,
unique=True,
)

email: Mapped[str] = mapped_column(
name="email",
type_=TEXT,
nullable=False,
unique=True,
)

is_superuser: Mapped[bool] = mapped_column(default=False)

password_hash: Mapped[str] = mapped_column(
name="password_hash",
type_=TEXT,
nullable=False,
)

published_article_id: Mapped[int] = mapped_column(
ForeignKey("article.id"),
type_=INTEGER,
nullable=True,
)
11 changes: 11 additions & 0 deletions src/cms/api/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"""API routes."""

from .article import article
from .author import author
from .category import category

__all__: list[str] = [
"article",
"author",
"category",
]
52 changes: 52 additions & 0 deletions src/cms/api/article.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
"""Article routes."""

from fastapi import APIRouter, Query, status
from fastapi.responses import ORJSONResponse
from src.cms.services.articles import fetch_latest_articles_from_db
from src.core.config.api import ArticleConfig

article = APIRouter(
prefix="/article",
tags=["article"],
default_response_class=ORJSONResponse,
)


@article.get(
path="/",
response_class=ORJSONResponse,
tags=["article"],
summary="List all articles in reverse chronological order.",
description="Get the list of all articles ordered in reverse chronological order.",
deprecated=False,
name="Article",
status_code=status.HTTP_200_OK,
)
def fetch_articles(
fetch_count: int = Query(
default=ArticleConfig.DEFAULT_FETCHABLE_ARTICLES,
alias="fetch_count",
le=ArticleConfig.MAX_FETCHABLE_ARTICLES,
ge=ArticleConfig.MIN_FETCHABLE_ARTICLES,
),
) -> ORJSONResponse:
"""
Fetch articles and return.
Parameters
----------
fetch_count : int, optional
Number of articles to fetch, by default 10.
Returns
-------
ORJSONResponse
Returns list of articles in reverse chronological order.
"""
if fetch_count > ArticleConfig.MAX_FETCHABLE_ARTICLES:
fetch_count = ArticleConfig.MAX_FETCHABLE_ARTICLES
elif fetch_count < ArticleConfig.MIN_FETCHABLE_ARTICLES:
fetch_count = ArticleConfig.MIN_FETCHABLE_ARTICLES

articles = fetch_latest_articles_from_db(n=fetch_count)
return ORJSONResponse(content=articles)
80 changes: 80 additions & 0 deletions src/cms/api/author.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""Author routes."""

from fastapi import APIRouter, Query, status
from fastapi.responses import ORJSONResponse
from src.cms.services.authors import fetch_articles_by_author, fetch_authors_from_db
from src.core.config.api import ArticleConfig

author = APIRouter(
prefix="/author",
tags=["author"],
default_response_class=ORJSONResponse,
)


@author.get(
path="/",
response_class=ORJSONResponse,
tags=["author"],
summary="List all published authors.",
description="List all published authors in the alphabetic order of their first name",
deprecated=False,
name="Author",
status_code=status.HTTP_200_OK,
)
def fetch_authors(
fetch_count: int = Query(
default=ArticleConfig.DEFAULT_FETCHABLE_ARTICLES,
alias="fetch_count",
le=ArticleConfig.MAX_FETCHABLE_ARTICLES,
ge=ArticleConfig.MIN_FETCHABLE_ARTICLES,
),
) -> ORJSONResponse:
"""
Fetch authors and return.
Parameters
----------
fetch_count : int, optional
Number of authors to fetch, by default 10.
Returns
-------
ORJSONResponse
Returns list of authors in alphabetical order.
"""
if fetch_count > ArticleConfig.MAX_FETCHABLE_ARTICLES:
fetch_count = ArticleConfig.MAX_FETCHABLE_ARTICLES
elif fetch_count < ArticleConfig.MIN_FETCHABLE_ARTICLES:
fetch_count = ArticleConfig.MIN_FETCHABLE_ARTICLES

authors = fetch_authors_from_db()
return ORJSONResponse(content=authors)


@author.get(
path="/{author_slug}/",
response_class=ORJSONResponse,
tags=["author", "article"],
summary="List all articles in reverse chronological order.",
description="Get the list of all articles ordered in reverse chronological order.",
deprecated=False,
name="Category-wise articles",
status_code=status.HTTP_200_OK,
)
def fetch_articles(author_slug: str) -> ORJSONResponse:
"""
Fetch articles by author and return.
Parameters
----------
fetch_count : int, optional
Number of articles to fetch, by default 10.
Returns
-------
ORJSONResponse
Returns list of articles in reverse chronological order.
"""
articles = fetch_articles_by_author(author_slug=author_slug)
return ORJSONResponse(content=articles)
Loading

0 comments on commit 93f3f44

Please sign in to comment.