Rocket.Chat App: Criando seu primeiro aplicativo

Rocket.Chat App: Criando seu primeiro aplicativo

Dando continuidade o nosso post anterior onde entendemos o que é um aplicativo do Rocket, o seu poder e como preparar o ambiente, agora criar o seu primeiro aplicativo.

Espero que nesse momento já tenha o  Rocket rodando e configurado (Utilizando o docker compose fornecido, basta realizar login com os dados do administrador e seguir o setup wizard). Além disso, também será necessário que já tenha instalado o node e a CLI do Rocket.

 

Qual problema vamos resolver?

 

Problema? Sim! Se vamos desenvolver algo, é porque precisamos resolver um problema/dificuldade. Então vamos lá!

O RocketChat tem a funcionalidade de Omnichannel, onde permite que clientes entrem em contato a partir de diferentes plataformas de comunicação, como web, Telegram, Whatsapp, Messager, etc. Mas um problema que ocorre nesse tipo de conversa, não só no Rocket mas na maioria das plataformas de atendimento, é que o usuário acaba não sabendo com quem conversa. Normalmente o atendente quando entra na sala se apresenta, para que o cliente saiba que ele está conversando com um humano e qual seu nome.

Além disso, quando ocorre a transferência de atendente ou departamento o usuário não fica sabendo que nada disso ocorreu a menos que alguém informe.

Não seria legal que o cliente tivesse essa informação de forma clara e visual? Melhor que isso, seria ótimo se tudo ocorre-se automático, sem depender de alguém apresentando sempre que trocar de atendente ou for transferido.

No caso do Rocket, é fornecido um widget para comunicação via Web onde tudo isso já ocorre com clareza. É visualmente claro o nome do atendente  atual (e até sua foto) e é informado quando ocorre transferências. Mas vamos tentar resolver esse problema independentemente da plataforma do cliente e tudo de forma automática utilizando aplicativos!

 

Criando o aplicativo

 

Para criar um aplicativo é bem simples. Basta ir até o terminal e digitar o comando:

rc-apps create

Com isso será solicitado algumas informações sobre o aplicativo a ser gerado. Sendo elas:

  • App Name – O nome do seu aplicativo. Será também o nome da pasta criada para armazenar os arquivos inicial.
  • App Description – Descrição das funcionalidades do seu aplicativo.
  • Author’s Name – Nome do autor.
  • Author’s Home Page – Página do autor.
  • Author’s Support Page – Página de suporte ao aplicativo (Útil principalmente se pretende disponibiliza-lo no marketplace).

Preencha essas informações como desejar, elas podem ser alteradas posteriormente. Preenchi da seguinte forma:

Ao acessar a pasta que acaba de ser gerada, você deverá ter a seguinte estrutura de arquivos:

Vamos entender melhor cada um deles:

  • app.json – Possui as configurações do seu aplicativo. Informações como seu ID (utilizado para identificá-lo no Rocket), versão atual, versão da API do Rocket Engine mínima necessária, etc.
  • .editorconfig – Define algumas configurações para serem utilizadas pelos editores de código a fim de padronizar formatação.
  • .gitignore – Define quais pastas/arquivos devem ser ignorados pelo git.
  • icon.png – É o ícone do aplicativo.
  • node_modules – Pastas das bibliotecas do node que seu aplicativo utiliza.
  • package.json e package-lock.json – Utilizados pelo npm para gerenciar dependências.
  • .rcappsconfig – Possui configurações de deploy da sua aplicação. Como URL do Rocket, usuário e senha do administrador e arquivos/pastas a serem ignorados na compilação.
  • RocketChatAppOmnichannelApp.ts – É o ponto inicial da nossa aplicação, é por esse arquivo que tudo irá passar. Pode ser alterado de nome/local desde que também ajustes no app.json.
  • tsconfig.json – Configurações do TypeScript.
  • tslint.json – Configurações do TSLint (analisador de códigos do TypeScript).
  • .vscode – Nessa pasta contem configurações do VSCode. Inicialmente vem com um arquivo de sugestão de extensões úteis.

 

