<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Vinicius Pereira de Oliveira</title>
    <description>The latest articles on DEV Community by Vinicius Pereira de Oliveira (@devinicius).</description>
    <link>https://dev.to/devinicius</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F582509%2F5ecb68d8-f1fb-4a38-9b06-398a9b630302.jpeg</url>
      <title>DEV Community: Vinicius Pereira de Oliveira</title>
      <link>https://dev.to/devinicius</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/devinicius"/>
    <language>en</language>
    <item>
      <title>Opentelemetry + Typescript</title>
      <dc:creator>Vinicius Pereira de Oliveira</dc:creator>
      <pubDate>Mon, 05 Feb 2024 10:54:00 +0000</pubDate>
      <link>https://dev.to/devinicius/opentelemetry-typescript-pa1</link>
      <guid>https://dev.to/devinicius/opentelemetry-typescript-pa1</guid>
      <description>&lt;h2&gt;
  
  
  Inserindo Observabilidade nos seus códigos Typescript
&lt;/h2&gt;

&lt;p&gt;Nesse artigo irei apresentar funcionalidades básicas de observabilidade para inserir nos seus projetos em Nest Js, para que o gerenciamento da sua aplicação e tratamento de erros se torne algo cada vez mais simples no seu desenvolvimento e a correção de incidentes, mais rápida.&lt;/p&gt;

&lt;h3&gt;
  
  
  O que é o Opentelemetry
&lt;/h3&gt;

&lt;p&gt;OpenTelemetry é uma ferramenta para observabilidade utilizada para criação e gerenciamento de traços, métricas e logs de uma aplicação. É uma funcionalidade open source com integração disponível a diversos sistemas de APM (Jaeger, Grafana, Dynatrace e Prometheus).&lt;/p&gt;

&lt;h3&gt;
  
  
  Jaeger
&lt;/h3&gt;

&lt;p&gt;Jaeger é uma plataforma open-source para gerenciamento de &lt;code&gt;trace&lt;/code&gt;. Por ser simples, utilizaremos para monitoramento da nossa aplicação em ambiente local.&lt;/p&gt;

&lt;h3&gt;
  
  
  1 - Organize seu docker compose
&lt;/h3&gt;

&lt;p&gt;Para conseguir testar em ambiente de desenvolvimento, utilizaremos uma ferramenta para gerenciamento de traces open source. Jaeger&lt;/p&gt;

&lt;p&gt;Crie um arquivo chamado &lt;code&gt;docker-compose.yaml&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;e insira o seguinte texto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: '3.0'
services:
  jaeger:
    platform: linux/x86_64
    image: jaegertracing/all-in-one:1.53
    environment:
      - COLLECTOR_ZIPKIN_HTTP_PORT=9411
    ports:
      - "5775:5775/udp"
      - "6831:6831/udp"
      - "6832:6832/udp"
      - "5778:5778"
      - "4317:4317"
      - "4318:4318"
      - "16686:16686"
      - "14250:14250"
      - "14268:14268"
      - "14269:14269"
      - "9411:9411"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;rode um&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker compose up -d
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E o Jaeger já estará instrumentado.&lt;/p&gt;

&lt;p&gt;Para testar se a instrumentação está correta abra a página&lt;br&gt;
&lt;code&gt;http://localhost:16686/&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  2 - Instrumentando os traces no Jaeger
&lt;/h3&gt;

&lt;p&gt;As nossas métricas da aplicação, traces e logs de sistema serão instrumentados a partir de 'exporters' que são configurados a partir do SDK do NodeJS.&lt;/p&gt;

&lt;p&gt;Para instrumentar o sdk do OpenTelemetry é necessário instalar a biblioteca necessária para implantação dessa classe&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @opentelemetry/sdk-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;crie um arquivo para inicializar o sdk, inserindo o seguinte código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { NodeSDK } from "@opentelemetry/sdk-node";

const sdk = new NodeSDK({

})

