
# AZUPCKTS - Framework

A framework foi feita para facilitar o desenvolvimento de api e app console encapsulando implementações genéricas entre projetos para facilitar e agilizar o desenvolvimento, dentre várias está o processo de criação de rotas e injeções de conexão microserviços e outros.
Há necessidade de iniciar o projeto seguindo os padrões de estrutura a partir do banco de dados.
## Instalação

Instale **azu-pck-ts** e **azu-pck-ts-cli** com npm.


```bash
  npm install azu-pck-ts
  npm install azu-pck-ts-cli -g
```

## Iniciando uma Aplicação

Use o comando abaixo para inicializar uma aplicação API/APP usando a cli.

Importante: ***A pasta do projeto deve estar vazia!***

```bash
  azupckts tsi -api ou -app
```
## Estrutura e Gerenciamento
A framework usa um conceito de gerenciamento usando o seguinte caminho operacional ***Service/Controller***, ou seja, o acesso a aplicação se da pelo ***Service*** onde os dados podem sofrer mudanças ou validações (vide documentação posterior) e em seguinda vai para o ***Controller*** onde há o processamento dos dados.

Os dados sao gerenciados usando uma estrutura de entidade onde delas o dado é encaminhado para o banco de dados. Todas as tabelas do banco de dados devem ser mapeadas para entidades (vide documentação posterior) e estas entidades são responsáveis pelo gerenciamento do dado e envio para o banco.
## Estrutura de Pastas
  * src;
      * class;

        _Pasta que armazena a estrutura das entidades do projeto._
      * controller;

        _Pasta que armazena a estrutura dos controladores das rotas de processamento._
      * service;

        _Pasta que armazena a estrutura dos serviços das rotas de processamento._
      * shared;

        _Pasta contendo as estruturas para definição de eventos, biblioteca genérica do aplicativo e definção do arquivo de configuração._
      * sql;

        _Pasta que armazena a estrutura de sql usado no projeto, desta forma o sql de banco de dados não fica misturado com código fonte._


## Arquivo de configuração.
O arquivo de configuração do projeto são ***app.json e app.prod.json***, o ***app.json*** deve conter apenas implementações que são para o uso em ambiente de _desenvolvimento_ e o ***app.prod.json*** deve-se conter dados para o ambiente de _produção_;
  #### Identificação do projeto.
  _Contem os dados da identifiação do projeto._
* ***name:*** _Nome do projeto (Não conter espaços)._
* ***description:*** _Uma breve descrição do objetivo do projeto._
* ***version:*** _Número da versão do projeto (Usar o padrão <Versao>.<Subversão>.<Release>)._

```javascript
  "project": {
    "name": "projeto_teste",
    "description": "Projeto de Teste",
    "version": "1.0.0"
  }
```
  #### Debugar SQL.
  _Contem a definição se visualiza os sql que estão sendo executados._
* ***viewsql:*** _Exibe ou não os sql que estão executados, valores: ***(true/false)***._

```javascript
  "debug": {
    "viewsql": false
  }
```
  #### Gerar log no console.
  _Contem a definição se visualiza ***(true)*** ou não ***(false)*** os logs de execuções para o log do console._
* ***log:*** _Exibe ou não os logs das execuções, valores: ***(true/false)***._

```javascript
  "applicationlog": {
    "log": false
  }
```
  #### Definição de acesso a banco de dados mongoDB.
  _Contem a definição dos acessos a banco de dados MongoDB._
* ***active:*** _Ativar ou desativar a conexão, valores: ***(true/false)***._
* ***name:*** _Nome da conexão (Não usar espaço), este nome é usado no método ***getMongodbConnection*** para obter a conexão._
* ***main:*** _Ativar a injeção da conexão no canal principal de conexão, ou seja, não há necessidade de informar o nome da conexão no método  ***getMongodbConnection***, valores: ***(true/false)***._
* ***user:*** _Usuário de acesso do banco de dados MongoDB._
* ***password:*** _Senha de acesso do banco de dados MongoDB._
* ***host:*** _Servidor do banco de dados MongoDB._
* ***port:*** _Porta do banco de dados MongoDB._
* ***database:*** _Nome do banco de dados MongoDB._
* ***autoinject:*** _Ativa a alto injeção da conexão na rota, valores: ***(true/false)***._
* ***fieldscontrol:*** _Ativa o gerenciamento dos campos de controle, valores: ***(true/false)***._