Analisando a documentação

 

Agora olhando a documentação, vamos tentar encontrar algo que nos ajude no nosso aplicativo. No post anterior eu havia dito que os aplicativos permitiam ouvir eventos e logo nessa primeira página já há uma explicação um pouco mais aprofundada sobre esse recurso.

Ele explica isso falando sobre Handlers, que são basicamente listeners (ficam escutando esperando por ações) para os eventos. Existem por padrão 2 tipos de handlers: Pre e Post. Os do tipo Pre ocorrem antes da finalização do evento, enquanto do tipo Post ocorrem após sua finalização.

Além disso, eles possuem variações que permitem ações diferentes. Vamos entender essas variações na sua ordem de execução:

  • PreEventPrevent – Permitir ou não que o evento ocorra. Ex: O evento PreMessageSentPrevent pode definir se uma mensagem será enviada ou não.
  • PreEventExtend – Permite realizar a extensão de informações sem alterar nada. Ex: O evento PreRoomCreateExtend permite que você adicione novos membros às salas quando criadas.
  • PreEventModify – Permite realizar a alteração de qualquer informação. Ex: O evento PreMessageSentModify permite modificar completamente uma mensagem, mudando seu texto, autor e até mesmo a sala a qual será enviada.
  • PostEvent – Não permite nenhuma alteração ou limitação, auxilia simplesmente como um aviso de que ocorreu. Ex: O evento PostMessageDeleted recebe os dados da mensagem que foi deletada, e você pode tomar ações em cima disso, como fazer um log adicional ou enviar essa mensagem para um canal separado.

Entendido agora como funciona os eventos, vamos tentar encontrar se algum pode nos ajudar.

O primeiro problema que queremos resolver é que quando a sala seja transferida, o cliente seja informado. Analisando a documentação, temos a interface para o evento PostLivechatRoomTransferred, que olhando seu nome e o padrão dos eventos, entendo que será executado após o evento de transferência de sala do livechat (Como estamos falando do Omnichannel, em alguns lugares ele é referenciado como livechat).

Agora para o segundo problema, de informar ao usuário com quem ele está conversando, temos a interface para o evento PreMessageSentModify, pois queremos modificar as mensagens enviadas de forma a adicionar antes do texto o nome do usuário que enviou.

Tendo encontrado os eventos que vão nos ajudar a resolver os problema, é hora de começar a implementar a solução!

 

Mãos na massa

 

Abrindo o VSCode, nosso ponto de entrada da aplicação (que no meu caso é o arquivo RocketChatAppOmnichannelApp.ts), temos o seguinte conteúdo:

Quando criamos o aplicativo pela CLI, ele já preencheu os arquivos com a estrutura mínima. Seria basicamente uma classe que herda a classe App  e implementa o construtor que recebe os parâmetros e chama o construtor da classe pai. Nada além do básico de OO temos até agora.

Implementando a funcionalidade de notificação de transferência

 

Vamos começar a criar nosso código implementando a interface IPostLivechatRoomTransferred. Se estiver utilizando o VSCode, ele vai te auxiliar na importação. Agora precisamos declarar os métodos que o contrato com a interface nos faz implementar. Nesse caso sendo apenas o método executePostLivechatRoomTransferred. Se estiver utilizando uma IDE que te ajude com isso, provavelmente ela vá declarar esse método com uma estrutura meio estranha, pois o seu nome vai estar na verdade apontando para uma chave de um enum. Não tem problema  utilizar dessa forma, mas eu prefiro deixar o nome mais explícito de forma que fique claro só de olhar. Ao final de tudo isso, nosso código já está assim:

 

Agora precisamos escrever nosso código. O que eu havia dito, é que no caso de transferência iríamos enviar uma mensagem para o usuário informando que isso ocorreu. Para isso, temos alguns passos a executar:

