Package: nsj-rest-lib
RestLib (nsj_rest_lib)Biblioteca para construção de APIs Rest Python, de acordo com o guidelines interno, e com paradigma declarativo.Variáveis de ambienteVariáveis geraisVariávelObrigatóriaDescriçãoAPP_NAMESimNome da aplicação.DEFAULT_PAGE_SIZENão (padrão: 20)Quantidade máxima de items retonardos numa página de dadosUSE_SQL_RETURNING_CLAUSENão (padrão: true)Montagem das cláusulas returningTESTS_TENANTSimCódigo do tenant obrigatório para rodar os testesVariáveis de bancoVariávelObrigatóriaDescriçãoDATABASE_HOSTSimIP ou nome do host, para conexão com o BD.DATABASE_PASSSimSenha para conexão com o BD.DATABASE_PORTSimPorta para conexão com o BD.DATABASE_NAMESimNome do BD.DATABASE_USERSimUsuário para conexão com o BD.DATABASE_DRIVERNão (padrão: POSTGRES)Driver para conexão com o BD.Variáveis do RabbitMQVariávelObrigatóriaDescriçãoRABBITMQ_HOSTNãoIP ou nome do host.RABBITMQ_HTTP_PORTNão (padrão: 15672)Porta para conexão.RABBITMQ_USERNão   (padrão: guest)Senha para conexão.RABBITMQ_PASSNão (padrão: guest)Senha para conexão.Como instalarMontando ambiente de desenvolvimentoPara rodar a biblioteca localmente, após a configuração da sua aplicação principal, siga os passos:Clone a biblioteca:git clone git@github.com:Nasajon/nsj_rest_lib.gitDesinstale a biblioteca no seu ambiente virtual da aplicação:pip uninstall nsj-rest-libNa variável de ambiente PYTHONPATH localizado no .env da aplicação principal, coloque o caminho da biblioteca após o caminho da aplicação, entre :PYTHONPATH=/home/@work/dados-mestre-api:/home/@work/nsj_rest_lib/srcHealthCheckA classeHealthCheckConfigoferece uma configuração para verificação da saúde em uma aplicação Flask. Verificando o status do banco de dados e do servidor RabbitMQ. Parâmetro obrigatório na inicialização é oflask_application, outros parâmetros como:injector_factory_class,app_name,rabbitmq_host,rabbitmq_http_port,rabbitmq_user,rabbitmq_pass, serão recuperados das variáveis de ambiente e/ou nulos. Naconfigpor padrão os parâmetroscheck_databaseecheck_rabbit_mqvirão True e False, podendo ser ajustados na chamada.Exemplo:#importando a classe
from nsj_rest_lib.healthcheck_config import HealthCheckConfig

HealthCheckConfig(
    flask_application=application
).config(check_database=True, check_rabbit_mq=False)EntityBaseEntityBaseé uma classe abstrata genérica para representar entidades no banco de dados. Ele fornece um modelo flexível para criar classes de entidade específicas. As subclasses devem herdar desta classe e implementar os métodos para configurar os detalhes da tabela no banco de dados.Métodosget_table_name(self) -> str: Método que deve ser implementado pela subclasse para retornar o nome da tabela associada à entidade no banco de dados.get_default_order_fields(self) -> List[str]: Método que deve ser implementado pela subclasse para retornar uma lista de campos para ordenação padrão quando não for especificada uma ordenação personalizada.get_pk_field(self) -> str: Método que deve ser implementado pela subclasse para retornar o nome do campo chave primária na tabela do banco de dados.get_fields_map(self) -> dict: Método que deve ser implementado pela subclasse para retornar um dicionário mapeando nomes de atributos para nomes de campos no banco de dados.get_insert_returning_fields(self) -> List[str]: Método que pode ser implementado pela subclasse para retornar uma lista de campos que devem ser retornados após uma operação de inserção no banco de dados.get_update_returning_fields(self) -> List[str]: Método que pode ser implementado pela subclasse para retornar uma lista de campos que devem ser retornados após uma operação de atualização no banco de dados.Exemplo:from nsj_rest_lib.entity.entity_base import EntityBase

class ClienteEntity(EntityBase):
    id: str
    estabelecimento: str
    cliente: str

    def __init__(self) -> None:
        self.id: str = None
        self.estabelecimento: str = None
        self.cliente: str = None

    def get_table_name(self) -> str:
        return 'cliente'

    def get_pk_field(self) -> str:
        return 'id'

    def get_default_order_fields(self) -> List[str]:
        return ['estabelecimento', 'cliente', 'id']

    def get_fields_map(self) -> dict: #rever
        return {
            "estabelecimento": "estabelecimento",
            "cliente": "cliente"
        }FilterA classeFilteré uma representação de um filtro que pode ser aplicado a uma consulta em um banco de dados ou a uma coleção de dados. Um filtro é composto por um FilterOperator e um valor que será usado para realizar a comparação.Enumeração FilterOperatorEQUALS = "equals"
