Skip to content

Commit a01c903

Browse files
committed
New version
1 parent dbf6eea commit a01c903

File tree

83 files changed

+166
-57
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

83 files changed

+166
-57
lines changed

.gitignore

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
docker/data
22
__pycache__
3-
.idea
3+
.idea
4+
.DS_Store
5+
app/.DS_Store
6+
docker/.DS_Store

app/.DS_Store

0 Bytes
Binary file not shown.

app/.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
1-
__pycache__
1+
__pycache__
2+
.DS_Store

app/__pycache__/main.cpython-39.pyc

0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
0 Bytes
Binary file not shown.
Binary file not shown.
Binary file not shown.
288 Bytes
Binary file not shown.
-24 Bytes
Binary file not shown.
-2 Bytes
Binary file not shown.

app/api/v1/auth/routes.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
11
from fastapi import APIRouter
22

3+
from api.v1.auth.schemas.responses import TokenResponse
34
from api.v1.auth.view import AuthView
5+
from api.v1.register.schemas.responses import UserNotFound
6+
from api.v1.shared.schemas.responses import ValidationErrorResponse
47

58
auth_views = AuthView()
69

710
auth_router = APIRouter(
8-
responses={404: {"description": "Not found"}},
11+
responses={
12+
404: {"description": "Not found", "model": UserNotFound},
13+
422: {"description": "Validation Error", "model": ValidationErrorResponse},
14+
},
15+
916
)
1017

1118
auth_router.add_api_route(
@@ -15,6 +22,7 @@
1522
description="User Authentication and create access token",
1623
name="Authentication-AccessToken",
1724
response_model_by_alias=False,
25+
response_model=TokenResponse,
1826
)
1927

2028
auth_router.add_api_route(
@@ -24,4 +32,5 @@
2432
description="Create access token from refresh token",
2533
name="Authentication-AccessToken-From-RefreshToken",
2634
response_model_by_alias=False,
35+
response_model=TokenResponse,
2736
)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

app/api/v1/auth/schemas/errors.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,27 @@
55

66
class CustomUnauthorizedError(BaseAuthenticationError):
77
status = StatusType.ERROR.value
8-
status_code = ExceptionStatus.UNAUTHORIZED.status_code
98
status_type = ExceptionStatus.UNAUTHORIZED.name
109
message = ExceptionStatus.UNAUTHORIZED.message
10+
_status_code = ExceptionStatus.UNAUTHORIZED.status_code
1111

1212

1313
class CustomInvalidTokenError(BaseAuthenticationError):
1414
status = StatusType.ERROR.value
15-
status_code = ExceptionStatus.INVALID_TOKEN.status_code
1615
status_type = ExceptionStatus.INVALID_TOKEN.name
1716
message = ExceptionStatus.INVALID_TOKEN.message
17+
_status_code = ExceptionStatus.INVALID_TOKEN.status_code
1818

1919

2020
class CustomDecodeTokenError(BaseAuthenticationError):
2121
status = StatusType.ERROR.value
22-
status_code = ExceptionStatus.UNPROCESSABLE_TOKEN.status_code
2322
status_type = ExceptionStatus.UNPROCESSABLE_TOKEN.name
2423
message = ExceptionStatus.UNPROCESSABLE_TOKEN.message
24+
_status_code = ExceptionStatus.UNPROCESSABLE_TOKEN.status_code
2525

2626

2727
class CustomExpiredTokenError(BaseAuthenticationError):
2828
status = StatusType.ERROR.value
29-
status_code = ExceptionStatus.TOKEN_EXPIRED.status_code
3029
status_type = ExceptionStatus.TOKEN_EXPIRED.name
3130
message = ExceptionStatus.TOKEN_EXPIRED.message
31+
_status_code = ExceptionStatus.TOKEN_EXPIRED.status_code

app/api/v1/auth/schemas/exceptions.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,27 +5,27 @@
55

66
class PasswordDoesNotMatchException(CustomException):
77
status = StatusType.ERROR.value
8-
status_code = ExceptionStatus.PASSWORD_NOT_MATCHED.status_code
98
status_type = ExceptionStatus.PASSWORD_NOT_MATCHED.name
109
message = ExceptionStatus.PASSWORD_NOT_MATCHED.message
10+
_status_code = ExceptionStatus.PASSWORD_NOT_MATCHED.status_code
1111