```javascript
  "mongodatabase": [
    {
      "active": false,
      "name": "<name>",
      "main": true,
      "user": "<user>",
      "password": "<password>",
      "host": "<ip>",
      "port": 27017,
      "database": "<database>",
      "autoinject": true,
      "fieldscontrol": true
    }
  ]
```
  #### Definição de acesso a banco de dados.
  _Contem a definição dos acessos aos bancos de dados _MySQL, Oracle, MSSQL, Firebird, PostgresSQL._
* ***driver:*** _Driver de conexão do banco de dados, valores: ***(mysql/oracle/mssql/postgres/firebird)***._
* ***main:*** _Ativar a injeção da conexão no canal principal de conexão, ou seja, não há necessidade de informar o nome da conexão no método  ***getConnection***, valores: ***(true/false)***._
* ***keydefinition:*** _Definição da chave primária das tabelas, padrão: ***{table}id***;
* ***keygenerate:*** _Ativa a gerção de um novo id usando a própria framework, por padrão usar a geração usando o próprio banco de dados, nos bancos que suportam ***autoincrement*** ou usando ***triggers*** nos que não suportam, valores: ***(true/false)***._
* ***keygeneratecommand:*** _Comando para geração de id usado somente quando o parâmetro ***keygenerate = true***._
* ***active:*** _Ativa a conexão, valores: ***(true/false)***._
* ***autoinject:*** _Ativa a alto injeção da conexão na rota, valores: ***(true/false)***._
* ***fieldscontrol:*** _Ativa o gerenciamento dos campos de controle, valores: ***(true/false)***._
* ***initscript:*** _Array de comandos ***(String)*** para execução ao iniciar a conexão._
* ***name:*** _Nome da conexão (Não usar espaço), este nome é usado no método ***getConnection*** para obter a conexão._
* ***user:*** _Usuário de acesso do banco de dados informado._
* ***password:*** _Senha de acesso do banco de dados informado._
* ***host:*** _Servidor do banco de dados informado._
* ***port:*** _Porta do banco de dados informado._
* ***database:*** _Nome do banco de dados informado._
* ***preservefieldsnames:*** _Caso seja true ele irá preservar os nomes dos campos como são exibidos no sql respeitando maiusculo ou minusculo, caso contrario ele irá colocar todas as colunas para minusculo ***(default=false)***._
* ***config:*** _Objeto contendo informações específicos de cada driver de conexão._
  * _mysql: ***mysql2***_
   ```javascript
     "config": {
       "whatForConnections": true,
       "connectionLimit": 10,
       "queueLimit": 10
     }
   ```
  * _oracle: ***oracledb***_
   ```javascript
     "config": {
       "pooMax": 10,
       "poolMin": 2,
       "pooIncrement": 2,
       "poolPingInterval": 1
     }
   ```
  * _mssql: ***mssql***_
   ```javascript
     "config": {
       "max": 10,
       "min": 0,
       "idleTimeoutMillis": 30000
     }
   ```
  * _firebird: ***firebird***_
   ```javascript
     "config": {
       "max": 10
     }
   ```
  * _postgres: ***pg***_
   ```javascript
     "config": {
       "max": 10,
       "idleTimeoutMillis": 30000,
       "connectionTimeoutMillis": 2000
     }
   ```

```javascript
  "database": [
    {
      "driver": "mysql",
      "main": true,
      "keydefinition": "{table}id",
      "keygenerate": false,
      "keygeneratecommand": "",
      "active": true,
      "autoinject": true,
      "fieldscontrol": true,
      "initscript": [],
      "name": "<name>",
      "host": "<ip>",
      "user": "<user>",
      "port": 3306,
      "password": "<password>",
      "database": "<database>",
      "config": {
        "waitForConnections": true,
        "connectionLimit": 10,
        "queueLimit": 10
      }
    }
  ]
```

  #### Configuração do gerador de relatório.
  _Contem a definição se inicializa ***(true)*** ou não ***(false)*** as libs para geração de relatório, caso queira usar o gerador de relatório é necessário que esta opção estja com ***(true)*** poise assim a framework efetuará o download da lib necessária para gerar relatório usando jasper._
