Normal Programming Mistake

    @seniorsistemas/fsw-aws-lambda

    0.0.9 • Public • Published

    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

    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:

    1. 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) => {}.

    2. 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 valor Customizacao, 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 axios@0.19.0 (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,'diego.cassandri@senior.com.br','Teste Via Customização','Conteúdo');
    
    console.log(responseEmail);
    
    const responseEmailHTML = await emailService.sendEmailHTML(eventInfo,'diego.cassandri@senior.com.br,diego.cassandri@gmail.com','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:

    1. 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". Menu de execução AWS Resources, indicando onde acessar o submenu "Run Local"

    2. 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ável event na execução da lambda. Executar função lambda com payload customizado

    3. 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. Controle de breakpoint e console de depuração

    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 <luiz.nazari@senior.com.br> Diego Cassandri <diego.cassandri@senior.com.br> Felipe Gonçalves <felipe.goncalves@senior.com.br>

    Install

    npm i @seniorsistemas/fsw-aws-lambda

    DownloadsWeekly Downloads

    2

    Version

    0.0.9

    License

    SEE LICENSE IN LICENCE.md

    Unpacked Size

    60.6 kB

    Total Files

    21

    Last publish

    Collaborators

    • seniorsistemas