DIFFERENT = "diferent"
GREATER_THAN = "greater_than"
LESS_THAN = "less_than"
GREATER_OR_EQUAL_THAN = "greater_or_equal_than"
LESS_OR_EQUAL_THAN = "less_or_equal_than"
LIKE = "like"
ILIKE = "ilike"
NOT_NULL = "not_null"Exemplo:from nsj_rest_lib.entity.filter import Filter, FilterOperator

Filter(FilterOperator.EQUALS, 'value')DAODAOBaseé uma classe genérica que serve como um Data Access Object (DAO) para simplificar o processo de interação com o banco de dados.Métodos:__init__(self, db: DBAdapter2, entity_class: EntityBase) -> None: Construtor da classe DAOBase. Inicializa um objeto DAOBase com uma instância de DBAdapter2 e uma classe de entidade (EntityBase) associada.begin(self)-> None: Inicia uma nova transação no banco de dados.commit(self)-> None: Realiza commit na transação corrente no banco de dados, se houver uma transação em andamento. Não gera erro se não houver uma transação.rollback(self)-> None: Realiza rollback na transação corrente no banco de dados, se houver uma transação em andamento. Não gera erro se não houver uma transação.in_transaction(self)-> bool: Verifica se há uma transação em andamento no DBAdapter. Retorna True se houver uma transação, caso contrário, retorna False.get(self, key_field: str, id: uuid.UUID, fields: List[str] = None, filters: Dict[str, List[Filter]] = None, conjunto_type: ConjuntoType = None, conjunto_field: str = None)-> EntityBase: Retorna uma instância de entidade com base no seu ID. Aceita parâmetros opcionais para especificar campos específicos, filtros adicionais ou junções de conjuntos.list(self, after: uuid.UUID, limit: int, fields: List[str], order_fields: List[str], filters: Dict[str, List[Filter]], conjunto_type: ConjuntoType = None, conjunto_field: str = None)-> List[EntityBase]: Retorna uma lista paginada de entidades. Aceita parâmetros para especificar a partir de qual registro (after) começar a paginar, o número máximo de registros (limit), campos a serem incluídos, campos pelos quais ordenar (order_fields), filtros adicionais e junções de conjuntos.insert_relacionamento_conjunto(self, id: str, conjunto_field_value: str, conjunto_type: ConjuntoType = None)-> None: Insere um relacionamento com um conjunto para uma entidade específica com base no seu ID e no valor do campo de conjunto. Permite especificar o tipo de conjunto, caso necessário.delete_relacionamento_conjunto(self, id: str, conjunto_type: ConjuntoType = None)-> None: Remove um relacionamento com um conjunto para uma entidade específica com base no seu ID. Permite especificar o tipo de conjunto, caso necessário.insert(self, entity: EntityBase)-> EntityBase: Insere um objeto de entidade no banco de dados. Retorna a entidade inserida com os dados atualizados, incluindo a chave primária, se for gerada automaticamente pelo banco de dados.update(self, key_field: str, key_value: Any, entity: EntityBase, filters: Dict[str, List[Filter]], partial_update: bool = False)-> EntityBase: Atualiza um objeto de entidade no banco de dados com base no campo de chave, valor de chave, filtros e entidade fornecidos. Permite a opção partial_update para atualização parcial de campos. Retorna a entidade atualizada com os dados mais recentes do banco de dados.list_ids(self, filters: Dict[str, List[Filter]])-> Optional[List[Any]]: Lista os IDs das entidades que correspondem aos filtros fornecidos. Retorna uma lista de IDs ou None se não houver correspondência.delete(self, filters: Dict[str, List[Filter]])-> None: Exclui registros do banco de dados com base nos filtros fornecidos. Gera uma exceção NotFoundException se nenhum registro for encontrado para exclusão.is_valid_uuid(self, value)-> bool: Verifica se um valor é um UUID válido.DTODTOBaseé uma classe abstrata que representa um Data Transfer Object (DTO) usado para transferir dados entre a camada de apresentação e a camada de serviço de uma aplicação. É especialmente útil em operações onde uma entidade de banco de dados precisa ser convertida em um formato adequado para interações com a interface do usuário.Atributos:resume_fields: Set[str]- Conjunto de campos que devem ser incluídos na visualização resumida do DTO.partition_fields: Set[str]- Conjunto de campos utilizados para particionamento de dados.fields_map: Dict[str, DTOField]- Dicionário mapeando nomes de atributos do DTO para configurações de campos correspondentes.list_fields_map: dict- Dicionário mapeando nomes de atributos do DTO para configurações de campos de lista.field_filters_map: Dict[str, DTOFieldFilter]- Dicionário mapeando nomes de atributos do DTO para configurações de filtros de campo.pk_field: str- Nome do campo chave primária na entidade associada ao DTO.fixed_filters: Dict[str, Any]- Dicionário contendo filtros fixos que devem ser aplicados ao recuperar dados da base de dados.conjunto_type: ConjuntoType- Tipo de conjunto utilizado para junções de dados relacionados.conjunto_field: str- Nome do campo utilizado para junções de dados relacionados.escape_validator: bool- Indica se a validação de dados deve ser ignorada.uniques: Dict[str, Set[str]]- Dicionário mapeando nomes de campos únicos para um conjunto de valores únicos.candidate_keys: List[str]- Lista de nomes de campos que juntos formam uma chave candidata única.Métodos:__init__(self, entity: Union[EntityBase, dict] = None, escape_validator: bool = False, generate_default_pk_value: bool = True, **kwargs)-> None: Construtor da classe DTOBase que inicializa um objeto DTOBase com base em uma entidade ou um dicionário de dados, permitindo determinar se a validação deve ser ignorada e se o valor da PK deve ser gerado se não for fornecido.convert_to_entity(self, entity_class: EntityBase, none_as_empty: bool = False)-> EntityBase - Converte o DTO para uma instância da entidade associada.convert_to_dict(self, fields: Dict[str, List[str]] = None, just_resume: bool = False)-> Dict - Converte o DTO para um dicionário, permitindo especificar campos para inclusão e se deve ser uma visualização resumida.DTOFieldA classeDTOFieldrepresenta uma propriedade de um objeto DTO e define várias configurações para essa propriedade, como tipo esperado, validações, formatações, entre outras. As validações personalizadas são acessadas emDTOFieldValidators. A classeDTOFieldFilterrepresenta um filtro que pode ser aplicado a uma propriedade DTO para consultas.Parâmetros:type [object = None]: Tipo esperado para a propriedade. Se for do tipo enum.Enum, o valor recebido será convertido para o enumerado.not_null [bool = False]: Indica se o campo não pode ser None ou vazio (no caso de strings).resume [bool = False]: Indica se o campo será usado como resumo, sempre retornado em consultas GET que listam os dados.min [int = None]: Menor valor permitido (ou menor comprimento, para strings).max [int = None]: Maior valor permitido (ou maior comprimento, para strings).validator [typing.Callable = None]: Função que valida o valor da propriedade antes de atribuí-lo.strip [bool = False]: Indica se espaços no início e no fim de strings devem ser removidos.entity_field [str = None]: Nome da propriedade equivalente na classe de entidade.filters [typing.List[DTOFieldFilter] = None]: Lista de filtros adicionais suportados para esta propriedade.pk [bool = False]: Indica se o campo é a chave primária da entidade.use_default_validator [bool = True]: Indica se o validador padrão deve ser aplicado à propriedade.default_value [typing.Union[typing.Callable, typing.Any] = None]: Valor padrão de preenchimento da propriedade, caso não seja fornecido.partition_data [bool = False]: Indica se esta propriedade participa dos campos de particionamento da entidade.convert_to_entity [typing.Callable = None]: Função para converter o valor do DTO para o valor da entidade.convert_from_entity [typing.Callable = None]: Função para converter o valor da entidade para o valor do DTO.unique [str = None]: Nome de chave de unicidade, usado para evitar duplicações no banco de dados.candidate_key [bool = False]: Indica se este campo é uma chave candidata.Exemplo:from nsj_rest_lib.descriptor.dto_field import DTOField, DTOFieldFilter
from nsj_rest_lib.descriptor.dto_field_validators import DTOFieldValidators