1212

1313
class InvalidTokenException(CustomException):
1414
status = StatusType.ERROR.value
15-
status_code = ExceptionStatus.INVALID_TOKEN.status_code
1615
status_type = ExceptionStatus.INVALID_TOKEN.name
1716
message = ExceptionStatus.INVALID_TOKEN.message
17+
_status_code = ExceptionStatus.INVALID_TOKEN.status_code
1818

1919

2020
class DecodeTokenException(CustomException):
2121
status = StatusType.ERROR.value
22-
status_code = ExceptionStatus.UNPROCESSABLE_TOKEN.status_code
2322
status_type = ExceptionStatus.UNPROCESSABLE_TOKEN.name
2423
message = ExceptionStatus.UNPROCESSABLE_TOKEN.message
24+
_status_code = ExceptionStatus.UNPROCESSABLE_TOKEN.status_code
2525

2626

2727
class ExpiredTokenException(CustomException):
2828
status = StatusType.ERROR.value
29-
status_code = ExceptionStatus.TOKEN_EXPIRED.status_code
3029
status_type = ExceptionStatus.TOKEN_EXPIRED.name
3130
message = ExceptionStatus.TOKEN_EXPIRED.message
31+
_status_code = ExceptionStatus.TOKEN_EXPIRED.status_code

app/api/v1/auth/schemas/responses.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class TokenPayload(BaseModel):
2121

2222
class TokenResponse(BaseResponse):
2323
status: int = StatusType.SUCCESS.value
24-
status_code: str = ResponseStatus.USER_LOGGED_IN.status_code
2524
status_type: str = ResponseStatus.USER_LOGGED_IN.name
2625
message: str = ResponseStatus.USER_LOGGED_IN.message
2726
data: TokenData
27+
_status_code: str = ResponseStatus.USER_LOGGED_IN.status_code

app/api/v1/auth/service.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ def __init__(self, user_repository: UserRepository = UserRepository()):
2525

2626
async def access(self, login_request: LoginRequest):
2727
user: UserModel = await self.user_repository.get_user_by_email(login_request.email)
28-
print(user.name)
2928
if not user:
3029
raise UserNotFoundException
3130

app/api/v1/auth/view.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from api.v1.auth.service import AuthService
2+
from core.utils import base_response_to_json_response
23
from .schemas.requests import LoginRequest, RefreshTokenRequest
34
from .schemas.responses import TokenData, TokenResponse
4-
from core.utils import make_response_from_model
55

66

77
class AuthView:
@@ -10,16 +10,13 @@ def __init__(self, auth_service: AuthService = AuthService()):
1010

1111
async def access(self, login_request: LoginRequest):
1212
token_data: TokenData = await self.auth_service.access(login_request)
13-
return make_response_from_model(
14-
TokenResponse(
13+
return base_response_to_json_response(TokenResponse(
1514
data=token_data
16-
)
17-
)
15+
))
1816

1917
async def refresh(self, refresh_token_request: RefreshTokenRequest):
2018
token_data: TokenData = await self.auth_service.refresh(refresh_token_request)
21-
return make_response_from_model(
22-
TokenResponse(
19+
return base_response_to_json_response(TokenResponse(
2320
data=token_data
24-
)
25-
)
21+
))
22+
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

app/api/v1/register/routes.py

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
from fastapi import APIRouter
22

3-
from api.v1.register.schemas.responses import UserRegisteredSuccessfully
3+
from api.v1.register.schemas.responses import UserRegisteredSuccessfully, DuplicateEmail
44
from api.v1.register.view import RegisterView
5+
from api.v1.shared.schemas.responses import ValidationErrorResponse
56

67
register_view = RegisterView()
78

89
register_router = APIRouter(
910
responses={
10-
404: {"description": "Not found"},
11+
209: {"description": "User Already Exists", "model": DuplicateEmail},
12+
422: {"description": "Validation Error", "model": ValidationErrorResponse},
1113
},
1214
)
1315