Verificar se o evento ocorreu em uma sala do Omnichannel – Apesar de ser explícito nesse caso que é um evento de livechat, é um costume importante essas validações, pois a maioria dos eventos são globais, então se você não quiser que ocorra independente da sala/usuário é importante validar.

  • Entender qual foi o tipo de transferência – Pode ter transferido de departamento ou para um usuário diretamente, então para cada caso quero uma mensagem informando o ocorrido.
  • Criar a mensagem – Preciso definir as informações da mensagem como: texto, sala e quem enviou, por exemplo.
  • Enviar a mensagem.

Atendendo a esses pontos, temos esse código:

Agora vamos entender passo a passo o que ele está fazendo:

  • A linha 8 verifica se o tipo da sala é diferente de livechat. Caso seja, retorna vazio para interromper a execução. Nada será feito nesse caso.
  • A linha 12 verifica qual foi o tipo de transferência. Se foi transferência de agente, temos uma mensagem, caso contrário temos a mensagem de mudança de departamento.
  • A linha 16 busca pelo usuário do aplicativo, pois será ele o autor da mensagem (usuário do aplicativo não é o atendente do RocketChat nem o cliente, e sim um usuário que o próprio Rocket cria para cada aplicativo instalado).
  • Alinha 18 resgata um “criador” de informações e começa a construir a mensagem que será enviada. Define a sala como sendo onde ocorreu o evento, a mensagem que foi criada e quem enviou como o usuário resgatado anteriormente.
  • A linha 24 finaliza enviando a mensagem.

Um ponto importante é que modifiquei o método para que ele fosse assíncrono, já que dependo de algumas ações assíncronas em sua execução e preciso aguardar pelo retorno de cada uma para continuar.

Outra coisa importante de entender são os parâmetros que o Rocket fornece para o método desse evento. Temos:

  • ILivechatTransferEventContext – Contém informações sobre a transferência, como seu tipo, a sala, com quem estava e para quem foi a conversa.
  • IRead – Permite fazer leitura de dados do RocketChat, sempre para somente leitura! Você pode buscar por informações do ambiente, mensagens, salas, usuários, etc.
  • IHttp – Permite realizar requisições HTTP.
  • IPersistence – Permite gravar dados no banco (Incluir, atualizar e remover). Mas importante: Esses o são apenas dados que seu aplicativo gera e tem acesso. Nada de outros aplicativos o do próprio Rocket é possível modificar.
  • IModify –  Permite acesso a alguns modificadores de dados. Os modificadores possíveis são:
    • Criadores (sala, mensagens, etc.);
    • Deleção (apenas sala);
    • Extensores (salas e mensagens);
    • Atualizadores (Salas, usuários, mensagens);
    • Notificações;
    • Agendados de tarefas;
    • UI (Não modificar a interface do próprio Rocket, mas é possível criar coisas a mais).

Tendo entendido toda essa parte, agora termos a nossa primeira funcionalidade pronta e funcional!

 

Implementando a funcionalidade de informar quem enviou a mensagem

 

Agora vamos implementar a interface IPreMessageSentModify. Uma diferença da anterior é que essa possui dois métodos: execute e check. O método execute é o que já tínhamos na anterior, e é exatamente o que o evento irá chamar. O check é um método com o objetivo de validar se poderia ou não ser executado esse handler para o evento ocorrido. Esse tipo de método está depreciado no Rocket. Novos eventos não o contemplam e no futuro será removido, então seu uso é desencorajado. A sua implementação não é obrigatória!