sdk.start();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após a criação do sdk, precisamos dizer ao OpenTelemetry para onde queremos exportar todos os logs que traços de sistema que ele gerar. Para facilitar esse trabalho, dentro do objeto de configuração do NodeSDK, podemos definir qual &lt;code&gt;exporter&lt;/code&gt; iremos utilizar. O próprio &lt;code&gt;OpenTelemetry Protocol (OTLP)&lt;/code&gt; nos fornece uma boa experiência e facilidade para exportar esses dados.&lt;/p&gt;

&lt;p&gt;Há varios protocolos disponíveis para envio dessas informações aos coletores, por exemplo, JSON, protobuf ou gRPC. &lt;/p&gt;

&lt;p&gt;Iremos utilizar utilizar o protocolo JSON, mas fique a vontade para utilizar o protocolo que quiser, todos irão funcionar bem.&lt;/p&gt;

&lt;p&gt;Instale as dependencias abaixo para gerenciar os &lt;code&gt;exporters&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @opentelemetry/exporter-trace-otlp-http \
  @opentelemetry/exporter-metrics-otlp-http \
  @opentelemetry/sdk-metrics
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Atualizando nossa instancia do SDK, ficaremos com o código da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { NodeSDK } from "@opentelemetry/sdk-node";

const sdk = new NodeSDK({
    traceExporter: new OTLPTraceExporter({}),
    metricReader: new PeriodicExportingMetricReader({
        exporter: new OTLPMetricExporter({})
    })
})

sdk.start();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OBS: Dentro de cada coletor, podemos definir URL's de destino da aplicação. Exemplo, ao utilizar o Dynatrace, temos que definir uma URL de destino dos &lt;code&gt;traces&lt;/code&gt; que criamos. Para realizar essa modificação. Dentro do objeto de &lt;code&gt;OTLPTraceExporter&lt;/code&gt; e &lt;code&gt;OTLPMetricExporter&lt;/code&gt; existe um campo, chamado &lt;code&gt;url&lt;/code&gt;, onde definimos o destino da informação&lt;/p&gt;

&lt;p&gt;Exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { OTLPMetricExporter } from "@opentelemetry/exporter-metrics-otlp-http";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
import { PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
import { NodeSDK } from "@opentelemetry/sdk-node";

const sdk = new NodeSDK({
    traceExporter: new OTLPTraceExporter({
        url: 'http://localhost:4318/v1/traces'
    }),
    metricReader: new PeriodicExportingMetricReader({
        exporter: new OTLPMetricExporter({
            url: 'http://localhost:4318/v1/metrics'
        })
    })
})

sdk.start();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Antes de finalizar a configuração do NodeSDK. Precisamos dizer ao NodeSDK do OpenTelemetry os dados da nossa aplicação, para conseguirmos identificar os traces de maneira mais rápida e como serão as instrumentações automáticas.&lt;/p&gt;

&lt;p&gt;Para definir dados da aplicação para o OpenTelemetry, precisamos instalar a biblioteca  &lt;code&gt;@opentelemetry/resources&lt;/code&gt; e &lt;code&gt;@opentelemetry/semantic-conventions&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @opentelemetry/resources \
  @opentelemetry/semantic-conventions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Onde o código ficará da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { Resource } from '@opentelemetry/resources';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'http://localhost:4318/v1/traces',
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: 'http://localhost:4318/v1/metrics',
    }),
  }),
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAMESPACE]: 'NOME DO SERVICO',
    [SemanticResourceAttributes.SERVICE_VERSION]: 'VERSAO',
  }),
});

sdk.start();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para finalizar, a instrumentação automática, é criada a partir da &lt;code&gt;@opentelemetry/auto-instrumentations-node&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Então&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @opentelemetry/auto-instrumentations-node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E modificamos o código, deixando-o da seguinte forma:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
import { Resource } from '@opentelemetry/resources';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
import { NodeSDK } from '@opentelemetry/sdk-node';
import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';

const sdk = new NodeSDK({
  traceExporter: new OTLPTraceExporter({
    url: 'http://localhost:4318/v1/traces',
  }),
  metricReader: new PeriodicExportingMetricReader({
    exporter: new OTLPMetricExporter({
      url: 'http://localhost:4318/v1/metrics',
    }),
  }),
  resource: new Resource({
    [SemanticResourceAttributes.SERVICE_NAMESPACE]: 'NOME',
    [SemanticResourceAttributes.SERVICE_VERSION]: '0.2',
    [SemanticResourceAttributes.SERVICE_NAME]: 'Nome Identificador da aplicação',
  }),
  instrumentations: [getNodeAutoInstrumentations()]
});