* ***init:*** _Inicializa a configuração das libs pra geração de relatório, valores: ***(true/false)***._

```javascript
  "reportLib": {
    "init": false
  }
```

  #### Configuração de repositório local.
  _Contem a lista de definições de repositórios locais do sistema._
* ***name:*** _Nome do repositório._
* ***path:*** _Caminho local do repositório._
* ***emptyFileName:*** _Nome do arquivo que será retornado em caso de não existir o arquivo informado._
* ***charFolderSeparator:*** _Caracter usado para separar pastas dentro da url do repositório._

```javascript
  "repository": [
    {"name": "PADRAO", "path": "<path>", "emptyFileName": "<empty filename>", "charFolderSeparator": "*"}
  ],
```

  #### Configuração link externo para o repositório local.
  _Contem a definição do link que será usado para o repositório local externamente._
* ***host:*** _Url base para o repositorio._

```javascript
  "repositoryUrl": {
    "host": "http://localhost:9090"
  }
```

  #### Configuração de repositório de armazenamento.
  _Contem a definição se inicializa ***(true)*** ou não ***(false)*** as libs para geração de relatório, caso queira usar o gerador de relatório é necessário que esta opção estja com ***(true)*** poise assim a framework efetuará o download da lib necessária para gerar relatório usando jasper._
* ***init:*** _Inicializa a configuração das libs pra geração de relatório, valores: ***(true/false)***._

```javascript
  "reportLib": {
    "init": false
  }
```

  #### Parâmetros específicos do usuário.
  _Contem a definição que são específicas do usuário, o objeto usersettings deve conter as mesmas propriedades da classe shared/user_settings.ts (UserSettings). Para acessar os valores desta definição espcífica deve-se usar como descrito no exemplo abaixo:_

```javascript
  "usersettings":{
      "teste": 10;
  }
```

_shared/user_settings.ts (UserSettings)_
```javascript
  export class UserSettings {
    teste!: number;
  }
```

```javascript
   import { ConfigApi } from 'azu-pck-ts';
   import { UserSettings } from '../../shared/user_settings';
   ...
   let valor = ConfigApi.getConfig<UserSettings>().usersettings.teste;

   // O valor=10

```







## Configuração API

  #### Definições basicas da API.
  _Contem as definições basicas para inicialização da API._
* ***environment:*** _Ambiente da configuração da API, valores: ***(development/production)***._
* ***port:*** _Porta que o processo redirecionará as rotas._
* ***limit_request:*** _Tamanho em mb do buffer de entrada da API._
* ***clearEmptyFieldBody:*** _Ativa ou não a remoção de fields vazios do objeto ***body***, valores: ***(true/false)***._
* ***clearEmptyFieldHeader:*** _Ativa ou não a remoção de fields vazios do objeto ***header***, valores: ***(true/false)***._
* ***returnJustApiData:*** _Ativa ou não o retorno direto dos daso na api, ou seja não inclui objetos ou valores a mais no retorno da api, caso esteja ***false*** o processo respeita o retorno padrão ou as anotações de retorno, valores: ***(true/false)***._

```javascript
  "api": {
    "environment": "development",
    "port": 9696,
    "limit_request": "50mb",
    "clearEmptyFieldBody": false,
    "clearEmptyFieldHeader": false,
    "returnJustApiData": false
  }
```

  #### Definições do serviço de https.
  _Contem as definições de certificado de acesso ao serviço de https._
* ***pathKey:*** _Caminho do Key do certificado._
* ***pathCrt:*** _Caminho do Crt do certificado._

```javascript
  "https": {
    "pathKey": "",
    "pathCrt": ""
  }
```

  #### Definições do serviço de swagger.
  _Contem as definições para gerar os serviços de Swagger da API._
* ***active:*** _Ativa ou não o serviço de Swagger na API, valores: ***(true/false)***._
* ***definition:*** _Configurações específicas do serviço de Swagger ***(vide lib express-swagger-generator)***._

