FEAT: remove deprecated protospack lib
Maestro
Maestro é a API principal da Dadosfera. É responsável pela comunicação do Frontend com nossos microsserviços.
graph TD;
Frontend<-->Maestro;
Maestro<-->duc;
Maestro<-->pi-factory;
Maestro<-->in-factory;
É uma API REST, desenvolvida em NodeJs utilizando o Framework NestJs.
🚀 Iniciando
Estas instruções permitirão que você obtenha uma cópia funcional do projeto em sua máquina local para desenvolvimento e testes.
📋 Requisitos
Dica: Utilize NVM para gerenciar facilmente as versões do node
- Solicite acesso à conta de desenvolvimento do AWS Console para todos os serviços necessários
- Crie sua Access Key no menu "Security credentials"
- Defina a Access Key em sua máquina de desenvolvimento local
- Solicite acesso às VPNs de stg e prd
🔧 Instalação
-
Clone o repositório
git clone git@github.com:dadosfera/maestro.git -
Selecione a versão correta do node (opcional, apenas se estiver usando o NVM):
nvm use -
Instale as dependências do projeto:
npm i -
Configure as seguintes variáveis de ambiente:
ENV= DUC_URL= INFACTORY_URL= OTFACTORY_URL= PIFACTORY_URL= SM_OAUTH_PATH= -
Inicie o servidor:
npm run start:devSe preferir, utilize o debbuger do VSCode apertando F5
O serviço deve iniciar com sucesso.
📄 Documentação
O NestJs facilita a documentação de cada rota usando decoradores em todas as propriedades de solicitações e respostas. Ele gera automaticamente um swagger para as informações fornecidas e fornece uma rota para acessá-lo em http://localhost:3333/api. Para mais informações, consulte a documentação oficial.
- Documentações múltiplas
Atualmente, estamos gerando 2 documentações diferentes: Interna e Externa.
Todas as rotas que têm o decorador @ApiInternalOnly() não serão visíveis no swagger da API Externa.
-
Quando você inicia a aplicação com
npm run start:dev, ela servirá e gerará o JSON do swagger da API Interna -
Quando você executa
npm run docs, ele gerará o JSON do swagger de ambas as APIs Interna e Externa e os salvará emdocsfera.jsonedocsfera.external.json, respectivamente;
Link para documentação interna: https://dadosfera.github.io/docsfera
É importante executar npm run docs antes de cada implantação para que sempre tenhamos as documentações mais atualizadas publicadas.
Decoradores de autenticação
O Maestro possui utilitários para facilitar a autenticação do usuário em todos os controladores e rotas. Os seguintes decoradores estão disponíveis:
@Authenticated
Se o usuário precisar estar autenticado para fazer uma solicitação, podemos usar o decorador @Authenticated no controlador ou rota, conforme necessário.
import { Authenticated } from '../../authentication/authentication.decorator';
@Controller('foo')
@Authenticated()
class FooController {
/* ... */
@Get('bar')
async getBar() {
this.logger.info('usuário está autenticado!');
return { authenticated: true };
}
}
import { Authenticated } from '../../authentication/authentication.decorator';
@Controller('foo')
class FooController {
/* ... */
@Get('bar')
@Authenticated()
async getBar() {
this.logger.info('usuário está autenticado!');
return { authenticated: true };
}
@Post('bar')
async postBar() {
this.logger.info('usuário NÃO está autenticado!');
return { authenticated: false };
}
}
@RequireAllPermissions
Este decorador exige que todas as permissões listadas sejam concedidas ao usuário solicitante.
import { RequireAllPermissions } from '../../authentication/authentication.decorator';
import { PERMISSIONS } from '../../authentication/permissions.enum';
@Controller('foo')
class FooController {
/* ... */
@Get('bar')
@RequireAllPermissions(Permissions.BAR.MANAGE, Permissions.BAR.CREATE)
async getBar() {
/* ... */
}
}
@RequireSomePermission
No caso seguinte, é necessário que o usuário tenha pelo menos uma das permissões listadas.
import { RequireSomePermission } from '../../authentication/authentication.decorator';
import { PERMISSIONS } from '../../authentication/permissions.enum';
@Controller('foo')
@RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.CREATE)
class FooController {
/* ... */
@Get('bar')
async getBar() {
/* ... */
}
}
@AuthenticateCondition
Se for necessária uma verificação de autenticação mais complicada, podemos usar o decorador @AuthenticateCondition para defini-la. A função personalizada deve retornar true para autenticar a solicitação.
No exemplo a seguir:
- todas as rotas no controlador
FooControllersó podem ser solicitadas a partir do localhost POST /foo/barsó pode ser solicitado a partir do localhost e por usuários do cliente com id111...eef
import { AuthenticateCondition } from '../../authentication/authentication.decorator';
@Controller('foo')
// permitir solicitações apenas do localhost
@AuthenticateCondition((request: Request) => request.ip === '::ffff:127.0.0.1')
class FooController {
/* ... */
@Get('bar')
async getBar() {
/* ... */
}
@Post('bar')
// permitir solicitações apenas de um cliente específico
@AuthenticateCondition(
(request: Request, user: RequestUser) =>
user.customer_id === '1113e943-2187-4fdd-9c2c-54338fedaeef',
)
async postBar() {
/* ... */
}
}
Nota sobre decoradores de autenticação
-
O método antigo de autenticação colocava os dados do usuário no campo
request.body.info, o que impõe certos problemas em relação ao corpo da solicitação, pois esses dados devem vir do frontend sem qualquer modificação pelo Maestro. Agora, esse uso está ⚠️ DESCONTINUADO ⚠️. Estamos trabalhando para migrar para o decorador de parâmetro@User. O método antigo está funcionando enquanto a migração está em andamento. -
Os decoradores de autenticação podem ser usados juntos e todos eles devem passar para que a solicitação seja autenticada, mas, no caso geral, você não precisa (e não gostaria de...) usar todos eles juntos, pois você pode codificar toda a lógica de autenticação no decorador
@AuthenticateCondition.import { RequireAllPermissions, RequireSomePermission, AuthenticateCondition, } from '../../authentication/authentication.decorator'; import { PERMISSIONS } from '../../authentication/permissions.enum'; import { Waa, Baz } from './authenticationFunctions'; @Controller('foo') @RequireAllPermissions(Permissions.FOO.USE, Permissions.FOO.REQUEST) @RequireSomePermission(Permissions.FOO.MANAGE, Permissions.FOO.ADMIN) @AuthenticateCondition(Waa) @AuthenticateCondition(Baz) class FooController { /* ... */ @Get('bar') async getBar() { /* ... */ } @Post('bar') @RequireAllPermissions( Permissions.BAR.MANAGE, Permissions.BAR.USE, Permissions.BAR.REQUEST, ) async postBar() { /* ... */ } } -
Se
@RequireAllPermissionse@RequireSomePermissionforem usados com apenas uma única permissão, eles apresentam o exatamente mesmo comportamento.// mesmo comportamento @RequireAllPermissions(Permissions.BAR.MANAGE) @RequireSomePermission(Permissions.BAR.MANAGE)
Decorador de parâmetro @User
Os dados do usuário solicitante podem ser obtidos usando o decorador de parâmetro @User, como no exemplo a seguir:
import { User, RequestUser } from '../../authentication/user.decorator';
@Controller('foo')
class FooController {
/* ... */
@Get('bar')
async getBar(@User() user: RequestUser) {
this.logger.info(user);
return { user };
}
}
Se o usuário precisar estar logado, defina required como true, como por exemplo:
import { User, RequestUser } from '../../authentication/user.decorator';
@Controller('user')
class UserController {
/* ... */
@Post('change-password')
@HttpCode(HttpStatus.OK)
async changePassword(
@User({ required: true }) user: RequestUser,
@Body() body: AuthChangePasswordRequest,
) {
const { oldPassword, newPassword } = body;
return this.authClient.changePassword({
userId: user.user_id,
oldPassword,
newPassword,
});
}
}
📦 Desenvolvimento
⌨️ Estilo de Codificação
Por padrão, usamos ESLint + Prettier com configurações padrão.
Recomendamos usar o Visual Studio Code e instalar as extensões recomendadas para facilitar o processo de desenvolvimento.
Padrão de commits
Nosso pipeline de fluxo de trabalho segue as especificações de commits convencionais (cheat sheets) para liberar versões conforme necessário.
Formato: <type>[optional scope]: <description>
Exemplo: FIX: ensure Range headers adhere more closely to RFC 2616
Convenção de nomenclatura de branchs
-
Feature: Quaisquer alterações de código para um novo módulo ou caso de uso devem ser feitas em uma branch de feature. Esta branch é criada com base na branch
main. Quando todas as alterações estiverem concluídas, será necessário um Pull Request/Merge Request para colocar todas essas alterações de volta na branchmain. Exemplos:feature/integrate-swagger,feature/JIRA-1234,feature/JIRA-1234_support-dark-theme.Recomenda-se usar todas as letras em minúsculas e hífen (-) para separar palavras, a menos que seja um nome ou ID de item específico. O sublinhado (_) pode ser usado para separar o ID e a descrição.
-
Bug Fix: Se as alterações de código feitas na branch de feature foram rejeitadas após um lançamento, sprint ou demo, quaisquer correções necessárias após isso devem ser feitas na branch de correção de bug. Exemplos:
bugfix/more-gray-shades,bugfix/JIRA-1444_gray-on-blur-fix. -
Hot Fix: Se houver necessidade de corrigir um bloqueador, fazer um patch temporário, aplicar uma mudança crítica de framework ou configuração que deva ser tratada imediatamente, ela deve ser criada como um Hotfix. Exemplos:
hotfix/disable-endpoint-zero-day-exploit,hotfix/increase-scaling-threshold. -
Experimental: Uma branch para experimentar. Qualquer nova feature ou ideia que não faça parte de um lançamento ou sprint. Exemplo:
experimental/dark-theme-support.
Fazendo um Pull Request
- Comite suas alterações
- Abra o Pull Request no GitHub
- Envie o link do Pull Request no grupo de chat do Google da Microsfera para revisão e possível aprovação
🛠️ Construído com
Algumas tecnologias usadas neste projeto:
- NestJS - Framework para construir aplicações NodeJS eficientes e escaláveis no lado do servidor
⚙️ Arquitetura de Back-end
A arquitetura pode ser encontrada neste link.