sdk.start();

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3 - Criando Traces Manuais
&lt;/h3&gt;

&lt;p&gt;Para criarmos os nossos traços na aplicação de forma manual, utilizaremos decorators que vão nos facilitar a não ficar repetindo código na aplicação inteira, para pegar todo o caminho de uma requisição ou lógica.&lt;/p&gt;

&lt;p&gt;Criaremos um decorator, que por enquanto apenas executará o método sem modificá-lo em nada&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const Trace = (value?: string) =&amp;gt; {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor,
  ) {
    const method = descriptor.value

    const args = arguments;

    descriptor.value = function() {
      const that = this
      return method?.apply(that, args)
    }
  };
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para iniciarmos a criação dos tracers, é necessário instalar mais uma biblioteca chamada &lt;code&gt;@opentelemetry/api&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn add @opentelemetry/api
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modificamos o decorator, para ficar do seguinte formato:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Span, trace } from '@opentelemetry/api';

export const Trace = (value?: string) =&amp;gt; {
  return function (
    target: any,
    propertyKey: string,
    descriptor: PropertyDescriptor,
  ) {
    const method = descriptor.value;

    const args = arguments;

    const tracer = trace.getTracer('');

    descriptor.value = function () {
      const that = this;
      return tracer.startActiveSpan(
        value ?? `${target.constructor.name}.${propertyKey}`,
        (span: Span) =&amp;gt; {
          console.log(propertyKey)
          const result = method?.apply(that, args);
          span.end();
          return result
        }
      );
    };
  };
};

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dessa forma conseguiremos todos os traços dos métodos que inserirem esse &lt;code&gt;decorator&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Criaremos também uma função de traceError para conseguir ter mapeados todos os erros que acontecerem na aplicação.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { Exception, SpanStatusCode, trace } from "@opentelemetry/api";

export function traceError(error: any) {
  const activeSpan = trace.getActiveSpan();

  activeSpan?.recordException(error as Exception);
  activeSpan?.setAttribute(
    "exception.response",
    JSON.stringify(
      {
        data: "Erro AXIOS",
      },
      null,
      2
    )
  );
  activeSpan?.setStatus({
    code: SpanStatusCode.ERROR,
    message: `Error: ${error.message}`,
  });
}

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
    </item>
    <item>
      <title>Simulando a AWS no seu ambiente Local</title>
      <dc:creator>Vinicius Pereira de Oliveira</dc:creator>
      <pubDate>Thu, 17 Aug 2023 22:41:38 +0000</pubDate>
      <link>https://dev.to/devinicius/simulando-a-aws-na-sua-maquina-local-43k3</link>
      <guid>https://dev.to/devinicius/simulando-a-aws-na-sua-maquina-local-43k3</guid>
      <description>&lt;p&gt;Opa, espero que esteja tudo bem com você que está lendo esse artigo no momento.&lt;br&gt;
Passei por situações interessantes, no período em que desenvolvia uma &lt;em&gt;feature&lt;/em&gt; na atual empresa em que me encontro. &lt;/p&gt;

&lt;p&gt;Tive a necessidade de utilizar recursos da AWS, seja para aprender o que eu iria manusear, como implementar no ambiente corporativo de forma correta.&lt;/p&gt;

&lt;p&gt;Durante meus estudos, para evitar gastos, procurei soluções que me permitiriam usar as funções de armazenamento e envio de e-mail da AWS de forma gratuita. Até que finalmente encontro um recurso, que simula o ambiente &lt;em&gt;cloud&lt;/em&gt; da AWS dentro da sua máquina local.&lt;/p&gt;

