-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
20 changed files
with
438 additions
and
54 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,9 @@ | ||
from src.api.v1.auth.deps import OAuth2BearerDepends, OAuth2PasswordRequestFormDepends, OptionalOAuth2BearerDepends | ||
from src.api.v1.auth.routes import router | ||
from src.api.v1.auth.schemas import JWT, AccessTokenResponse | ||
|
||
__all__ = [ | ||
"router", | ||
"OAuth2BearerDepends", | ||
"OptionalOAuth2BearerDepends", | ||
"OAuth2PasswordRequestFormDepends", | ||
"AccessTokenResponse", | ||
"JWT", | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
from src.api.v1.quotes.models import Quote | ||
from src.api.v1.quotes.routes import router | ||
|
||
__all__ = ["Quote"] | ||
__all__ = ["Quote", "router"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from typing import Annotated | ||
|
||
from fastapi import Depends | ||
|
||
from src.api.v1.quotes.service import QuoteService | ||
|
||
QuoteServiceDepends = Annotated[QuoteService, Depends(QuoteService)] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
from enum import StrEnum, auto | ||
|
||
|
||
class UserQuotesType(StrEnum): | ||
ALL = auto() | ||
SAVED = auto() | ||
CREATED = auto() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
from typing import Self | ||
|
||
from sqlalchemy import or_ | ||
from sqlalchemy.orm import Query | ||
|
||
from src.api.params import SearchParams | ||
from src.api.v1.authors.models import Author | ||
from src.api.v1.collections.models import Collection | ||
from src.api.v1.quotes.enums import UserQuotesType | ||
from src.api.v1.quotes.models import Quote | ||
|
||
|
||
class QuoteQuery(Query[Quote]): | ||
def filter_by_search_params(self, search_params: SearchParams) -> Self: | ||
if search_params.q: | ||
self = self.filter( | ||
or_( | ||
Quote.content.ilike(f"%{search_params.q}%"), | ||
Quote.author.has(Author.name.ilike(f"%{search_params.q}%")), | ||
) | ||
) | ||
|
||
return self.offset(search_params.offset).limit(search_params.limit) | ||
|
||
def filter_by_collection_id(self, collection_id: int) -> Self: | ||
return self.filter(Quote.collections.any(Collection.id == collection_id)) | ||
|
||
def filter_by_user_quotes_type(self, user_id: int, type: UserQuotesType) -> Self: | ||
match type: | ||
case UserQuotesType.ALL: | ||
return self.filter_by_user_id_all(user_id) | ||
case UserQuotesType.SAVED: | ||
return self.filter_by_user_id_saved(user_id) | ||
case UserQuotesType.CREATED: | ||
return self.filter_by_user_id_created(user_id) | ||
|
||
def filter_by_user_id_all(self, user_id: int) -> Self: | ||
return self.outerjoin(Quote.collections).filter( | ||
or_( | ||
Quote.collections.any((Collection.created_by_user_id == user_id)), | ||
Quote.created_by_user_id == user_id, | ||
) | ||
) | ||
|
||
def filter_by_user_id_saved(self, user_id: int) -> Self: | ||
return self.join(Quote.collections).filter( | ||
Quote.collections.any((Collection.created_by_user_id == user_id)), | ||
Quote.created_by_user_id != user_id, | ||
) | ||
|
||
def filter_by_user_id_created(self, user_id: int) -> Self: | ||
return self.filter(Quote.created_by_user_id == user_id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
from fastapi import APIRouter, HTTPException, status | ||
|
||
from src.api.deps import SearchParamsDepends | ||
from src.api.v1.authors import AuthorServiceDepends | ||
from src.api.v1.quotes.deps import QuoteServiceDepends | ||
from src.api.v1.quotes.schemas import QuoteCollectionsResponse, QuoteCreateRequest, QuoteResponse, QuoteUpdateRequest | ||
from src.api.v1.users.me.deps import CurrentUser | ||
from src.i18n import gettext as _ | ||
|
||
router = APIRouter(prefix="/quotes", tags=["Quotes"]) | ||
|
||
|
||
@router.get("/{quote_id}", response_model=QuoteCollectionsResponse) | ||
def get_quote(quote_id: int, service: QuoteServiceDepends): | ||
quote = service.get_quote_by_id(quote_id) | ||
|
||
if not quote: | ||
raise HTTPException(status.HTTP_404_NOT_FOUND, _("No quote found with the ID %s." % (quote_id,))) | ||
|
||
return quote | ||
|
||
|
||
@router.get("/", response_model=list[QuoteResponse]) | ||
def get_quotes(search_params: SearchParamsDepends, service: QuoteServiceDepends): | ||
quotes = service.get_quotes(search_params) | ||
|
||
if not quotes: | ||
raise HTTPException(status.HTTP_404_NOT_FOUND, _("No quotes found matching the provided search parameters.")) | ||
|
||
return quotes | ||
|
||
|
||
@router.post("/", response_model=QuoteResponse, status_code=status.HTTP_201_CREATED) | ||
def create_quote( | ||
args: QuoteCreateRequest, | ||
current_user: CurrentUser, | ||
quote_service: QuoteServiceDepends, | ||
author_service: AuthorServiceDepends, | ||
): | ||
if args.author_id and not author_service.get_author_by_id(args.author_id): | ||
raise HTTPException(status.HTTP_404_NOT_FOUND, _("No author found with the ID '%s'." % (args.author_id,))) | ||
return quote_service.create_quote(args, created_by_user_id=current_user.id) | ||
|
||
|
||
@router.patch("/{quote_id}", response_model=QuoteResponse) | ||
def update_quote(quote_id: int, args: QuoteUpdateRequest, current_user: CurrentUser, service: QuoteServiceDepends): | ||
quote = service.get_quote_by_id(quote_id) | ||
|
||
if not quote: | ||
raise HTTPException(status.HTTP_404_NOT_FOUND, _("No quote found with the ID %s." % (quote_id,))) | ||
if quote.created_by_user_id != current_user.id: | ||
raise HTTPException(status.HTTP_403_FORBIDDEN, _("You do not have permission to modify this quote.")) | ||
|
||
return service.update_quote(quote, args) | ||
|
||
|
||
@router.delete("/{quote_id}", status_code=status.HTTP_204_NO_CONTENT) | ||
def delete_quote(quote_id: int, current_user: CurrentUser, service: QuoteServiceDepends): | ||
quote = service.get_quote_by_id(quote_id) | ||
|
||
if not quote: | ||
raise HTTPException(status.HTTP_404_NOT_FOUND, _("No quote found with the ID %s." % (quote_id,))) | ||
if quote.created_by_user_id != current_user.id: | ||
raise HTTPException(status.HTTP_403_FORBIDDEN, _("You do not have permission to modify this quote.")) | ||
|
||
service.delete_quote(quote) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
from pydantic import BaseModel | ||
|
||
from src.api.v1.authors.schemas import AuthorResponse | ||
from src.api.v1.collections.schemas import CollectionResponse | ||
from src.api.v1.schemas import AuditResponse | ||
|
||
|
||
class QuoteResponse(AuditResponse): | ||
id: int | ||
content: str | ||
created_by_user_id: int | None | ||
author: AuthorResponse | None | ||
|
||
|
||
class QuoteCollectionsResponse(QuoteResponse): | ||
collections: list[CollectionResponse] | ||
|
||
|
||
class QuoteCreateRequest(BaseModel): | ||
content: str | ||
author_id: int | None | ||
|
||
|
||
class QuoteUpdateRequest(BaseModel): | ||
content: str | None = None | ||
author_id: int | None = None |
Oops, something went wrong.