cliente: str = DTOField(resume=True, not_null=True, strip=True, min=11, max=60, 
    validator=DTOFieldValidators().validate_cpf_or_cnpj)
criado_em: datetime.datetime = DTOField(
        resume=True,
        filters=[
            DTOFieldFilter('criado_apos', FilterOperator.GREATER_THAN),
            DTOFieldFilter('criado_antes', FilterOperator.LESS_THAN),
        ],
        default_value=datetime.datetime.now
    )Controllersget_routeExemplo:from nsj_rest_lib.controller.get_route import GetRoute

@GetRoute(
    url=GET_ROUTE,
    http_method='GET',
    dto_class=ClienteDTO,
    entity_class=ClienteEntity
)list_routeExemplo:from nsj_rest_lib.controller.list_route import ListRoute

@ListRoute(
    url=LIST_ROUTE,
    http_method='GET',
    dto_class=ClienteDTO,
    entity_class=ClienteEntity
)post_routeExemplo:from nsj_rest_lib.controller.post_route import PostRoute

@PostRoute(
    url=LIST_POST_ROUTE,
    http_method='POST',
    dto_class=ClienteDTO,
    entity_class=ClienteEntity,
    dto_response_class=ClientePostReturnDTO
)put_routeExemplo:from nsj_rest_lib.controller.put_route import PutRoute