&lt;p&gt;O Localstack: &lt;code&gt;https://localstack.cloud/&lt;/code&gt;, é um recurso que possibilita simular diversos recursos AWS (dynamoDB, s3, iam, cognito, ses), dentro da sua máquina, utilizando o docker. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pré-requisitos&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Docker &amp;amp; Docker-compose&lt;/li&gt;
&lt;li&gt;Node (Minimo na versão 16)&lt;/li&gt;
&lt;li&gt;Aws-cli instalada na máquina&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Para ativar, basta criar um docker compose no seu projeto com os seguintes comandos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;version: "3.8"

services:
  localstack:
    container_name: "localstack"
    image: localstack/localstack:1.0.4
    ports:
      - "4566:4566"            # LocalStack Gateway
    environment:
      - DOCKER_HOST=unix:///var/run/docker.sock
      - DEFAULT_REGION=us-east-1
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rode o comando &lt;code&gt;docker-compose up&lt;/code&gt; para inicializar o funcionamento do localstack&lt;/p&gt;

&lt;p&gt;Para validar se está realmente funcionando, abra o seu navegador na seguinte url =&amp;gt; &lt;a href="http://localhost:4566/health" rel="noopener noreferrer"&gt;http://localhost:4566/health&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Iniciando um projeto Node&lt;/strong&gt;&lt;br&gt;
Não iremos nos preocupar com padrão de projetos, apenas queremos criar uma aplicação, para: criar um arquivo num bucket S3, remover o arquivo, receber a url assinada, enviar um e-mail pelo SES&lt;/p&gt;

&lt;p&gt;crie uma pasta e abra no editor de texto da sua preferencia&lt;/p&gt;

&lt;p&gt;Inicie o seu projeto node com o seguinte comando: &lt;/p&gt;

&lt;p&gt;yarn init -y&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Instalando as dependencias&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;yarn add &lt;a href="mailto:aws-sdk@2.1438"&gt;aws-sdk@2.1438&lt;/a&gt; &lt;a href="mailto:axios@1.4"&gt;axios@1.4&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;yarn add @types/&lt;a href="mailto:node@20.5"&gt;node@20.5&lt;/a&gt; @types/&lt;a href="mailto:axios@0.14"&gt;axios@0.14&lt;/a&gt; localstack@1 ts-node-dev@2 &lt;a href="mailto:typescript@5.1.6"&gt;typescript@5.1.6&lt;/a&gt; -D&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Inicializando o Typescript&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;yarn tsc --init&lt;/p&gt;

&lt;p&gt;dentro do package.json, adicione o script para rodar o projeto&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
    "dev": "ts-node-dev --respawn --transpile-only index.ts"
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;crie um arquivo chamado index.ts&lt;/p&gt;

&lt;p&gt;O projeto terá a seguinte estrutura&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi20yvekilb75fx1czlf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffi20yvekilb75fx1czlf.png" alt="Image description" width="506" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Codificando o projeto&lt;/strong&gt;&lt;br&gt;
Dentro do index.ts, iremos realizar as configurações dos serviços S3 e SES&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import AWS from 'aws-sdk'
import axios from 'axios'

const s3config = {
    s3ForcePathStyle: true
}

const sesConfig = {
    apiVersion: '2010-12-01'
}

const isLocal = true

if(isLocal) {
    AWS.config.update({
        credentials: {
            accessKeyId: 'test',
            secretAccessKey: 'test'
        },
        region: 'us-east-1'
    })

    const host = 'localhost'
    const awsEndpoint = new AWS.Endpoint(`http://${host}:4566`);

    //@ts-ignore
    s3config.endpoint = awsEndpoint
    //@ts-ignore
    sesConfig.endpoint = awsEndpoint
}

const s3 = new AWS.S3(s3config);
const ses = new AWS.SES(sesConfig)

const bucketName = 'local-data-files'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;OBSERVAÇÕES:&lt;/strong&gt; Dentro da constante isLocal, poderemos inserir esse valor em uma variável de ambiente, para que quando for enviado para ambientes de homologação e Produção, utilizemos os reais valores da AWS e não do Localstack&lt;/p&gt;

&lt;p&gt;Para definir que o ambiente utilizado será o localstack, é necessário configurar as chaves da AWS e alterar o endpoint que as classes irão realizar as requisições&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS.config.update({
        credentials: {
            accessKeyId: 'test',
            secretAccessKey: 'test'
        },
        region: 'us-east-1'
})

