-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* deps(main/dev): Add coverage.py and update de deps (#125) * Update requirements Add new requirements for refactor * Add user domain This change adds the user domain and associated unit tests * Update dependencies Add Beanie ORM and dependency injection framework. * Add refactored user stuff * Add changed queries interface * Add queries interface and models
- Loading branch information
Showing
35 changed files
with
1,420 additions
and
647 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
# from fastapi import FastAPI | ||
|
||
# from .containers import Container | ||
# from .api.v1 import users | ||
|
||
|
||
# def create_app() -> FastAPI: | ||
# container = Container() | ||
|
||
# app = FastAPI() | ||
# app.container = container | ||
# app.include_router(users.router) | ||
# return app | ||
|
||
|
||
# application = create_app() |
Empty file.
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,15 @@ | ||
from typing import Optional, List | ||
|
||
from pydantic import BaseModel | ||
|
||
|
||
from nmdc_runtime.domain.users.userSchema import UserOut | ||
|
||
|
||
class Response(BaseModel): | ||
query: str | ||
limit: int | ||
|
||
|
||
class UserResponse(Response): | ||
users: List[UserOut] |
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,9 @@ | ||
from fastapi import APIRouter | ||
|
||
from . import users | ||
|
||
router = APIRouter( | ||
prefix="/v1", tags=["v1"], responses={404: {"description": "Not found"}} | ||
) | ||
|
||
router.include_router(users.router, tags=["users"]) |
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,38 @@ | ||
"""Endpoints module.""" | ||
from typing import List, Optional | ||
|
||
from fastapi import APIRouter, HTTPException, Depends, Response, status | ||
from dependency_injector.wiring import inject, Provide | ||
|
||
from nmdc_runtime.containers import Container | ||
|
||
from nmdc_runtime.domain.users.userService import UserService | ||
from nmdc_runtime.domain.users.userSchema import UserAuth, UserOut | ||
|
||
|
||
router = APIRouter(prefix="/users", tags=["users"]) | ||
|
||
|
||
# @router.get("", response_model=Response) | ||
# @inject | ||
# async def index( | ||
# query: Optional[str] = None, | ||
# limit: Optional[str] = None, | ||
# user_service: UserService = Depends(Provide[Container.user_service]), | ||
# ) -> List[UserOut]: | ||
# query = query | ||
# limit = limit | ||
|
||
# users = await user_service.search(query, limit) | ||
|
||
# return {"query": query, "limit": limit, "users": users} | ||
|
||
|
||
@router.post("", response_model=Response, status_code=status.HTTP_201_CREATED) | ||
@inject | ||
async def add( | ||
user: UserAuth, | ||
user_service: UserService = Depends(Provide[Container.user_service]), | ||
) -> UserOut: | ||
new_user = await user_service.create_user(user) | ||
return new_user |
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,15 @@ | ||
"""Containers module.""" | ||
|
||
from dependency_injector import containers, providers | ||
|
||
from nmdc_runtime.domain.users.userService import UserService | ||
from nmdc_runtime.infrastructure.database.impl.mongo.models.user import ( | ||
UserQueries, | ||
) | ||
|
||
|
||
class Container(containers.DeclarativeContainer): | ||
|
||
user_queries = providers.Singleton(UserQueries) | ||
|
||
user_service = providers.Factory(UserService, user_queries=user_queries) |
Empty file.
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,15 @@ | ||
from contextlib import contextmanager, AbstractContextManager | ||
from typing import Callable | ||
import logging | ||
|
||
from motor import motor_asyncio | ||
|
||
|
||
class Database: | ||
def __init__(self, db_url: str) -> None: | ||
self._client = motor_asyncio.AsyncIOMotorClient(db_url) | ||
self._db = self._client["database"] | ||
|
||
@contextmanager | ||
def session(self): | ||
return self._db |
Empty file.
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,23 @@ | ||
from .base import ( | ||
CustomException, | ||
BadRequestException, | ||
NotFoundException, | ||
ForbiddenException, | ||
UnprocessableEntity, | ||
DuplicateValueException, | ||
UnauthorizedException, | ||
) | ||
from .token import DecodeTokenException, ExpiredTokenException | ||
|
||
|
||
__all__ = [ | ||
"CustomException", | ||
"BadRequestException", | ||
"NotFoundException", | ||
"ForbiddenException", | ||
"UnprocessableEntity", | ||
"DuplicateValueException", | ||
"UnauthorizedException", | ||
"DecodeTokenException", | ||
"ExpiredTokenException", | ||
] |
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,47 @@ | ||
from http import HTTPStatus | ||
|
||
|
||
class CustomException(Exception): | ||
code = HTTPStatus.BAD_GATEWAY | ||
error_code = HTTPStatus.BAD_GATEWAY | ||
message = HTTPStatus.BAD_GATEWAY.description | ||
|
||
def __init__(self, message=None): | ||
if message: | ||
self.message = message | ||
|
||
|
||
class BadRequestException(CustomException): | ||
code = HTTPStatus.BAD_REQUEST | ||
error_code = HTTPStatus.BAD_REQUEST | ||
message = HTTPStatus.BAD_REQUEST.description | ||
|
||
|
||
class NotFoundException(CustomException): | ||
code = HTTPStatus.NOT_FOUND | ||
error_code = HTTPStatus.NOT_FOUND | ||
message = HTTPStatus.NOT_FOUND.description | ||
|
||
|
||
class ForbiddenException(CustomException): | ||
code = HTTPStatus.FORBIDDEN | ||
error_code = HTTPStatus.FORBIDDEN | ||
message = HTTPStatus.FORBIDDEN.description | ||
|
||
|
||
class UnauthorizedException(CustomException): | ||
code = HTTPStatus.UNAUTHORIZED | ||
error_code = HTTPStatus.UNAUTHORIZED | ||
message = HTTPStatus.UNAUTHORIZED.description | ||
|
||
|
||
class UnprocessableEntity(CustomException): | ||
code = HTTPStatus.UNPROCESSABLE_ENTITY | ||
error_code = HTTPStatus.UNPROCESSABLE_ENTITY | ||
message = HTTPStatus.UNPROCESSABLE_ENTITY.description | ||
|
||
|
||
class DuplicateValueException(CustomException): | ||
code = HTTPStatus.UNPROCESSABLE_ENTITY | ||
error_code = HTTPStatus.UNPROCESSABLE_ENTITY | ||
message = HTTPStatus.UNPROCESSABLE_ENTITY.description |
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,13 @@ | ||
from core.exceptions import CustomException | ||
|
||
|
||
class DecodeTokenException(CustomException): | ||
code = 400 | ||
error_code = 10000 | ||
message = "token decode error" | ||
|
||
|
||
class ExpiredTokenException(CustomException): | ||
code = 400 | ||
error_code = 10001 | ||
message = "expired token" |
Empty file.
Empty file.
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,18 @@ | ||
from __future__ import annotations | ||
from abc import ABC | ||
|
||
from abc import abstractmethod | ||
|
||
from nmdc_runtime.domain.users.userSchema import UserAuth, UserUpdate, UserOut | ||
|
||
|
||
class IUserQueries(ABC): | ||
@abstractmethod | ||
async def create(self, user: UserAuth) -> UserOut: | ||
"""Create new user""" | ||
raise NotImplementedError | ||
|
||
@abstractmethod | ||
async def update(self, user: UserUpdate) -> UserOut: | ||
"""Update user data""" | ||
raise NotImplementedError |
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,37 @@ | ||
from typing import Optional, List | ||
|
||
|
||
from pydantic import BaseModel, EmailStr | ||
|
||
|
||
class UserBase(BaseModel): | ||
username: Optional[str] = None | ||
email: Optional[str] = None | ||
full_name: Optional[str] = None | ||
site_admin: Optional[List[str]] = [] | ||
disabled: Optional[bool] = False | ||
|
||
|
||
class UserAuth(UserBase): | ||
"""User register and login auth""" | ||
|
||
username: str | ||
password: str | ||
|
||
|
||
# Properties to receive via API on update | ||
class UserUpdate(UserBase): | ||
"""Updatable user fields""" | ||
|
||
email: Optional[EmailStr] = None | ||
|
||
# User information | ||
full_name: Optional[str] = None | ||
password: Optional[str] = None | ||
|
||
|
||
class UserOut(UserUpdate): | ||
"""User fields pushed to the client""" | ||
|
||
email: EmailStr | ||
disabled: Optional[bool] = False |
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,16 @@ | ||
from typing import Any | ||
|
||
from nmdc_runtime.domain.users.userSchema import UserAuth, UserUpdate, UserOut | ||
|
||
|
||
class UserService: | ||
def __init__(self, user_queries: Any) -> None: | ||
self.__user_queries = user_queries | ||
|
||
async def create_user(self, user: UserAuth) -> UserOut: | ||
return await self.__user_queries.create(user) | ||
|
||
async def update_user( | ||
self, username: str, new_user: UserUpdate | ||
) -> UserOut: | ||
pass |
Empty file.
Empty file.
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,3 @@ | ||
""" | ||
Database initialization | ||
""" |
Empty file.
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,19 @@ | ||
""" | ||
Database initialization | ||
""" | ||
import os | ||
|
||
from beanie import init_beanie | ||
from motor.motor_asyncio import AsyncIOMotorClient | ||
|
||
from nmdc_runtime.infrastructure.database.impl.mongo.models import User | ||
|
||
|
||
async def mongo_init(app): | ||
"""Initialize database service""" | ||
app.db = AsyncIOMotorClient( | ||
host=os.getenv("MONGO_HOST"), | ||
username=os.getenv("MONGO_USERNAME"), | ||
password=os.getenv("MONGO_DBNAME"), | ||
).account | ||
await init_beanie(app.db, document_models=[User]) |
Empty file.
57 changes: 57 additions & 0 deletions
57
nmdc_runtime/infrastructure/database/impl/mongo/models/user.py
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,57 @@ | ||
""" | ||
User models | ||
""" | ||
from typing import Optional, List | ||
|
||
from beanie import Document, Indexed | ||
from pydantic import EmailStr | ||
|
||
from nmdc_runtime.api.core.auth import verify_password | ||
from nmdc_runtime.domain.users.userSchema import UserAuth, UserUpdate, UserOut | ||
from nmdc_runtime.domain.users.queriesInterface import IUserQueries | ||
|
||
|
||
# User database representation | ||
class User(Document): | ||
class DocumentMeta: | ||
collection_name = "users" | ||
|
||
username: Indexed(str, unique=True) | ||
email: Indexed(EmailStr, unique=True) | ||
full_name: Optional[str] = None | ||
site_admin: Optional[List[str]] = [] | ||
disabled: Optional[bool] = False | ||
|
||
class Config: | ||
schema_extra = { | ||
"username": "bob", | ||
"email": "[email protected]", | ||
"full_name": "test", | ||
"password": "test", | ||
"site_admin": ["test_site"], | ||
"created_date": "1/1/2020", | ||
} | ||
|
||
|
||
class UserQueries(IUserQueries): | ||
"""Implementation of the User query interface""" | ||
|
||
async def create(self, user: UserAuth) -> UserOut: | ||
|
||
auth_user = await User.get(user.username) | ||
if not auth_user: | ||
auth_user = User( | ||
username=user.username, | ||
email=user.email, | ||
full_name=user.full_name, | ||
site_admin=user.site_admin, | ||
password=user.password, | ||
) | ||
await auth_user.insert() | ||
|
||
if not verify_password(user.password, auth_user.password): | ||
return False | ||
return UserOut(auth_user) | ||
|
||
async def update(self, user: UserUpdate) -> UserOut: | ||
pass |
Empty file.
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,10 @@ | ||
from __future__ import annotations | ||
|
||
from abc import abstractmethod | ||
from typing import List | ||
|
||
from nmdc_runtime.domain.users.userSchema import ( | ||
UserAuth, | ||
UserUpdate, | ||
UserOut, | ||
) |
Empty file.
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,7 +1,10 @@ | ||
-c main.txt | ||
black | ||
coverage | ||
flake8 | ||
invoke | ||
pytest | ||
pytest-asyncio | ||
pytest-cov | ||
setuptools-scm | ||
twine |
Oops, something went wrong.