├── requirements.txt ├── src ├── __init__.py ├── data │ ├── __init__.py │ ├── find_pet │ │ ├── __init__.py │ │ ├── find.py │ │ └── find_test.py │ ├── register_user │ │ ├── __init__.py │ │ ├── register.py │ │ └── register_test.py │ ├── find_user │ │ ├── __init__.py │ │ ├── find_test.py │ │ └── find.py │ ├── register_pet │ │ ├── __init__.py │ │ ├── register_test.py │ │ └── register.py │ ├── test │ │ ├── __init__.py │ │ ├── find_user_spy.py │ │ └── register_pet_spy.py │ └── interfaces │ │ ├── __init__.py │ │ ├── user_repository_interface.py │ │ └── pet_repository_interface.py ├── domain │ ├── __init__.py │ ├── models │ │ ├── __init__.py │ │ ├── users.py │ │ └── pets.py │ ├── test │ │ ├── __init__.py │ │ ├── mock_user.py │ │ └── mock_pet.py │ └── use_cases │ │ ├── __init__.py │ │ ├── register_user.py │ │ ├── register_pet.py │ │ ├── find_user.py │ │ └── find_pet.py ├── infra │ ├── __init__.py │ ├── repo │ │ ├── __init__.py │ │ ├── user_repository_test.py │ │ ├── user_repository.py │ │ ├── pet_repository_test.py │ │ └── pet_repository.py │ ├── entities │ │ ├── __init__.py │ │ ├── users.py │ │ └── pets.py │ ├── config │ │ ├── __init__.py │ │ ├── db_base.py │ │ └── db_config.py │ └── test │ │ ├── __init__.py │ │ ├── user_repository_spy.py │ │ └── pet_repository_spy.py ├── main │ ├── __init__.py │ ├── configs │ │ ├── __init__.py │ │ └── app.py │ ├── interface │ │ ├── __init__.py │ │ └── route.py │ ├── routes │ │ ├── __init__.py │ │ └── api_route.py │ └── composer │ │ ├── __init__.py │ │ └── register_pet_composite.py └── presenters │ ├── __init__.py │ ├── errors │ ├── __init__.py │ └── http_errors.py │ ├── helpers │ ├── __init__.py │ └── http_models.py │ └── controllers │ ├── __init__.py │ ├── find_user_controller_test.py │ ├── register_pet_controller_test.py │ ├── find_user_controller.py │ └── register_pet_controller.py ├── .gitignore ├── .flake8 ├── run.py ├── .vscode └── settings.json ├── .editorconfig ├── .pre-commit-config.yaml ├── README.md └── .pylintrc /requirements.txt: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/data/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/domain/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/infra/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/infra/repo/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/presenters/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/data/find_pet/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/data/register_user/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/configs/__init__.py: -------------------------------------------------------------------------------- 1 | from .app import app 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | venv 2 | **/__pycache__ 3 | *.db 4 | .pytest_cache -------------------------------------------------------------------------------- /src/data/find_user/__init__.py: -------------------------------------------------------------------------------- 1 | from .find import FindUser 2 | -------------------------------------------------------------------------------- /src/main/interface/__init__.py: -------------------------------------------------------------------------------- 1 | from .route import RouteInterface 2 | -------------------------------------------------------------------------------- /src/main/routes/__init__.py: -------------------------------------------------------------------------------- 1 | from .api_route import api_route_bp 2 | -------------------------------------------------------------------------------- /src/data/register_pet/__init__.py: -------------------------------------------------------------------------------- 1 | from .register import RegisterPet 2 | -------------------------------------------------------------------------------- /src/presenters/errors/__init__.py: -------------------------------------------------------------------------------- 1 | from .http_errors import HttpErrors 2 | -------------------------------------------------------------------------------- /src/domain/models/__init__.py: -------------------------------------------------------------------------------- 1 | from .users import Users 2 | from .pets import Pets 3 | -------------------------------------------------------------------------------- /src/main/composer/__init__.py: -------------------------------------------------------------------------------- 1 | from .register_pet_composite import register_pet_compose 2 | -------------------------------------------------------------------------------- /src/presenters/helpers/__init__.py: -------------------------------------------------------------------------------- 1 | from .http_models import HttpRequest, HttpResponse 2 | -------------------------------------------------------------------------------- /src/domain/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .mock_user import mock_user 2 | from .mock_pet import mock_pet 3 | -------------------------------------------------------------------------------- /src/infra/entities/__init__.py: -------------------------------------------------------------------------------- 1 | from .pets import Pets, AnimalTypes 2 | from .users import Users 3 | -------------------------------------------------------------------------------- /src/infra/config/__init__.py: -------------------------------------------------------------------------------- 1 | from .db_base import Base 2 | from .db_config import DatabaseConnectionHandler 3 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | ignore = E722, W503 3 | max-line-length = 120 4 | per-file-ignores = 5 | __init__.py: F401 -------------------------------------------------------------------------------- /run.py: -------------------------------------------------------------------------------- 1 | from src.main.configs import app 2 | 3 | if __name__ == "main": 4 | app.run(host="0.0.0.0", port=3333) 5 | -------------------------------------------------------------------------------- /src/data/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .find_user_spy import FindUserSpy 2 | from .register_pet_spy import RegisterPetSpy 3 | -------------------------------------------------------------------------------- /src/infra/config/db_base.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy.ext.declarative import declarative_base 2 | 3 | Base = declarative_base() 4 | -------------------------------------------------------------------------------- /src/domain/models/users.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | Users = namedtuple("Users", "id, name, password") 4 | -------------------------------------------------------------------------------- /src/domain/models/pets.py: -------------------------------------------------------------------------------- 1 | from collections import namedtuple 2 | 3 | Pets = namedtuple("Pets", "id, name, specie, age, user_id") 4 | -------------------------------------------------------------------------------- /src/infra/test/__init__.py: -------------------------------------------------------------------------------- 1 | from .user_repository_spy import UserRepositorySpy 2 | from .pet_repository_spy import PetRepositorySpy 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.pythonPath": "venv/bin/python", 3 | "python.linting.pylintEnabled": true, 4 | "python.linting.enabled": true 5 | } -------------------------------------------------------------------------------- /src/presenters/controllers/__init__.py: -------------------------------------------------------------------------------- 1 | from .find_user_controller import FindUserController 2 | from .register_pet_controller import RegisterPetController 3 | -------------------------------------------------------------------------------- /src/data/interfaces/__init__.py: -------------------------------------------------------------------------------- 1 | from .pet_repository_interface import PetRepositoryInterface 2 | from .user_repository_interface import UserRepositoryInterface 3 | -------------------------------------------------------------------------------- /src/domain/use_cases/__init__.py: -------------------------------------------------------------------------------- 1 | from .register_user import RegisterUserInterface 2 | from .find_user import FindUser 3 | from .find_pet import FindPet 4 | from .register_pet import RegisterPetInterface 5 | -------------------------------------------------------------------------------- /src/main/configs/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_cors import CORS 3 | from src.main.routes import api_route_bp 4 | 5 | app = Flask(__name__) 6 | CORS(app) 7 | 8 | app.register_blueprint(api_route_bp) 9 | -------------------------------------------------------------------------------- /src/main/routes/api_route.py: -------------------------------------------------------------------------------- 1 | from flask import Blueprint, jsonify 2 | 3 | api_route_bp = Blueprint("api_route", __name__) 4 | 5 | 6 | @api_route_bp.route("/api", methods=["GET"]) 7 | def something(): 8 | """teste""" 9 | 10 | return jsonify({"Success": True}) 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | indent_style = space 8 | indent_size = 4 9 | end_of_line = lf 10 | charset = utf-8 11 | trim_trailing_whitespace = false 12 | insert_final_newline = false -------------------------------------------------------------------------------- /src/domain/test/mock_user.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.domain.models import Users 3 | 4 | faker = Faker() 5 | 6 | 7 | def mock_user() -> Users: 8 | """Mockging users""" 9 | 10 | return Users( 11 | id=faker.random_number(digits=5), name=faker.name(), password=faker.word() 12 | ) 13 | -------------------------------------------------------------------------------- /src/domain/use_cases/register_user.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from abc import ABC, abstractmethod 3 | from src.domain.models import Users 4 | 5 | 6 | class RegisterUserInterface(ABC): 7 | """Interface to register an user""" 8 | 9 | @abstractmethod 10 | def execute(self, name: str, password: str) -> Dict[bool, Users]: 11 | """abstract method""" 12 | 13 | raise Exception("Method not implemented") 14 | -------------------------------------------------------------------------------- /src/main/interface/route.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from abc import ABC, abstractmethod 3 | from src.presenters.helpers import HttpRequest, HttpResponse 4 | 5 | 6 | class RouteInterface(ABC): 7 | """Interface to routes""" 8 | 9 | @abstractmethod 10 | def handle(self, http_request: Type[HttpRequest]) -> HttpResponse: 11 | """Define route""" 12 | 13 | raise Exception("Should implement method: route") 14 | -------------------------------------------------------------------------------- /src/presenters/errors/http_errors.py: -------------------------------------------------------------------------------- 1 | class HttpErrors: 2 | """Class to define HTTP Errors""" 3 | 4 | @staticmethod 5 | def error_400(): 6 | """Define HTTP 400""" 7 | 8 | return {"status_code": 400, "body": {"error": "Bad Request"}} 9 | 10 | @staticmethod 11 | def error_422(): 12 | """Define HTTP 422""" 13 | 14 | return {"status_code": 422, "body": {"error": "Unprocessable Entity"}} 15 | -------------------------------------------------------------------------------- /src/domain/test/mock_pet.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.domain.models import Pets 3 | from src.infra.entities import AnimalTypes 4 | 5 | faker = Faker() 6 | 7 | 8 | def mock_pet() -> Pets: 9 | """Mockging pets""" 10 | 11 | return Pets( 12 | id=faker.random_number(digits=5), 13 | name=faker.name(), 14 | specie=AnimalTypes.cat, 15 | age=faker.random_number(digits=1), 16 | user_id=faker.random_number(digits=5), 17 | ) 18 | -------------------------------------------------------------------------------- /src/domain/use_cases/register_pet.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractclassmethod 2 | from typing import Dict 3 | from src.domain.models import Pets 4 | 5 | 6 | class RegisterPetInterface(ABC): 7 | """Interface to Register Pet Use Case""" 8 | 9 | @abstractclassmethod 10 | def registry( 11 | cls, name: str, specie: str, user_information: Dict[int, str], age: int = None 12 | ) -> Dict[bool, Pets]: 13 | """Register pet method""" 14 | 15 | raise Exception("Should implement mehtod: registry") 16 | -------------------------------------------------------------------------------- /src/domain/use_cases/find_user.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractclassmethod 2 | from typing import Dict 3 | from src.domain.models import Users 4 | 5 | 6 | class FindUser(ABC): 7 | """Interface to find user user case""" 8 | 9 | @abstractclassmethod 10 | def by_id(cls, user_id: int) -> Dict[bool, Users]: 11 | """Abstract method""" 12 | 13 | raise Exception("This method should be implemented") 14 | 15 | @abstractclassmethod 16 | def by_name(cls, name: str) -> Dict[bool, Users]: 17 | """Abstract method""" 18 | 19 | raise Exception("This method should be implemented") 20 | -------------------------------------------------------------------------------- /src/data/interfaces/user_repository_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from src.domain.models import Users 3 | 4 | 5 | class UserRepositoryInterface(ABC): 6 | """Interface to User Repository""" 7 | 8 | @abstractmethod 9 | def insert_user(self, name: str, password: str) -> Users: 10 | """abstract method""" 11 | 12 | raise Exception("Method not implemented") 13 | 14 | @abstractmethod 15 | def find_by_id(self, user_id: int) -> Users: 16 | """abstract method""" 17 | 18 | raise Exception("Method not implemented") 19 | 20 | @abstractmethod 21 | def find_by_name(self, name: str) -> Users: 22 | """abstract method""" 23 | 24 | raise Exception("Method not implemented") 25 | -------------------------------------------------------------------------------- /src/main/composer/register_pet_composite.py: -------------------------------------------------------------------------------- 1 | from src.main.interface import RouteInterface 2 | from src.presenters.controllers import RegisterPetController 3 | from src.data.register_pet import RegisterPet 4 | from src.infra.repo.pet_repository import PetRepository 5 | from src.infra.repo.user_repository import UserRepository 6 | 7 | 8 | def register_pet_compose() -> RouteInterface: 9 | """Composing register user route 10 | :param - None 11 | :return - Object with registered user 12 | """ 13 | pets_repository = PetRepository() 14 | users_repository = UserRepository() 15 | use_case = RegisterPet(pets_repository, users_repository) 16 | register_pet_route = RegisterPetController(use_case) 17 | 18 | return register_pet_route 19 | -------------------------------------------------------------------------------- /src/infra/entities/users.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, String, Integer 2 | from sqlalchemy.orm import relationship 3 | from src.infra.config import Base 4 | 5 | 6 | class Users(Base): 7 | """Users Entity""" 8 | 9 | __tablename__ = "users" 10 | 11 | id = Column(Integer, primary_key=True) 12 | name = Column(String, nullable=False, unique=True) 13 | password = Column(String, nullable=False) 14 | id_pet = relationship("Pets") 15 | 16 | def __rep__(self): 17 | return f"Usr [name={self.name}]" 18 | 19 | def __eq__(self, other): 20 | if ( 21 | self.id == other.id 22 | and self.name == other.name 23 | and self.password == other.password 24 | ): 25 | return True 26 | return False 27 | -------------------------------------------------------------------------------- /src/domain/use_cases/find_pet.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractclassmethod 2 | from typing import Dict, List 3 | from src.domain.models import Pets 4 | 5 | 6 | class FindPet(ABC): 7 | """Abstract to find pet use case""" 8 | 9 | @abstractclassmethod 10 | def by_id(cls, pet_id: int) -> Dict[bool, Pets]: 11 | """Abstract method""" 12 | 13 | raise Exception("This method should be implemented") 14 | 15 | @abstractclassmethod 16 | def by_name(cls, name: str) -> Dict[bool, Pets]: 17 | """Abstract method""" 18 | 19 | raise Exception("This method should be implemented") 20 | 21 | @abstractclassmethod 22 | def by_user_id(cls, user_id: int) -> Dict[bool, List[Pets]]: 23 | """Abstract method""" 24 | 25 | raise Exception("This method should be implemented") 26 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/ambv/black 3 | rev: stable 4 | hooks: 5 | - id: black 6 | language_version: python3.8 7 | stages: [commit] 8 | - repo: https://gitlab.com/pycqa/flake8 9 | rev: 3.7.9 10 | hooks: 11 | - id: flake8 12 | stages: [commit] 13 | - repo: local 14 | hooks: 15 | - id: pytest 16 | name: pytest 17 | language: system 18 | entry: pytest -v -s 19 | always_run: true 20 | pass_filenames: false 21 | stages: [commit] 22 | - repo: local 23 | hooks: 24 | - id: requirements 25 | name: requirements 26 | entry: bash -c 'venv/bin/pip3 freeze > requirements.txt; git add requirements.txt' 27 | language: system 28 | pass_filenames: false 29 | stages: [commit] -------------------------------------------------------------------------------- /src/presenters/helpers/http_models.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | 3 | 4 | class HttpRequest: 5 | """Class to http requests representation""" 6 | 7 | def __init__(self, header: Dict = None, body: Dict = None, query: Dict = None): 8 | self.header = header 9 | self.body = body 10 | self.query = query 11 | 12 | def __repr__(self) -> str: 13 | return ( 14 | f"HttpRequest (header={self.header}, body={self.body}, query={self.query}" 15 | ) 16 | 17 | 18 | class HttpResponse: 19 | """Class to http response representation""" 20 | 21 | def __init__(self, status_code: int, body: any): 22 | self.status_code = status_code 23 | self.body = body 24 | 25 | def __repr__(self) -> str: 26 | return f"HttpResponse (status_code={self.status_code}, body={self.body}" 27 | -------------------------------------------------------------------------------- /src/presenters/controllers/find_user_controller_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | 3 | from src.data.test import FindUserSpy 4 | from src.infra.test import UserRepositorySpy 5 | from src.presenters.helpers import HttpRequest 6 | from .find_user_controller import FindUserController 7 | 8 | faker = Faker() 9 | 10 | 11 | def test_handler(): 12 | """Testing Handle Method""" 13 | 14 | find_user_use_case = FindUserSpy(UserRepositorySpy()) 15 | find_user_controller = FindUserController(find_user_use_case) 16 | 17 | http_request = HttpRequest(query={"user_id": faker.random_number()}) 18 | 19 | response = find_user_controller.handle(http_request) 20 | 21 | # Testing Inputs 22 | assert find_user_use_case.by_id_param["user_id"] == http_request.query["user_id"] 23 | 24 | # Testing Outputs 25 | assert response.status_code == 200 26 | assert response.body 27 | -------------------------------------------------------------------------------- /src/infra/config/db_config.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.orm import sessionmaker 3 | 4 | 5 | class DatabaseConnectionHandler: 6 | """ " Sqlalchemy database connection""" 7 | 8 | def __init__(self): 9 | self.__connection_string = "sqlite:///storage.db" 10 | self.session = None 11 | 12 | def get_engine(self): 13 | """Return connection engine 14 | :param - None 15 | :return - engine connection to Database 16 | """ 17 | engine = create_engine(self.__connection_string) 18 | return engine 19 | 20 | def __enter__(self): 21 | engine = create_engine(self.__connection_string) 22 | session_maker = sessionmaker() 23 | self.session = session_maker(bind=engine) 24 | return self 25 | 26 | def __exit__(self, exc_type, exc_val, exc_tb): 27 | self.session.close() # pylint: disable=no-member 28 | -------------------------------------------------------------------------------- /src/data/interfaces/pet_repository_interface.py: -------------------------------------------------------------------------------- 1 | from abc import ABC, abstractmethod 2 | from typing import List 3 | from src.domain.models import Pets 4 | 5 | 6 | class PetRepositoryInterface(ABC): 7 | """Interface to Pet Repository""" 8 | 9 | @abstractmethod 10 | def create(self, name: str, specie: str, age: int, user_id: int) -> Pets: 11 | """abstract method""" 12 | 13 | raise Exception("Method not implemented") 14 | 15 | @abstractmethod 16 | def find_by_id(self, pet_id: int) -> Pets: 17 | """abstract method""" 18 | 19 | raise Exception("Method not implemented") 20 | 21 | @abstractmethod 22 | def find_by_name(self, name: str) -> Pets: 23 | """abstract method""" 24 | 25 | raise Exception("Method not implemented") 26 | 27 | @abstractmethod 28 | def find_by_user_id(self, user_id: int) -> List[Pets]: 29 | """abstract method""" 30 | 31 | raise Exception("Method not implemented") 32 | -------------------------------------------------------------------------------- /src/infra/test/user_repository_spy.py: -------------------------------------------------------------------------------- 1 | from src.domain.test import mock_user 2 | from src.domain.models import Users 3 | 4 | 5 | class UserRepositorySpy: 6 | """Spy to User Repository""" 7 | 8 | def __init__(self): 9 | self.insert_user_params = {} 10 | self.find_user_by_id_params = {} 11 | self.find_user_by_name_params = {} 12 | 13 | def insert_user(self, name: str, password: str) -> Users: 14 | """Spy to allthe attributes""" 15 | 16 | self.insert_user_params["name"] = name 17 | self.insert_user_params["password"] = password 18 | 19 | return mock_user() 20 | 21 | def find_by_name(self, name: str) -> Users: 22 | """Spy to user attributes""" 23 | 24 | self.find_user_by_name_params["name"] = name 25 | 26 | return mock_user() 27 | 28 | def find_by_id(self, user_id: int) -> Users: 29 | """Spy to user attributes""" 30 | 31 | self.find_user_by_id_params["user_id"] = user_id 32 | 33 | return mock_user() 34 | -------------------------------------------------------------------------------- /src/data/register_user/register.py: -------------------------------------------------------------------------------- 1 | from typing import Type, Dict 2 | from src.domain.use_cases import RegisterUserInterface 3 | from src.domain.models import Users 4 | from src.data.interfaces import UserRepositoryInterface as UserRepository 5 | 6 | 7 | class RegisterUser(RegisterUserInterface): 8 | """Class to define use case: Register User""" 9 | 10 | def __init__(self, user_repository: Type[UserRepository]): 11 | self.user_repository = user_repository 12 | 13 | def execute(self, name: str, password: str) -> Dict[bool, Users]: 14 | """Register user use case 15 | :param - name: person name 16 | :param - password: user password 17 | :return - Dictionary with informations of the process 18 | """ 19 | 20 | user = None 21 | validate_entry = isinstance(name, str) and isinstance(password, str) 22 | 23 | if validate_entry: 24 | user = self.user_repository.insert_user(name, password) 25 | 26 | return {"Success": validate_entry, "Data": user} 27 | -------------------------------------------------------------------------------- /src/presenters/controllers/register_pet_controller_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.data.test import RegisterPetSpy 3 | from src.infra.test import PetRepositorySpy 4 | from src.presenters.helpers import HttpRequest 5 | from .register_pet_controller import RegisterPetController 6 | 7 | faker = Faker() 8 | 9 | 10 | def test_route(): 11 | """Test route RegisterPetController""" 12 | 13 | register_pet_use_case = RegisterPetSpy(PetRepositorySpy(), None) 14 | register_pet_route = RegisterPetController(register_pet_use_case) 15 | 16 | attributes = { 17 | "name": faker.word(), 18 | "specie": "dog", 19 | "age": faker.random_number(), 20 | "user_information": { 21 | "user_id": faker.random_number(), 22 | "user_name": faker.word(), 23 | }, 24 | } 25 | 26 | register_pet_route.handle(HttpRequest(body=attributes)) 27 | 28 | # Testing input 29 | assert register_pet_use_case.registry_param["name"] == attributes["name"] 30 | assert register_pet_use_case.registry_param["age"] == attributes["age"] 31 | -------------------------------------------------------------------------------- /src/infra/entities/pets.py: -------------------------------------------------------------------------------- 1 | import enum 2 | from sqlalchemy import Column, String, Integer, Enum, ForeignKey 3 | from src.infra.config import Base 4 | 5 | 6 | class AnimalTypes(enum.Enum): 7 | """Define Animals Types""" 8 | 9 | dog = "dog" 10 | cat = "cat" 11 | fish = "fish" 12 | turtle = "turtle" 13 | 14 | 15 | class Pets(Base): 16 | """Pets Entity""" 17 | 18 | __tablename__ = "pets" 19 | 20 | id = Column(Integer, primary_key=True) 21 | name = Column(String(length=20), nullable=False, unique=True) 22 | specie = Column(Enum(AnimalTypes), nullable=False) 23 | age = Column(Integer) 24 | user_id = Column(Integer, ForeignKey("users.id")) 25 | 26 | def __repr__(self): 27 | return f"Pet: [name={self.name}, specie={self.specie}, user_id={self.user_id}]" 28 | 29 | def __eq__(self, other): 30 | if ( 31 | self.id == other.id 32 | and self.name == other.name 33 | and self.specie == other.specie 34 | and self.age == other.age 35 | and self.user_id == other.user_id 36 | ): 37 | return True 38 | return False 39 | -------------------------------------------------------------------------------- /src/data/test/find_user_spy.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from src.domain.models import Users 3 | from src.domain.test import mock_user 4 | 5 | 6 | class FindUserSpy: 7 | """Class to define use case: Select User""" 8 | 9 | def __init__(self, user_repository: any) -> None: 10 | self.user_repository = user_repository 11 | self.by_id_param = {} 12 | self.by_name_param = {} 13 | 14 | def by_id(self, user_id: int) -> Dict[bool, Users]: 15 | """Select User By Id""" 16 | 17 | self.by_id_param["user_id"] = user_id 18 | 19 | response = None 20 | 21 | validate_entry = isinstance(user_id, int) 22 | 23 | if validate_entry: 24 | response = mock_user() 25 | 26 | return {"Success": validate_entry, "Data": response} 27 | 28 | def by_name(self, user_name: str) -> Dict[bool, Users]: 29 | """Select User By Id""" 30 | 31 | self.by_name_param["user_name"] = user_name 32 | 33 | response = None 34 | 35 | validate_entry = isinstance(user_name, str) 36 | 37 | if validate_entry: 38 | response = mock_user() 39 | 40 | return {"Success": validate_entry, "Data": response} 41 | -------------------------------------------------------------------------------- /src/data/find_user/find_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.test import UserRepositorySpy 3 | from .find import FindUser 4 | 5 | 6 | faker = Faker() 7 | 8 | 9 | def test_by_id(): 10 | "Should be able test the method by_id()" 11 | 12 | user_repository = UserRepositorySpy() 13 | find_user = FindUser(user_repository) 14 | 15 | attrbutes = {"id": faker.random_number(digits=3)} 16 | 17 | response = find_user.by_id(user_id=attrbutes["id"]) 18 | 19 | # testing inputs 20 | assert user_repository.find_user_by_id_params["user_id"] == attrbutes["id"] 21 | 22 | # testing outputs 23 | assert response["Success"] is True 24 | assert response["Data"] 25 | 26 | 27 | def test_by_name(): 28 | "Should be able test the method by_name()" 29 | 30 | user_repository = UserRepositorySpy() 31 | find_user = FindUser(user_repository) 32 | 33 | attrbutes = {"name": faker.name()} 34 | 35 | response = find_user.by_name(name=attrbutes["name"]) 36 | 37 | # testing inputs 38 | assert user_repository.find_user_by_name_params["name"] == attrbutes["name"] 39 | 40 | # testing outputs 41 | assert response["Success"] is True 42 | assert response["Data"] 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 | 4 |

