@seniorsistemas/fsw-aws-lambda
v0.0.9
Published
Biblioteca de utilitários para construção de lambdas no ambiente SKD G7 para interceptação de primitivas.
Downloads
13
Maintainers
Readme
Fábrica de Software Senior - Aws Lambda Lib
Biblioteca da Fábrica de Software Senior para padronização e auxílio na construção de constomizações utilizando lambdas no ambiente SDK G7 para interceptação de primitivas.
- Instalação
- Principais módulos
- Utilização
- Customizando o ambiente de execução do evento
- Criando evento de execução com autenticação da lambda (Autenticação de aplicação externa)
- AsyncRuleValidator
- Padrão de retorno da função lambda
- Validando existência de propriedades em objetos
- Requisições para a plataforma
- Requisições para a plataforma via utilitário (PlatformApi)
- Consumindo serviços da G5
- Utilitário para envio de E-mail
- Testando as regras dentro do Cloud9
- Contribuindo com o projeto
- Licença
Instalação
A partir do root de um projeto NodeJs, instalar a dependência:
npm install --save @seniorsistemas/fsw-aws-lambda
Feito isso, os módulos já estarão disponíveis para utilização:
require('@seniorsistemas/fsw-aws-lambda');
Dependências opcionais
São dependências requeridas por alguns módulos da lib (o módulo core não requer dependências).
npm install --save axios moment
Principais módulos
A biblioteca é distribuída como módulos no formato CommonJS por compatibilidade nativa com Node v10.16.3 (versão atual suportada pela IDE AWS Cloud9). Os principais módulos são:
@seniorsistemas/fsw-aws-lambda
: Módulo principal, provê funções para manipular o evento nativo da Lambda AWS, configuração da resposta da lambda e gerencia a execução de regras;@seniorsistemas/fsw-aws-lambda/utils
: Utilitários;@seniorsistemas/fsw-aws-lambda/services
: Services com lógicas em comum entre projetos.
Utilização
A utilização da biblioteca visa padronizar o tratamento de eventos recebidos e resposta retornada pela função Lambda e a execução de regras de execução. A documentação também pode ser consultada na respectiva documentação de código em cada função e classe.
Com a integração da biblioteca, o index.js
da função lambda deve-se assimilar com o modelo abaixo:
index.js
// Importação de funções e classe para execução de validações.
const {
lambdaEvent,
lambdaResponse,
AsyncRuleValidator
} = require('@seniorsistemas/fsw-aws-lambda');
// Função principal da lambda, sempre deve ser 'async' ou retornar
// uma Promise com o payload de resposta.
exports.handler = async event => {
// Retorna o payload da requisição original do evento.
// Sempre será um objeto JSON.
const body = lambdaEvent.parseBody(event);
// Lê e extrai informações do evento original da função lambda e
// define as configurações de ambiente, como o ambiente de execução
// (development, homologx, production) e variáveis de ambiente, como
// a URL da plataforma e o token para autenticação.
const eventInfo = lambdaEvent.createEventInfo(event);
// Criação do executor das regras de validação, recebe como parâmetro
// as informações extraídas do evento.
return new AsyncRuleValidator(body, eventInfo)
.validate([ // Adiciona as regras de validação a serem executadas.
'rf01',
'rf02',
/* outras regras */
])
// Após todas as regras serem executadas, avalia se há erros de
// validação e retorna a resposta correspondente, sendo elas:
// Erro de cliente (código 400) caso houveram erros de validação,
// sucesso (código 200) caso contrário.
.then(validationResult => {
if (validationResult.hasErrors()) {
return lambdaResponse
.validationError(validationResult.getErrorsAsString());
}
return lambdaResponse.success(body);
})
// Em caso de alguma exceção ou erro inesperado, retorna erro
// interno (código 417).
.catch(lambdaResponse.internalError);
};
Customizando o ambiente de execução do evento
Ao executar o código:
const eventInfo = lambdaEvent.createEventInfo(event);
Já temos em mãos as variáveis de ambiente necessária para a execução das regras de validação. Entretanto, pode ser necessária a adição de mais variáveis ou a definição de valores padrão para as mesmas.
Os valores padrão podem ser informados como segundo parâmetro da função createEventInfo
, tendo como definição:
interface EnvironmentData {
basePlatformUrl: string;
bearerToken: string;
}
Por exemplo:
const eventInfo = lambdaEvent.createEventInfo(event, {
basePlatformUrl: 'https://platform.senior.com.br/t/senior.com.br/bridge/1.0/rest',
bearerToken: 'Bearer 15c2a9eca2e0f8faac36de609c7d05ca'
});
Caso não forem informados os valores padrão de variáveis de ambiente, serão utilizados os declarados nas constantes environments.js conforme o ambiente identificado a partir dos dados do evento, no atributo event.environment
.
ARQPDT-1923: O usuário liberado pelo SDK (ainda) não possui acesso às configurações de variáveis de ambiente, conforme documentação oficial da AWS. Portanto, faz-se necessário outro meio para definir as variáveis de ambiente.
Criando evento de execução com autenticação da lambda (Autenticação de aplicação externa)
Em alguns momentos é necessário realizar chamadas HTTP para a plataforma com um token que tenha permissões de administrador e não utilizar o token do usuário recebido no evento original. Através desse recurso é possível criar o evento de execução para extrair as informações do evento original e autenticar a lambda na plataforma através da chaves de aplicações geradas na plataforma.
Ao optar por utilizar essa forma de criação do evento, é necessário substituir a implementação utilizada no item "Customizando o ambiente de execução do evento" dessa documentação.
O metodo recebe como parâmetro o evento original, a chave de acesso e a senha de acesso (Essa informações serão geradas através da plataforma).
const eventInfo = await lambdaEvent.createEventInfoWithAuthentication(event, 'PwBbpdyzKSEGrZmfeEiCkrb3awEa', 'mXVJMIs2o7pTsVjMUpaWyVVKlFUa');
Ao executar esse trecho de código será criada uma nova variável no eventInfo chamada "lambdaToken". Através dessa variável é possível obter um token válido para chamadas para a plataforma. Essa informação pode ser acessada da seguinte forma:
const eventInfo = await lambdaEvent.createEventInfoWithAuthentication(event, 'PwBbpdyzKSEGrZmfeEiCkrb3awEa', 'mXVJMIs2o7pTsVjMUpaWyVVKlFUa');
let lambdaToken = eventInfo.lambdaToken;
AsyncRuleValidator
Como o próprio nome sugere, é um validador de regras assíncrono (tudo é ou vira uma Promise
).
As regras de validação são apenas funções, não precisam, necessariamente, serem módulos externos, apenas precisam obedecer à seguinte interface:
function (body, event) {
return 'Mensagem de erro';
}
// Ou
function (body, event) {
return ['Uma mensagem de erro', 'Outra mensagem de erro'];
}
// Ou
async function (body, event) {
return 'Mensagem de erro';
}
Onde o parâmetro body
é o payload da requisição convertido no formato
objeto JSON e event
é o objeto contendo os dados de evento original da função lambda. Cada regra de validação pode ser síncrona ou assíncrona, entretanto,
a execução sempre será assíncrona e podem retornar um, nenhum, ou uma lista de erros de validação.
Possui dois métodos principais para validação:
Ambos os métodos aceitam dois formatos de parâmetros:
O nome da regra. Nesse caso, a regra será carregada de um arquivo no diretório fixo
src/rules
a partir do root do projeto. Por exeplo: 'rf01' será requerido do arquivo '/src/rules/rf01.js'. O arquivo da regra deve obedecer arquitetura de módulos, exportando a função para validação:module.exports = async (body, event) => {}
.A função para execução das regras, podendo conter nenhum, um ou dois parâmetros e retornar uma mensagem, uma lista de mensagens ou uma Promise.
AsyncRuleValidator.validate()
Executa uma série de regras de validação de forma assíncrona, onde todas as regras serão executadas ao mesmo tempo e o resultado será apresentado apenas quando todas as regras terminarem sua execução.
Exemplo:
asyncRuleValidator
.validate([
'rf01',
function (body) { return 'Mensagem de erro' },
async (body, event) => ['Mensagem de erro'] ,
() => 'Mensagem de erro'
]).then(/* ... */);
AsyncRuleValidator.validateOneByOne()
Executa uma série de regras de validação, executando uma regra por vez. Cada regra de validação pode ser síncrona ou assíncrona, entretanto, a execução sempre terá comportamento síncrono (a execução de uma regra trava a execução das seguintes). A primeira regra que retornar uma mensagem de validação excerrará a execução da cadeia de regras.
Exemplo:
asyncRuleValidator
.validateOneByOne([
'rf01',
function (body) { return 'Mensagem de erro' },
async (body, event) => ['Mensagem de erro'] ,
() => 'Mensagem de erro'
]).then(/* ... */);
Tratando o retorno das regras de validação
Após a execução bem sucedida das regras de validação, será invocado o método .then()
da Promise retornada pelos métodos de validação. Este método receberá como argumento uma instância da classe ValidationResult
a qual tem a responsabilidade de armazenar todas as mensagens retornadas por regras de validação e possui métodos auxiliares para posterior tratamento das mensagens.
Principais métodos da classe ValidationResult
:
.hasErros()
: avalia se houveram erros de validação;.getErrors()
: retorna um objeto contendo todas as mensagens de erro de validação, se houver..getErrorsAsString()
retorna uma string concatenando todas as mensagens de erro de validação separadas pelo caractere quebra de linha\n
.
Caso alguma regra falhar na execução e lençar uma exceção, será invocado o método .catch()
da Promise contendo informações sobre qual regra falhou e o detalhe da exceção.
Padrão de retorno da função lambda
Os retornos da função lambda customizada são criados pelas funções do módulo lambda-response
, tanto para sucesso, falha em regras de validação e erros inesperados.
O padrão de retorno sempre será:
{
statusCode: 200 | 400 | 417
headers: {
'Content-Type': 'application/json' | 'text/plain',
'X-Senior-FSW': 'Customizacao'
},
body: {} | ''
}
Importante: Em todos os cenários, o header
X-Senior-FSW
é retornado na resposta com o valorCustomizacao
, identificando que esse retorno foi interceptado e, possivelmente, modificado pela equipe de Customização da Fábrica de Software Senior.
Os códigos padrão de status HTTP retornados são:
200
: Sucesso;400
: Erro de validação retornado por regras;417
: Erro interno, durante a execução de regras ou outro erro inesperado.
O Content-Type
pode variar dependendo do parâmetro informado para o body
, caso o body seja um objeto JSON, o Content-Type será application/json
, caso o body seja um texto, o Content-Type será text/plain. Quando uma resposta for gerada por exceções ou erros inesperados, o corpo da resposta sempre terá o prefixo [FSW-ERROR]
.
Funções para construção da resposta
As funções disponíveis para criação do payload de resposta da função lambda são os seguintes:
const { lambdaResponse } = require('@seniorsistemas/fsw-aws-lambda');
const body = {}; // Também pode ser string.
lambdaResponse.success(body);
lambdaResponse.validationError(body);
lambdaResponse.internalError(body);
const httpStatus = 404;
lambdaResponse.response(httpStatus, body);
Validando existência de propriedades em objetos
Por muitas vezes é necessário validar se determinada propriedade/atritbuo de um objeto existe e se o valor dele é diferente de vazio.
Existem duas formas de realizar esse tratamento:
const { lambdaUtils } = require('@seniorsistemas/fsw-aws-lambda');
//Objeto de exemplo
const obj = {
name: 'Fulano de tal',
nickname: '',
city: 'Blumenau',
infos: {
cel: '47999999999'
},
emptyObj: {
name: ''
}
};
//forma nativa - Valida se a propriedade existe e tem valor. nesse caso a propriedade nickname existe mais não tem valor, então a comapração retoraná false
if(!!obj.nickname) {
//implementation
}
// Via utiliário - Realiza a mesma validação. Se a propriedade existe e tem valor
if(lambdaUtils.isPresent(obj.nickname)){
// implementation
}
Requisições para a plataforma
A biblioteca possui um utilitário para facilitar a realização de requisições para a plataforma SeniorX, funciona como um wrapper para as funções do axios
. Tem por objetivo:
- Autenticação com a plataforma, adiciona o token no cabeçalho da requisição;
- Tratamento da resposta com sucesso;
- Tratamento da resposta com erro. Realiza logs e adiciona informações sobre a URL e o erro ocorrido (não suprime a exceção, a mesma será propagada).
OBS.: Requer dependência com [email protected] (ou posterior).
Exemplo:
const { axiosW } = require('@seniorsistemas/fsw-aws-lambda/utils');
const eventInfo = {}; // O mesmo evento recebido pelas regras de validação.
axiosW.platformGet(eventInfo, '/uri');
// .platformHead(eventInfo, uri, config)
// .platformDelete(eventInfo, uri, config)
// .platformPut(eventInfo, uri, data, config)
// .platformPost(eventInfo, uri, data, config)
// .platformPatch(eventInfo, uri, data, config)
// .get(url, config)
// .head(url, config)
// .delete(url, config)
// .put(url, data, config)
// .post(url, data, config)
// .patch(url, data, config)
Requisições para a plataforma via utilitário (PlatformApi)
Esse novo utilitário PlatformApi facilita as chamadas a outras primtivas da Plataforma de uma maneira mais simples.
Lembrando que o método do tópico acima ainda continua funcional.
Esse novo método é apenas uma absstração que utiliza por baixo as chamadas utilizando o utiliátios axiosW.
const {
lambdaEvent,
PlatformApi
} = require('@seniorsistemas/fsw-aws-lambda');
/*Evento vindo da requisição*/
const eventInfo = lambdaEvent.createEventInfo(event);
const userData = await PlatformApi.Get(eventInfo,'/usuarios/userManager/queries/obterMeusDados');
/*O utilitário possui os seguintes métodos:
PlatformApi.Get(event,primitive,params)
PlatformApi.Delete (event,primitive,params)
PlatformApi.Head (event,primitive,params)
PlatformApi.Post (event, primitive, data, params)
PlatformApi.Put (event, primitive, data, params)
PlatformApi.Patch = (event, primitive, data, params)
*/
Além das chamadas para a plataforma utilizando o token do evento original (usuário logado na plataforma), é possível realizar chamadas utilizando o token obtido na lambda através de uma autenticação de aplicação externa. Para mais informações, verificar o item "Criando evento de execução com autenticação da lambda (Autenticação de aplicação externa)" dessa documentação.
const {
lambdaEvent,
PlatformApi
} = require('@seniorsistemas/fsw-aws-lambda');
/*Evento vindo da requisição*/
const eventInfo = await lambdaEvent.createEventInfoWithAuthentication(event, 'PwBbpdyzKSEGrZmfeEiCkrb3awEa', 'mXVJMIs2o7pTsVjMUpaWyVVKlFUa');
const userData = await PlatformApi.GetWithLambdaToken(eventInfo,'/usuarios/userManager/queries/obterMeusDados');
/*O utilitário possui os seguintes métodos:
PlatformApi.GetWithLambdaToken(event,primitive,params)
PlatformApi.DeleteWithLambdaToken (event,primitive,params)
PlatformApi.HeadWithLambdaToken (event,primitive,params)
PlatformApi.PostWithLambdaToken (event, primitive, data, params)
PlatformApi.PutWithLambdaToken (event, primitive, data, params)
PlatformApi.PatchWithLambdaToken = (event, primitive, data, params)
*/
Consumindo serviços da G5
A biblioteca possui um utilitário para auxiliar nas chamadas aos Webservices SOAP da G5. Esse utilitário realiza a chamada no SXIAPI que é uma biblioteca que possibilita a chamada dos WS SOAP G5, enviando um corpo em JSON e recebendo o corpo também em JSON. Visto que os webservices SOAP da G5 trabalham com dados no formato XML.
Para maiores informações sobre o SXIAPI verificar o link a seguir presente na página dev.senior.com.br: SXIAPI
OBS.: Chamadas a G5 devem ser evitadas a todo custo quando estamos em um contexto de execução dentro de customizações na G7, visto que é uma chamada externa que aumenta consideravelmente o tempo de execução da função lambda, aumentando assim o tempo de respota ao usuário. Esse recurso deve ser utilizado apenas se não existir outra solução.
Exemplo:
const {
lambdaEvent,
G5Api
} = require('@seniorsistemas/fsw-aws-lambda');
/*Evento vindo da requisição*/
const eventInfo = lambdaEvent.createEventInfo(event);
/*Instância classe de utiliários da chamada as APIS da G5
Parâmetros:
- sxiServer: Endereço onde se encontra deployado o utilitário SXI para comunicação com a G5
- sxiContext: Contexto que o utilitário SXI responde dentro do servidor Glassfish da G5
- g5Server: Endereço onde os Webservices SOAP da G5 estão implantados
- user: Nome do Usuário G5 para execução dos webservices
- token: Token da plataforma referente ao usuário logado, ou Senha do usuário da G5 informado
*/
const g5 = new g5Api('http://desktop-lb7k1kk:8000','API','http://desktop-lb7k1kk:8000','senior', eventInfo.platformToken);
/* Realizada a chamada do WS da G5 através do método callService passando como parâmetro:
- g5Module: Módulo da G5 referente ao serviço a ser executado
- service: Nome completo do webservice da G5
- port: Nome da Porta do webservice da G5
- body: Objeto contendo o corpo da requisição (parâmetros de entrada no foramto json)
- dataSourceName: Parâmetro opcional, quando passado altera o nome do objeto principal retornado
*/
const bodyCol = {
numEmp: 1,
abrTipCol: "1",
iniPer: "01/01/2000",
fimPer: "31/01/2001"
};
const response = await g5.callService("rubi","com.senior.g5.rh.fp.colaboradoresAdmitidos","ColaboradoresAdmitidos",bodyCol);
Utilitário para envio de E-mail
Esse utilitário é um facilitador para realizar envio de e-mail através da primitiva de notifação da Pltaforma Senior X: /platform/notifications/actions/notifyUser
O utiliário E-mail Service possui dos métodos para realizar o envio:
sendEmail
Realiza o envio de um email simples, conteúdo em texto Plano, pramâmetros:
- Evento: Enviar o eventIndo que contém os metadados da requisição
- Destinatário: Lista de destinatários, pode ser passado mais de um e-mail separando com " , - virgula"
- Assunto: Assunto do E-mail
- Contéudo: Corpo do E-mail em texto Plano
sendEmailHTML
Realiza o envio de um email com conteúdo formatado em HTML, pramâmetros:
- Evento: Enviar o eventIndo que contém os metadados da requisição
- Destinatário: Lista de destinatários, pode ser passado mais de um e-mail separando com " , - virgula"
- Assunto: Assunto do E-mail
- Contéudo: Corpo do E-mail em HTML
Ambos metódos retornam um objeto de responto se sucesso o retorno será: { ok: true }
const { lambdaEvent } = require('@seniorsistemas/fsw-aws-lambda');
const { emailService } = require('@seniorsistemas/fsw-aws-lambda/services/email-service');
/*Evento vindo da requisição*/
const eventInfo = lambdaEvent.createEventInfo(event);
const responseEmail = await emailService.sendEmail(eventInfo,'[email protected]','Teste Via Customização','Conteúdo');
console.log(responseEmail);
const responseEmailHTML = await emailService.sendEmailHTML(eventInfo,'[email protected],[email protected]','Teste Via Customização HTML','<h1>Conteúdo</h1>');
console.log(responseEmailHTML);
Testando as regras dentro do Cloud9
Após criada, a função lambda pode ser executada e depurada diretamente da IDE AWS Cloud9. Para isso, é necessário abrir o menu de execução da lambda:
No menu lateral direito, clicar na aba "AWS Resources" para listar as lambdas criadas. Escolha a lambda desejada para execução e clique com o botão direito, selecione a opção "Run" e, então, "Run Local".
A aba que será aberta, é a aba de configuração de execução da função lambda. Para executar a função basta clicar no botão "Run" e para executar em modo debug, basta ativar o botão "Run in debug mode" e, então, clicar no botão "Run". Note que no payload há um atributo
"environment": "development"
, este atributo é customizado e não existirá em eventos reais quando a lambda for invocada, ele é utilizado para indicar à biblioteca configurar o ambiente de execução com as variáveis de desenvolvimento. Todo o conteúdo do campo payload será o valor da variávelevent
na execução da lambda.Durante a execução em modo de depuração, no topo da janela de execução é possível controlar os passos de avanço da depuração, acompanhar valor de variáveis e a stack de execução. Clicando na aba "Immediate" na parte inferior da janela, será aberto um console para depuração onde é possível executar um código dentro do contexto do breakpoint.
Contribuindo com o projeto
Para ver como pode contribuir com o projeto, leia as instruções de contribuição.
Licença
Senior Sistemas SA © Ver licença em LICENSE.
Autores: Luiz Nazari <[email protected]> Diego Cassandri <[email protected]> Felipe Gonçalves <[email protected]>