Com isso explicado, vamos ver o que precisamos para esse caso:

  • Validar se o evento ocorreu em uma sala do Omnichannel – Esse já é um evento que vai ser executado em qualquer sala, pública, privada, de mensagem direta e livechat. Nesse caso é essencial que seja validado ou teremos reclamações dos usuários.
  • Validar quem enviou a mensagem – Toda mensagem é mensagem, então o evento é chamado independente de quem a envie. Nesse caso eu só quero executar quando quem enviou é o atendente do livechat.
  • Validar se é a primeira mensagem de uma sequência – Apesar de não ser o fim do mundo fazer isso em todas as  mensagens, é mais interessante que só na primeira mensagem de uma sequencia eu informe quem é o usuário, e nas seguintes não se faz necessário já que estão sendo enviadas todas em seguida e sem interrupção.
  • Criar a mensagem – Nesse caso está mais para modificar a mensagem. Eu vou pegar o texto originalmente digitado pelo usuário e antes dele colocar seu nome para identificá-lo.
  • Enviar a mensagem.

Já sabemos o que se faz necessário, então é hora de criar o código. Atendendo todas as necessidades, teremos algo assim:

Vamos entender melhor o que ele faz:

  • A linha 8 verifica se não é uma sala de livechat. Caso não seja, retorna a mensagem original.
  • A linha 12 verifica quem enviou a mensagem. Se não foi um usuário, retorna a mensagem original.
  • A linha 16 resgata a sala da mensagem e especifica ela como ILivechatRoom para que possamos acessar os atributos específicos desse tipo de sala.
  • A linha 18 verifica se não estava sendo esperado uma mensagem. Isso é para sabermos se é a primeira mensagem de uma sequência. Explicando melhor: Quando o cliente envia uma mensagem, a sala fica na situação “Aguardando por resposta”. Então, se ela não está aguardando por resposta significa que a última mensagem já foi do atendente. Logo nesse caso nada será feito, apenas retornado a mensagem original.
  • A linha 22 cria um novo texto da mensagem. É colocado o nome do atendente, quebrado linhas e adicionado o texto original.
  • A linha 24 modifica a mensagem definindo o novo texto.
  • A linha 24 finaliza enviando a mensagem.

Esse método recebe dois novos parâmetros. Vamos entendê-los melhor?

  • IMessage – A mensagem que está sendo enviada (E todos seus dados além de simplesmente o texto). Útil apenas como leitura, não use para modificar nada pois existe outro parâmetro para isso.
  • IMessageBuilder – Um construtor de mensagens. Ele já vem preenchido com todos os dados da mensagem original. Sendo assim, você pode utilizar os setters que ele fornece para modificar apenas o que é necessário. No fim ele fornece um método getMessage para que você pode usar para
  • resgatar a mensagem.

Assim temos o aplicativo com as funcionalidades planejadas implementadas.

 

Preparando o ambiente para testar

 

No post anterior já não havia feito tudo para o Rocket aceitar aplicativos? Sim! Mas como estamos trabalhando também agora com o Omnichannel, existem algumas coisas adicionais para fazermos antes de testar.

Primeiro, vamos acessar a Administração, procurar pelo menu Omnichannel e marcar a opção Omnichannel habilitado. Não se esqueça de salvar.

Ainda na Administração, vamos realizar outra mudança que vai facilitar os testes. No menu Geral desmarque a opção Restrict access inside any Iframe. Como vamos simular um livechat localmente, isso iria bloquear o carregamento dele sem um servidor web rodando e liberado.

Agora que temos tudo de configuração ajustado, falta apenas preparar o Omnichannel. Saindo da Administração deve ter aparecido novas opções na tela. Abaixo da opção de acessar a Administração selecione uma nova opção chamada Omnichannel. Entre nela e vá no menu Instalação do Livechat. Nele vai existir um código HTML que vamos utilizar para conversar com o Rocket, simulando um cliente. Coloque dentro de um arquivo HTML.

Agora, o último passo (Prometo!) é criar um outro usuário no Rocket para que possamos testar a transferência. Também é necessário adicionar todos os usuários como agentes do Omnichannel para que eles possam realizar atendimentos. Para isso, dentro ainda da tela de configurações do Omnichannel, vá no menu Agentes, procure pelos usuários que vai utilizar no teste, e adicione ambos.