5 | 6 |

7 | Estudo e desenvolvimento de uma API REST 8 |

9 | 10 |

11 | Issues 12 | Language 13 |

14 | 15 |
16 | 17 | ## :technologist: Tecnologias 18 | 19 | Esse projeto utilizará as seguintes tecnologias: 20 | 21 | - [Git](https://git-scm.com/) 22 | - [Python](https://www.python.org/downloads/release/python-380/) 23 | - **[Flask](https://flask.palletsprojects.com/en/2.0.x/)** 24 | - [DBeaver](https://dbeaver.io/download/) 25 | - [Vscode](https://code.visualstudio.com/) 26 | - [SQLite](https://www.sqlite.org/index.html) 27 | 28 | ## :dart: Metas 29 | ~ Aplicar conhecimentos de **Clean Architecture** no desenvolvimento de uma API REST. 30 | 31 | ~ Aprender sobre o framework web **Flask**. 32 | 33 | ~ Aprender sobre a linguagem de programção **Python** no desenvolvimento de aplicações Web. 34 | -------------------------------------------------------------------------------- /src/data/find_user/find.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Type 2 | from src.domain.use_cases import FindUser as FindUserInterface 3 | from src.data.interfaces import UserRepositoryInterface as UserRepository 4 | from src.domain.models import Users 5 | 6 | 7 | class FindUser(FindUserInterface): 8 | """Class to define use case find user""" 9 | 10 | def __init__(self, user_repository: Type[UserRepository]): 11 | self.user_repository = user_repository 12 | 13 | def by_id(self, user_id: int) -> Dict[bool, Users]: 14 | """Select user by id 15 | :param - user_id: id of th user 16 | :return - Dictionary with proccess info 17 | """ 18 | 19 | response = None 20 | input_is_valid = isinstance(user_id, int) 21 | 22 | if input_is_valid: 23 | response = self.user_repository.find_by_id(user_id=user_id) 24 | 25 | return {"Success": input_is_valid, "Data": response} 26 | 27 | def by_name(self, name: str) -> Dict[bool, Users]: 28 | """Select user by name 29 | :param - name: name of th user 30 | :return - Dictionary with proccess info 31 | """ 32 | 33 | response = None 34 | input_is_valid = isinstance(name, str) 35 | 36 | if input_is_valid: 37 | response = self.user_repository.find_by_name(name=name) 38 | 39 | return {"Success": input_is_valid, "Data": response} 40 | -------------------------------------------------------------------------------- /src/infra/test/pet_repository_spy.py: -------------------------------------------------------------------------------- 1 | from typing import List 2 | from src.domain.test import mock_pet 3 | from src.domain.models import Pets 4 | 5 | 6 | class PetRepositorySpy: 7 | """Spy to Pet Repository""" 8 | 9 | def __init__(self): 10 | self.insert_pet_params = {} 11 | self.find_pet_by_id_params = {} 12 | self.find_pet_by_name_params = {} 13 | self.find_pet_by_user_id_params = {} 14 | 15 | def create(self, name: str, specie: str, age: int, user_id: int) -> Pets: 16 | """Spy to all the attributes""" 17 | 18 | self.insert_pet_params["name"] = name 19 | self.insert_pet_params["specie"] = specie 20 | self.insert_pet_params["age"] = age 21 | self.insert_pet_params["user_id"] = user_id 22 | 23 | return mock_pet() 24 | 25 | def find_by_id(self, pet_id: int) -> Pets: 26 | """Spy to all the attributes""" 27 | 28 | self.find_pet_by_id_params["pet_id"] = pet_id 29 | 30 | return mock_pet() 31 | 32 | def find_by_name(self, name: str) -> Pets: 33 | """Spy to all the attributes""" 34 | 35 | self.find_pet_by_name_params["name"] = name 36 | 37 | return mock_pet() 38 | 39 | def find_by_user_id(self, user_id: int) -> List[Pets]: 40 | """Spy to all the attributes""" 41 | 42 | self.find_pet_by_user_id_params["user_id"] = user_id 43 | 44 | return [mock_pet()] 45 | -------------------------------------------------------------------------------- /src/data/register_pet/register_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | 3 | from src.infra.test import PetRepositorySpy, UserRepositorySpy 4 | from src.data.test import FindUserSpy 5 | from .register import RegisterPet 6 | 7 | faker = Faker() 8 | 9 | 10 | def test_registry(): 11 | """Should be able register a pet""" 12 | 13 | pet_repo = PetRepositorySpy() 14 | find_user = FindUserSpy(UserRepositorySpy()) 15 | register_pet = RegisterPet(pet_repo, find_user) 16 | 17 | attributes = { 18 | "name": faker.name(), 19 | "specie": faker.name(), 20 | "age": faker.random_number(digits=1), 21 | "user_information": {"user_id": faker.random_number(digits=5)}, 22 | } 23 | 24 | response = register_pet.registry( 25 | name=attributes["name"], 26 | specie=attributes["specie"], 27 | age=attributes["age"], 28 | user_information=attributes["user_information"], 29 | ) 30 | 31 | # Testing Inputs 32 | assert pet_repo.insert_pet_params["name"] == attributes["name"] 33 | assert pet_repo.insert_pet_params["specie"] == attributes["specie"] 34 | assert pet_repo.insert_pet_params["age"] == attributes["age"] 35 | 36 | # Testing FindUser Inputs 37 | assert find_user.by_id_param["user_id"] == attributes["user_information"]["user_id"] 38 | 39 | # Testing Outputs 40 | assert response["Success"] is True 41 | assert response["Data"] is not None 42 | -------------------------------------------------------------------------------- /src/data/register_user/register_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.test import UserRepositorySpy 3 | from .register import RegisterUser 4 | 5 | 6 | faker = Faker() 7 | 8 | 9 | def test_register(): 10 | """Should be able register a new user""" 11 | 12 | user_repo = UserRepositorySpy() 13 | register_user = RegisterUser(user_repo) 14 | 15 | attributes = {"name": faker.name(), "password": faker.word()} 16 | 17 | response = register_user.execute( 18 | name=attributes["name"], password=attributes["password"] 19 | ) 20 | 21 | # testing inputs 22 | assert user_repo.insert_user_params["name"] == attributes["name"] 23 | assert user_repo.insert_user_params["password"] == attributes["password"] 24 | 25 | # testing outputs 26 | assert response["Success"] is True 27 | assert response["Data"] 28 | 29 | 30 | def test_register_fail(): 31 | """Should not be able register a new user""" 32 | 33 | user_repo = UserRepositorySpy() 34 | register_user = RegisterUser(user_repo) 35 | 36 | attributes = {"name": faker.random_number(digits=3), "password": faker.word()} 37 | 38 | response = register_user.execute( 39 | name=attributes["name"], password=attributes["password"] 40 | ) 41 | 42 | # testing inputs 43 | assert user_repo.insert_user_params == {} 44 | 45 | # testing outputs 46 | assert response["Success"] is False 47 | assert response["Data"] is None 48 | -------------------------------------------------------------------------------- /src/infra/repo/user_repository_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.config import DatabaseConnectionHandler 3 | from .user_repository import UserRepository 4 | 5 | faker = Faker() 6 | user_repository = UserRepository() 7 | db_connection_handler = DatabaseConnectionHandler() 8 | 9 | 10 | def test_insert_user(): 11 | """Should be able insert an user""" 12 | 13 | name = faker.name() 14 | password = faker.word() 15 | 16 | engine = db_connection_handler.get_engine() 17 | 18 | new_user = user_repository.insert_user(name, password) 19 | user = engine.execute(f"SELECT * FROM users WHERE id={new_user.id};").fetchone() 20 | 21 | engine.execute(f"DELETE FROM users WHERE id={new_user.id};") 22 | 23 | assert new_user.id == user.id 24 | assert new_user.name == user.name 25 | assert new_user.password == user.password 26 | 27 | 28 | def test_find_by_name(): 29 | """Should be able return an user by your name""" 30 | 31 | name = faker.name() 32 | password = faker.word() 33 | user = user_repository.insert_user(name=name, password=password) 34 | 35 | created_user = user_repository.find_by_name(name=name) 36 | 37 | assert user == created_user 38 | 39 | engine = db_connection_handler.get_engine() 40 | engine.execute(f"DELETE FROM users WHERE id='{user.id}';") 41 | 42 | 43 | def test_find_by_id(): 44 | """Should be able return an user by your id""" 45 | 46 | name = faker.name() 47 | password = faker.word() 48 | user = user_repository.insert_user(name=name, password=password) 49 | 50 | created_user = user_repository.find_by_id(user_id=user.id) 51 | 52 | assert user == created_user 53 | 54 | engine = db_connection_handler.get_engine() 55 | engine.execute(f"DELETE FROM users WHERE id='{user.id}';") 56 | -------------------------------------------------------------------------------- /src/data/find_pet/find.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, List, Type 2 | from src.domain.use_cases import FindPet as FindPetInterface 3 | from src.data.interfaces import PetRepositoryInterface as PetRepository 4 | from src.domain.models import Pets 5 | 6 | 7 | class FindPet(FindPetInterface): 8 | """Class to define use case find pet""" 9 | 10 | def __init__(self, pet_repository: Type[PetRepository]): 11 | self.pet_repository = pet_repository 12 | 13 | def by_id(self, pet_id: int) -> Dict[bool, Pets]: 14 | """Select pet by id 15 | :param - pet_id: id of the pet 16 | :return - Dictionary with proccess info 17 | """ 18 | 19 | response = None 20 | input_is_valid = isinstance(pet_id, int) 21 | 22 | if input_is_valid: 23 | response = self.pet_repository.find_by_id(pet_id=pet_id) 24 | 25 | return {"Success": input_is_valid, "Data": response} 26 | 27 | def by_name(self, name: str) -> Dict[bool, Pets]: 28 | """Select pet by name 29 | :param - name: name of the pet 30 | :return - Dictionary with proccess info 31 | """ 32 | 33 | response = None 34 | input_is_valid = isinstance(name, str) 35 | 36 | if input_is_valid: 37 | response = self.pet_repository.find_by_name(name=name) 38 | 39 | return {"Success": input_is_valid, "Data": response} 40 | 41 | def by_user_id(self, user_id: int) -> Dict[bool, List[Pets]]: 42 | """select all pets by user id 43 | :param - user_id: id of the user 44 | :return - Dictionary with proccess info 45 | """ 46 | 47 | data = None 48 | input_is_valid = isinstance(user_id, int) 49 | 50 | if input_is_valid: 51 | data = self.pet_repository.find_by_user_id(user_id=user_id) 52 | 53 | return {"Success": input_is_valid, "Data": data} 54 | -------------------------------------------------------------------------------- /src/data/test/register_pet_spy.py: -------------------------------------------------------------------------------- 1 | from typing import Dict 2 | from src.domain.models import Pets 3 | from src.domain.models.users import Users 4 | from src.domain.test import mock_user, mock_pet 5 | 6 | 7 | class RegisterPetSpy: 8 | """Spy to Class register pet""" 9 | 10 | def __init__(self, pet_repository: any, find_user: any) -> None: 11 | self.pet_repository = pet_repository 12 | self.find_user = find_user 13 | self.registry_param = {} 14 | 15 | def register( 16 | self, name: str, specie: str, user_information: Dict[int, str], age: int = None 17 | ) -> Dict[bool, Pets]: 18 | """Registry Pet""" 19 | 20 | self.registry_param["name"] = name 21 | self.registry_param["specie"] = specie 22 | self.registry_param["user_information"] = user_information 23 | self.registry_param["age"] = age 24 | 25 | response = None 26 | 27 | validate_entry = isinstance(name, str) and isinstance(specie, str) 28 | user = self.__find_user_information(user_information) 29 | checker = validate_entry and user["Success"] 30 | 31 | if checker: 32 | response = mock_pet() 33 | 34 | return {"Success": checker, "Data": response} 35 | 36 | def __find_user_information( 37 | self, user_information: Dict[int, str] 38 | ) -> Dict[bool, Users]: 39 | """Check user infos and select user""" 40 | 41 | user_founded = None 42 | user_params = user_information.keys() 43 | 44 | if "user_id" in user_params and "user_name" not in user_params: 45 | user_founded = {"Success": True, "Data": mock_user()} 46 | 47 | elif "user_id" not in user_params and "user_name" in user_params: 48 | user_founded = {"Success": True, "Data": mock_user()} 49 | 50 | elif "user_id" in user_params and "user_name" in user_params: 51 | user_founded = {"Success": True, "Data": mock_user()} 52 | 53 | else: 54 | return {"Success": False, "Data": None} 55 | 56 | return user_founded 57 | -------------------------------------------------------------------------------- /src/data/register_pet/register.py: -------------------------------------------------------------------------------- 1 | from typing import Dict, Type 2 | 3 | from src.domain.models import Pets, Users 4 | from src.data.find_user import FindUser 5 | from src.data.interfaces import PetRepositoryInterface as PetRepository 6 | from src.domain.use_cases import RegisterPetInterface 7 | 8 | 9 | class RegisterPet(RegisterPetInterface): 10 | """Class to define use case: Register Pet""" 11 | 12 | def __init__( 13 | self, pet_repository: Type[PetRepository], find_user: Type[FindUser] 14 | ) -> None: 15 | self.pet_repository = pet_repository 16 | self.find_user = find_user 17 | 18 | def registry( 19 | self, name: str, specie: str, user_information: Dict[int, str], age: int = None 20 | ) -> Dict[bool, Pets]: 21 | """Registry Pet""" 22 | 23 | response = None 24 | 25 | validate_entry = isinstance(name, str) and isinstance(specie, str) 26 | user = self.__find_user_information(user_information) 27 | checker = validate_entry and user["Success"] 28 | 29 | if checker: 30 | response = self.pet_repository.create( 31 | name, specie, age, user_information["user_id"] 32 | ) 33 | 34 | return {"Success": checker, "Data": response} 35 | 36 | def __find_user_information( 37 | self, user_information: Dict[int, str] 38 | ) -> Dict[bool, Users]: 39 | """Check user infos and selct user""" 40 | 41 | user_founded = None 42 | user_params = user_information.keys() 43 | 44 | if "user_id" in user_params and "user_name" not in user_params: 45 | user_founded = self.find_user.by_id(user_id=user_information["user_id"]) 46 | 47 | elif "user_id" not in user_params and "user_name" in user_params: 48 | user_founded = self.find_user.by_name(user_id=user_information["user_name"]) 49 | 50 | elif "user_id" in user_params and "user_name" in user_params: 51 | user_founded = self.find_user.by_id(user_id=user_information["user_id"]) 52 | 53 | else: 54 | return {"Success": False, "Data": None} 55 | 56 | return user_founded 57 | -------------------------------------------------------------------------------- /src/presenters/controllers/find_user_controller.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from src.main.interface import RouteInterface 3 | from src.domain.use_cases import FindUser 4 | from src.presenters.helpers import HttpRequest, HttpResponse 5 | from src.presenters.errors import HttpErrors 6 | 7 | 8 | class FindUserController(RouteInterface): 9 | """Class to define controller to find user use case""" 10 | 11 | def __init__(self, find_user_use_case: Type[FindUser]): 12 | self.find_user_use_case = find_user_use_case 13 | 14 | def handle(self, http_request: Type[HttpRequest]) -> HttpResponse: 15 | """Method to call use case""" 16 | 17 | data = None 18 | 19 | if http_request.query: 20 | query_string_params = http_request.query.keys() 21 | 22 | if ( 23 | "user_id" in query_string_params 24 | and "user_name" not in query_string_params 25 | ): 26 | user_id = http_request.query["user_id"] 27 | data = self.find_user_use_case.by_id(user_id=user_id) 28 | 29 | elif ( 30 | "user_name" in query_string_params 31 | and "user_id" not in query_string_params 32 | ): 33 | user_name = http_request.query["user_name"] 34 | data = self.find_user_use_case.by_name(name=user_name) 35 | 36 | elif ( 37 | "user_name" in query_string_params and "user_id" in query_string_params 38 | ): 39 | user_id = http_request.query["user_id"] 40 | data = self.find_user_use_case.by_id(user_id=user_id) 41 | 42 | else: 43 | data = {"Success": False, "Data": None} 44 | 45 | if data["Success"] is False: 46 | http_error = HttpErrors.error_422() 47 | return HttpResponse( 48 | status_code=http_error["status_code"], body=http_error["body"] 49 | ) 50 | 51 | return HttpResponse(status_code=200, body=data) 52 | 53 | # if no query in http request 54 | http_error = HttpErrors.error_400() 55 | return HttpResponse( 56 | status_code=http_error["status_code"], body=http_error["body"] 57 | ) 58 | -------------------------------------------------------------------------------- /src/infra/repo/user_repository.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=E1101 2 | 3 | from src.domain.models import Users 4 | from src.data.interfaces import UserRepositoryInterface 5 | from src.infra.config import DatabaseConnectionHandler 6 | from src.infra.entities import Users as UsersModel 7 | 8 | 9 | class UserRepository(UserRepositoryInterface): 10 | """Class to manage User Repository""" 11 | 12 | @classmethod 13 | def insert_user(cls, name: str, password: str) -> Users: 14 | """insert data in user entity 15 | :param - name: user name 16 | : - password: user password 17 | :return - tuple with new user inserted 18 | """ 19 | 20 | with DatabaseConnectionHandler() as db_connection: 21 | try: 22 | new_user = UsersModel(name=name, password=password) 23 | db_connection.session.add(new_user) 24 | db_connection.session.commit() 25 | 26 | return Users( 27 | id=new_user.id, name=new_user.name, password=new_user.password 28 | ) 29 | except: 30 | db_connection.session.rollback() 31 | raise 32 | finally: 33 | db_connection.session.close() 34 | 35 | @classmethod 36 | def find_by_name(cls, name: str) -> Users: 37 | """Find an User by your name 38 | :param - name: User's name 39 | :return - Returns an user 40 | """ 41 | 42 | with DatabaseConnectionHandler() as db_connection: 43 | try: 44 | user = ( 45 | db_connection.session.query(UsersModel).filter_by(name=name).one() 46 | ) 47 | return user 48 | except: 49 | db_connection.session.rollback() 50 | raise 51 | finally: 52 | db_connection.session.close() 53 | 54 | @classmethod 55 | def find_by_id(cls, user_id: str) -> Users: 56 | """Find an User by your id 57 | :param - user_id: User's id 58 | :return - Returns an user 59 | """ 60 | 61 | with DatabaseConnectionHandler() as db_connection: 62 | try: 63 | user = ( 64 | db_connection.session.query(UsersModel).filter_by(id=user_id).one() 65 | ) 66 | return user 67 | except: 68 | db_connection.session.rollback() 69 | raise 70 | finally: 71 | db_connection.session.close() 72 | -------------------------------------------------------------------------------- /src/presenters/controllers/register_pet_controller.py: -------------------------------------------------------------------------------- 1 | from typing import Type 2 | from src.main.interface import RouteInterface 3 | from src.domain.use_cases.register_pet import RegisterPetInterface 4 | from src.presenters.helpers import HttpRequest, HttpResponse 5 | from src.presenters.errors import HttpErrors 6 | 7 | 8 | class RegisterPetController(RouteInterface): 9 | """Class to define route to register a pet""" 10 | 11 | def __init__(self, register_pet_use_case: Type[RegisterPetInterface]): 12 | self.register_pet_use_case = register_pet_use_case 13 | 14 | def handle(self, http_request: Type[HttpRequest]) -> HttpResponse: 15 | """Method do call use case""" 16 | 17 | response = None 18 | 19 | if http_request.body: 20 | body_params = http_request.body.keys() 21 | 22 | if ( 23 | "name" in body_params 24 | and "specie" in body_params 25 | and "user_information" in body_params 26 | ): 27 | user_information_params = http_request.body["user_information"].keys() 28 | 29 | if ( 30 | "user_id" in user_information_params 31 | or "user_name" in user_information_params 32 | ): 33 | name = http_request.body["name"] 34 | specie = http_request.body["specie"] 35 | 36 | if "age" in body_params: 37 | age = http_request.body["age"] 38 | else: 39 | age = None 40 | 41 | response = self.register_pet_use_case.register( 42 | name=name, 43 | specie=specie, 44 | user_information=http_request.body["user_information"], 45 | age=age, 46 | ) 47 | 48 | else: 49 | response = {"Success": False, "Data": None} 50 | else: 51 | response = {"Success": False, "Data": None} 52 | 53 | if response["Success"] is False: 54 | http_error = HttpErrors.error_422() 55 | return HttpResponse( 56 | status_code=http_error["status_code"], body=http_error["body"] 57 | ) 58 | 59 | return HttpResponse(status_code=200, body=response["Data"]) 60 | 61 | http_error = HttpErrors.error_400() 62 | return HttpResponse( 63 | status_code=http_error["status_code"], body=http_error["body"] 64 | ) 65 | -------------------------------------------------------------------------------- /src/data/find_pet/find_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.test import PetRepositorySpy 3 | from src.infra.entities import AnimalTypes 4 | from .find import FindPet 5 | 6 | 7 | faker = Faker() 8 | 9 | 10 | def test_by_pet_id(): 11 | """Should be able find a pet by id""" 12 | 13 | pet_repository = PetRepositorySpy() 14 | find_pet = FindPet(pet_repository) 15 | 16 | attributes = { 17 | "id": faker.random_number(digits=5), 18 | "name": faker.name(), 19 | "specie": AnimalTypes.cat, 20 | "age": faker.random_number(digits=1), 21 | "user_id": faker.random_number(digits=5), 22 | } 23 | 24 | data = find_pet.by_id(pet_id=attributes["id"]) 25 | 26 | # testing inputs 27 | assert pet_repository.find_pet_by_id_params["pet_id"] == attributes["id"] 28 | 29 | # testing outputs 30 | assert data["Success"] is True 31 | assert data["Data"] 32 | 33 | 34 | def test_find_by_invalid_id(): 35 | """Should not be able find a pet with invalid input""" 36 | 37 | pet_repository = PetRepositorySpy() 38 | find_pet = FindPet(pet_repository) 39 | 40 | attributes = { 41 | "id": faker.name(), 42 | "name": faker.name(), 43 | "specie": AnimalTypes.cat, 44 | "age": faker.random_number(digits=1), 45 | "user_id": faker.random_number(digits=5), 46 | } 47 | 48 | data = find_pet.by_id(pet_id=attributes["id"]) 49 | 50 | # testing outputs 51 | assert data["Success"] is False 52 | assert data["Data"] is None 53 | 54 | 55 | def test_by_name(): 56 | """Should be able find a pet by name""" 57 | 58 | pet_repository = PetRepositorySpy() 59 | find_pet = FindPet(pet_repository) 60 | 61 | attributes = { 62 | "id": faker.random_number(digits=5), 63 | "name": faker.name(), 64 | "specie": AnimalTypes.cat, 65 | "age": faker.random_number(digits=1), 66 | "user_id": faker.random_number(digits=5), 67 | } 68 | 69 | data = find_pet.by_name(name=attributes["name"]) 70 | 71 | # testing inputs 72 | assert pet_repository.find_pet_by_name_params["name"] == attributes["name"] 73 | 74 | # testing outputs 75 | assert data["Success"] is True 76 | assert data["Data"] 77 | 78 | 79 | def test_by_user_id(): 80 | """Should be able find all pets by user id""" 81 | 82 | pet_repository = PetRepositorySpy() 83 | find_pet = FindPet(pet_repository) 84 | 85 | attributes = { 86 | "id": faker.random_number(digits=5), 87 | "name": faker.name(), 88 | "specie": AnimalTypes.cat, 89 | "age": faker.random_number(digits=1), 90 | "user_id": faker.random_number(digits=5), 91 | } 92 | 93 | data = find_pet.by_user_id(user_id=attributes["user_id"]) 94 | 95 | # testing inputs 96 | assert pet_repository.find_pet_by_user_id_params["user_id"] == attributes["user_id"] 97 | 98 | # testing outputs 99 | assert data["Success"] is True 100 | assert isinstance(data["Data"], list) 101 | -------------------------------------------------------------------------------- /src/infra/repo/pet_repository_test.py: -------------------------------------------------------------------------------- 1 | from faker import Faker 2 | from src.infra.config import DatabaseConnectionHandler 3 | from src.infra.entities import AnimalTypes, Pets 4 | from .pet_repository import PetRepository 5 | 6 | faker = Faker() 7 | pet_repository = PetRepository() 8 | db_connection_handler = DatabaseConnectionHandler() 9 | 10 | 11 | def test_create_pet(): 12 | """Should be able create a new pet""" 13 | 14 | name = faker.name() 15 | specie = AnimalTypes("cat") 16 | age = faker.random_number(digits=1) 17 | user_id = faker.random_number() 18 | 19 | pet = pet_repository.create(name, specie, age, user_id) 20 | 21 | engine = db_connection_handler.get_engine() 22 | created_pet = engine.execute(f"SELECT * FROM pets WHERE id='{pet.id}';").fetchone() 23 | 24 | assert pet.id == created_pet.id 25 | assert pet.name == created_pet.name 26 | assert pet.specie == created_pet.specie 27 | assert pet.age == created_pet.age 28 | 29 | engine.execute(f"DELETE FROM pets WHERE id='{pet.id}';") 30 | 31 | 32 | def test_find_by_id(): 33 | """Should be able return by your id""" 34 | 35 | name = faker.name() 36 | specie = AnimalTypes("cat") 37 | age = faker.random_number(digits=1) 38 | user_id = faker.random_number() 39 | 40 | pet = pet_repository.create(name, specie, age, user_id) 41 | 42 | engine = db_connection_handler.get_engine() 43 | created_pet = pet_repository.find_by_id(pet_id=pet.id) 44 | 45 | assert pet.id == created_pet.id 46 | assert pet.name == created_pet.name 47 | assert pet.specie == created_pet.specie 48 | assert pet.age == created_pet.age 49 | 50 | engine.execute(f"DELETE FROM pets WHERE id='{pet.id}';") 51 | 52 | 53 | def test_find_by_name(): 54 | """Should be able return by your name""" 55 | 56 | name = faker.name() 57 | specie = AnimalTypes("cat") 58 | age = faker.random_number(digits=1) 59 | user_id = faker.random_number() 60 | 61 | pet = pet_repository.create(name, specie, age, user_id) 62 | 63 | engine = db_connection_handler.get_engine() 64 | created_pet = pet_repository.find_by_name(name=name) 65 | 66 | assert pet.id == created_pet.id 67 | assert pet.name == created_pet.name 68 | assert pet.specie == created_pet.specie 69 | assert pet.age == created_pet.age 70 | 71 | engine.execute(f"DELETE FROM pets WHERE id='{pet.id}';") 72 | 73 | 74 | def test_find_by_user_id(): 75 | """Should be able return by your name""" 76 | 77 | pet_id = faker.random_number(digits=4) 78 | name = faker.name() 79 | specie = "dog" 80 | age = faker.random_number(digits=1) 81 | user_id = faker.random_number(digits=5) 82 | 83 | specie_mock = AnimalTypes("dog") 84 | 85 | data = Pets(id=pet_id, name=name, specie=specie_mock, age=age, user_id=user_id) 86 | 87 | engine = db_connection_handler.get_engine() 88 | 89 | engine.execute( 90 | "INSERT INTO pets (id, name, specie, age, user_id) VALUES ('{}', '{}', '{}', '{}', '{}');".format( 91 | pet_id, name, specie, age, user_id 92 | ) 93 | ) 94 | 95 | pets = pet_repository.find_by_user_id(user_id=user_id) 96 | 97 | assert data in pets 98 | 99 | engine.execute(f"DELETE FROM pets WHERE id='{pet_id}';") 100 | -------------------------------------------------------------------------------- /src/infra/repo/pet_repository.py: -------------------------------------------------------------------------------- 1 | # pylint: disable=E1101 2 | 3 | from typing import List 4 | from src.data.interfaces import PetRepositoryInterface 5 | from src.domain.models import Pets 6 | from src.infra.config import DatabaseConnectionHandler 7 | from src.infra.entities import Pets as PetsModel 8 | 9 | 10 | class PetRepository(PetRepositoryInterface): 11 | """Class to manage Pet Repository""" 12 | 13 | @classmethod 14 | def create(cls, name: str, specie: str, age: int, user_id: int) -> Pets: 15 | """ 16 | Create a new pet 17 | :param - name: 18 | :param - specie: 19 | :param - age: 20 | :param - user_id: 21 | :return - tuple with new pet 22 | """ 23 | 24 | with DatabaseConnectionHandler() as db_connection: 25 | try: 26 | pet = PetsModel(name=name, specie=specie, age=age, user_id=user_id) 27 | db_connection.session.add(pet) 28 | db_connection.session.commit() 29 | 30 | return Pets( 31 | id=pet.id, 32 | name=pet.name, 33 | specie=pet.specie.value, 34 | age=pet.age, 35 | user_id=pet.user_id, 36 | ) 37 | except: 38 | db_connection.session.rollback() 39 | raise 40 | finally: 41 | db_connection.session.close() 42 | 43 | @classmethod 44 | def find_by_id(cls, pet_id: int) -> Pets: 45 | """Return a pet by you id 46 | :param - pet_id: Pet's id 47 | return: Returns a pet 48 | """ 49 | 50 | with DatabaseConnectionHandler() as db_connection: 51 | try: 52 | pet = db_connection.session.query(PetsModel).filter_by(id=pet_id).one() 53 | 54 | return PetsModel( 55 | id=pet.id, 56 | name=pet.name, 57 | specie=pet.specie.value, 58 | age=pet.age, 59 | user_id=pet.user_id, 60 | ) 61 | except: 62 | db_connection.session.rollback() 63 | raise 64 | finally: 65 | db_connection.session.close() 66 | 67 | @classmethod 68 | def find_by_name(cls, name: str) -> Pets: 69 | """Return a pet by you name 70 | :param - name: Pet's name 71 | return: Returns a pet 72 | """ 73 | 74 | with DatabaseConnectionHandler() as db_connection: 75 | try: 76 | pet = db_connection.session.query(PetsModel).filter_by(name=name).one() 77 | 78 | return PetsModel( 79 | id=pet.id, 80 | name=pet.name, 81 | specie=pet.specie.value, 82 | age=pet.age, 83 | user_id=pet.user_id, 84 | ) 85 | except: 86 | db_connection.session.rollback() 87 | raise 88 | finally: 89 | db_connection.session.close() 90 | 91 | @classmethod 92 | def find_by_user_id(cls, user_id: int) -> List[Pets]: 93 | """Return a list of pets by user_id 94 | :param - user_id: Pet's user_id 95 | return: Returns a list with pets selecteds 96 | """ 97 | 98 | with DatabaseConnectionHandler() as db_connection: 99 | try: 100 | pets = ( 101 | db_connection.session.query(PetsModel) 102 | .filter_by(user_id=user_id) 103 | .all() 104 | ) 105 | 106 | return pets 107 | except: 108 | db_connection.session.rollback() 109 | raise 110 | finally: 111 | db_connection.session.close() 112 | -------------------------------------------------------------------------------- /.pylintrc: -------------------------------------------------------------------------------- 1 | [MASTER] 2 | 3 | disable= 4 | C0114 # missing-module-docstring 5 | # A comma-separated list of package or module names from where C extensions may 6 | # be loaded. Extensions are loading into the active Python interpreter and may 7 | # run arbitrary code. 8 | extension-pkg-allow-list= 9 | 10 | # A comma-separated list of package or module names from where C extensions may 11 | # be loaded. Extensions are loading into the active Python interpreter and may 12 | # run arbitrary code. (This is an alternative name to extension-pkg-allow-list 13 | # for backward compatibility.) 14 | extension-pkg-whitelist= 15 | 16 | # Specify a score threshold to be exceeded before program exits with error. 17 | fail-under=10.0 18 | 19 | # Files or directories to be skipped. They should be base names, not paths. 20 | ignore=CVS 21 | 22 | # Files or directories matching the regex patterns are skipped. The regex 23 | # matches against base names, not paths. 24 | ignore-patterns= 25 | 26 | # Python code to execute, usually for sys.path manipulation such as 27 | # pygtk.require(). 28 | #init-hook= 29 | 30 | # Use multiple processes to speed up Pylint. Specifying 0 will auto-detect the 31 | # number of processors available to use. 32 | jobs=1 33 | 34 | # Control the amount of potential inferred values when inferring a single 35 | # object. This can help the performance when dealing with large functions or 36 | # complex, nested conditions. 37 | limit-inference-results=100 38 | 39 | # List of plugins (as comma separated values of python module names) to load, 40 | # usually to register additional checkers. 41 | load-plugins= 42 | 43 | # Pickle collected data for later comparisons. 44 | persistent=yes 45 | 46 | # When enabled, pylint would attempt to guess common misconfiguration and emit 47 | # user-friendly hints instead of false-positive error messages. 48 | suggestion-mode=yes 49 | 50 | # Allow loading of arbitrary C extensions. Extensions are imported into the 51 | # active Python interpreter and may run arbitrary code. 52 | unsafe-load-any-extension=no 53 | 54 | 55 | [MESSAGES CONTROL] 56 | 57 | # Only show warnings with the listed confidence levels. Leave empty to show 58 | # all. Valid levels: HIGH, INFERENCE, INFERENCE_FAILURE, UNDEFINED. 59 | confidence= 60 | 61 | # Disable the message, report, category or checker with the given id(s). You 62 | # can either give multiple identifiers separated by comma (,) or put this 63 | # option multiple times (only on the command line, not in the configuration 64 | # file where it should appear only once). You can also use "--disable=all" to 65 | # disable everything first and then reenable specific checks. For example, if 66 | # you want to run only the similarities checker, you can use "--disable=all 67 | # --enable=similarities". If you want to run only the classes checker, but have 68 | # no Warning level messages displayed, use "--disable=all --enable=classes 69 | # --disable=W". 70 | disable=print-statement, 71 | parameter-unpacking, 72 | unpacking-in-except, 73 | old-raise-syntax, 74 | backtick, 75 | long-suffix, 76 | old-ne-operator, 77 | old-octal-literal, 78 | import-star-module-level, 79 | non-ascii-bytes-literal, 80 | raw-checker-failed, 81 | bad-inline-option, 82 | locally-disabled, 83 | file-ignored, 84 | suppressed-message, 85 | useless-suppression, 86 | deprecated-pragma, 87 | use-symbolic-message-instead, 88 | apply-builtin, 89 | basestring-builtin, 90 | buffer-builtin, 91 | cmp-builtin, 92 | coerce-builtin, 93 | execfile-builtin, 94 | file-builtin, 95 | long-builtin, 96 | raw_input-builtin, 97 | reduce-builtin, 98 | standarderror-builtin, 99 | unicode-builtin, 100 | xrange-builtin, 101 | coerce-method, 102 | delslice-method, 103 | getslice-method, 104 | setslice-method, 105 | no-absolute-import, 106 | old-division, 107 | dict-iter-method, 108 | dict-view-method, 109 | next-method-called, 110 | metaclass-assignment, 111 | indexing-exception, 112 | raising-string, 113 | reload-builtin, 114 | oct-method, 115 | hex-method, 116 | nonzero-method, 117 | cmp-method, 118 | input-builtin, 119 | round-builtin, 120 | intern-builtin, 121 | unichr-builtin, 122 | map-builtin-not-iterating, 123 | zip-builtin-not-iterating, 124 | range-builtin-not-iterating, 125 | filter-builtin-not-iterating, 126 | using-cmp-argument, 127 | eq-without-hash, 128 | div-method, 129 | idiv-method, 130 | rdiv-method, 131 | exception-message-attribute, 132 | invalid-str-codec, 133 | sys-max-int, 134 | bad-python3-import, 135 | deprecated-string-function, 136 | deprecated-str-translate-call, 137 | deprecated-itertools-function, 138 | deprecated-types-field, 139 | next-method-defined, 140 | dict-items-not-iterating, 141 | dict-keys-not-iterating, 142 | dict-values-not-iterating, 143 | deprecated-operator-function, 144 | deprecated-urllib-function, 145 | xreadlines-attribute, 146 | deprecated-sys-function, 147 | exception-escape, 148 | comprehension-escape 149 | 150 | # Enable the message, report, category or checker with the given id(s). You can 151 | # either give multiple identifier separated by comma (,) or put this option 152 | # multiple time (only on the command line, not in the configuration file where 153 | # it should appear only once). See also the "--disable" option for examples. 154 | enable=c-extension-no-member 155 | 156 | 157 | [REPORTS] 158 | 159 | # Python expression which should return a score less than or equal to 10. You 160 | # have access to the variables 'error', 'warning', 'refactor', and 'convention' 161 | # which contain the number of messages in each category, as well as 'statement' 162 | # which is the total number of statements analyzed. This score is used by the 163 | # global evaluation report (RP0004). 164 | evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10) 165 | 166 | # Template used to display messages. This is a python new-style format string 167 | # used to format the message information. See doc for all details. 168 | #msg-template= 169 | 170 | # Set the output format. Available formats are text, parseable, colorized, json 171 | # and msvs (visual studio). You can also give a reporter class, e.g. 172 | # mypackage.mymodule.MyReporterClass. 173 | output-format=text 174 | 175 | # Tells whether to display a full report or only the messages. 176 | reports=no 177 | 178 | # Activate the evaluation score. 179 | score=yes 180 | 181 | 182 | [REFACTORING] 183 | 184 | # Maximum number of nested blocks for function / method body 185 | max-nested-blocks=5 186 | 187 | # Complete name of functions that never returns. When checking for 188 | # inconsistent-return-statements if a never returning function is called then 189 | # it will be considered as an explicit return statement and no message will be 190 | # printed. 191 | never-returning-functions=sys.exit,argparse.parse_error 192 | 193 | 194 | [VARIABLES] 195 | 196 | # List of additional names supposed to be defined in builtins. Remember that 197 | # you should avoid defining new builtins when possible. 198 | additional-builtins= 199 | 200 | # Tells whether unused global variables should be treated as a violation. 201 | allow-global-unused-variables=yes 202 | 203 | # List of names allowed to shadow builtins 204 | allowed-redefined-builtins= 205 | 206 | # List of strings which can identify a callback function by name. A callback 207 | # name must start or end with one of those strings. 208 | callbacks=cb_, 209 | _cb 210 | 211 | # A regular expression matching the name of dummy variables (i.e. expected to 212 | # not be used). 213 | dummy-variables-rgx=_+$|(_[a-zA-Z0-9_]*[a-zA-Z0-9]+?$)|dummy|^ignored_|^unused_ 214 | 215 | # Argument names that match this expression will be ignored. Default to name 216 | # with leading underscore. 217 | ignored-argument-names=_.*|^ignored_|^unused_ 218 | 219 | # Tells whether we should check for unused import in __init__ files. 220 | init-import=no 221 | 222 | # List of qualified module names which can have objects that can redefine 223 | # builtins. 224 | redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io 225 | 226 | 227 | [SPELLING] 228 | 229 | # Limits count of emitted suggestions for spelling mistakes. 230 | max-spelling-suggestions=4 231 | 232 | # Spelling dictionary name. Available dictionaries: none. To make it work, 233 | # install the 'python-enchant' package. 234 | spelling-dict= 235 | 236 | # List of comma separated words that should be considered directives if they 237 | # appear and the beginning of a comment and should not be checked. 238 | spelling-ignore-comment-directives=fmt: on,fmt: off,noqa:,noqa,nosec,isort:skip,mypy: 239 | 240 | # List of comma separated words that should not be checked. 241 | spelling-ignore-words= 242 | 243 | # A path to a file that contains the private dictionary; one word per line. 244 | spelling-private-dict-file= 245 | 246 | # Tells whether to store unknown words to the private dictionary (see the 247 | # --spelling-private-dict-file option) instead of raising a message. 248 | spelling-store-unknown-words=no 249 | 250 | 251 | [SIMILARITIES] 252 | 253 | # Ignore comments when computing similarities. 254 | ignore-comments=yes 255 | 256 | # Ignore docstrings when computing similarities. 257 | ignore-docstrings=yes 258 | 259 | # Ignore imports when computing similarities. 260 | ignore-imports=no 261 | 262 | # Minimum lines number of a similarity. 263 | min-similarity-lines=4 264 | 265 | 266 | [STRING] 267 | 268 | # This flag controls whether inconsistent-quotes generates a warning when the 269 | # character used as a quote delimiter is used inconsistently within a module. 270 | check-quote-consistency=no 271 | 272 | # This flag controls whether the implicit-str-concat should generate a warning 273 | # on implicit string concatenation in sequences defined over several lines. 274 | check-str-concat-over-line-jumps=no 275 | 276 | 277 | [LOGGING] 278 | 279 | # The type of string formatting that logging methods do. `old` means using % 280 | # formatting, `new` is for `{}` formatting. 281 | logging-format-style=old 282 | 283 | # Logging modules to check that the string format arguments are in logging 284 | # function parameter format. 285 | logging-modules=logging 286 | 287 | 288 | [FORMAT] 289 | 290 | # Expected format of line ending, e.g. empty (any line ending), LF or CRLF. 291 | expected-line-ending-format= 292 | 293 | # Regexp for a line that is allowed to be longer than the limit. 294 | ignore-long-lines=^\s*(# )??$ 295 | 296 | # Number of spaces of indent required inside a hanging or continued line. 297 | indent-after-paren=4 298 | 299 | # String used as indentation unit. This is usually " " (4 spaces) or "\t" (1 300 | # tab). 301 | indent-string=' ' 302 | 303 | # Maximum number of characters on a single line. 304 | max-line-length=120 305 | 306 | # Maximum number of lines in a module. 307 | max-module-lines=1000 308 | 309 | # Allow the body of a class to be on the same line as the declaration if body 310 | # contains single statement. 311 | single-line-class-stmt=no 312 | 313 | # Allow the body of an if to be on the same line as the test if there is no 314 | # else. 315 | single-line-if-stmt=no 316 | 317 | 318 | [MISCELLANEOUS] 319 | 320 | # List of note tags to take in consideration, separated by a comma. 321 | notes=FIXME, 322 | XXX, 323 | TODO 324 | 325 | # Regular expression of note tags to take in consideration. 326 | #notes-rgx= 327 | 328 | 329 | [TYPECHECK] 330 | 331 | # List of decorators that produce context managers, such as 332 | # contextlib.contextmanager. Add to this list to register other decorators that 333 | # produce valid context managers. 334 | contextmanager-decorators=contextlib.contextmanager 335 | 336 | # List of members which are set dynamically and missed by pylint inference 337 | # system, and so shouldn't trigger E1101 when accessed. Python regular 338 | # expressions are accepted. 339 | generated-members= 340 | 341 | # Tells whether missing members accessed in mixin class should be ignored. A 342 | # mixin class is detected if its name ends with "mixin" (case insensitive). 343 | ignore-mixin-members=yes 344 | 345 | # Tells whether to warn about missing members when the owner of the attribute 346 | # is inferred to be None. 347 | ignore-none=yes 348 | 349 | # This flag controls whether pylint should warn about no-member and similar 350 | # checks whenever an opaque object is returned when inferring. The inference 351 | # can return multiple potential results while evaluating a Python object, but 352 | # some branches might not be evaluated, which results in partial inference. In 353 | # that case, it might be useful to still emit no-member and other checks for 354 | # the rest of the inferred objects. 355 | ignore-on-opaque-inference=yes 356 | 357 | # List of class names for which member attributes should not be checked (useful 358 | # for classes with dynamically set attributes). This supports the use of 359 | # qualified names. 360 | ignored-classes=optparse.Values,thread._local,_thread._local 361 | 362 | # List of module names for which member attributes should not be checked 363 | # (useful for modules/projects where namespaces are manipulated during runtime 364 | # and thus existing member attributes cannot be deduced by static analysis). It 365 | # supports qualified module names, as well as Unix pattern matching. 366 | ignored-modules= 367 | 368 | # Show a hint with possible names when a member name was not found. The aspect 369 | # of finding the hint is based on edit distance. 370 | missing-member-hint=yes 371 | 372 | # The minimum edit distance a name should have in order to be considered a 373 | # similar match for a missing member name. 374 | missing-member-hint-distance=1 375 | 376 | # The total number of similar names that should be taken in consideration when 377 | # showing a hint for a missing member. 378 | missing-member-max-choices=1 379 | 380 | # List of decorators that change the signature of a decorated function. 381 | signature-mutators= 382 | 383 | 384 | [BASIC] 385 | 386 | # Naming style matching correct argument names. 387 | argument-naming-style=snake_case 388 | 389 | # Regular expression matching correct argument names. Overrides argument- 390 | # naming-style. 391 | #argument-rgx= 392 | 393 | # Naming style matching correct attribute names. 394 | attr-naming-style=snake_case 395 | 396 | # Regular expression matching correct attribute names. Overrides attr-naming- 397 | # style. 398 | #attr-rgx= 399 | 400 | # Bad variable names which should always be refused, separated by a comma. 401 | bad-names=foo, 402 | bar, 403 | baz, 404 | toto, 405 | tutu, 406 | tata 407 | 408 | # Bad variable names regexes, separated by a comma. If names match any regex, 409 | # they will always be refused 410 | bad-names-rgxs= 411 | 412 | # Naming style matching correct class attribute names. 413 | class-attribute-naming-style=any 414 | 415 | # Regular expression matching correct class attribute names. Overrides class- 416 | # attribute-naming-style. 417 | #class-attribute-rgx= 418 | 419 | # Naming style matching correct class constant names. 420 | class-const-naming-style=UPPER_CASE 421 | 422 | # Regular expression matching correct class constant names. Overrides class- 423 | # const-naming-style. 424 | #class-const-rgx= 425 | 426 | # Naming style matching correct class names. 427 | class-naming-style=PascalCase 428 | 429 | # Regular expression matching correct class names. Overrides class-naming- 430 | # style. 431 | #class-rgx= 432 | 433 | # Naming style matching correct constant names. 434 | const-naming-style=UPPER_CASE 435 | 436 | # Regular expression matching correct constant names. Overrides const-naming- 437 | # style. 438 | #const-rgx= 439 | 440 | # Minimum line length for functions/classes that require docstrings, shorter 441 | # ones are exempt. 442 | docstring-min-length=-1 443 | 444 | # Naming style matching correct function names. 445 | function-naming-style=snake_case 446 | 447 | # Regular expression matching correct function names. Overrides function- 448 | # naming-style. 449 | #function-rgx= 450 | 451 | # Good variable names which should always be accepted, separated by a comma. 452 | good-names=i, 453 | j, 454 | k, 455 | ex, 456 | Run, 457 | _ 458 | 459 | # Good variable names regexes, separated by a comma. If names match any regex, 460 | # they will always be accepted 461 | good-names-rgxs= 462 | 463 | # Include a hint for the correct naming format with invalid-name. 464 | include-naming-hint=no 465 | 466 | # Naming style matching correct inline iteration names. 467 | inlinevar-naming-style=any 468 | 469 | # Regular expression matching correct inline iteration names. Overrides 470 | # inlinevar-naming-style. 471 | #inlinevar-rgx= 472 | 473 | # Naming style matching correct method names. 474 | method-naming-style=snake_case 475 | 476 | # Regular expression matching correct method names. Overrides method-naming- 477 | # style. 478 | #method-rgx= 479 | 480 | # Naming style matching correct module names. 481 | module-naming-style=snake_case 482 | 483 | # Regular expression matching correct module names. Overrides module-naming- 484 | # style. 485 | #module-rgx= 486 | 487 | # Colon-delimited sets of names that determine each other's naming style when 488 | # the name regexes allow several styles. 489 | name-group= 490 | 491 | # Regular expression which should only match function or class names that do 492 | # not require a docstring. 493 | no-docstring-rgx=^_ 494 | 495 | # List of decorators that produce properties, such as abc.abstractproperty. Add 496 | # to this list to register other decorators that produce valid properties. 497 | # These decorators are taken in consideration only for invalid-name. 498 | property-classes=abc.abstractproperty 499 | 500 | # Naming style matching correct variable names. 501 | variable-naming-style=snake_case 502 | 503 | # Regular expression matching correct variable names. Overrides variable- 504 | # naming-style. 505 | #variable-rgx= 506 | 507 | 508 | [CLASSES] 509 | 510 | # Warn about protected attribute access inside special methods 511 | check-protected-access-in-special-methods=no 512 | 513 | # List of method names used to declare (i.e. assign) instance attributes. 514 | defining-attr-methods=__init__, 515 | __new__, 516 | setUp, 517 | __post_init__ 518 | 519 | # List of member names, which should be excluded from the protected access 520 | # warning. 521 | exclude-protected=_asdict, 522 | _fields, 523 | _replace, 524 | _source, 525 | _make 526 | 527 | # List of valid names for the first argument in a class method. 528 | valid-classmethod-first-arg=cls 529 | 530 | # List of valid names for the first argument in a metaclass class method. 531 | valid-metaclass-classmethod-first-arg=cls 532 | 533 | 534 | [IMPORTS] 535 | 536 | # List of modules that can be imported at any level, not just the top level 537 | # one. 538 | allow-any-import-level= 539 | 540 | # Allow wildcard imports from modules that define __all__. 541 | allow-wildcard-with-all=no 542 | 543 | # Analyse import fallback blocks. This can be used to support both Python 2 and 544 | # 3 compatible code, which means that the block might have code that exists 545 | # only in one or another interpreter, leading to false positives when analysed. 546 | analyse-fallback-blocks=no 547 | 548 | # Deprecated modules which should not be used, separated by a comma. 549 | deprecated-modules=optparse,tkinter.tix 550 | 551 | # Output a graph (.gv or any supported image format) of external dependencies 552 | # to the given file (report RP0402 must not be disabled). 553 | ext-import-graph= 554 | 555 | # Output a graph (.gv or any supported image format) of all (i.e. internal and 556 | # external) dependencies to the given file (report RP0402 must not be 557 | # disabled). 558 | import-graph= 559 | 560 | # Output a graph (.gv or any supported image format) of internal dependencies 561 | # to the given file (report RP0402 must not be disabled). 562 | int-import-graph= 563 | 564 | # Force import order to recognize a module as part of the standard 565 | # compatibility libraries. 566 | known-standard-library= 567 | 568 | # Force import order to recognize a module as part of a third party library. 569 | known-third-party=enchant 570 | 571 | # Couples of modules and preferred modules, separated by a comma. 572 | preferred-modules= 573 | 574 | 575 | [DESIGN] 576 | 577 | # Maximum number of arguments for function / method. 578 | max-args=5 579 | 580 | # Maximum number of attributes for a class (see R0902). 581 | max-attributes=7 582 | 583 | # Maximum number of boolean expressions in an if statement (see R0916). 584 | max-bool-expr=5 585 | 586 | # Maximum number of branch for function / method body. 587 | max-branches=12 588 | 589 | # Maximum number of locals for function / method body. 590 | max-locals=15 591 | 592 | # Maximum number of parents for a class (see R0901). 593 | max-parents=7 594 | 595 | # Maximum number of public methods for a class (see R0904). 596 | max-public-methods=20 597 | 598 | # Maximum number of return / yield for function / method body. 599 | max-returns=6 600 | 601 | # Maximum number of statements in function / method body. 602 | max-statements=50 603 | 604 | # Minimum number of public methods for a class (see R0903). 605 | min-public-methods=1 606 | 607 | 608 | [EXCEPTIONS] 609 | 610 | # Exceptions that will emit a warning when being caught. Defaults to 611 | # "BaseException, Exception". 612 | overgeneral-exceptions=BaseException, 613 | Exception 614 | --------------------------------------------------------------------------------