Transcrições
1. Introdução: Go é uma linguagem de back-end incrivelmente
poderosa que pode ser uma vantagem para o kit de ferramentas de
qualquer desenvolvedor É de alto desempenho. Ele tem serialização e
desserialização
integradas JSON, ele tem testes
integrados e muito mais. Neste curso,
vamos começar completamente do zero,
criando uma nova pasta, estruturando um projeto G, escrevendo todo o código para ele, escrevendo testes para o Dockerizando o aplicativo
e, finalmente,
automatizando um pipeline CICD de
fluxo de trabalho de teste
e criação com o CircleCI Ao final do
curso, espero que você tenha uma
visão geral prática de como criar aplicativos go
dockerizados,
incluindo testes Espero que você se junte a
mim neste curso, Go for real world applications.
2. Por que ir: Antes mesmo de começarmos a
falar sobre o que nosso aplicativo fará ou qualquer programação, quero explicar rapidamente
por que escolhemos G como
opção de linguagem em primeiro lugar. Então, por que escolheríamos G? Em primeiro lugar, é
extremamente eficiente. Até agora, escrevi pelo
menos uma dúzia de aplicativos de
go e nunca
precisei identificar qualquer tipo de condição de corrida
ou problema de desempenho. E uma delas era uma API
para uma startup relativamente grande. Tínhamos cerca de 10.000 usuários
e, em alguns momentos, 1.000
a 2.000 usuários simultâneos E mesmo assim, nunca tivemos grandes
problemas de desempenho, e isso estava sendo executado em uma máquina virtual de
quatro núcleos. O Go também é extremamente compacto. Portanto, o aplicativo
normalmente é executado a partir de um único arquivo dot go
principal, e isso pode ser feito simplesmente
emitindo go run main dot go Como veremos mais adiante
no curso, isso é extremamente poderoso para quando queremos
dockerizar nosso aplicativo É muito simples transportar
e executar um aplicativo go. O Go também é extremamente testável. Ele tem uma
estrutura de teste incorporada. Então, importaríamos
esse pacote de teste. Passamos isso para
nossa função de teste. E então é tão simples quanto chamar a função
que queremos testar. E nesse caso,
retornei um erro
dessa função. Se o erro não for Nulo, isso é válido para não
nulo ou vazio Em seguida, usamos esse ponteiro
para a estrutura de teste e emitimos um erro.
Então, isso está embutido. Não precisamos de nenhuma configuração
externa. Não precisamos de bibliotecas
externas. Ele é incorporado ao idioma. O Go também é extremamente compatível com a
API. Então, eu tenho um exemplo aqui para
mostrar como isso funciona em Go. Então, se
assumirmos que temos algum tipo de resposta JSON de
uma API como essa, podemos usar o pacote JSON e também o identificador JSON
quando definimos um tipo aqui, chamá-lo de API, é uma
estrutura E sabemos que
nossa resposta tem algum tipo int e
alguma matriz de string. É uma matriz de strings. Usamos esse identificador JSON. Agora, a mágica
acontece quando usamos esses identificadores e usamos
o pacote JSON incorporado, vamos supor que já
temos a resposta da API, algum tipo de resultado binário Podemos simplesmente chamar JSON
Unmarshal e Golang serializará
esse resultado binário serializará
esse resultado binário em nosso objeto de resposta. E quando ele vê essas chaves, ele sabe colocar esses valores em cada uma
dessas partes dessa estrutura. Então,
assumimos que não havia nenhum erro, organizando a
resposta. Se desconectássemos algum int,
obteríamos cinco e,
se fizéssemos um loop na desconectássemos algum int,
obteríamos cinco e, obteríamos cinco e matriz de strings,
obteríamos esses A, B
e C. Então, novamente, não
precisamos de nenhum arquivo de configuração
, de nenhuma biblioteca externa Nós apenas usamos a biblioteca
JSON integrada com o método
Unmarshal, e Golang lida com
toda a serialização
e desserialização do e desserialização Ir também é extremamente uniforme. Então, quando você estiver escrevendo com um editor de código moderno,
como o Visual Studio Code, use os formatos ao salvar de
acordo com suas próprias regras incorporadas. E isso significa que não importa onde você esteja
lendo o código G, seja um pacote
externo,
um pacote de terceiros código de
seus colegas de equipe, você sempre
verá
os mesmos padrões, recuo e
espaçamento em Isso facilita muito a
leitura de outros projetos go. Isso também evita que você e
sua equipe
debatam sobre regras de formatação como guias, espaços
e todas Essas regras também são
incorporadas à linguagem go. Então, em resumo, G é
muito eficiente. É muito rápido, pelo menos em toda a experiência que
usei, é muito compacto. Você só precisa do seu
único arquivo dot go principal. É facilmente testável. O teste vem com
a própria linguagem. É muito compatível com a API. Essa serialização
e desserialização de JSON também vem pronta
para uso com
a linguagem e é uniforme Essas regras de linting e
formatação também vêm com o idioma Portanto, o G é uma escolha muito sólida para qualquer software de back-end que
você precise criar Então, com um histórico básico
do que é G e de
algumas de suas vantagens,
na próxima lição, podemos começar
a analisar o aplicativo real
que criaremos
e, finalmente, começar
a codificar o aplicativo
3. API de alergia: Neste curso,
vamos criar um aplicativo que nos envia
mensagens e nos avisa diariamente sobre os níveis esperados de
pólen no Se você é como eu,
pode sofrer de febre do feno de meados de
maio a meados de junho de cada ano, pelo
menos no
hemisfério norte. E não se preocupe. Mesmo que você não tenha nenhuma
alergia, prometo que o conteúdo
e os padrões deste curso são extremamente úteis, independentemente do tipo de
aplicativo go
que você esteja usando Então, onde eu moro na Áustria, a
Universidade de Medicina de Viena tem um ótimo site que prevê os
níveis de pólen do dia E com algumas pesquisas e inspeções das
chamadas de API para este site, encontrei os endpoints que
produzem todos os dados Existem basicamente dois endpoints
que usaremos. A primeira é obter dados de carga
por hora, que têm uma
chave de sucesso e o resultado. Mais importante ainda, aqui
está essa matriz horária. Então, para cada hora, ele fornecerá um valor esperado
do pólen no ar, variando de zero a, acredito, oito ou nove O segundo ponto final
que
utilizaremos neste curso é
obter os dados atuais do gráfico Este será usado
para médias históricas. Então, como você pode ver
aqui, também temos uma chave de sucesso.
Temos alguns resultados. E para cada resultado, temos uma data, a
atual ou, neste caso, o que realmente aconteceu
nessa data este ano
e, em seguida, a média histórica. No entanto,
agora é 16 de maio e, como você pode ver, a corrente é zero e, mesmo ontem,
a corrente é zero. Portanto, esses dados estão um
pouco atrasados. No entanto, como mencionei, vamos usá-lo principalmente
para os valores médios. Então,
enviaremos o valor esperado para o dia a partir desses dados horários e também a
média histórica do dia. Então, com uma visão geral
e uma ideia do que nosso aplicativo
será na próxima lição, discutiremos brevemente os requisitos
técnicos que queremos que nosso aplicativo atenda
e, em seguida, poderemos finalmente
começar a escrever alguns códigos.
4. Requisitos de aplicação: Na última lição, analisamos a API de alergia que
usaríamos e de onde vinham
esses dados. E nesta lição, discutiremos
brevemente requisitos
do aplicativo em um nível mais técnico antes de
começarmos a codificar. Portanto, do ponto de vista do
fluxo de aplicativos, em um horário diário específico, queremos chamar esses endpoints de
alergia Vamos analisar
a resposta JSON cada chamada e, em seguida, fazer
algum trabalho
de formatação
para
transformá-la em uma boa mensagem legível por humanos Então, finalmente, queremos enviar essa boa mensagem
por meio dos webhooks do Slack Todas as funções que
escrevemos devem ter testes. O aplicativo deve ser
executado a partir de um contêiner docker. O contêiner docker deve ser reiniciado em caso de qualquer tipo
de falha ou falha Deve ser
construído um pipeline automatizado que execute todos
os testes que escrevemos e ,
em seguida, publique o contêiner do
Docker , caso todos
esses testes sejam realmente E, finalmente, os
vários valores importantes usados no aplicativo,
como os endpoints da API, o URL do web hook
do Slack, o horário do Cron Job e
o fuso horário do
Crown job, Cron Job e
o fuso horário do
Crown job devem ser todos Portanto, ao longo deste curso, abordaremos
e atenderemos passo a passo todos
esses requisitos. Na próxima lição, finalmente
começaremos a montar
nosso aplicativo go
5. Instale o Go e a extensão do Visual Studio Go: Comece, todos devemos verificar se temos o G instalado. Para verificar se
tenho o G instalado, simplesmente
lançarei a versão G. E eu posso ver que tenho G 1.20 0.2. Se esse comando não
retornar nada ou um erro, provavelmente você
precisará
instalar o G no seu sistema. Você pode acessar go dot dev
slash dClash Install, e eles têm as instruções de
instalação para Linux, Mac No restante
deste curso, presumo que
você esteja programando junto
comigo no Visual Studio Code comigo no Visual Studio Portanto, eu também recomendo que você tenha a extensão G instalada. Para encontrar a extensão G, vá até a
guia de extensões e pesquise por G. Deve ser
o primeiro resultado
e, claro, eu já a tenho
instalada. Essa extensão é um balcão
único para tudo o que você
precisa para editar o código G. É uma combinação de inter,
testador, depurador
e formato para G. Depois de
instalar essa extensão, podemos começar
6. Como estruturar o projeto: Na última lição, nos certificamos de
que tínhamos o G instalado e também instalamos a extensão do
Visual Studio Code G. Agora vamos montar nosso aplicativo. Normalmente, ao
iniciar um novo projeto G, você cria uma nova
pasta para esse aplicativo. Nesse caso, eu o chamaria de
Allergy Cron e você
emitiria make DR Allergy cron No entanto, como já estou
no repositório do curso, tenho uma pasta aqui para o projeto e não vou
criar uma nova pasta A próxima etapa seria então
inicializar um módulo
para o projeto Normalmente, uso o mesmo nome
do próprio nome da pasta. No seu caso, isso
seria alergia. Então, aqui, vamos
inicializá-lo de acordo com
o nome da pasta O comando para
inicializar um módulo G é Gomd init e, em
nosso caso, Allergy Crown Go nos dirá que
criou um novo arquivo mod go dot. Podemos entrar nesse
arquivo gomd e ver o que está nele. Por enquanto, como não
instalamos nenhum pacote
adicional, ele tem apenas o nome do módulo e a versão go que
foi usada para criá-lo. Em seguida, podemos criar
o arquivo principal, ou
seja, toque em main dot go. E se você ainda estivesse em
apenas um ambiente de terminal, você não tinha um editor aberto. Você poderia, é claro,
abrir o ponto
principal com o comando Abrir. Ou se você quiser
abri-lo no código do Visual Studio, você pode emitir o
código main dot go. Então, depois de toda essa configuração, finalmente
estamos prontos para
começar a escrever algum código G.
7. Como criar o trabalho Cron: Na última lição,
estruturamos nosso novo projeto G,
inicializando um módulo G, inicializando um módulo G que nos deixou com
esse arquivo go dot mod,
e criamos um arquivo dot go
principal vazio Nesta lição,
começaremos a abordar o primeiro requisito técnico, que é ter
um cron job diário que seja acionado exatamente
ao mesmo tempo Cada arquivo go tem uma
declaração de pacote na parte superior. Nesse caso, esse
é o pacote Min. Então você pode ter
algumas importações aqui. E no caso
do arquivo dot go principal, teremos nossa função principal, que é simplesmente a
função main. Agora, para trabalhos em Crown e G, gosto de usar a popular biblioteca
Rob Fig Cron. Essa biblioteca também nos
permite definir o Cron Job de
acordo com um fuso horário específico Então, primeiro, vou usar o pacote de horário do
Go para
carregar o fuso horário
e, em seguida, podemos configurar o
Crown Job nesse fuso horário. Então, obtemos uma localização e um erro e, a partir do
momento, a localização do carregamento. E como estou na Áustria, quero a Europa, Viena. Então, é claro,
precisamos verificar se o erro não é Nil, então entraremos
em pânico com esse erro
e, caso contrário,
podemos continuar Agora, para configurar um Crown
Job com esse pacote, tudo o que precisamos fazer é
ligar para um novo com a localização e
passaremos por essa localização. Agora, vemos com o inter, assim que eu faço
referência a esse cron, o inter sabe qual
pacote eu quero usar O único problema aqui é que esse pacote ainda não está
em nosso arquivo mod. Então, se passarmos o mouse sobre
a importação aqui, Go reclama que
não há nenhum módulo fornecendo
esse pacote Mas, felizmente, o Linter nos
fornece uma solução rápida, que é simplesmente a instalação
ou o comando go get package Então, se clicarmos para que
o pacote esteja instalado. Você deve ver seu arquivo mod
obtido modificado
e, em seguida, podemos
continuar com nosso código aqui. Para não ultrapassar o tempo
real do Cron, precisamos adicionar o
ponto Cron Job ao Funk e isso usa o identificador cron padrão do
tipo unix precisamos adicionar o
ponto Cron Job ao Funk e isso usa
o identificador cron padrão do
tipo unix com seis caracteres. E esses são especificamente
os segundos, os minutos, as horas, o dia do mês, o mês em si
e o dia da semana. No nosso caso, acho que
um horário razoável, pelo
menos para mim, é às 8:00 da
manhã todas as manhãs. Então, vou especificar zero
para os segundos e minutos
e, em seguida, oito para a hora. E então o segundo parâmetro desse ad funk é a função que
realmente queremos executar E, por enquanto, vou
colocar uma função vazia lá. Eu posso limpar esse comentário de
importação e podemos salvar nosso arquivo. Outra coisa que
quero destacar aqui é a
capacidade de formatação automática das extensões Go do Visual Studio. Então, se tivermos algum espaçamento
desleixado aqui ou algo parecido,
assim que salvamos, no meu caso,
em um Mac Command S, vemos que o inter formata
automaticamente Também devo mencionar que,
para esses fusos horários, eles são
consultados em algo
chamado banco de dados de fusos
horários da Iana. Encontrei um bom site
chamado Nota time, que lista todos os
vários fusos horários. Então, onde quer que você esteja no mundo,
fique à vontade para
examinar esta tabela e definir o fuso horário de
acordo com onde quer que esteja ou sempre que quiser que
seu Cron Job seja acionado Também precisamos garantir que
nosso cron job seja iniciado. Então, adicionamos a função, mas precisamos
chamar explicitamente cronjob E então,
se deixássemos isso, quando executássemos
a função principal, ela simplesmente seria executada
aqui e depois sairia. Portanto, precisamos bloquear
o encadeamento principal para manter o processo go
em execução, para que possamos manter esse cron job
ativo depois de iniciá-lo, e o cron
continuará sendo executado até cada dia, quando atingir
o horário das 8h. Nesta lição, começamos a
escrever nosso arquivo dot go principal. Aprendemos que todo arquivo go começa com uma declaração de
pacote. Então, geralmente há
algumas importações e, em seguida, suas funções. Nesse caso, é um caso um
pouco especial. O arquivo dot go principal sempre
tem uma função principal. E começamos a escrever o
corpo da nossa função principal. Carregamos o fuso horário de Viena e usamos esse fuso
horário de localização em nosso cron job, e configuramos o Cron job para ser acionado às 8:00 da manhã todos Vimos um bom recurso de como o formato de
código do Visual Studio pode corrigir e formatar
automaticamente
o recuo em nosso E também discutimos todas as várias possibilidades
que você pode passar para essa função de localização de carga para que você possa definir o fuso
horário desejado. Na próxima lição, abordaremos a criação de uma função
utilitária HTTP, para que possamos começar a ver
como chamaremos os URLs
da API que eventualmente
adicionaremos ao URLs
da API que eventualmente
adicionaremos corpo da função
aqui do nosso trabalho na Crown
8. Como criar uma função de utilitário HTTP genérica: Na última lição,
começamos a montar
nosso arquivo dot go principal e a
configurar o cron job que poderia
iniciar nossas várias
tarefas às 8h no fuso nosso arquivo dot go principal e a
configurar o cron job que poderia horário de Viena. horário de Viena. Agora vamos escrever uma
função HTP genérica que será usada tanto para as
chamadas da API de alergia quanto para enviar
nossa mensagem do Slack Agora, essa função
é bastante complexa e faz uso das funcionalidades
genéricas de G. Não vou entrar em muitos detalhes sobre
como a função
foi construída. Tenho uma postagem separada no
blog que
detalha sua implementação, e você é mais do que bem-vindo a ler sobre isso separadamente. Para começar com
essa função, criaremos uma nova
pasta chamada Utils e um novo arquivo chamado make
http request dot go O pacote aqui será Utils. E para nossa função,
faça uma solicitação de HDP. Isso exige um tipo genérico. Em seguida, passaremos a URL completa, que é uma string,
o método HTTP esperamos, que também
é uma string. Qualquer cabeçalho que a chamada precise, que é um mapa de string
e string Os parâmetros de consulta, que
são do tipo RL, o corpo, que é leitor de E/S, e a resposta do tipo T, e retornaremos esse
tipo ou um erro A primeira coisa que faremos
nessa função é inicializar nosso cliente HTTP E converteremos
o URL da string um objeto URL completo com Rl
dot pars com esse URL completo Se o erro não for NIL, retornaremos apenas nosso tipo de
resposta e o erro Se o método for G, precisamos acrescentar qualquer dos parâmetros
de consulta
que foram passados Então, primeiro, obterei a
consulta do objeto URL e precisamos
percorrer esses pares de
valores-chave dentro
dos parâmetros da consulta. E definiremos
a chave e o valor. Para cada parâmetro.
Em seguida, definiremos a consulta bruta como a
consulta construída codificada idiota que criamos aqui Também podemos definir o corpo fazendo
uma nova solicitação, passando o método,
a versão
da string da URL e o corpo. Se o erro não for nulo, novamente, retornaremos o
tipo de resposta e o erro E agora que
temos nossa solicitação, precisamos definir os cabeçalhos. Quanto à chave e ao
valor desses cabeçalhos, definiremos o valor do cabeçalho E, finalmente, podemos fazer
sua solicitação real, literalmente com a função dot do do cliente. Novamente, se o erro não for NIL, retornaremos esse
tipo de resposta e o erro Também verificaremos se
a resposta em si é NIL. Retornaremos o tipo de resposta e um erro informando que chamar a URL
retornou uma resposta vazia e queremos passar essa versão
de string do objeto URL Caso contrário, se
continuarmos aqui, queremos ler o corpo. E, novamente, se o
erro não for nulo,
retorne esse erro do tipo de resposta adie o fechamento desse corpo E também queremos verificar se o código de status não é
o código de status o. Tipo de resposta, e também
cometeremos um erro personalizado aqui. Formatação semelhante aqui. Você pode inserir uma nova
linha com o código de status, outra nova linha com os dados
reais da resposta. E então queremos
inserir a string do URL, o código de status real
e os dados da resposta. Agora, se chegamos até aqui, podemos finalmente desagrupar os dados de resposta
no objeto de resposta,
que é
o tipo T que esperamos. Então, declararemos o objeto de
resposta e
tentaremos desagrupar os dados de resposta
no objeto de resposta Se o erro não for novo, altere o tipo de resposta
e o erro em si. Por fim, podemos retornar com segurança o
objeto de resposta completa com um erro NL. Agora, quando eu salvo aqui,
a extensão do Visual Studio Go
importará automaticamente todas as
bibliotecas que usamos. Então IO, a biblioteca HDP,
a biblioteca de URL,
strings e assim por diante Então, com um pouco de esforço, temos uma função
genérica muito poderosa, e eu gosto de usar essa
função para praticamente qualquer
chamada HTTP REST padrão que eu precise fazer. Como vimos, ele foi implementado para funcionar com get put, post e todos os tipos
de verbos HTTP Então, com essa poderosa
função agora implementada, na próxima lição, vamos realmente
chamar a API de alergia. Também definiremos os tipos que passarão como esse tipo genérico. Portanto, sabemos que a resposta do JCN será serializada corretamente
e, em seguida, poderemos consumi-la
mais abaixo em nosso código G.
9. Como chamar e analisar a API de alergia: Na última lição, criamos essa função genérica de solicitação de
HDP bastante poderosa
e, agora, nesta lição, vamos usá-la chamando a API de alergia da Universidade de
Medicina de Viena Então, para começar,
criaremos uma nova pasta. Chamado Allergy API e um novo arquivo go Allergy
underscore api dot go E esse pacote é igual ao nome
da pasta e do arquivo,
Allergy underscore API Agora, a primeira coisa que
precisamos fazer é definir os tipos que
passaremos para nossa função
genérica aqui para que o JSON
aqui,
quando for desempacotado, saiba em que tipo deve ser
desagrupado E analisando essas
duas chamadas de API separadas, temos nossos dados de carga horária, que têm uma espécie de estrutura
abrangente com as chaves de sucesso e resultado,
e, dentro do resultado,
temos outro objeto, que tem um total, algum tipo de opção
personalizada
e, o mais importante para nós,
a matriz horária, que
é uma matriz de Para obter os dados atuais do gráfico, também
temos uma estrutura
semelhante. Temos um objeto grande que tem as chaves de sucesso e resultado. Mas os resultados
aqui são diretamente uma matriz com um
objeto repetido que tem data atual, temporada
média e data e hora E mais uma vez, o
mais importante para nós aqui é a média. Vamos usar
esse endpoint para a parte média histórica da mensagem que enviamos Então, vamos começar com
os tipos de dados de carregamento por hora. Então, primeiro, vou
definir a resposta, e isso tem uma chave de sucesso
e resultado aqui. Então, vou definir isso
como resposta de carga por hora. Este é um Strup e
temos nosso sucesso,
que é um int, e o identificador JSON aqui
é um sucesso em minúsculas E o resultado, que
precisamos para definir um novo tipo, uma nova estrutura para
esse tipo aninhado, vou chamá-lo de resultado de carga
horária E também precisamos
definir a chave JSON
que é o resultado em minúsculas Agora, como esse tipo
usa essa outra estrutura, normalmente
gosto de colocar
o tipo acima aqui E o que temos? o total e o total
por hora é um int. total em minúsculas e
por hora do JSON é uma fatia dos pontos, e o E quando eu salvo aqui novamente, os Linters
formatam tudo bem e arrumado em colunas Agora podemos passar
para os dados do gráfico. Da mesma forma, temos
a resposta de dados do gráfico. E vamos dar uma olhada nisso. Além disso, chaves de sucesso e resultado. Portanto, o sucesso é uma aposta. E o resultado ou, devo dizer, resultados no plural está em matriz, e esse tipo precisará
ser outro tipo personalizado, que chamarei de resultado atual dos dados
do gráfico E também precisamos
especificar a chave JSON. Vou pegar isso e
definir o tipo aqui. E tudo o que realmente
precisamos é a data, que por enquanto será
uma string, e a média, que é uma flutuação Portanto, temos a data e a média posso salvar isso, e isso deve ser tudo o que precisamos para
digitações personalizadas Agora, escreveremos uma função
separada para cada endpoint da API O primeiro, podemos chamar de
get hour load data. E a segunda, chamaremos
get current chart data. Agora, para ambos,
esperamos que eles retornem um ponteiro de string e um erro E essa sequência será
, em última análise, a mensagem que será transmitida ao nosso
slack messenger E sabemos que o objetivo aqui é
chamar nossa função genérica aqui. Então, teremos uma resposta
e um erro de can
import this Utils package
que acabamos de criar na lição anterior e
na requisição make HTTP Agora, não precisamos especificar
o tipo explicitamente. Golan vai inferir isso para nós. E para o URL,
nós o temos aqui. Esse é o ponto índice PhP. Nós podemos colocar isso. Sabemos
que será uma solicitação de obtenção. Não precisamos
passar nenhum cabeçalho. Precisaremos passar
alguns parâmetros de consulta. Não há nenhum corpo que
precisemos passar e o tipo de resposta é a resposta de carregamento
por hora. E se o erro
não for NL, retornaremos uma string Nil e
o erro, por enquanto, vamos passar para esses parâmetros de
consulta Esse tipo são os valores de URL. E podemos adicionar todos os
parâmetros de que precisamos. Portanto, há muitos
aqui para isso de hora em hora. Temos um EID, um tipo de ação pode fornecer
um país postal e assim por diante Então, forneceremos todos
esses parâmetros de consulta. Portanto, temos certeza de que
nossa solicitação Get funciona exatamente como
funciona no navegador. Então, vamos adicionar esse EID O tipo é ZIP O CEP é 6.800. O país é Austria A. T. E também há essa bandeira para o Cure JSON, e essa é uma delas Pode importar esse pacote de URL. E agora vamos
prosseguir com a formatação e construção dessa string
que queremos retornar Então, a partir dos dados aqui, temos a matriz completa da carga
horária esperada. Então, o que faremos é
percorrer essa matriz, criar uma média
e, em seguida, criar uma string que mencione
qual é essa média Então, primeiro definiremos
uma carga média. E então vamos fazer um loop
no intervalo que
sabemos que está dentro da
parte horária do resultado. Então, isso é apenas mais
igual ao valor da hora, e então a
carga média se torna a carga média dividida pela
duração de todas as Outra coisa que
notei ao examinar a API é que eles estão fazendo algum tipo de normalização
dos dados Não é mostrado aqui,
mas esses números podem chegar a oito ou nove, mas mostram apenas uma classificação 0 a 4 na
interface real do site Então, por enquanto, para
simular essa funcionalidade, vamos apenas
dividir a média, vamos chamá-la de carga média
escalonada, e isso será apenas essa carga
média dividida por dois Na mensagem formatada
que enviaremos,
usaremos o
pacote de formato, sprint F, e diremos a carga média de pólen de
hoje é a média escalonada e retornaremos essa mensagem
formatada sem erros A implementação para obter os dados
atuais do gráfico
é bastante semelhante. Na verdade, vou
copiar tudo isso
e, em seguida, poderemos fazer
as alterações necessárias. E eu cometi um
pequeno erro aqui. Essa é a chave de ação e o EID é,
na verdade, a interface do aplicativo Você pode adicionar isso aqui. Agora, aqui, nesse
caso, a ação é, como podemos ver aqui na URL
, obter os dados atuais do gráfico. A identificação completa, na verdade, é
para o tipo de planta. Nesse caso, eu o fiz
para minha alergia, que é para gramíneas ou feno Ainda podemos passar o zíper. Só nos preocupamos com
os dados da temporada, e essa é a bandeira
dos dados da temporada. E também queremos que essa bandeira
JCN pura seja ativada. O URL é o mesmo.
O método é o mesmo. Ainda passamos, é claro,
os parâmetros da consulta e agora só precisamos
alterar o tipo de resposta,
que é o tipo de resposta atual dos dados do
gráfico. Agora, é claro, G
reclamará aqui porque a forma da
resposta é diferente Então, o que queremos fazer
com esse método é
repetir todos esses resultados
até encontrarmos uma data correspondente
e, em seguida, queremos imprimir
a média
histórica da data atual. Vou definir uma variável
chamada YY MMDD atual. Agora é o ponto temporal
e, em seguida, precisamos
formatá-lo usando o estilo Go
de formatação de strings Definiremos um histórico
médio que inicializará
um zero e, em seguida,
examinaremos esses resultados e
encontraremos a data correspondente Portanto, se a data do resultado for
igual à data atual, sabemos que a média histórica
é a média do resultado Da mesma forma, como fizemos acima, devemos criar
uma média em escala E vamos fazer uma conversão interna
disso, pois queremos apenas de zero a quatro, e vamos apenas
arredondar a média histórica encontrada dividida por dois, e então podemos criar
nossa mensagem formatada Nesse caso,
diremos que, historicamente, a
carga média de pólen de hoje é e passaremos
essa média em escala Salvamos o inter, importaremos esses pacotes e tudo deve estar pronto Então, nesta lição,
começamos a pensar em como podemos chamar esses dois endpoints e desserializar o JSON Começamos
examinando a estrutura do JSON e definindo os tipos necessários e
as partes necessárias
desse JSON que precisamos usar
para criar nossas mensagens Em seguida, começamos
a escrever as funções reais
que chamam o endpoint, usando nossa solicitação make HTP que criamos
na lição anterior Depois de obtermos a resposta, fazemos um pouco de
cálculo e formatação. E para cada função, finalmente
retornamos
a string formatada, que poderemos
enviar pelo Slack Na próxima lição, pegaremos essa string de retorno e a
enviaremos pelo Slack por meio de webhooks
10. Como criar um aplicativo do Slack e uma função de mensagens: Na última lição,
definimos alguns tipos e escrevemos duas funções para
chamar a API Allergy, fazer alguns cálculos e, finalmente, retornar uma string
bem formatada, que dissemos que encaminharíamos
para o
Slack para que pudéssemos enviar
a mensagem Para enviar nossas
mensagens pelo Slack, primeiro
precisaremos de um aplicativo Slack Em seguida, precisaremos ativar webhooks
recebidos para esse aplicativo
e, finalmente,
teremos uma URL que podemos realmente publicar para
enviar a mensagem Em seguida, voltaremos
ao código e escreveremos outra função utilitária para enviar qualquer mensagem
ao nosso aplicativo Slack Para começar a
criar um aplicativo Slack, api dotslaq.com acesse api dotslaq.com
e clique em seus aplicativos,
e você precisará fazer
login para acessar Vou entrar aqui com o Google. E eu quero a conta
Full Stack Craft da minha empresa. E podemos simplesmente
clicar em Cancelar aqui. Agora que estamos logados
, podemos
voltar para api dotslag.com voltar para api dotslag.com Em seguida, clique em seus aplicativos. Quando estivermos na página de listagem de
aplicativos, clique em Criar novo aplicativo. E queremos criar
nosso aplicativo do zero. Vou chamar o meu de
Allergy Cron Bot. E, novamente, eu o quero em meu
próprio Fullstack Craft Works. Na página resultante,
queremos então adicionar webhooks recebidos E só queremos colocar essa chave na posição ligada, e veremos alguns
códigos aparecerem Agora, queremos
adicionar um novo webhook, então clicaremos aqui e
precisaremos escolher um canal
para permitir que ele publique Por enquanto, vou escolher
Geral e clicar em AAO. Se já tivermos um
aplicativo Slack aberto, seja na versão web ou
desktop, veremos que a
integração foi adicionada Então, aqui, em geral, vejo que adicionei meu bot de coroa de alergia. De volta à interface do usuário da web, você pode ver que o Slack
criou uma URL de webhook para nós
e, portanto, podemos copiá-la
imediatamente Como alternativa, você pode
copiar o exemplo deles aqui. Este é um exemplo de olá mundo. E logo no terminal aqui, vou colar este exemplo, deve disparar
sem problemas. E se formos para o Slack, realmente
veremos aquela mensagem
Hello World do nosso novo aplicativo Slack Por enquanto, vamos apenas copiar o URL si e
entrar em nosso projeto G. Criaremos uma função utilitária a qual podemos enviar uma mensagem. Então eu vou chamar esse arquivo,
enviar uma mensagem para o Slack. Este é o pacote Utils e a função aqui é
enviar mensagem para o Slack Apenas aceitaremos uma mensagem, que é uma string, e ela
retornará um erro, se houver. Tudo o que precisamos fazer aqui
é criar um corpo, usar
o pacote JSON e um mapa
de string
com esse parâmetro de texto,
e essa será
a mensagem que
passaremos para a passaremos para Se o erro não for Nil, retornaremos esse erro Então, podemos usar nossa solicitação
make HTP. Essa será a nossa URL. Vamos postar sobre isso. Os cabeçalhos, não precisamos
fornecer parâmetros de consulta. Também podemos sair como Nil. Criamos um buffer
a partir do nosso corpo e podemos ter apenas uma string
vazia como nosso tipo Nós realmente não
recebemos nenhuma resposta. Só receberíamos um
código 200 quando fosse bem-sucedido. Portanto, não há nada
que precisemos analisar. E a partir daqui,
podemos devolver Nil. E se eu salvar esse arquivo, o Linter importará
o pacote JSON e estaremos prontos Agora, esse URL é
tecnicamente secreto, pois se alguém
soubesse disso,
poderá enviar spam para seu canal do Slack
indefinidamente Por enquanto, vamos deixá-lo
codificado , mas em uma lição posterior, coletaremos todos esses valores codificados e colocaremos em um arquivo de
ambiente Então, para resumir nesta lição, criamos um novo aplicativo Slack
e ativamos os webhooks
recebidos E depois de selecionar um canal para
o qual queríamos enviar uma mensagem, Slack gerou uma URL na
qual podemos postar que, por fim, encaminharia a mensagem para esse canal De volta ao nosso código G, escrevemos um UTIL conciso de envio de mensagens do
Slack que podemos usar para encaminhar o que analisamos da API de
alergia Na próxima lição,
combinaremos todas
as funções que
escrevemos em main dot go
e, em seguida, estaremos prontos pela primeira vez para
executar nosso aplicativo.
11. Concluindo o trabalho Cron: Na última lição,
criamos um aplicativo Slack e essa função de utilitário de envio de
mensagens do Slack que pode realmente enviar as
mensagens para o Slack que são geradas em nossas funções de API de
alergia Nesta lição,
finalmente combinaremos todas essas funções que
escrevemos em main dot go. Então, dentro dessa função Cron, vou chamar nossos dados de carga
horária G e obter os dados
atuais do gráfico e atuais do gráfico em
seguida, encaminhar essa
mensagem combinada para o Slack Então, temos nossa mensagem
média diária. E isso vem
da API de alergia. Obtenha dados de carregamento por hora. Se o erro não for nulo, entraremos em pânico E também obteremos a média
histórica. E da mesma forma, se o ar
não estiver nulo, entraremos em pânico. E então a mensagem real do
Slack
que enviaremos combinará essas
duas mensagens e colocaremos um novo
caractere de linha entre as duas Com isso combinado, podemos chamar nossa função Enviar mensagem do Slack
com essa mensagem do Slack E, novamente, se o
erro não for nulo, entraremos em pânico se for Utils Pode importar isso. E, por enquanto, para depuração, vou me desconectar que
enviamos a mensagem do Slack com sucesso e adicionaremos a mensagem real do Podemos salvar isso, e
devemos estar prontos para partir. Então, como uma rápida revisão
desta lição, finalmente
voltamos ao nosso arquivo dot go
principal e chamamos de Obter dados de carga
horária e
também de dados do
gráfico atual G para ter uma mensagem de média diária e média
histórica Nós os combinamos
com uma nova linha
e, em seguida, chamamos nossa função New
Send Slack message,
que, em última análise, completa o fluxo funcional completo
do nosso Na próxima lição,
modificaremos o horário
do cron job para ser acionado
mais ou menos imediatamente
e, em seguida, executaremos
nosso aplicativo
12. Executando o aplicativo: Na última lição,
concluímos o corpo
da função que realmente será acionada em nosso horário cron especificado Agora, como estamos prontos para
realmente executar nosso aplicativo, devemos atualizá-lo para
a próxima hora e
minuto imediatos para que possamos
ver rapidamente o cron job sendo
acionado e
testarmos o corpo real aqui para ver se
tudo está funcionando Então, a partir desse tempo de
gravação atual, é 1209, então vou aumentar
isso para 1210, e então podemos
chamar nossa função com go run main dot go E devemos esperar que,
assim que o relógio chegar às 1210, essas APIs sejam chamadas e
nossa mensagem do Slack seja enviada Então, de fato, vemos apenas
um carrapato depois de 1210. Recebemos a seguinte
mensagem do Slack que é enviada. A
carga média de pólen de hoje é uma
e, historicamente, a carga média de
pólen de hoje é duas Também podemos ver
aqui em nosso aplicativo Slack que realmente recebemos exatamente a
mesma mensagem Portanto, nosso aplicativo está funcionando
exatamente como esperamos. Até agora, o que criamos
neste curso segue os requisitos técnicos
que especificamos em termos de fluxo
e de como o aplicativo funciona. Ele obtém os dados da API, os converte em uma boa string legível por
humanos e envia essa
mensagem via No entanto, ainda há
muitas otimizações que podemos fazer para tornar esse aplicativo mais corporativo Então, nas próximas aulas, veremos coisas
como remover essas sequências de caracteres e
colocá-las em um ambiente, bem
como coisas
que realmente deveriam ser deixadas em um arquivo de
ambiente, como o URL do Slack e também o
endpoint da API de alergia Depois de fazermos isso,
passaremos a analisar os
testes e
até mesmo a implementar um pipeline CICD
automatizado
13. Testes de escrita: Na última lição, executamos
nosso aplicativo
e vimos que, de fato, tudo
está funcionando conforme o esperado. Agora, está tudo bem,
mas e se a API mudar ou algo estranho acontecer em nosso aplicativo
fazendo com que ele falhe? Provavelmente queremos
saber se alguma de nossas funções está
quebrada sem precisar executar o aplicativo em um ambiente
de produção completo. Portanto, nesta lição, escreveremos testes para cada uma das
funções que escrevemos. Especificamente, a função Enviar mensagem do
Slack e também as duas funções da
API de alergia,
a obtenção de dados de carga horária e
a obtenção de dados atuais do gráfico Não escreveremos explicitamente
um teste para a solicitação M HTP porque essa
função é, na verdade chamada pelas outras três para as
quais escreveremos testes Então, por proxy, se escrevermos um teste para essas
três funções, também,
por proxy, testaremos a função de solicitação
make HTP Para começar a
escrever nossos testes, criaremos uma pasta
chamada tests. E criarei dois arquivos, um para cada um
dos módulos ou funções
que testaremos. Então, faremos
nosso teste de API de alergia
e também faremos nosso teste de
envio de mensagens para o Slack E nota quatro,
reconheça esses arquivos
como testes reais. Eles precisam terminar com o dot go
do teste de sublinhado Então, dentro de cada um deles,
este é o nosso teste de pacote. Sabemos que
precisaremos do módulo de teste,
o módulo de teste embutido do G. E também para
o nome da função, há uma regra de que o nome
deve começar com test. Portanto, precisamos do sufixo do
teste de sublinhado aqui e também
do nome da função para começar com a palavra
teste em maiúscula para que go
reconheça totalmente o
arquivo e as funções dentro dos arquivos para
que seja um Então, aqui vou
chamá-lo de test Allergy API, e temos que passar um ponteiro para esse módulo de
teste Deixe-o vazio por enquanto
e faça o mesmo aqui. Para nosso teste de mensagens do Slack,
isso é um teste de pacote Importe o módulo de teste. E vou chamar esse teste de
enviar mensagem ao Slack, e passamos um ponteiro
para esse módulo de teste Agora, para realmente
testar nossas funções, chamaremos os dados de carga
horária G e as funções de dados do
gráfico. E agora chamamos nossa função como faríamos
em qualquer outro lugar em nosso código. Então, primeiro chamarei isso de
get hour load data
e, em seguida, faremos
várias verificações sobre o erro e a mensagem
para concluir nosso teste. Portanto, se o erro não for novo, queremos usar
a função T dot f, e podemos dizer erro ao
obter dados de carregamento por hora. Podemos transmitir o erro real. Você também deve verificar
se a mensagem é Nil. Isso também é um caso de erro. E, nesse caso, diremos apenas erro ao
obter dados de carregamento por hora. A mensagem é NIL e
também podemos verificar se a mensagem
em si é uma string vazia Isso provavelmente também não um resultado
muito bom
de nossa função. Portanto, erro ao obter dados de carregamento
por hora. A mensagem está vazia. E quase o mesmo para
obter os dados atuais do gráfico. Recebi nossa mensagem, nosso
erro e as mesmas verificações. Então, se o erro não for nulo,
pode realmente copiar isso. Só não se esqueça de alterar
a mensagem aqui. Então, fica claro quando
executamos nossos testes. Também verificaremos se a mensagem é Nil ou se
a mensagem está vazia, e eu apenas
substituirei essa string
de carregamento por hora pelos dados atuais do gráfico Agora, no teste
Send Slack message, temos apenas um erro retornado
dessa função Util, e eu vou chamar a Send Slack
com
a mensagem Se o erro
não for igual a Nil, diremos erro ao
enviar mensagem de folga
e inseriremos
o erro real
agora, e inseriremos
o erro real usaremos o comando go test integrado para executar esses Então, eu gosto de passar algumas
bandeiras quando faço meu teste. O primeiro é P, que define o número de testes
paralelos como um. Em outras palavras, isso
executaria seus testes em série. E eu gosto desse formato porque seus testes
sempre serão executados na mesma ordem e você sempre obterá um resultado
esperado de
como seus testes são executados. Então, com o tempo, você pode se
acostumar com a aparência dessa saída. Se você tiver uma base de código muito, muito
grande, é claro, considere
modificá-la para executar vários
testes em paralelo Eu também adiciono o sinalizador V, que permite a saída detalhada. E isso é útil para que caso algo
dê errado, você obtenha mais resultados e
encontre o erro mais rapidamente. Também precisamos passar
pela pasta em que
queremos executar nossos testes. Nesse caso, é
a pasta de testes. Então, juntos, o
comando é ir testar P um e depois para o Verbose
e depois para a pasta tests E se formos
bons em nosso trabalho, ao executarmos esse comando, esperamos
que todos os testes sejam aprovados. E, de fato, nossos dois testes, a API de teste de alergia e o
teste Senss Slack Message, foram aprovados e nossos
testes parecem bons Então, para recapitular rapidamente, escrevemos os testes para maioria das funções que
escrevemos em nossa base de código Nós os colocamos na pasta de teste e discutimos como
ser um teste válido para G.
G precisa ver o teste de
sublinhado no final e o nome
da função
no arquivo de teste para começar com o teste em maiúsculas Em seguida, escrevemos nossos testes. Nós os executamos com
o comando G test com alguns sinalizadores adicionais e vimos que todos os
nossos testes foram aprovados Na próxima lição,
veremos como
podemos dockerizar nosso aplicativo
14. Dockerize o aplicativo: Na última lição, escrevemos
testes para nosso aplicativo
e vimos que, de fato,
os testes estavam sendo aprovados. Nesta lição,
veremos como
podemos dockerizar nosso aplicativo Como o GO compila
em um único binário, não
há muitas
etapas complexas que precisamos
realizar para que nosso aplicativo
GO funcione sem problemas no Precisaremos definir dois arquivos, ou
seja, o arquivo Docker em si
e, em seguida, o arquivo de
composição do Docker, que nos
permitirá executar nosso contêiner com uma ferramenta como o Docker Compose, onde podemos gerenciar Então, vou começar
com o arquivo Docker e colocá-lo aqui
na raiz do nosso projeto Isso é simplesmente um arquivo Docker. E vou começar com
o contêiner alpino Gang
e vou pegar o contêiner 1.2
Golang Então, isso é do Golang
1.2 oh Alpine. Em seguida,
definiremos o Wd como aplicativo, e essa é uma prática
comum, pois não estamos construindo ou criando arquivos na raiz
real desse contêiner, mas a definição de
nosso próprio espaço de trabalho normalmente é considerada apenas
como aplicativo nesse contêiner, e é aí que
faremos a construção real Então, queremos copiar
tudo o que temos aqui em nossa base de código para a
pasta do aplicativo, e isso é apenas ponto a ponto. Então, na verdade,
criaremos nosso aplicativo. Então, vamos fazer Go Build O, e vamos chamá-lo de Allergy
Cron e, para executar isso, conforme mencionado, G
criará um único binário, e só precisamos
executar esse binário Portanto, esse Dockerfile já
seria suficiente para criar um contêiner que
pudéssemos construir e depois executar, mas para torná-lo mais amigável
com o Docker Compose, para
que possamos executá-lo imediatamente
com o Docker Compose, também
definiremos um ponto Yamofle do Docker Compose também
definiremos Então, assim como o Dockerfile, colocarei aqui na raiz o Docker Compose Dot Yamofle e usarei a versão mais recente do Isso é 3,9. Em seguida,
definimos nossos serviços. Temos o Allergy Cron, que é nosso único serviço E então definiremos
o contexto de construção. Está aqui, o que significa que o
Docker Compose
procurará esse arquivo Docker diretamente em nossa raiz e
usará Então, também podemos definir
a política de reinicialização, e eu vou defini-la
com o valor stop less stop. Então, isso significa que se nosso contêiner
travar por algum motivo, Docker detectará isso e
reiniciará o contêiner para Agora, para ver se
configuramos tudo
corretamente nesses dois arquivos, podemos criar e
executar nosso contêiner. Então, primeiro, para criar
o contêiner, vou usar o comando
Docker Compose e queremos E como é a
primeira vez que construímos nosso aplicativo como
uma verificação dupla de segurança, também não
publicarei nenhum caso, que significa que
tudo o que for necessário para criar o contêiner será
baixado recentemente da Internet Não vamos usar
nenhum arquivo local aqui. Feito isso, também
podemos executar o Docker Compose. E nesse caso, queremos subir e eu também passarei o sinalizador D, que é o sinalizador separado, que significa que o
Docker iniciará nosso contêiner em segundo plano e nos devolverá nosso terminal Se não passarmos esse sinalizador D, começaremos a ver os registros do nosso contêiner
anexados diretamente, e sair ou sair ambiente
do terminal
também sairemos do nosso Então, passamos esse D para que o contêiner continue
funcionando em segundo plano. Portanto, se tudo funcionar corretamente, veremos um belo verde feito aqui no final de
toda a saída da compilação. E podemos verificar se
o contêiner está realmente execução com o comando
Docker PsA E, de fato, vemos
nossa alergia Cron Running criada
há 18 segundos, até 15 segundos. Por isso, criamos e
executamos
com sucesso nosso aplicativo go
em um contêiner Docker Como uma rápida recapitulação,
definimos um arquivo Docker e
um arquivo Docker Compose para permitir que nosso aplicativo seja executado com o Nós dois construímos e depois executamos nosso aplicativo
em segundo plano, e vemos, de fato, nosso aplicativo está sendo executado em seu contêiner em
nosso sistema local. Na próxima lição,
veremos brevemente reiniciar nosso contêiner
com o mínimo de tempo de inatividade
15. Como reiniciar o recipiente com tempo de inatividade mínimo: Na última lição, criamos os dois arquivos de configuração
necessários para criar próprio contêiner Docker e, em seguida,
executar esse contêiner Docker
com o Docker Nesta lição, veremos
rapidamente como podemos
substituir um contêiner em execução
com o mínimo de tempo de inatividade E, na verdade, tudo se resume a reconstruir o aplicativo
e substituí-lo E, novamente, isso é usar
esse comando Docker Compose. Vamos construir
novamente sem cache. E emita o comando up separado e, o mais importante,
com force recreate Passamos esse sinalizador para ter certeza de que
o Docker
sempre substituirá o contêiner
existente pelo recém-construído Portanto, se emitirmos esse comando, veremos novamente que ele é concluído. E se eu verificar nossos
contêineres com o PSA, veremos que a nova versão foi criada
há 10 segundos em 7 segundos. Então, acabou de ser substituído. Agora, para um
aplicativo Cron Job como esse, esse comando provavelmente é
suficiente para suas necessidades Mas lembre-se de que esse
não é um método de tempo de inatividade zero. Esse é apenas um método de
tempo de inatividade mínimo. Você teria um tempo de inatividade durante os curtos momentos
necessários para Docker substituir o Para uma
solicitação de cron job como essa, é importante
lembrar de não substituir esse contêiner perto
do momento em que seu cron job é executado Se você estiver fazendo algo mais complicado que exigisse
um verdadeiro tempo de inatividade zero,
precisaria fazer algo mais complexo, como criar
vários contêineres, trocar
balanceadores de carga e assim por diante Mas esse tipo de estratégia fora do
escopo deste curso. Mas, em resumo, se você sabe o que seu
aplicativo está fazendo e quando é seguro
substituir esse contêiner, esse método funciona
muito bem para muitos tipos
de aplicativos. Na próxima lição,
voltaremos a programar e a melhorar a mensagem
que enviamos via Slack
16. Adicionando formatação sofisticada às mensagens do Slack: Na última lição,
discutimos brevemente como poderíamos reconstruir e substituir um contêiner
com o mínimo de tempo de inatividade Nesta lição,
voltaremos a alguns códigos e veremos como podemos melhorar a formatação das mensagens
do Slack
que enviamos Portanto, se acessarmos nosso arquivo da API de
alergia, podemos lembrar que estamos enviando duas mensagens bastante simples do Slack Pode ser bom adicionar um pouco mais de cor e talvez
até alguns emojis para que o sentimento de
nossa mensagem possa ser forma
mais rápida e
interessante Então, o que faremos é
modificar essa mensagem inicial. Vamos deixar a mensagem
histórica como está. Mas para os dados
reais em tempo real, melhoraremos a
formatação dessa mensagem Então, na parte inferior, vou definir
uma nova função chamada formatar dados de alergia. E é apenas uma função em
minúsculas aqui porque não
precisamos exportá-la Ele só será usado
neste arquivo. Vou passar a carga média escalonada que temos, que é um int, e
retornaremos uma string O que temos atualmente é nossa mensagem formatada
e ela se parece com esta A carga média de pólen de hoje e passamos essa carga média
escalonada Mas vamos prefixar
e
sufixar essa mensagem para
torná-la um pouco mais Podemos fazer isso com uma caixa de
comutação e o que
vou fazer é ativar
a carga média escalonada E, por um lado, esse é um caso meio
moderado. Então, vamos prefixá-lo com esse
emoji de círculo amarelo e colocar Ok, depois um espaço e, em seguida, a mensagem
existente que temos E talvez queiramos colocar limites em um nível baixo
ou moderado Então, no caso de serem dois, podemos aumentar a cor
para uma cor de aviso
laranja e dizer algo como cuidado novamente com
nossa mensagem formatada E nesse caso,
isso é médio, e vamos terminar com
aquele emoji laranja Depois, quatro ou três.
Colocaremos vermelho e avisaremos novamente com mensagem formatada e mencionaremos que está alto
e terminaremos com o emoji vermelho Agora, se for o
nível mais alto, que é quatro, colocaremos três
desses emojis vermelhos e
colocaremos desses emojis vermelhos e um alerta com nossa mensagem
formatada E coloque muita ênfase
nesses casos mais altos aqui. E também concluiremos isso com três desses círculos emogi
vermelhos Então, se for o caso, se for
zero, falharemos e também usaremos o
padrão
e retornaremos
esse emogi verde e, em seguida, ficaremos também usaremos o
padrão
e retornaremos
esse emogi verde e , em seguida, bem com a
mensagem formatada, Só para tornar a pontuação
consistente aqui, podemos fechar o
colchete da nossa caixa de distribuição Então, agora, em vez de
nosso formato aqui, vamos nos livrar disso,
pois ele já está inativo em nossa nova função, chamaremos
a função de formatação de dados de alergia e passaremos essa carga média
escalonada Agora, se salvarmos isso,
podemos testar nosso aplicativo. Então, agora é 318. Vamos aumentar
nosso cron para 319. Então, acho que serão 15
e 19. E podemos emitir
para executar o Dotco principal. E, de fato, logo após 319 aqui, recebemos uma cópia do
que enviamos pelo Slack com nossa nova mensagem agradável E hoje, está meio
quente e seco, então eu tenho
que cuidar um pouco. O nível está alto hoje. Então, como uma revisão desta lição,
acessamos nosso arquivo de API de
alergia e substituímos a
primeira mensagem que
enviamos por essa nova função de dados de
alergia de formato. E adicionamos um texto
extra aqui, bem
como alguns emojis
que refletem melhor o sentimento das informações
que estamos enviando E, no geral, acho que
parece muito bom e é mais fácil determinar a natureza da
mensagem imediatamente. Então, faltam apenas
algumas aulas. Na próxima lição,
veremos como mover todos esses valores codificados, como o URL e o fuso horário do Cron, bem
como o intervalo Cron, e colocá-los em um arquivo M. E depois de fazer isso e
garantir que nosso aplicativo ainda
esteja funcionando, usaremos o
CircleCI para criar
um pipeline CICD que testará,
criará e
implantará automaticamente nosso
17. Movendo segredos e valores codificados para um arquivo ENV: Na última lição, escrevemos essa função de
formatação de dados de alergia para melhorar a formatação e tornar as mensagens do Slack um
pouco mais coloridas Nesta lição,
vamos pegar todos esses valores codificados, como o URL da API, bem
como a localização do cron e o cronograma do cron, bem
como a localização do cron e o cronograma do cron e
colocá-los em um Então, para começar,
vou criar um arquivo N, então ponto N. E imediatamente, queremos criar um arquivo Get Ignore e adicionar
esse arquivo N ao G Ignore. Essa é uma prática comum, pois normalmente você terá pelo
menos um ou possivelmente
mais valores secretos que não deseja verificar
no seu repositório Git Então, vou criar esse Get
Ignore e vou
adicionar o arquivo M. Então, o que realmente temos para nossas variáveis de ambiente? Temos o URL raiz da
API Allergy aqui. Também temos nosso URL do web hook do
Slack
e temos nosso fuso horário Cron, e temos nosso fuso horário Cron bem
como a string de programação Então, vamos adicionar tudo isso
ao nosso arquivo de ambiente. Então, temos nosso URL do webhook
do Slack. Pode colar isso. Tenho a
raiz do URL da nossa API de alergia, vou chamá-la. Esse é esse cara. Temos
nosso fuso horário cron Isso está aqui. E o
cronograma do cron. E esse é esse. E enquanto eu estiver aqui,
vou reverter isso volta para o valor
original que tínhamos às 8:00 da manhã. valor
original que Agora, para que o
tempo de execução do Gang realmente veja ou tenha esses valores
no ambiente, precisamos garantir que
esse arquivo de ambiente seja carregado no tempo de execução. Para fazer isso, podemos usar o
popular pacote Johogo dot Nv. Então isso é github.com, Joho e go dot v. E
podemos recuperar E logo no topo
da nossa função principal, podemos carregar isso. Isso retorna um erro e é apenas carregar pontos N pontos. E não precisamos
passar nenhum parâmetro. Por padrão
, ele procurará nosso arquivo ponto N. Claro, queremos
verificar se o erro não é Nil, então
teremos uma mensagem fatal aqui Erro ao carregar o arquivo. Agora, precisamos realmente obter o valor dessas variáveis de
ambiente. Isso pode ser feito
com a chamada s.gn. Então, nós os carregamos
logo de cara, e eles devem estar disponíveis
em nosso ambiente. Então, temos o OS GNV. E esse será
o fuso horário Cron. E para este,
temos o OS GNN. Este é o Cron Schedule. E da mesma forma, aqui na API OSG N você também precisa adicioná-la abaixo, eu acredito, ou acima e também em nossa função slack Este é o OSG N. E nosso URL do
webhook do Slack. Agora, também precisaremos adicionar esse carregamento do ambiente
em nosso teste. Como agora que
refatoramos o código,
o código executado nesses testes também
espera ter essas variáveis de Portanto, é um pouco diferente
no teste porque
não estamos na raiz, não
podemos simplesmente chamar
a carga padrão, mas precisamos passar explicitamente o caminho de volta para o
ambiente na raiz Vou copiar isso para o
topo de nossos dois testes. E só precisamos passar
explicitamente por esse caminho. E importe os pacotes Godot
end e log aqui. E a mesma coisa para
o outro teste. Importe-os. E nós
já definimos o erro agora, então é apenas o normal igual E isso deve
bastar para os testes. Então, para revisar rapidamente, criamos um arquivo de ponto N para todos os vários valores
codificados que estávamos usando
em nosso aplicativo Também criamos um
arquivo get Ignore para ter certeza de que estamos ignorando o ponto N porque, de fato,
existem segredos Nesse caso, o mais importante
é esse URL do webhook. Não queremos que ninguém
entenda isso,
senão eles poderiam
postar em nosso canal. Examinamos nosso
código e substituímos esses valores codificados por seus
respectivos nomes de
ambiente E também adicionamos aos nossos testes a forma de carregar a variável de
ambiente. Portanto, no caso de nosso teste passar
explicitamente
o caminho para o nosso lado, que está nas pastas superiores Então, usamos a notação
Unix de pontos para chegar à pasta raiz
da nossa pasta de teste Em última análise, esse é
um padrão muito bom porque, nesse arquivo M, você vê imediatamente alguns
dos valores-chave mais importantes de como nosso aplicativo funciona, seja, o
fuso horário e a programação, mas também se o URL mudar ou se você
encontrar um fornecedor diferente, poderá alterar
esse URL e escrever um cliente diferente para essa API Além disso, por exemplo, se
você
alterasse URL do webhook para um canal diferente ou
se a equipe mudasse, você também poderia
substituí-lo rapidamente de vez em quando, sabendo que ele está sendo usado em sua base de código,
onde quer que seja Então, agora que
encaixamos nosso aplicativo, limpamos as mensagens
do Slack e também limpamos os valores
codificados Finalmente, chegou a hora da
próxima lição de criarmos um pipeline CICD completo e
completo para criar nosso aplicativo e
implantá-lo no Docker E finalmente
encerraremos o curso
retirando esse contêiner
recém-criado do Docker Hub e executando-o uma última vez para ver se
tudo ainda está
18. Criando um pipeline de CI CD com Circle CI: Na última lição,
criamos um arquivo de ambiente para armazenar todos os
valores e segredos codificados em todo nosso aplicativo e substituímos
esses valores
codificados pelos nomes das variáveis
de ambiente
do E essa foi realmente a
etapa final para estarmos prontos para
empacotar e automatizar a forma como
construímos nosso contêiner Portanto, nesta lição,
criaremos um pipeline completo de CD de CI que
criará e testará nosso aplicativo com os
testes que escrevemos. Então, se esses testes passarem, ele implantará ou carregará nosso
contêiner no Docker Hub Então, no final,
retiraremos esse contêiner e executaremos como um teste final para ver
se tudo está funcionando. Então, para começar com o CircleCI, precisamos primeiro criar uma
pasta que seja Circle CI
e, dentro dessa pasta, um arquivo config Agora, a primeira coisa que
você precisa fazer em seu arquivo Yamal de ponto de configuração, semelhante ao Docker compose,
é fornecer a Atualmente, essa versão ou
a versão mais recente é 2.1. E também listaremos um orbe. Agora, orbs são
comandos ou trabalhos pré-empacotados que são
muito comuns Por exemplo, eles
têm uma esfera nodal. Eles têm uma esfera frouxa, e isso só
economiza seu tempo. Em vez de escrever
comandos bash explícitos para realizar tarefas, eles têm Por exemplo, envie uma mensagem do Slack e você só precisa
passar a mensagem de string Você não precisa emitir essa declaração
curl ou
coisas assim No nosso caso, o único orbe de que precisamos é o hub do Docker, e isso pode ser feito
especificando o Docker, e usaremos a
versão mais recente desse orbe do Docker, que é precisamos é o hub do Docker,
e isso pode ser feito
especificando o Docker,
e usaremos a
versão mais recente desse orbe do Docker, que é 2.2 0.0. Tudo o que resta agora
são duas partes principais de um arquivo config dot yaml. Existem trabalhos
e fluxos de trabalho. Os trabalhos são várias
etapas que você pode usar uma ou mais vezes
em seus fluxos de trabalho Assim, você pode pensar neles como blocos de construção individuais, e os fluxos de trabalho
realmente se combinam e dizem em que ordem e como
esses trabalhos devem ser executados Então, normalmente, os fluxos de trabalho são listados mais acima
no ponto de configuração yam Mas, como vamos
passo a passo aqui, primeiro
escreveremos os trabalhos e depois escreveremos
os fluxos de trabalho Faz um pouco mais
de sentido do ponto de vista passo a passo. Então, começaremos
definindo nossos empregos. E, por enquanto, nós realmente
só temos um trabalho, que é testar
nosso aplicativo. Então, vou chamá-lo de
teste, temos que especificar
um diretório de trabalho. E no mundo do Circle CI, esse repositório é o
significante especial para o repositório
local E vamos
usar uma imagem do Docker e vamos
usar a imagem GO Então, podemos definir
nossas etapas do que realmente
queremos que seja
feito nesse trabalho. Então, primeiro,
vamos, é claro, verificar o código e
podemos armazenar em cache nosso arquivo go sum. Isso tornará as
compilações subsequentes mais rápidas. E
vamos verificar algumas de nossas opções e também
instalaremos
dependências a partir delas. verificar algumas de nossas opções e também
instalaremos
dependências a partir É necessário emitir o GGet e, em seguida,
salvaremos esse cache. Então, caso
mais tarde, se instalarmos novos pacotes ou alterarmos as versões deles, isso será
refletido com precisão
nessa chave que
definimos para nosso cache. E também precisamos definir em
qual caminho isso está. Portanto, a maior parte disso foi retirada de um exemplo que a CircleCI tem em
seu site sobre as práticas recomendadas
e recomendadas para um E vou colocar um link para isso
nos recursos da lição. Agora também precisamos criar
nosso arquivo de ambiente porque sabemos que nosso aplicativo não pode ser executado sem essas variáveis de
ambiente. Então, teremos outra etapa de
corrida aqui. O nome é criar arquivo ponto N. O comando pode usar o pipe para executar várias etapas ou
vários comandos. Primeiro, vamos
criar esse arquivo. Em seguida, usaremos o Echo para
ecoar todas as
variáveis de ambiente de que precisamos Para escapar para o ambiente
CircleCI, usamos essa sintaxe, os colchetes com
o cifrão, e usaremos
os mesmos nomes que temos Veremos como defini-los
posteriormente na interface do usuário do CircleCI. E vamos apenas acrescentar
a esse novo arquivo. Então, vou copiar
isso algumas vezes e sabemos que temos a raiz do URL
da API de alergia. Também temos nosso fuso horário cron e também temos aquela string de programação
cron e, com ela completa
, emitiremos
testes Então, vou chamar
isso de testes de execução. E o comando que conhecemos da
lição anterior é ir testar. Vamos definir esse sinalizador paralelo
e também o sinalizador Verbose, e queremos executá-lo
na pasta de testes Então, definimos nosso
único teste de trabalho e agora precisamos
definir os fluxos de trabalho Portanto, o padrão normal,
como mencionei, é que os fluxos de trabalho
superam os trabalhos, então voltaremos aqui e
também especificaremos nossos fluxos de trabalho Portanto, também
temos apenas um fluxo de trabalho, que será o fluxo
de trabalho de produção, e precisamos especificar os trabalhos. E temos nosso trabalho de teste e também vamos
filtrar no galho. E queremos apenas
a filial principal. E então, o segundo trabalho que
queremos é usar
esse orbe Docker E isso será o Docker Publish. E a imagem será
nosso nome de usuário e nome do repositório, que também
definiremos posteriormente no CircleCI WebUI junto Então, chegaremos a isso
em alguns instantes. Agora, também precisamos definir
a ordem desses trabalhos. Se não especificarmos nenhuma ordem, CircleCI, vamos
executá-las Isso pode ser útil
dependendo do que você está fazendo. Mas é claro que, em nosso caso, queremos
garantir que esses testes sejam
aprovados antes de
publicarmos em nosso contêiner. A maneira de fazer isso é com
a diretiva de requisitos, e exigimos, é claro, que o trabalho de teste seja concluído. E também queremos filtrar
na ramificação principal. Agora, há muito código
Yamil nesse arquivo e não está claro se temos algum erro ou
problema de sintaxe, mas, felizmente, CircleCI fornece uma ferramenta
CLI na qual podemos verificar esse arquivo de configuração e não está claro se temos
algum erro ou
problema de sintaxe, mas, felizmente, o
CircleCI fornece uma ferramenta
CLI na qual podemos verificar esse arquivo de configuração. Adicionarei nos recursos da lição o link para a
documentação oficial sobre como
você pode instalá-la. Eu já o tenho
instalado no meu sistema e a forma de
verificar a configuração é
com o CircleCI Config com o CircleCI Podemos ver aqui que eu esqueci que parece o ponto e vírgula E vou executar novamente essa verificação, e também esqueci um S. E, finalmente, parece que
temos uma configuração válida aqui Então, obtemos que o é válido. Portanto, já podemos ver que é bastante prático
encontrar qualquer tipo de
erro de digitação ou problemas de formatação
em nosso arquivo de configuração Agora, neste ponto do seu código
, provavelmente faria sentido
ramificar e criar
uma ramificação desenvolvida. Primeiro, é claro, exigindo
que você tenha inicializado o repositório Git
e assumindo
que talvez você ainda esteja na ramificação
principal ou principal, você, é claro
, emitiria, verificaria B develop e
confirmaria tudo nessa ramificação Então, o que você faria
seria mesclar quando estiver pronto, mesclar com sua ramificação principal
ou master, e então isso
iniciaria este caso, teria que ser a ramificação
principal ou então o
CircleCI não faria nada CircleCI não Ele está aguardando confirmações para essa ramificação principal
, conforme especificamos Mas se você, por exemplo, adotou a convenção de
nomenclatura master, precisaria alterá-la para master para que o
CircleCI
fizesse qualquer coisa
nessa ramificação No meu caso, já estou aqui no repositório
específico do curso e tenho um nome de ramificação personalizado Então, por enquanto, vou
deixar isso como está e vamos
entrar nos aplicativos web Docker Hub e CircleCI e configurar o que precisamos Então, dentro do doctor Hubb, faça login ou crie uma conta Se você não tiver
um, eles são gratuitos. E vamos clicar em
Criar repositório aqui, e eu chamarei o
meu Allergy Cron Podemos simplesmente clicar em Criar aqui. E agora temos um repositório para
o qual podemos enviar. Em seguida, acessaremos circleci.com e
clicaremos E na página de login aqui, como o repositório, no meu caso, está no Github, vou
fazer login com Se você decidiu
acompanhar o Bitbucket,
por exemplo, você pode fazer login com o Bitbucket Então, estou em várias
organizações, mas o repositório em que estamos
programando está na
minha conta pessoal Então, vou selecionar isso e você
verá todos os seus repositórios
no seu perfil do GitHub. E, claro, eu quero que este curso
vá para
aplicativos do mundo real, e podemos clicar em Configurar projeto. Então, antes que o CircleCI possa
encontrar o arquivo Arc fig, eu tenho que voltar aqui
no código e confirmar Então, vou adicionar tudo. Vou confirmar algo como se o
CircleCI Config terminasse. Vou insistir que tenho que
definir o nome da lição com
o nome da ramificação. Então, aqui eu deveria ser capaz de
especificar a ramificação da Lição 18, e vemos que o CircleCI até
examinará nosso código e encontrará Então, é
um serviço muito, muito bom. Eu realmente gosto de usar o CircleCI.
Eles tornam isso muito fácil. Portanto, no seu caso, dependendo de onde
você fez o push, se você o enviou para a ramificação desenvolvida,
se você
o enviou para a ramificação
principal ou para a ramificação
master,
você pode especificar isso aqui, e espero que o CircleCI
encontre seu ponto de configuração YamoFle encontre seu Então, depois de descobrir isso, basta clicar em
Configurar projeto. E até tentará
iniciar o primeiro fluxo de trabalho. Mas é claro que, como
o commit foi para a ramificação da Lição 18, ele vê na configuração: Ok, não
há nada a
fazer com a Lição 18, e ela simplesmente
dirá que não há fluxo Então, agora que temos nosso projeto
CircleCI configurado, devemos adicionar todas
essas
variáveis de ambiente ao ambiente CircleCI real Para fazer isso, podemos ir
até aqui para
as configurações do projeto e aqui para as variáveis de
ambiente. E podemos adicionar nossos pares de variáveis de
ambiente de valor-chave. E sabemos que temos
nosso URL do webhook do Slack. E podemos continuar adicionando todas as variáveis que
sabemos que precisamos para
nosso aplicativo. Tenho fuso horário. Eu tenho o cronograma do cron. E agora também precisamos adicionar
alguns para nossa integração com o Docker
Hub Portanto, precisamos adicionar o nome de
usuário do Docker, o login do Docker, que na verdade
são os mesmos, a senha do Docker e o nome do repositório
do Docker Então, no meu caso, meu Docker Hub é a conta da nossa empresa,
Full Stack Craft Então, vou usar isso para
o nome de usuário do Docker. E também as variáveis de
ambiente de login do Docker. O nome do repositório Docker é
o nome que você forneceu
para seu E no meu caso,
isso é Allergy Cron. E, finalmente, a senha do
Docker. Claro, eu não vou
mostrar isso aqui. Então, agora definimos todas
as variáveis de ambiente que
precisamos para executar o pipeline CICD De volta ao código,
como no meu caso, eu já tenho uma ramificação principal que não quero estragar, vou criar uma
ramificação separada chamada Pipeline e
também atualizar esse filtro de ramificação
no arquivo Yam de pontos de configuração,
e então poderemos
testar o pipeline também atualizar esse filtro de ramificação no arquivo Yam de pontos de configuração, e então poderemos
testar Então, vou apenas modificar
isso para Pipeline. E lembre-se,
é claro, no seu caso, você pode deixar isso para o Maine
ou para qualquer filial em que
queira que seu
gasoduto CICD seja acionado Então, vou criar
essa nova ramificação com G checkout B Pipeline De fato, posso ver que estamos no ramal
do oleoduto. Vou adicionar tudo. E vou adicionar uma mensagem Algo como uma ramificação personalizada do
pipeline, e podemos enviar. Agora, de volta ao CircleCI, devemos ver que o
fluxo de trabalho de produção realmente começa porque o CircleCI vê
aquele Podemos clicar aqui
para ver nossos dois trabalhos. Portanto, temos nosso trabalho de teste e
nosso trabalho publicado no Docker. E mesmo dentro dos
trabalhos em si, você pode ver todas as
etapas e a saída. Então, parece que nossos
testes foram aprovados. Esse é o resultado familiar que vimos
na lição anterior. Então, é claro,
de volta ao fluxo de trabalho, ele passará
para a publicação do Docker e veremos como isso acontece Parece que também foi um
sucesso. E, de fato, no Docker Hub, vemos que ele foi
publicado há alguns segundos, então nosso pipeline CICD
funcionou perfeitamente Agora, embora nossa compilação e fluxo de trabalho
pareçam estar funcionando, se realmente pegássemos nosso contêiner docker
e tentássemos executá-lo, veríamos que G
reclama que não
consegue encontrar a variável de
ambiente Esquecemos a etapa fundamental
em nosso pipeline de CICD para manter essa
variável de ambiente entre esses dois trabalhos Então, para fazer isso aqui no final
do nosso trabalho de teste, podemos especificar esse
comando persist espaço de trabalho e
queremos especificar que
a raiz está aqui, e vamos apenas persistir o arquivo
dot m que criamos E então, em nosso comando de publicação do
Docker, precisamos especificar que, quando estamos publicando ou
construindo esse contêiner, queremos anexar a
essa raiz atual E então, quando o Docker estiver sendo construído, ele terá esse arquivo de
ambiente em seu espaço de trabalho de construção Então, uma coisa muito importante
que eu quero enfatizar aqui é que isso é um
pequeno risco de segurança. Portanto, observe
aqui que, neste caso, este é um contêiner
listado publicamente e o arquivo estará
dentro do contêiner. Portanto, embora isso seja bom para variáveis
públicas como o fuso horário do Cron e
a string de programação do cron, não
é adequado para segredos
como nosso webhook do slack Nesse caso,
sugiro que você
crie um arquivo separado e o transmita ao executar seu contêiner Docker,
onde quer que esteja No entanto, essas especificidades secretas de
gerenciamento
tendem a ser muito diferentes dependendo de organização
para organização, então vou deixar isso fora
do escopo deste curso Por enquanto, vamos apenas
ilustrar como podemos incluir esse arquivo em nosso CICD Mas observe que é um possível problema de segurança. Uma pequena alteração final
que precisamos fazer aqui em nosso arquivo amo configurado é escapar dessa variável de ambiente do
cronograma cron Como temos esses
asteriscos aqui, quando eles escapam
pelo CircleCI, temos Então, para remediar isso, tudo o que precisamos fazer é colocar essa
variável entre aspas duplas, e isso resolverá o problema de ecoar esse valor
em nosso arquivo final Então, como teste final
para nosso contêiner, vou mudar esse cronograma de coroas para algo
nos próximos minutos, e então poderemos retirar
nosso contêiner, executá-lo e garantir que tudo
esteja funcionando e funcionando, de
fato, no momento em que
definimos nosso cronograma de cron Então, são cerca de 4:18 agora. Então, vou deletar
isso e adicioná-lo novamente. Vamos fazer por 225. Então esse é o 25º minuto, a 14ª hora e depois os
asteriscos para Pode adicionar isso de volta. E de volta ao projeto aqui, executarei novamente o
último fluxo de trabalho para garantir que o contêiner seja reconstruído com essa nova variável Então, espere aqui até que
o pipeline seja concluído, e então podemos
entrar no Doctor Hub, obter nosso ID de contêiner e executar esse contêiner Então, o pipeline foi concluído
e, se pularmos até aqui, para o Docker Hub, veremos que nossa nova imagem
acabou de ser publicada Clicamos nisso.
Podemos obter a identificação completa. Vou copiar isso.
Então, para executá-lo, podemos emitir o Docker Run Em seguida, queremos desanexar esse ID
completo do contêiner. Ele será baixado para
nós e começará a executá-lo. Assim, podemos verificar
se está sendo executado com P A.
Vemos que está em execução
e ainda não há registros, mas esperamos que às 2:25, recebamos uma
cópia registrada da nossa mensagem do Slack
e, claro, da própria mensagem do Ok, então 225 acabaram de passar. Vamos conferir nossa folga aqui. De fato, recebemos essa mensagem. Então, hoje está um pouco chuvoso. A carga de pólen é um pouco
menor que a média. Algumas coisas boas para eu saber. E se voltarmos
aqui para nossos registros do docker, obteremos aquela cópia
da mensagem que foi
enviada
de volta daquela mensagem de registro que colocamos no ponto
principal
algumas lições atrás Então, parabéns. Você chegou ao final
da última
aula de codificação técnica deste curso Só resta uma lição, que é o
tipo de lição de conclusão e recapitulação que aborda o que
abordamos neste curso, além de discutir onde
você pode encontrar o código, o livro e quaisquer outros recursos
adicionais do curso
19. Conclusão: Então, parabéns. É isso mesmo. Esse é o fim deste curso de escolha de
aplicativos do mundo real. Espero que você tenha gostado de tomá-lo tanto
quanto eu gostei de fazer. Apenas um lembrete, há uma versão em livro em PDF
deste curso Vou adicionar o link para isso
nos recursos da lição. Há também o
repositório GitHub para este curso, onde cada lição corresponde a tudo o que fizemos
naquela Eles são nomeados por filial. Então isso é tudo que eu
tenho do meu lado. Divirta-se escrevendo código G, e eu vou ver todos
vocês na próxima vez. O.