Caso queira pode também criar departamentos e adicionar os usuários, para testar a mensagem de transferência entre departamentos.

Com isso, temos nosso Omnichannel configurado e pronto para testar nosso aplicativo!

Deploy e testes

 

Para instalar o nosso aplicativo é bem simples, a CLI nos ajuda com isso. Pela linha de comando, dentro da pasta do aplicativo, execute o comando:

rc-apps deploy --username=rocket --password=PZxs7wsHWEJLP98z --url=http://localhost:3000

Nele estamos fornecendo o URL do Rocket e o usuário e senha de um usuário que posso realizar a instalação de aplicativos (nesse caso um administrador). Caso não tenha utilizado o docker compose fornecido, ou tenha outra url de instalação, precisa alterar para os dados corretos.

Agora se entrar na Administração, menu Apps, verá que seu aplicativo já aparece como instalado e habilitado.

Pronto. Só falta testar, não é mesmo? Antes de começarmos, certifique-se de se logar em pelo menos duas contas do Rocket que sejam agentes (Pode utilizar guia anônima para isso) e deixar elas disponíveis no Omnichannel (A partir da nova opção que apareceu no menu lateral esquerdo da tela).

Com isso resolvido, agora é hora de abrir aquele arquivo HTML que criamos. Lembra dele?  Ao abrir deve aparecer no canto inferior direito um ícone do RocketChat. Clique nele e será aberto a tela para digitar nome e email, além de  iniciar a conversa.

Agora vamos preencher o Nome e o E-mail e clicar em Iniciar chat. Quando enviar a primeira mensagem, será criado uma sala para o atendente que receber esse atendimento.

Nesse caso quem recebeu o chat foi o atendente “Blog 4Linux”. Como eu havia dito lá no início, o widget de livechat fornecido pelo Rocket não tem esses problemas que apresentei, mas mesmo assim estamos resolvendo esse problema pensando em todos os possíveis tipos de clientes e meios de conexão.

Agora quando o atendente responder, sua primeira mensagem será identificada com seu nome:

Veja que a primeira mensagem depois de uma interação do cliente, sempre vai com o nome do atendente.

Agora vamos testar a transferência para outro atendente:

E isso também funcionou! O usuário foi informado que foi transferido.

Agora que temos nossos problemas resolvidos, vamos tentar melhorar um pouco esse código?

 

Melhorando o código

 

Agora que temos nosso aplicativo funcionando corretamente, chegou a hora de pensar em melhorar o código. Tanto em funcionalidades como organização.

Escutando um novo evento

Primeiramente, gostaria de adicionar uma nova opção de ação: quando um atendente se conectar à sala, gostaria de enviar uma mensagem informando que o agente X entrou na conversa. Para isso temos o evento PostLivechatAgentAssigned . Implementando a interface IPostLivechatAgentAssigned vamos adicionar o método executePostLivechatAgentAssigned com a seguinte lógica:

Dessa vez acho que o código já está mais claro, pois tudo nele já fizemos anteriormente. Mas vamos mais uma vez entender cada ação:

  • A linha 8 verifica se não é uma sala de livechat. Caso não seja, retorna para sair do método.
  • A linha 12 busca pelo usuário do aplicativo, pois será ele o autor da mensagem.
  • Alinha 14 resgata o criador de mensagens e começa a construir a que será enviada. Define a sala como sendo onde ocorreu o evento, a mensagem informando o atendente que se conectou e o usuário resgatado anteriormente como o autor.
  • A linha 20 finaliza enviando a mensagem.

Com isso funcionando, pode ser que você pense: será que essa funcionalidade não substitui os outros dois eventos que criamos? Já que quando um novo agente se conectar, seu nome será informado. Inutilizando assim a mensagem de transferência e a adição do nome do atendente ao início das mensagens.

Entendendo as configurações