1416
register_router.add_api_route(
1517
"",
1618
register_view.register,
1719
methods=["POST"],
18-
description="User Register",
19-
name="Registration",
20+
description="New user registration",
21+
name="New user registration",
2022
response_model=UserRegisteredSuccessfully,
23+
status_code=UserRegisteredSuccessfully().status_code,
2124
)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
1-
from pydantic import BaseModel, EmailStr, validator, Field
1+
from enum import Enum
2+
3+
from pydantic import BaseModel, EmailStr, Field
4+
5+
6+
class RolesEnum(str, Enum):
7+
renter = 'renter'
8+
landlord = 'landlord'
29

310

411
class RegisterRequest(BaseModel):
5-
name: str = Field(..., min_length=1, max_length=100)
12+
name: str = Field(..., min_length=1, max_length=100, description="User full name")
613
email: EmailStr
7-
phone: str = Field(..., min_length=1, max_length=32)
8-
password: str = Field(..., min_length=1, max_length=32)
14+
phone: str = Field(..., min_length=4, max_length=32)
15+
password: str = Field(..., min_length=8, max_length=32)
16+
role: RolesEnum

app/api/v1/register/schemas/responses.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,21 @@
44

55

66
class UserRegisteredSuccessfully(BaseResponse):
7-
status: int = StatusType.SUCCESS.value
8-
status_code: str = ResponseStatus.USER_REGISTERED.status_code
7+
status: str = StatusType.SUCCESS.value
98
status_type: str = ResponseStatus.USER_REGISTERED.name
10-
message: str = ResponseStatus.USER_REGISTERED.message
9+
message: str = ResponseStatus.USER_REGISTERED.message
10+
_status_code: int = ResponseStatus.USER_REGISTERED.status_code
11+
12+
13+
class UserNotFound(BaseResponse):
14+
status: str = StatusType.ERROR.value
15+
status_type: str = ResponseStatus.USER_NOT_FOUND.name
16+
message: str = ResponseStatus.USER_NOT_FOUND.message
17+
_status_code: int = ResponseStatus.USER_NOT_FOUND.status_code
18+
19+
20+
class DuplicateEmail(BaseResponse):
21+
status = StatusType.ERROR.value
22+
status_type = ResponseStatus.DUPLICATE_EMAIL.name
23+
message = ResponseStatus.DUPLICATE_EMAIL.message
24+
_status_code = ResponseStatus.DUPLICATE_EMAIL.status_code

app/api/v1/register/view.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from api.v1.register.service import RegisterService
2+
from core.utils import base_response_to_json_response
23
from .schemas.requests import RegisterRequest
34
from .schemas.responses import UserRegisteredSuccessfully
4-
from core.utils import make_response_from_model
55

66

77
class RegisterView:
@@ -14,4 +14,4 @@ async def register(self, register_request: RegisterRequest):
1414
except Exception as exc:
1515
print(exc)
1616
raise
17-
return make_response_from_model(UserRegisteredSuccessfully())
17+
return base_response_to_json_response(UserRegisteredSuccessfully())

app/api/v1/shared/__init__.py

Whitespace-only changes.
Binary file not shown.

app/api/v1/shared/schemas/__init__.py

Whitespace-only changes.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
from http import HTTPStatus
2+
from typing import List, Any
3+
4+
from core.enums.status_type import StatusType
5+
from core.schemas.base import BaseResponse
6+
7+
8+
class ValidationErrorResponse(BaseResponse):
9+
status: str = StatusType.ERROR.value
10+
status_type: str = HTTPStatus.UNPROCESSABLE_ENTITY.name
11+
message: str = HTTPStatus.UNPROCESSABLE_ENTITY.phrase
12+
errors: List = {
13+
'field_name': ["validation error message"],
14+
'another_field_name': ["validation error message"]
15+
}
Binary file not shown.
236 Bytes
Binary file not shown.
Binary file not shown.
126 Bytes
Binary file not shown.
Binary file not shown.
0 Bytes
Binary file not shown.

app/api/v1/user/models.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717

1818
class RoleModel(Base):
19+
def __init__(self):
20+
pass
21+
1922
__tablename__ = "roles"
2023

2124
id: int = Column(Integer, primary_key=True, autoincrement=True)
@@ -28,6 +31,9 @@ class RoleModel(Base):
2831

2932

3033
class UserModel(Base):
34+
def __init__(self):
35+
pass
36+
3137
__tablename__ = "users"
3238