const host = 'localhost'
    const awsEndpoint = new AWS.Endpoint(`http://${host}:4566`);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para realizar a codificação dos demais serviços, iremos criar duas classes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class StorageManagement {

}

class SendEmail {

}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para as funções de gerenciamento no S3, adicionaremos os seguintes comandos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class StorageManagement {
    public async execute() {
        const image = 'https://www.purina.co.uk/sites/default/files/2020-12/Dog_1098119012_Teaser.jpg'

        const buffer = await this.downloadImage(image);
        const upload = await this.upload(buffer);

        await this.remove(upload.Key);
    }

    private async downloadImage(imageUrl: string) {
        const response = await axios.get(imageUrl, {
            responseType: 'arraybuffer'
        })

        const buffer = Buffer.from(response.data, 'base64');
        return buffer
    }

    private async upload(buffer: Buffer) {
        const s3Upload = await s3.upload({
            Bucket: bucketName,
            Key: `FILE_${(new Date()).getTime()}`,
            Body: buffer
        }).promise();

        return s3Upload
    }

    private async remove(key: string) {
        await s3.deleteObject({
            Bucket: bucketName,
            Key: key
        })
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E para o envio de e-mail, temos o seguinte código:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class SendEmail {
    public async execute() {
        var params = {
            Destination: {
             ToAddresses: [
                'envio@teste.com', 
             ]
            }, 
            Message: {
             Body: {
              Html: {
               Charset: "UTF-8", 
               Data: "This message body contains HTML formatting. It can, for example, contain links like this one: &amp;lt;a class=\"ulink\" href=\"http://docs.aws.amazon.com/ses/latest/DeveloperGuide\" target=\"_blank\"&amp;gt;Amazon SES Developer Guide&amp;lt;/a&amp;gt;."
              }, 
              Text: {
               Charset: "UTF-8", 
               Data: "This is the message body in text format."
              }
             }, 
             Subject: {
              Charset: "UTF-8", 
              Data: "Test email"
             }
            },
            Source: "teste@mailinator.com", 
           };

        const emailSend = ses.sendEmail(params).promise()
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Antes de rodar nossa aplicação e ver o funcionamento, precisamos realizar alguns comandos via script para que o código funcione&lt;/p&gt;

&lt;p&gt;crie um arquivo script.sh e adicione os seguintes comandos&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Nome do Bucket
BUCKET_NAME=local-data-files

# Comando AWS para criar um bucket no S3
# o Endpoint é para definir que iremos criar dentro do localstack
aws \
    s3 mb s3://$BUCKET_NAME \
    --endpoint http://localhost:4566

# Comando para validar o e-mail que iremos utilizar para teste no Localstack
aws ses verify-email-identity \
    --email-address teste@mailinator.com \
    --endpoint-url=http://localhost:4566
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para executar digite no terminal&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sh script.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Testando&lt;/strong&gt;&lt;br&gt;
Para Testar cada uma das funcionalidades, instancie as classes e chame a função &lt;em&gt;execute&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const test = new StorageManagement()
const testSES = new SendEmail()

testSES.execute()

test.execute()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Validando&lt;/strong&gt;&lt;br&gt;
Para Validar se o arquivo está presente no s3, deve-se executar o seguinte comando sh&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;BUCKET_NAME=local-data-files
aws \
    s3 ls s3://$BUCKET_NAME\
    --endpoint http://localhost:4566
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para visualizar se o email foi enviado, entre na seguinte url&lt;/p&gt;

&lt;p&gt;&lt;a href="http://localhost:4566/_localstack/ses" rel="noopener noreferrer"&gt;http://localhost:4566/_localstack/ses&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Espero que esse artigo tenha sido útil para você. Deixe seu comentário e sugestão de próximo tema.&lt;/p&gt;

&lt;p&gt;Até mais! ❤️&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Repositório GitHub do artigo&lt;/strong&gt;&lt;br&gt;
&lt;a href="https://github.com/DEVinicius/use-s3-ses-localstack" rel="noopener noreferrer"&gt;https://github.com/DEVinicius/use-s3-ses-localstack&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Minhas Redes Sociais
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://www.instagram.com/viniciusfingerstyle/" rel="noopener noreferrer"&gt;Instagram&lt;/a&gt;📗&lt;br&gt;
&lt;a href="https://www.linkedin.com/in/vinicius-dev/" rel="noopener noreferrer"&gt;LinkedIn&lt;/a&gt;💼&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>aws</category>
      <category>node</category>
    </item>
    <item>
      <title>KAFKA CONNECT: Conectando em um banco de Dados Oracle</title>
      <dc:creator>Vinicius Pereira de Oliveira</dc:creator>
      <pubDate>Sun, 27 Nov 2022 01:45:40 +0000</pubDate>
      <link>https://dev.to/devinicius/kafka-connect-conectando-em-banco-de-dados-oracle-224l</link>
      <guid>https://dev.to/devinicius/kafka-connect-conectando-em-banco-de-dados-oracle-224l</guid>
      <description>&lt;p&gt;Clima natalino chegando, na verdade, mais perto do que em janeiro. Vamos apenas pensar em resolver problemas que um dia foram complicados para muitos, mas por meio do artigo certo, conseguimos resolver nossos empecilhos facilmente.&lt;/p&gt;

&lt;p&gt;Já passou pelo problema de tentar se conectar com um banco oracle, para conseguir captar todos os eventos de insert ou update de uma tabela específica a partir do kafka-connect?&lt;/p&gt;

&lt;p&gt;Caso tenha ou já passou por problemas semelhantes a esses, venho aqui lhe trazer boas novas sobre formas de conexão a partir do conector jdbc.&lt;/p&gt;

&lt;p&gt;O connector jdbc, é Open source, presente na documentação oficial da confluent, com todas as descrições bem claras dos campos que são utilizados, e seus níveis de prioridades.&lt;br&gt;
&lt;a href="https://docs.confluent.io/kafka-connectors/jdbc/current/"&gt;JDBC&lt;/a&gt;&lt;br&gt;
&lt;a href="https://docs.confluent.io/kafka-connectors/jdbc/current/source-connector/source_config_options.html"&gt;Documentação do Connector&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos deixar de enrolação e partir logo pro abraço, porque estamos no fim de ano, e quanto mais rápido aprendermos essa funcionalidade, mais cedo comemoraremos a ceia de natal.&lt;/p&gt;

&lt;p&gt;Antes de tudo, certifique-se que seu kafka-connect possui o seguinte connector instalado na sua máquina:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;confluent-hub install confluentinc/kafka-connect-jdbc:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirmando a instalação, vamos para alguns casos em que o connector pode ser utilizado:&lt;/p&gt;

&lt;p&gt;1 - Fazer Dump completo do banco de dados, ou de tabelas específicas;&lt;/p&gt;

&lt;p&gt;2 - Buscar apenas dados que foram inseridos na tabela;&lt;/p&gt;

&lt;p&gt;3 - Receber apenas os dados que sofreram updates;&lt;/p&gt;

&lt;p&gt;4 - Receber mensagens de itens que foram inseridos ou atualizados na tabela;&lt;/p&gt;

&lt;p&gt;Esses 4 cenários conseguimos tratar com o kafka-connect, cada um com sua forma de configurar um connector.&lt;/p&gt;

&lt;h2&gt;
  
  
  1 - Dump completo do banco, ou de tabelas específicas
&lt;/h2&gt;

&lt;p&gt;Esse processo requer que configuremos o nosso connector da seguinte forma: &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Obs: Estarei especificando o significado de cada campo do objeto&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "nome do connector",
     "config": {
       "connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
       "connection.url": "jdbc:oracle:thin:@&amp;lt;host do banco&amp;gt;:&amp;lt;porta&amp;gt;/&amp;lt;nome do banco de dados&amp;gt;",
       "connection.user": "&amp;lt;username do banco&amp;gt;",
       "connection.password": "&amp;lt;senha do banco&amp;gt;",
       "topic.prefix": "&amp;lt;prefixo do tópico kafka que será produzido&amp;gt;",
       "table.whitelist": "&amp;lt;tabelas que serão lidas&amp;gt;",
       "poll.interval.ms": 5000,
      "value.converter":"org.apache.kafka.connect.json.JsonConverter",
       "mode": "bulk"
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para a criação de um connector jdbc, definimos dois campos primários (name e config). "name" se baseia no nome do nosso connector e "config" são as definições do comportamento desse connector.&lt;/p&gt;

&lt;h3&gt;
  
  
  Config
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;connector.class =&amp;gt; Classe que tem as funções do nosso connector, dentro do pacote que instalamos anteriormente&lt;/li&gt;
&lt;li&gt;connection.url =&amp;gt; URL de conexão com o banco dados, devendo seguir a seguinte sintaxe do comando jdbc -&amp;gt; jdbc:oracle:thin:@:/&lt;/li&gt;
&lt;li&gt;connection.user =&amp;gt; usuário do banco de dados&lt;/li&gt;
&lt;li&gt;connection.password =&amp;gt; senha para acessar ao banco&lt;/li&gt;
&lt;li&gt;topic.prefix =&amp;gt; Inicio do nome do nosso tópico. Geralmente ao criarmos um connector, o nosso tópico vem escrito com: &lt;/li&gt;
&lt;li&gt;table.whitelist =&amp;gt; Tabelas que serão mapeadas no processo. Caso o número de tabelas seja muito grande, podemos definir quais tabelas não serão lidas, a partir do parâmetro "table.blacklist". Não obstante, deve existir apenas um desses campos, ou utilizamos a "whitelist" ou "blacklist"&lt;/li&gt;
&lt;li&gt;poll.interval.ms =&amp;gt; Intervalo em milisegundos que o nosso connector fará o dump das tabelas&lt;/li&gt;
&lt;li&gt;value.converter =&amp;gt; Definindo para o formato que os nossos dados serão convertidos. Com essa informação no campo: org.apache.kafka.connect.json.JsonConverter. Sempre iremos transformar nossas mensagens em formato json;&lt;/li&gt;
&lt;li&gt;mode =&amp;gt; Modo que será feito o mapeamento. O modo bulk se refere a produção dos dados completos da(s) tabela(s), partir de x tempo (definido pelo poll.interval.ms)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  2 - Buscar apenas dados que foram inseridos na tabela
&lt;/h2&gt;

&lt;p&gt;Configuração do connector, para que apenas eventos de inserts sejam capturados pelo connector&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Obs: Os campos repetidos, não serão replicados na descrição&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "nome do connector",
     "config": {
        "connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
        "connection.url": "jdbc:oracle:thin:@&amp;lt;host do banco&amp;gt;:&amp;lt;porta&amp;gt;/&amp;lt;nome do banco de dados&amp;gt;",
        "connection.user": "&amp;lt;username do banco&amp;gt;",
        "connection.password": "&amp;lt;senha do banco&amp;gt;",
        "topic.prefix": "&amp;lt;prefixo do tópico kafka que será produzido&amp;gt;",
        "value.converter":"org.apache.kafka.connect.json.JsonConverter",
        "mode":"incrementing",
        "query": "&amp;lt;QUERY SELECT PARA BUSCAR OS DADOS ESPECIFICOS DA TABELA&amp;gt;",
        "numeric.mapping":"best_fit",
        "incrementing.column.name": "&amp;lt;NOME DO CAMPO DA TABELA, QUE OBEDEÇA VALOR DE CHAVE PRIMARIA&amp;gt;",
        "validate.non.null": true
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Config
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;mode =&amp;gt; Incrementing, tem o significado que apenas será executado, a partir do momento que a chave primária (Obrigatoriamente sendo de um tipo número), ao se produzir um novo dado, será usada como parâmetro para verificar se é um novo dado.&lt;/li&gt;
&lt;li&gt;query =&amp;gt; Select que será executado, quando essa nova mensagem for gerada. Nesse momento, esse select será replicado em forma de mensagem, então o select contendo os dados corretos e úteis que o seu consumidor fará a leitura, estará presente nesse comando&lt;/li&gt;
&lt;li&gt;incrementing.column.name =&amp;gt; Campo que o connector terá como parâmetro, para validar se a informação é nova na tabela;&lt;/li&gt;
&lt;li&gt;numeric.mapping =&amp;gt; Validação se o campo definido referência para o connector é do tipo primitivo correto. O valor "best_fit", significa que será levado em conta vários tipos como parâmetro, desde INT até DECIMAL.&lt;/li&gt;
&lt;li&gt;validate.non.null =&amp;gt; Parâmetro booleano, que exigirá ou não que o campo utilizado como referência seja não nulo&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  3 - Buscar apenas dados que foram atualizados na tabela
&lt;/h2&gt;

&lt;p&gt;Tem um corpo semelhante ao item anterior, apenas modificando alguns campos&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "nome do connector",
     "config": {
        "connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
        "connection.url": "jdbc:oracle:thin:@&amp;lt;host do banco&amp;gt;:&amp;lt;porta&amp;gt;/&amp;lt;nome do banco de dados&amp;gt;",
        "connection.user": "&amp;lt;username do banco&amp;gt;",
        "connection.password": "&amp;lt;senha do banco&amp;gt;",
        "topic.prefix": "&amp;lt;prefixo do tópico kafka que será produzido&amp;gt;",
        "value.converter":"org.apache.kafka.connect.json.JsonConverter",
        "mode":"timestamp",
        "query": "&amp;lt;QUERY SELECT PARA BUSCAR OS DADOS ESPECIFICOS DA TABELA&amp;gt;",
        "timestamp.column.name": "&amp;lt;NOME DO CAMPO DA TABELA, QUE OBEDEÇA VALOR DE TIMESTAMP&amp;gt;",
        "validate.non.null": false
   }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Config
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;mode: Timestamp, para apenas captar itens de atualização&lt;/li&gt;
&lt;li&gt;timestamp.column.name =&amp;gt; Coluna da tabela, que será utilizada como referência pelo connector. Esse campo deve ser do tipo datetime para que o connector funcione&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Os demais campos se repetem na lógica&lt;/p&gt;

&lt;h2&gt;
  
  
  4 - Receber mensagens de itens que foram inseridos ou atualizados na tabela
&lt;/h2&gt;

&lt;p&gt;Se baseia na junção dos dois itens anteriores. Tendo o seguinte corpo do objeto&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "name": "nome do connector",
     "config": {
        "connector.class": "io.confluent.connect.jdbc.JdbcSourceConnector",
        "connection.url": "jdbc:oracle:thin:@&amp;lt;host do banco&amp;gt;:&amp;lt;porta&amp;gt;/&amp;lt;nome do banco de dados&amp;gt;",
        "connection.user": "&amp;lt;username do banco&amp;gt;",
        "connection.password": "&amp;lt;senha do banco&amp;gt;",
        "topic.prefix": "&amp;lt;prefixo do tópico kafka que será produzido&amp;gt;",
        "value.converter":"org.apache.kafka.connect.json.JsonConverter",
        "mode":"timestamp+incrementing",
        "query": "SELECT CAST(p.PERSON_ID AS NUMERIC(8,0)) AS PERSON_ID, p.CREATED_AT, p.UPDATED_AT FROM PERSONS p",
        "numeric.mapping":"best_fit",
        "incrementing.column.name": "PERSON_ID",
        "timestamp.column.name": "UPDATED_AT",
        "validate.non.null": false
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Config
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;mode =&amp;gt; timestamp+incrementing
Deve seguir esse nome para que funcione no connector.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Agradecimentos
&lt;/h2&gt;

&lt;p&gt;Espero que esse artigo seja útil para você, facilitando seu trabalho, evitando horas de pesquisa para um problema que já esteve no dia-a-dia de outros.&lt;/p&gt;

&lt;h3&gt;
  
  
  Muito Obrigado! ❤️
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Minhas Redes Sociais
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://www.instagram.com/viniciusfingerstyle/"&gt;Instagram&lt;/a&gt;📗&lt;br&gt;
&lt;a href="https://www.linkedin.com/in/vinicius-dev/"&gt;LinkedIn&lt;/a&gt;💼&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