Bom, para alguns pode ser que inutilize, para outros pode ser ainda interessante. E como agir nesses casos? Deixar essas opções de forma que possam ser habilitadas ou não!

A classe App (Aquele que herdamos no ponto de entrada da nossa aplicação) possui um método que pode ser sobrescrito chamado extendConfiguration. Esse método recebe como parâmetro do tipo IConfigurationExtend. Esse tipo possui algumas propriedades capazes de adicionar funcionalidades extras, sendo elas:

  • Customizar as requisições HTTP;
  • Adicionar configurações ao App;
  • Declarar comandos (Que podem ser chamas pelo chat digitando / + seu nome e passar parametros);
  • Declarar APIs;
  • Declarar componentes externos;
  • Declarar tarefas agendadas.

Nesse momento vamos nos atentar à adição de configurações. Uma configuração é um objeto do tipo ISetting. Essa interface possui alguns parâmetros obrigatórios, sendo eles:

  • id – Um identificar da configuração;
  • type – O tipo do valor da configuração. Podendo ser: boolean, code, color, font, int, select, string;
  • packageValue – Valor padrão da configuração;
  • required – Se é obrigatória ou não;
  • public – Se pública, todos podem ver a configuração, não apenas administradores;
  • i18nLabel – Uma legenda para configuração para possa ser exibida no formulário de edição.

Dentre os parâmetros opcionais gostaria de destacar o i18nDescription pois permite adicionar uma descrição mais completa a configuração, também sendo exibido no formulário de edição.

Veja que dois dos campos citados, e mais alguns que não citamos pois são opcionais, possui o prefixo i18n. Isso significa que esses campos podem ser internacionalizados. Palavra difícil que indica que eles são traduzíveis. Mas como fazer isso?

Na raiz do seu projeto você deve criar uma pasta nomeada i18n. Dentro dela,então criar um arquivo json para cada linguagem que deseja fornecer traduções, sendo o nome do arquivo a sigla para a língua. Ex: arquivo pt.json para português e en.json para inglês.

Dentro desses arquivos, a chave de cada tradução será o valor definido por você e que colocou no campo traduzível, e consequentemente o valor seria o texto. Por exemplo, vamos supor que tenha uma configuração que se chame “Habilitar opção A”. No campo i18nLabel da configuração você pode definir com o valor “habilitar_opcao_a”. E no arquivo de tradução fatia algo como:

{
        "habilitar_opcao_a": "Habilitar Opção A"
}

E para cada língua, criaria o arquivo com a mesma estrutura e mudaria apenas a tradução.

Habilitando/desabilitando ações

Com tudo isso entendido, vamos alterar nosso código para atender a essa necessidade.

Já começando a organizar em estrutura de pastas, vou criar uma pasta na raiz chamada src que vai conter todo nosso código. Dentro dela vou criar ainda a pasta settings para concentrar todos os possíveis arquivos de configurações. E dentro ainda dessa pasta, criar o arquivo settings.ts. Nele vou definir a estrutura de nossas configurações. Ficando algo como:

Vamos entender um pouco desse arquivo:

  • Nas linhas 7, 8 e 9 foram criadas constantes que vão representar as configurações que vamos definir. É interessante definir dessa forma pois tanto nesse arquivo será reaproveitado para definir a configuração, como externamente para resgatar seu valor;
  • Na linha 11 é iniciado uma array contendo as configurações que desejamos criar. O id delas é definido a partir das contantes, todas do tipo booleana com valor padrão ativado, obrigatória e privada. Além disso, é definido um rótulo e uma descrição a partir de um padrão: “config_” + id + “_label” ou “_description”;
  • Na linha 41 defino uma função capaz de receber o id de uma configuração e retornar o seu valor. É útil para não ficarmos repetindo essa lógica em todo lugar.

Além disso, criei um arquivo nomeado pt.json com os dados:

{
        "config_notify_agent_assigned_label":"Notificação agente atríbuido",
        "config_notify_agent_assigned_description":"Notificar usuário (cliente) do Omnichannel quando um novo agente for atribudi pra sala.",
        "config_notify_room_transferred_label":"Notificação transferência de sala",
        "config_notify_room_transferred_description":"Notificar usuário (cliente) do Omnichannel quando a sala for transferida.",
        "config_message_add_agent_name_label":"Informar nome do atendente",
        "config_message_add_agent_name_description":"Na primeira mensagem de uma sequência de mensagens do atendente, adicionar antes do texto o seu nome para informar com quem está sendo realizado a conversa."
}

Agora basta no arquivo principal, sobrescrever o método extendConfiguration e declarar as configurações.

Agora com as configurações definidas, só precisamos utilizá-las!

Em cada método, antes de executar o código, adicionei a seguinte ação:

const allowExecute = await getSettingValue(read.getEnvironmentReader(), CONFIG_NOTIFY_AGENT_ASSIGNED);

if (!allowExecute) {
        return;
}

Claro, mudando em cada método para o ID de configuração respectivo. Assim posso em tempo de execução, sem precisar mudar código e fazer deploy, ativar ou desativar as funcionalidades que criei. Ao detalhar uma aplicação, terá acesso as suas configurações:

 

Uma outra melhoria que fiz foi criar arquivos de serviços isolados para cada tipo de evento e extrai o código para lá. Deixamos assim no arquivo principal apenas a declaração dos métodos das interfaces e a chamada para serviço respectivo.

Com isso  o arquivo principal ficou mais enxuto. Visto que sempre será quem herdará novas interfaces e novos métodos, é importante sempre tentar extrair seu código para classes auxiliares quando possível.

 

Oque vem depois?

Bom, agora que já tivemos uma introdução ao RocketChat Apps e criamos nosso primeiro aplicativo, você já tem a noção básica por onde começar a criar os seus próprios aplicativos. Você pode encontrar o código do aplicativo criado nesse post no nosso Github.

Encontre um problema, levante soluções e olhe a documentação a fim de encontrar algo que te auxilie na solução.

Em breve podemos trazer mais conteúdos sobre desenvolvimento de aplicativos, dessa vez com possibilidades mais complexas.

 

 

Líder em Treinamento e serviços de Consultoria, Suporte e Implantação para o mundo open source. Conheça nossas soluções:

CURSOSCONSULTORIA

Anterior Guia completo: Aprenda a usar o Gitlab e suas funcionalidades DevOps
Próxima Entenda o Celery: a biblioteca Python para filas de tarefas

About author

Caique Portela
Caique Portela 8 posts

Caique Portela é formado em Análise e Desenvolvimento de Sistemas na FATEC e técnico em Redes de Computadores pelo CEAP. Possui mais de 7 anos de experiência com TI, sendo pelo menos 3 anos atuando diretamente como desenvolvedor. Já trabalhou como suporte técnico e consultor de infraestrutura Linux, hoje atua como desenvolvimento e treinamento na 4Linux.

View all posts by this author →

Você pode gostar também

Cloud

Criando e atualizando um cluster Kubernetes com kubeadm

Introdução Este post faz parte de uma série sobre a certificação CKA – Certified Kubernetes Administrator [1]. Dentre os tópicos que possuem mais peso na certificação, o item Cluster Architeture,

Infraestrutura TI

Monitoramento de Dados: Como Utilizar o PostgreSQL e o PgPool com Zabbix

Com o mercado tecnológico cada vez mais crescente, o volume de dados aumenta significativamente a cada minuto, e esse volume é armazenado em diversos sistemas de banco de dados distribuídos.

Desenvolvimento

Guia Fundamental: Como Utilizar o Bootstrap para Sites Responsivos

O Bootstrap é um framework front-end utilizado para criar sites responsivos. Neste pequeno artigo ofereço um guia fundamental sobre este recurso para ajudá-lo deixar seus sites compatíveis com qualquer dimensão