3339
id: int = Column(Integer, primary_key=True, autoincrement=True)

app/api/v1/user/repository.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from sqlalchemy.orm import joinedload, lazyload, contains_eager
55

66
from core.db.db import database
7-
from .models import UserModel
7+
from .models import UserModel, RoleModel, user_roles_table
88
from .schemas.requests import UserCreateSchema
99

1010

@@ -13,8 +13,22 @@ def __init__(self, db=database):
1313
self.db = db
1414

1515
async def create_user(self, user: UserCreateSchema):
16-
query = insert(UserModel).values(**user.dict())
17-
await self.db.execute(query)
16+
await self.db.rollback()
17+
18+
user_dict = user.dict()
19+
role_name = user.role
20+
del user_dict["role"]
21+
22+
query = insert(UserModel).values(**user_dict)
23+
user = await self.db.execute(query)
24+
25+
query = select(RoleModel).where(RoleModel.role == role_name)
26+
role = await self.db.execute(query)
27+
role = role.scalars().first()
28+
if role:
29+
query = insert(user_roles_table).values({'role_id': role.id, 'user_id': user.inserted_primary_key[0]})
30+
await self.db.execute(query)
31+
1832
return await self.db.commit()
1933

2034
async def get_user_by_email(self, email: str):

app/api/v1/user/routes.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,15 @@
33

44
from .view import UserView
55
from ..shared.role_checker import CheckRole
6+
from ..shared.schemas.responses import ValidationErrorResponse
67

78
user_views = UserView()
89

910
user_router = APIRouter(
10-
responses={404: {"description": "Not found"}},
11+
responses={
12+
404: {"description": "Not found"},
13+
422: {"description": "Validation Error", "model": ValidationErrorResponse},
14+
},
1115
)
1216

1317
user_router.add_api_route(
@@ -17,5 +21,5 @@
1721
description="User",
1822
name="Get All Users",
1923
response_model_by_alias=False,
20-
dependencies=[Depends(CheckRole(['Admin']))],
24+
dependencies=[Depends(CheckRole(['landlord']))],
2125
)
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

app/api/v1/user/schemas/exceptions.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,13 @@
55

66
class DuplicateEmailException(CustomException):
77
status = StatusType.ERROR.value
8-
status_code = ExceptionStatus.DUPLICATE_EMAIL.status_code
98
status_type = ExceptionStatus.DUPLICATE_EMAIL.name
109
message = ExceptionStatus.DUPLICATE_EMAIL.message
10+
_status_code = ExceptionStatus.DUPLICATE_EMAIL.status_code
1111

1212

1313
class UserNotFoundException(CustomException):
1414
status = StatusType.ERROR.value
15-
status_code = ExceptionStatus.USER_NOT_FOUND.status_code
1615
status_type = ExceptionStatus.USER_NOT_FOUND.name
1716
message = ExceptionStatus.USER_NOT_FOUND.message
17+
_status_code = ExceptionStatus.USER_NOT_FOUND.status_code

app/api/v1/user/schemas/requests.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,4 @@ class UserCreateSchema(BaseModel):
66
email: EmailStr
77
phone: str
88
password: str
9+
role: str
-5 Bytes
Binary file not shown.
11 Bytes
Binary file not shown.
-16 Bytes
Binary file not shown.

app/core/bootstrap/handlers.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ async def custom_exception_handler(request: Request, exc: CustomException):
2323
@app_.exception_handler(RequestValidationError)
2424
async def validation_exception_handler(request: Request, exc: RequestValidationError):
2525
reformatted_message = defaultdict(list)
26+
field_string = ''
27+
msg = ''
28+
2629
for pydantic_error in exc.errors():
2730
loc, msg, error_type = pydantic_error["loc"], pydantic_error["msg"], pydantic_error["type"]
2831

@@ -34,7 +37,7 @@ async def validation_exception_handler(request: Request, exc: RequestValidationE
3437
reformatted_message[field_string].append(msg)
3538

3639
return JSONResponse(
37-
status_code=UnprocessableEntity.status_code,
40+
status_code=UnprocessableEntity().status_code,
3841
content={
3942
"status": UnprocessableEntity.status,
4043
"status_type": UnprocessableEntity.status_type,

0 commit comments

Comments
 (0)