```javascript
  "swagger": {
    "active": false,
    "definition": {
      "info": {
        "description": "This is a sample server",
        "title": "Swagger",
        "version": "1.0.0"
      },
      "host": "localhost:3000",
      "basePath": "/v1",
      "produces": [
        "application/json",
        "application/xml"
      ],
      "schemes": [
        "http",
        "https"
      ]
    }
  }
```

  #### Definições do serviço de limite requisições.
  _Contem as definições de número limite de requisições subsequentes em um determinado tempo._
* ***active:*** _Ativa ou não o serviço de limite de requisição na API, valores: ***(true/false)***._
* ***definition:*** _Configurações específicas do serviço de Swagger ***(vide lib express-rate-limit)***._

```javascript
  "limit_rate": {
    "active": false,
    "definition": {
      "windowMs": 2000,
      "max": 1,
      "message": "Muitas conexões criadas a partir deste ip, tente novamente após 15 minutos!"
    }
  }
```

  #### Definições de autenticação da API.
  _Contem as definições de autenticação da API._
* ***generatenewtokenallendpoint:*** _Gera ou não um novo token em cada requisição, valores: ***(true/false)***._
* ***expiresIn:*** _Tempo de expiração do token ***(vide site [vercel/ms](https://github.com/vercel/ms))***._
* ***loginpath:*** _Preencher somente se a rota do arquivo de login for alterada._
* ***algorithm:*** _Algoritimo de geração do token ***(vide lib jsonwebtoken)***._
* ***authorization:*** _Chave secreta para geração do token de acesso da API._

```javascript
  "tokenaccess": {
    "generatenewtokenallendpoint": true,
    "expiresIn": "1d",
    "loginpath": "",
    "algorithm": "HS256",
    "authorization": "<token authorization>"
  }
```

  #### Definições de microserviços.
  _Contem as definições dos microserviços._
* ***name:*** _Nome do repositório do microserviço, (não pode conter espaços)._
* ***url:*** _Este parâmetro pode ser uma string ou um array de strings, nele contem a url do microserviço, a url deve conter apenas o host e a porta, não colocar path interno do microserviço. Outro detalhe é que se houver mais de um endereço neste campo, o projeto do microserviço deve ser o mesmo em todos os endereços o que muda é onde e como está instalado, iste recurso serve para quando um microserviço demandar muito processamento pode-se subi-lo em mais de um processo no com portas diferentes ou em servicores diferentes, o processo de chamada do microserviço no main irá balanciar as requisições entre os endereços._

```javascript
  "microservice_repository": [
    {
        "name": "<Nome do Microserviço>",
        "url": "<Url única>" ou ["Array de Urls"]
    }
  ],
```
## Service
Os _services_ são a porta de entrada das rotas (API, APP), as funções do services vão conter o código de validação dos dados recebidos e as configurações de injeções de configuração da rota, nos tópicos posteriors serão explicados com detalhes cada uma destas funções;

#### Validação
A validação conciste em verficar se os dados estão de acordo com as necessidados do _controller_, uma vez não estando de acordo, o service pode rejeitar a requisição com uma mensagem de erro que será retornada na requisição solicitante.
Para efetuar estas validações de dados utiliza-se a biblioteca ***zod*** que já vem instalado junto com o pacote principal, ***vide site https://zod.dev***, segue um exemplo de validação;

* ***@field:*** Variável para exibir o nome do campo na mensagem;

```javascript
import { BodyService, RequestApi, StandardLoginService, Validation } from "azu-pck-ts";
import { z } from 'zod'

export class LoginService extends StandardLoginService {
  login(req: RequestApi, bodyService: BodyService): Promise<any> {
    return new Promise(async (resolve, reject) => {
      try {
        const LoginSchema = z.object({
          user: z.string({
            required_error: 'O campo @field deve ser preenchido!',
            invalid_type_error: 'O campo @field deve ser do tipo string!'
          }).email({message: 'O campo @field deve ser um email!'}),
          password: z.string({
            required_error: 'O campo @field deve ser preenchido!',
            invalid_type_error: 'O campo @field deve ser do tipo string!'
          })
        })
        await Validation.isValidObject(LoginSchema, bodyService.header)
        resolve(bodyService)
      } catch (error) {
        reject(error)
      }
    })
  }
}

```

#### Modificadores (Annotations);
Os modificadores tem como função alterar o processo para efetuar determinadas operações diferenciadas do processo normal, este modificadores podem ser usados tanto na ***class*** _(Ativo em todos os métodos da class)_ como no ***método*** _(Ativo somente no método)_;

* ***queryValuesIntoBody:*** Caso não haja conteudo no body da requisição mas possua no query este anotação pega o conteudo do query e coloca no body;
* ***requestProtocol:***
    * _type:_ Especifica o tipo de protocolo que a rota permite, ***TypeProtocol.GET, TypeProtocol.POST e TypeProtocol.All***
```javascript
        @requestProtocol({
            type: TypeProtocol.POST
        })

```
* ***errorServiceEvent:***
    * _event:_ Especifica o evento que será executado quando houver alguma rejeição na rota.
```javascript
        @errorServiceEvent({
            event: (io: RequestApi, errorServiceEventDef: ErrorServiceEventDef): Promise<ErrorServiceEventDef> => {
                return new Promise(async (resolve, reject)=>{
                    // Implementações
                    resolve(errorServiceEventDef)
                })
            }
        })

```
* ***endServiceEvent:***
    * _event:_ Especifica o evento que será executado quando finalizar a rota.
```javascript
        @endServiceEvent({
            event: (io: RequestApi, responseBody: any) => Promise<any> => {
                return new Promise(async (resolve, reject)=>{
                    // Implementações
                    resolve(responseBody)
                })
            }
        })

```
* ***startServiceEvent:***
    * _event:_ Especifica o evento que será executado quando inicializa a rota.
```javascript
        @startServiceEvent({
            event: (io: RequestApi, bodyService: BodyService) => Promise<BodyService> => {
                return new Promise(async (resolve, reject)=>{
                    // Implementações
                    resolve(bodyService)
                })
            }
        })

        @startServiceEvent({
            event: (io: RequestApi, bodyService: BodyTypeService<MetodoHeader, MetodoBody, UserBody, MetodoQuery>) => Promise<BodyService> => {
                return new Promise(async (resolve, reject)=>{
                    // Implementações
                    resolve(bodyService)
                })
            }
        })

```
* ***repository:***
    * _name:_ Define o nome do repositório, o mesmo deve ser definido no arquivo de configuração parâmetro ***repository***.
    * _contentPath:_ Define o subdiretório dentro do diretório base do repositorio, caso não haja não passar o parâmetro.
    ***Importante:*** _Quando se usa esta anotação não deve definir o método no controller._
```javascript
        @repository({
            name: '<Nome do Repositório>',
            contentPath: '<Path Interno no Repositório>'
        })

```
* ***microservice:***
    * _link:_ Define o nome do link para o repositório que contem os endereços dos microserviços.
    * _entity:_ Caso a entidade no projeto main for diferente da do projeto do microserviços deve-se informar aqui a entidade do microserviço.
    * _method:_ Caso o método no projeto main for diferente da do projeto do microserviço deve-se informar aqui o método do microserviço;
    ***Importante:*** _Quando se usa esta anotação não deve definir o método no controller._
```javascript
        @microservice({
            link: '<Nome do Link>',
            entity: '<Nome da Entidade>',
            method: '<Nome do Método>'
        })

```
* ***blockExternalAccess:*** Caso o acesso ao endpoint venha de uma requisição api a mesma será bloqueada, caso a rota criada no service for para que um job execute o mesmo deve conter esta anotação para impedir o acesso externo ao service usado no job;
* ***ignoreToken:*** Ignora a validação de token na requisição;
* ***inTransacton:*** Criar uma requisição transacionada no banco de dados, ou seja, se tudo der ok o processo efetuará o commit no final da requisição, caso de erro, o processo irá efetuar um rollback.
* ***customResponseOk:***
* ***customResponseError:***
* ***injectMongoConnection:***
* ***injectConnection:***
* ***jasperReport:***