@PutRoute(
    url=GET_PUT_ROUTE,
    http_method='PUT',
    dto_class=ClienteDTO,
    entity_class=ClienteEntity
)delete_routeExemplo:from nsj_rest_lib.controller.delete_route import DeleteRoute

@DeleteRoute(
    url=GET_DELETE_ROUTE,
    http_method="DELETE",
    dto_class=ClienteDTO,
    entity_class=ClienteEntity,
    injector_factory=InjectorFactoryMultibanco,
    service_name="cliente_service",
)

## Testes Automatizadossrc/
│...
├── tests/
│   ├── api/
│   │   ├── casos_de_teste/
│   │   │   ├── clientes/
│   │   │   │   ├── delete/
│   │   │   │   │   ├── entradas_json/
│   │   │   │   │   │   ├── exemplo1_204.json
│   │   │   │   │   ├── saidas_json/
│   │   │   │   │   │   ├── exemplo1_204.json
│   │   │   │   │   ├── test_clientes_delete.py
│   │   │   │   ├── get/
│   │   │   │   │   ├── entradas_json/
│   │   │   │   │   │   ├── exemplo1_200.json
│   │   │   │   │   ├── saidas_json/
│   │   │   │   │   │   ├── exemplo1_200.json
│   │   │   │   │   ├── test_clientes_get.py
│   │   │   │   ├── post/
│   │   │   │   │   ├── entradas_json/
│   │   │   │   │   │   ├── exemplo1_201.json
│   │   │   │   │   ├── saidas_json/
│   │   │   │   │   │   ├── exemplo1_201.json
│   │   │   │   │   ├── test_clientes_post.py
│   │   │   │   ├── put/
│   │   │   │   │   ├── entradas_json/
│   │   │   │   │   │   ├── exemplo1_204.json
│   │   │   │   │   ├── saidas_json/
│   │   │   │   │   │   ├── exemplo1_204.json
│   │   │   │   │   ├── test_clientes_put.py
│   │   │   ├── dump_sql/
│   │   │   │   ├── global.sql### Montando o ambiente
* Instalar todas as dependências do projeto.pip install -r requirements.txt* Subir o container do banco de testes, para cada teste pode ou não rodar o script contido no `global.sql`, basta configurar no `pre_setup` conforme necessidade.docker-compose up -d postgres* Na configuração do Debug (launch.json), configure o Flask e o Pytest. Certifique-se de que o Flask esteja em execução para o Pytest funcionar corretamente.{
    "name": "Python: Flask",
    "type": "python",
    "request": "launch",
    "module": "flask",
    "env": {
        "FLASK_APP": "src/nsj_rest_lib/wsgi.py",
        "FLASK_DEBUG": "1"
    },
    "args": [
        "run",
        "--no-debugger",
        "--no-reload"
    ],
    "jinja": true,
    "justMyCode": true
},
{
    "name": "Pytest",
    "type": "python",
    "request": "launch",
    "module": "pytest",
    "args": [
        "-s",
        "tests/api/casos_de_teste"
    ]
}### Criação dos testes
Para a criação dos casos de testes de forma automática pode seguir o exemplo ["Criação de caso de teste"](https://github.com/Nasajon/nsj-rest-test-util#cria%C3%A7%C3%A3o-de-caso-de-teste).
Neste contexto, foram desenvolvidos testes para entidade de clientes ([DELETE](tests/api/casos_de_teste/clientes/delete/test_clientes_delete.py), [GET](tests/api/casos_de_teste/clientes/get/test_clientes_get.py), [POST](tests/api/casos_de_teste/clientes/post/test_clientes_post.py) e [PUT](tests/api/casos_de_teste/clientes/put/test_clientes_put.py)).