Páginas filhas
  • Integração RM x SmartLink Behavior Sharing - Visão desenvolvedor

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.

Índice
maxLevel2

01. Apresentação

Este documento técnico tem por objetivo auxiliar os desenvolvedores dos segmentos na criação de classes que "publicam" e/ou "consomem" mensagens através da plataforma Totvs SmartLink.

02. Responsabilidades

O desenvolvimento e manutenção dos publicadores e consumidores de mensagens são de responsabilidade das equipes de segmentos. Os artefatos genéricos que controlam a fila de execução das mensagens são de responsabilidade da equipe de Framework.

03. Implementando um publicador de mensagens

Para que uma classe seja um publicador de mensagens,  deve-se atender aos pré-requisitos listados abaixo:

Pré-requisitos

  1. Criar um projeto (classLibrary .NET) na solution do segmento em questão, com o seguinte padrão de nome: RM.[Segmento].XXX.SmartLink.Service.
    1. Exemplo: RM.Glb.SGDP.SmartLink.Service.dll
  2. Adicionar referência para a dll "RM.Lib.SmartLink.dll";
  3. Criar uma classe que receba em seu construtor instâncias das seguintes interfaces:

    1. "IRMSSmartLinkPublisherService": interface de serviço responsável em incluir a mensagem na fila do RM.SmartLink.Client.

    2. "IRMSLogger": serviço usado para incluir logs relacionados à regra de negócio em questão. 

      Informações

      Todos os logs adicionados nesse serviço serão gravados automaticamente na tabela "GTotvsLinkLog"

    3. Para publicar uma mensagem na fila do RM.SmartLink.Client, basta chamar o método "AddMessage" da instância de interface "IRMSSmartLinkPublisherService". Os seguinte dados devem ser enviados através de uma classe de parâmetros do tipo "SmartLinkPublisherAddMessageParams":

      PropriedadeDescrição
      CommandNome do comando a ser incluído na fila do SmartLink. Ex: SGDPUpdateTenantMetada
      CorrelatedIdPode ser ser enviado nessa propriedade um identificador (guid) que correlacionam mensagens de envio e resposta.
      Data

      Dados da mensagem contendo informações relacionadas ao negócio. Pode ser em qualquer formato (json, xml, etc)  desde que o consumidor consiga interpretá-lo.

      RouterMessage

      Rota de envio da mensagem. Será concatenada ao endpoint do serviço do Totvs SmartLink.Server. ex: /api/v1/link/send/SGDPMaskResponse/SGDP

      Na versão 12.1.2402, que utiliza api REST para envio de mensagem, o valor do Audience é definido no final do RouterMessage. No caso desse exemplo o Audience é o SGDP.
      A partir da versão 12.1.2406 não é necessário passar o RouterMessage como parâmetro pois utiliza o protocolo gRPC para envio de mensagem.

      Audience

      Código do TOTVS App específico que irá receber a mensagem.

      Para versões 12.1.2406 e acima, que já utiliza o gRPC para envio de mensagem, o Audience deve ser passado por esse parâmetro.

  4. Diagrama de classes contendo um publicador de mensagem de exemplo:


Image Added


   5. Diagrama de sequência contendo um exemplo do ciclo de vida da inclusão da mensagem "SGDPTenantMetada"

Image Added

   6. Código fonte de exemplo (extraído da classe "GlbSGDPPublisherMessageService" localizada na solution de Globais, projeto "RM.Glb.SGDP.SmartLink.Service". Esse exemplo é para versões a partir da 12.1.2406 que já utiliza o protocolo gRPC.

Bloco de código
languagec#
firstline1
linenumberstrue
using RM.Glb.SGDP.SmartLink.Service.Domain;
using RM.Glb.SGDP.SmartLink.Service.Domain.Interfaces;
using RM.Lib.Log;
using RM.Lib.SmartLink.Domain;
using RM.Lib.SmartLink.Domain.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;

namespace RM.Glb.SGDP.SmartLink.Service
{
  /// <summary>
  /// SGDP Publisher Message Service class
  /// </summary>
  public class GlbSGDPPublisherMessageService : IGlbSGDPPublisherMessageService
  {
    private readonly IGlbSGDPResolverService _sgdpResolverService;
    private readonly IRMSSmartLinkPublisherService _smartLinkPublisherService;
    private readonly IRMSLogger _logService;

    private const string ctSGDPAudience = "SGDP"; // <-- SEU CÓDIGO DE AUDIENCE DEVE VIR AQUI. NO CASO DESSE EXEMPLO É SGDP. OBTER O CÓDIGO COM A EQUIPE DE TOTVS APPS.

    private const string ctSGDPUpdateTenantMetadata = "SGDPUpdateTenantMetadata";

    private const string ctSGDPDataCommand = "SGDPDataCommand";
    private const string ctSGDPResponseDataCommand = "SGDPDataResponse";

    private const string ctSGDPMaskCommand = "SGDPMaskCommand";
    private const string ctSGDPResponseMaskCommand = "SGDPMaskResponse";

    private const string ctSGDPLogsCommand = "SGDPLogsCommand";
    private const string ctSGDPResponseLogCommand = "SGDPLogsResponse";

    private const string ctSmartLinkSetupCommand = "setup";
    private const string ctSmartLinkUnSetupCommand = "unSetup";

    /// <summary>
    /// Constructor
    /// </summary>
    /// <param name="smartLinkPublisherService"></param>
    /// <param name="sgdpResolverService"></param>
    /// <param name="logService"></param>
    public GlbSGDPPublisherMessageService(IRMSSmartLinkPublisherService smartLinkPublisherService,
      IGlbSGDPResolverService sgdpResolverService,
      IRMSLogger logService)
    {
      _smartLinkPublisherService = smartLinkPublisherService;
      _sgdpResolverService = sgdpResolverService;
      _logService = logService;
    }

    /// <summary>
    /// Adiciona uma mensagem de reposta para o comando "SGDPResponseDataCommand".
    /// </summary>
    /// <param name="parms"></param>
    public void AddResponseDataCommand(AddResponseDataCommandParms parms)
    {
      SmartLinkPublisherAddMessageParams parPublisher = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSGDPResponseDataCommand,
        CorrelatedId = parms.CorrelatedId,
        Data = parms.Message,
        Audience = ctSGDPAudience
      };

      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPResponseDataCommandGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parPublisher);
    }

    /// <summary>
    /// Adiciona uma mensagem de reposta para o comando "SGDPResponseMaskCommand".
    /// </summary>
    /// <param name="parms"></param>
    public void AddResponseMaskCommand(AddResponseMaskCommandParms parms)
    {
      SmartLinkPublisherAddMessageParams parPublisher = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSGDPResponseMaskCommand,
        CorrelatedId = parms.CorrelatedId,
        Data = parms.Message,
        Audience = ctSGDPAudience
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPResponseMaskCommandGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parPublisher);
    }
    /// <summary>
    /// Adiciona uma mensagem de reposta para o comando "SGDPResponseLogCommand".
    /// </summary>
    /// <param name="parms">Parms</param>
    public void AddResponseLogCommand(AddResponseLogCommandParms parms)
    {
      SmartLinkPublisherAddMessageParams parPublisher = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSGDPResponseLogCommand,
        CorrelatedId = parms.CorrelatedId,
        Data = parms.Message,
        Audience = ctSGDPAudience
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPResponseLogCommandGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parPublisher);
    }

    /// <summary>
    /// Adiciona uma mensagem para envio de um comando de sgldpdateapplicationmetada.
    /// </summary>
    /// <param name="parms"></param>
    public void AddMessageUpdateTenantMetadata(AddMessageUpdateTenantMetadataParms parms)
    {
      try
      {
        if (string.IsNullOrEmpty(parms.TenantId))
        {
          _logService.NotifyLogWarning(Properties.Resources.sSGDPTenantMetadataInvalidSGDPUpdateTenantMetadata);
          return;
        }
        List<string> jsons = GetUpdateTenandMetadata(parms.TenantId);
        foreach (string json in jsons)
        {
          SmartLinkPublisherAddMessageParams smartLinkParms = new SmartLinkPublisherAddMessageParams
          {
            Command = ctSGDPUpdateTenantMetadata,
            CorrelatedId = "",
            Data = json,
            Audience = ctSGDPAudience
          };
          _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPTenantMetadaGravadoNaFila);
          _smartLinkPublisherService.AddMessage(smartLinkParms);
        }
      }
      catch (Exception ex)
      {
        _logService.NotifyLogWarning(ex, Properties.Resources.sconTotvsAppErroAoEnviarSGDPUpdateTenantMetadata);
      }
    }
    private List<string> GetUpdateTenandMetadata(string tenantId)
    {
      return _sgdpResolverService.GetSGDPTenantMetadata(
        new MetadataTenantParms() { TenantId = tenantId }
        )?.JsonsResult;
    }

    /// <summary>
    /// Adiciona mensagem UnSetup
    /// </summary>
    public void AddMessageUnSetup()
    {
      var pendingMessages = _smartLinkPublisherService.GetPendingMessages();
      var messagesSGDP =
        pendingMessages?.Where(y => y.TypeEvent.ToUpper() == ctSGDPUpdateTenantMetadata.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPDataCommand.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPMaskCommand.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPLogsCommand.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPResponseDataCommand.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPResponseMaskCommand.ToUpper()).ToList();

      if (messagesSGDP != null)
      {
        foreach (SmartLinkMessageEntity model in messagesSGDP)
        {
          _smartLinkPublisherService.RemoveMessageById(model.Id);
        }
      }

      SmartLinkPublisherAddMessageParams parms = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSmartLinkUnSetupCommand,
        CorrelatedId = "",
        Audience = ctSGDPAudience,
        Data = _sgdpResolverService.GetSGDPSetup().JsonResult
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppUnSetupGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parms);
    }

    /// <summary>
    /// Adiciona messagem setup
    /// </summary>
    public void AddMessageSetup()
    {
      SmartLinkPublisherAddMessageParams parms = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSmartLinkSetupCommand,
        CorrelatedId = "",
        Audience = ctSGDPAudience,
        Data = _sgdpResolverService.GetSGDPSetup().JsonResult
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSetupGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parms);
    }
  }
}

7. Código fonte de exemplo (extraído da classe "GlbSGDPPublisherMessageService" localizada na solution de Globais, projeto "RM.Glb.SGDP.SmartLink.Service". Esse exemplo é para a versão 12.1.2402 que ainda utiliza api REST para envio de mensagem. Nesse caso o Audience é definido no final do endpoint passado como parâmetro para o RouterMessage.

Bloco de código
languagec#
firstline1
linenumberstrue
using RM.Glb.SGDP.SmartLink.Service.Domain;
using RM.Glb.SGDP.SmartLink.Service.Domain.Interfaces;
using RM.Lib.Log;
using RM.Lib.SmartLink.Domain;
using RM.Lib.SmartLink.Domain.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;

namespace RM.Glb.SGDP.SmartLink.Service
{
  public class GlbSGDPPublisherMessageService : IGlbSGDPPublisherMessageService
  {
    private readonly IGlbSGDPResolverService _sgdpResolverService;
    private readonly IRMSSmartLinkPublisherService _smartLinkPublisherService;
    private readonly IRMSLogger _logService;

    private const string ctSGDPUpdateTenantMetadataEndPoint = "/api/v1/link/send/SGDPUpdateTenantMetadata/SGDP";
    private const string ctSGDPDataResponseEndPoint = "/api/v1/link/send/SGDPDataResponse/SGDP";

    private const string ctSGDPUpdateTenantMetadata = "SGDPUpdateTenantMetadata";

    private const string ctSGDPDataCommand = "SGDPDataCommand";
    private const string ctSGDPResponseDataCommand = "SGDPDataResponse";

    private const string ctSGDPMaskResponseEndPoint = "/api/v1/link/send/SGDPMaskResponse/SGDP";
    private const string ctSGDPMaskCommand = "SGDPMaskCommand";
    private const string ctSGDPResponseMaskCommand = "SGDPMaskResponse";

    private const string ctSGDPLogResponseEndPoint = "/api/v1/link/send/SGDPLogsResponse/SGDP";
    private const string ctSGDPLogsCommand = "SGDPLogsCommand";
    private const string ctSGDPResponseLogCommand = "SGDPLogsResponse";

    private const string ctSmartLinkSetupCommand = "setup";
    private const string ctSmartLinkUnSetupCommand = "unSetup";


    public GlbSGDPPublisherMessageService(IRMSSmartLinkPublisherService smartLinkPublisherService,
      IGlbSGDPResolverService sgdpResolverService,
      IRMSLogger logService)
    {
      _smartLinkPublisherService = smartLinkPublisherService;
      _sgdpResolverService = sgdpResolverService;
      _logService = logService;
    }

    /// <summary>
    /// Adiciona uma mensagem de reposta para o comando "SGDPResponseDataCommand".
    /// </summary>
    /// <param name="parms"></param>
    public void AddResponseDataCommand(AddResponseDataCommandParms parms)
    {
      SmartLinkPublisherAddMessageParams parPublisher = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSGDPResponseDataCommand,
        CorrelatedId = parms.CorrelatedId,
        Data = parms.Message,
        RouterMessage = ctSGDPDataResponseEndPoint
      };

      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPResponseDataCommandGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parPublisher);
    }

    /// <summary>
    /// Adiciona uma mensagem de reposta para o comando "SGDPResponseMaskCommand".
    /// </summary>
    /// <param name="parms"></param>
    public void AddResponseMaskCommand(AddResponseMaskCommandParms parms)
    {
      SmartLinkPublisherAddMessageParams parPublisher = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSGDPResponseMaskCommand,
        CorrelatedId = parms.CorrelatedId,
        Data = parms.Message,
        RouterMessage = ctSGDPMaskResponseEndPoint
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPResponseMaskCommandGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parPublisher);
    }
    /// <summary>
    /// Adiciona uma mensagem de reposta para o comando "SGDPResponseLogCommand".
    /// </summary>
    /// <param name="parms">Parms</param>
    public void AddResponseLogCommand(AddResponseLogCommandParms parms)
    {
      SmartLinkPublisherAddMessageParams parPublisher = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSGDPResponseLogCommand,
        CorrelatedId = parms.CorrelatedId,
        Data = parms.Message,
        RouterMessage = ctSGDPLogResponseEndPoint
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPResponseLogCommandGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parPublisher);
    }

    /// <summary>
    /// Adiciona uma mensagem para envio de um comando de sgldpdateapplicationmetada.
    /// </summary>
    public void AddMessageUpdateTenantMetadata(AddMessageUpdateTenantMetadataParms parms)
    {
      try
      {
        if(string.IsNullOrEmpty(parms.TenantId))
        {
          _logService.NotifyLogWarning(Properties.Resources.sSGDPTenantMetadataInvalidSGDPUpdateTenantMetadata);
          return;
        }
        List<string> jsons = GetUpdateTenandMetadata(parms.TenantId);
        foreach (string json in jsons)
        {
          SmartLinkPublisherAddMessageParams smartLinkParms = new SmartLinkPublisherAddMessageParams
          {
            Command = ctSGDPUpdateTenantMetadata,
            CorrelatedId = "",
            Data = json,
            RouterMessage = ctSGDPUpdateTenantMetadataEndPoint
          };
          _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSGDPTenantMetadaGravadoNaFila);
          _smartLinkPublisherService.AddMessage(smartLinkParms);
        }
      }
      catch (Exception ex)
      {
        _logService.NotifyLogWarning(ex, Properties.Resources.sconTotvsAppErroAoEnviarSGDPUpdateTenantMetadata);
      }
    }
    private List<string> GetUpdateTenandMetadata(string tenantId)
    {
      return _sgdpResolverService.GetSGDPTenantMetadata(
        new MetadataTenantParms() { TenantId = tenantId }
        )?.JsonsResult;
    }

    public void AddMessageUnSetup()
    {
      var pendingMessages = _smartLinkPublisherService.GetPendingMessages();
      var messagesSGDP =
        pendingMessages?.Where(y => y.TypeEvent.ToUpper() == ctSGDPUpdateTenantMetadata.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPDataCommand.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPMaskCommand.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPLogsCommand.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPResponseDataCommand.ToUpper() &&
                                              y.TypeEvent.ToUpper() == ctSGDPResponseMaskCommand.ToUpper()).ToList();

      if (messagesSGDP != null)
      {
        foreach (SmartLinkMessageEntity model in messagesSGDP)
        {
          _smartLinkPublisherService.RemoveMessageById(model.Id);
        }
      }

      SmartLinkPublisherAddMessageParams parms = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSmartLinkUnSetupCommand,
        CorrelatedId = "",
        Data = _sgdpResolverService.GetSGDPSetup().JsonResult
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppUnSetupGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parms);
    }

    public void AddMessageSetup()
    {
      SmartLinkPublisherAddMessageParams parms = new SmartLinkPublisherAddMessageParams
      {
        Command = ctSmartLinkSetupCommand,
        CorrelatedId = "",
        Data = _sgdpResolverService.GetSGDPSetup().JsonResult
      };
      _logService.NotifyLogInfo(Properties.Resources.sconTotvsAppSetupGravadoNaFila);
      _smartLinkPublisherService.AddMessage(parms);
    }
  }
}

04. Implementando um consumidor de mensagens

Para que uma classe seja um consumidor de mensagens no SmartLink, deve-se atender aos pré-requisitos listados abaixo:

Pré-requisitos

  1. Criar um projeto (classLibrary .NET) na solution do segmento em questão, com o seguinte padrão de nome: RM.[Segmento].XXX.SmartLink.Service.
    1. Exemplo: RM.Glb.SGDP.SmartLink.Service.dll
  2. Adicionar referência para a dll "RM.Lib.SmartLink.dll";
  3. Criar uma classe herdando da ancestral "RMSSmartLinkConsumerMessageBase".

  4. A classe "RMSSmartLinkConsumerMessageBase" herda da classe RMSObject da Lib. Consequentemente, as classes de "consumers" poderão chamar os métodos "CreateFacade" e "CreateModule" dentro de suas estruturas.
  5. Carimbar a classe com o atributo "RMSSmartLinkConsumerMessageAttr". Nesse atributo, devem ser informados os dados abaixo:

    PropriedadeDescrição
    Cod.SistemaIdentificador da aplicação
    ComandoNome do Comando da mensagem
  6. Para realização de tratamento de exceções no caso de ocorrer erro no processamento da mensagem
    Quando for considerado um erro e um nova tentativa de processamento deve ser realizada: 
    A classe RMSSmartLinkConsumerMessageException ou outra, pode ser utilizada.
    Neste caso a mensagem será reenfileirada e novas tentativas de processamento serão realizadas.

    Sendo considerado que a mensagem deve ser descartada, ou seja, esta mensagem não deve ser processada neste momento ou apenas sob intervenção manual:
    A classe RMSSmartLinkConsumerFatalException, deverá ser utilizada e informado o motivo.
    Neste caso a mensagem será colocada em um fila de espera (DLQ) e somente será reprocessada por uma intervenção manual ou será descartada.

  7. Código fonte de exemplo (extraído da classe "GlbSGDPConsumerDataCommandMessage" localizada na solution de Globais, projeto "RM.Glb.SGDP.SmartLink.Service":

    Bloco de código
    languagec#
    firstline1
    linenumberstrue
    using RM.Glb.SGDP.SmartLink.Service.Domain.Interfaces;
    using RM.Lib;
    using RM.Lib.SmartLink.Domain;
    using RM.Lib.SmartLink.Domain.Consumer;
    using RM.Lib.SmartLink.Domain.Interfaces;
    using RM.Lib.SmartLink.Domain.Publisher;
    using System;
    using System.Runtime.Serialization;
    
    namespace RM.Glb.SGDP.SmartLink.Service.Domain
    {
      /// <summary>
      /// Regras de implementação do mecanismo de processamento da mensagem SGDDataCommand do SGDP
      /// </summary>
      [RMSSmartLinkConsumerMessageAttr(CodSistema.Glb, "SGDPDataCommand")]
      public class GlbSGDPConsumerDataCommandMessage : RMSSmartLinkConsumerMessageBase
      {
        protected override ConsumerMessageExecuteResult DoExecute(string message)
        {
          logService.NotifyLogInfo(Properties.Resources.sconTotvsAppInicioExecucaoDoConsumerSGDPDataCommandConsumer);
          ConsumerMessageExecuteResult result = new ConsumerMessageExecuteResult();
          IGlbSGDPResolverService resolver = GlbSGDPResolveFactory.NewSGDPResolve(this.DBS, this.logService);
          IGlbSGDPPublisherMessageService publisher = new GlbSGDPPublisherMessageService(this.SmartLinkPublisherService, resolver, logService);
    
          ExecuteDataCommandParms par = new ExecuteDataCommandParms();
          par.Message = message;
          try
          {
            var execDataResult = resolver.ExecuteSGDPDataCommand(par);
            logService.NotifyLogInfo(Properties.Resources.sconTotvsAppJSonSGDPDataResponseCommandForamGerados, 
              Properties.Resources.sconTotvsAppQuantidadeDeJSonsGerados, execDataResult?.JsonResults?.Count);
            result.CorrelatedID = execDataResult.RequestId;
            if (execDataResult?.JsonResults != null)
            {
              foreach (var jsonResult in execDataResult.JsonResults)
              {
                AddResponseDataCommandParms parResponse = new AddResponseDataCommandParms();
                parResponse.Message = jsonResult;
                parResponse.CorrelatedId = execDataResult.RequestId;
                publisher.AddResponseDataCommand(parResponse);
              }
            }
          }
          catch(Exception ex)
          {
            logService.NotifyLogError(new GlbSGDPConsumerDataCommandMessageException(
              Properties.Resources.sconTotvsAppErroAoProcessarSGDDataCommand, ex));
          }
          logService.NotifyLogInfo(Properties.Resources.sconTotvsAppFimExecucaoDoConsumerSGDPDataCommandConsumer);
          return result;
        }
      }
    
      [Serializable]
      public class GlbSGDPConsumerDataCommandMessageException : RMSApplicationException
      {
        public GlbSGDPConsumerDataCommandMessageException() : base()
        {
        }
    
        public GlbSGDPConsumerDataCommandMessageException(string message) : base(message)
        {
        }
    
        public GlbSGDPConsumerDataCommandMessageException(string message, Exception ex) : base(message, ex, string.Empty)
        {
        }
    
        public GlbSGDPConsumerDataCommandMessageException(SerializationInfo info, StreamingContext context)
      : base(info, context)
        {
        }
      }
    }

05. Diagrama de classes

Image Added


06. DER


Image Added


Tabela
GTOTVSLINKMESSAGE

Tabela utilizada para armazenar todos as mensagens geradas e enviadas para execução pelo SmartLink.

Sempre que uma mensagem é executa com sucesso, a linha referente a mensagem é excluída

dessa tabela , deixando essa tabela sempre leve. Esse procedimento melhora a performance do processo,

visto que vários "updates" são realizados nessa tabela.Quando menor o volume de uma tabela, mais rápido

será a operação do update.

GTOTVSLINKMESSAGEEXEC

Tabela contendo todos os dados de execução de uma determinada mensagem.


GTOTVSLINKLOGTabela contendo todos os logs de execução de uma mensagem. 
  1. Visão Geral
  2. Configuração passo a passo
  3. Assistente de ativação / desativação do processo
  4. Como Funciona
  5. Visualização de Log
  6. Habilitar log TLC
  7. Mecanismo de execução da integração com o TLC

01. VISÃO GERAL 

A partir da versão 12.1.28 é possível realizar a integração do RM com o TOTVS App (Carol). 

Neste documento é descrito o processo de configuração da integração.

Informações
titleImportante

Para a ativação da integração funcionar corretamente em bases Oracle é necessário ter a versão igual ou superior ao Oracle12.2.

02. CONFIGURAÇÃO PASSO A PASSO 

Existem 2 formas de integrar o RM ambiente com o TOTVS App. 

Primeira: Com a ativação baseada no Client Id e Client Secret do RAC. Dessa forma é necessário um ambiente provisionado (RAC ou Carol). Esta é a opção indicada para novas integrações.

        Os endereços do TotvsApp devem ser recuperados pelos links abaixo:

Segunda: Com ativação baseada nas credenciais da Carol.

Os passos descritos abaixo, dentro do tópico 2,  são necessários apenas se a ativação usar as credenciais da Carol. Para ambientes provisionados (Client Id e Client Secret) o arquivo de configuração e os conceitos serão obtido automaticamente pelo sistema, de acordo com a versão do RM e não é necessário alterar os configs do ambiente.

Configuração do RM.Host.exe.config (Ativação baseada nas credenciais da Carol)

Nos arquivos de configuração do host (RM.Host.exe.config, RM.Host.Service.exe.config), adicione a TAG “FileServerPath”. Essa deverá apontar para um caminho compartilhado na rede, conforme abaixo:

Image Removed

Obs: Em ambiente 3 camadas, essa configuração deve ser realizada nas máquinas JobServer.

Verifique se o serviço do Host possui permissão de leitura nesse caminho compartilhado

Cópia dos arquivos de Conceito

Todos os arquivos de conceito, com extensão “*.concept” disponíveis no projeto, deverão ser copiados para esse diretório compartilhado. O configurador fará automaticamente o download desses arquivos e os mesmos serão gravados na tabela GTOTVSAPPCONCEITO.CONCEITO.

Aviso
titleAtenção

 A partir dos patches 12.1.28.221 e 12.1.29.151 não é necessário deixar os conceitos nesse diretório, pois serão obtidos automaticamente pelo sistema.

.Arquivo de configuração do projeto

Será disponibilizado um modelo de arquivo de configuração Json do projeto. Esse arquivo deve ter obrigatoriamente o nome de “totvsAppConfig.json”. Ele deve ser copiado para o mesmo caminho de rede descrito acima.

O correto preenchimento do mesmo é muito importante para o funcionamento do processo. Segue abaixo uma explicação de cada item da configuração:

Arquivo exemplo: totvsAppConfig.json.

Image Removed

a) RMVersion: versão do RM (essa opção ainda não está sendo usada pelo processo);

b) ScheduleInfo: informações de periodicidade da execução do job de integração. Conforme exemplo acima, o job será executado de 5 em 5 minutos.

Opções:

• Exemplo de execução mensal: [RecurencyType = “Monthly” | DayOfMonth = 14 | Hour = 10 | Minute = 15]. Nesse caso, o job será executado todo dia 14 de cada mês às 10 horas e 15 minutos;
• Exemplo de execução semanal: [RecurrencyType = “Weekly” | Weekdays = “Monday” | Hour = 14 | Minute = 20]. Nesse caso, o job será executado toda segunda feira, às 14 horas e 20 minutos.
• Exemplo de execução horária: [RecurrencyType = “Hourly” | HoursInterval = 2 | MinutesInterval= 0]. Nesse caso, o job será executado todos os dias de 2 em 2 horas.

c) BusinessDomain: Trata-se de uma lista que contemplará todas as integrações futuras do RM com a plataforma Totvs App. No caso do consignado, iremos criar um único objeto dentro dessa lista:

i. AppId: “1” = identificador do aplicativo consignado (valor = 1);
ii. SoftDeleteTables: informe as tabelas que farão parte do soft delete, ou seja, para cada registro excluído no RM na tabela em questão, será gravada uma informação de log na tabela de softDelete da tabela em questão, EX: ppessoa_softdelete;
iii. UpdaterControlColumns: informe as tabelas que terão todos os registros do campo “RecModifiedOn” sendo atualizados para “1900/1/01” se caso ele for null;
iv. ConceptGroup: Lista contendo os grupos de conceito.

1) GroupName: Nome do grupo (essa opção ainda não está sendo usada pelo processo)
2) Concepts: grupo de conceitos contendo informações dos conceitos que participarão do processo:

a) ConceptID: guid contendo a identificação única do conceito. Com esse atributo, poderemos resolver a situação de um conceito ser utilizado por várias integrações (businessDomain). Daí ele será executado apenas uma única vez. Ex: conceito para exportação de dados da GColigada/GFilial.
b) ConceptFileName: Nome físico do arquivo de conceito “*.concept” gravado no caminho de rede compartilhado. Esse nome tem que ser exatamente igual, contendo a extensão.
ex: "ConceptFileName": "ExportacaoFuncionarios.concept",
c) ConceptVersion: guid representando a versão do arquivo de conceito. Se o conceito sofrer alguma alteração que necessita do mesmo ser baixado novamente, esse guid deverá ser modificado. (essa opção ainda não está sendo usada pelo processo)
d) ProcessingDependency: Ordem de dependência de execução dos conceito. Ou seja, o processo não poderá executar o conceito “A” se o conceito “B” não for executado com sucesso. (essa opção ainda não está sendo usada pelo processo)

03. ASSISTENTE DE ATIVAÇÃO / DESATIVAÇÃO DO PROCESSO

Permissão no item de menu

No RM, acesse a opção “Serviços globais / Perfis / Integração” e forneça as permissões para o perfil do usuário em questão.

Image Removed

Selecione o sistema “Integração / Totvs App / Ativação Totvs App” e verifique se o item de menu está habilitado.

Image Removed

Processo de ativação

Nos patches mais recentes são disponibilizados duas formas de autenticação no ambiente: Credenciais do RAC ou credenciais da Carol. Apenas 1 mecanismo de autenticação deve ser utilizado.

Selecione a opção “Consignado” para ativar o processo de integração. Outras opções de integração serão criadas futuramente. 

Na versão 12.1.28 está disponível apenas a integração com o "Consignado". A partir da versão 12.1.29 está disponível também o "Antecipa".

Image Removed

Para configuração das credenciais da Carol:

  • Informe o endereço da Carol:
    ex: https://totvstechfindev.carol.ai/
  • Informe o Id do Connector da Carol:
    ex: 09ddb0e29e604e599a1356dab2f89df9
  • Informe o token da Carol: Esse token, deverá ser recuperado do ambiente Carol, conforme este link.

Para configuração das credenciais do RAC, os dados da Carol não precisam ser preenchidos, pois o sistema usará a autenticação RAC para buscar as credenciais da Carol. 

Novas integrações devem ser configuradas com esse modelo de autenticação.

  • Informe o RAC Client Id.
  • Informe o RAC Client Secret: Essas informações de autenticação (Client Id, Client Secret) serão enviadas automaticamente para o cliente via e-mail ao ser realizado o provisionamento de seu ambiente na plataforma TotvsApp.

Após execução do processo de ativação, os seguintes procedimentos serão realizados:

  • Será gravada uma linha na tabela GTOTVSAPP contendo informações globais da configuração.
  • Os conceitos disponíveis no caminho de rede ou obtidos pelo sistema de forma automática e que estão mapeados no arquivo de configuração serão baixados e gravados na tabela “GTOTVSAPPCONCEITO”;
  • Será criado um Job agendado e recorrente para execução conforme informações de agendamento disponíveis no arquivo de config;
  • As stored Procedures de softDelete serão executadas para as tabelas mapeadas para criação automática das estruturas de softDelete;
  • As tabelas mapeadas serão atualizadas na colulna “RECMODIFIEDON” para a data “1900/01/01” em caso de possuir o valor null;

Para cada execução do job de exportação:

  • Os conceitos serão executados e os dados serão enviados para Carol conforme definido nos conceitos;
  • Será gravada uma linha na tabela “GTOTVSAPPCONCEITOHST” conforme informações de execução de cada conceito (sucesso ou erro e mensagem de erro);
  • Em caso de sucesso, a coluna “GTOTVSAPPCONCEITOHST.DATAULTEXEC” será atualizada para a data de início da execução do processo. 
  • O status da execução pode ser consultado também na coluna “GTOTVSAPPCONCEITOHST.STATUS” (1 - sucesso, 2 - falha, 0 - não executado). E na coluna "GTOTVSAPPCONCEITOHST.MENSAGEM" a mensagem do erro ou de sucesso.

12.1.33

A partir da versão 12.1.33, estará disponível apenas autenticação com as Credenciais do RAC.

Além disso, o processo de ativação irá ativar ou desativar a integração com todos os aplicativos disponíveis. Será necessário clicar no botão "Buscar Apps" antes de avançar com o processo, e será retornado todos os Apps disponíveis na Carol para a integração com ambiente RM.

Caso a integração já esteja ativa e um novo App for disponibilizado (Exemplo: SGDP), não será necessário refazer o processo de ativação.

Image Removed

Aviso
titleAtenção

Ao clicar para Buscar Apps, serão retornados somente os Apps disponíveis em cada Client Id e Client Secret. 
Ex.: Caso seja informado um Client Id e um Client Secret que possui somente o App "Consignado", só o mesmo será retornado na busca.

Image Removed

Aviso
titleAtenção

Após ativar a integração um serviço de monitoramento da saúde do processo será disparado com o intuito de manter o processo da integração executando de acordo com a configuração de periodicidade.

Ver mais em: Serviço De Monitoração Da Saúde Da Integração RM x TOTVS App

Processo de desativação

Após ativado, o usuário poderá a qualquer momento desativar o processo conforme opção abaixo:

Image Removed

O processo de desativação executará os seguintes procedimentos:

  • As informações das tabelas “GTOTVSAPP, GTOTVSAPPCONCEITO, GTOTVSAPPCONCEITOHST e GTOTVSAPPLOG " serão excluídas da base;
  • O Job Executor do processo será finalizado e excluído;
  • As tabelas de softDelete não serão mais alimentadas com informações de deleção.
Aviso
titleAtenção

Ao desativar a integração todos os serviços que monitoram a saúde do processo serão finalizados.

04. Como Funciona

Através da ferramenta de Conceito o RM envia para a Carol (Inteligência artificial da TOTVS) informações chaves baseadas nas configurações dos conceitos de cada integração.

Após a ativação da integração o processo ConTotvsAppActivatorProcess que configura a integração será disparado.

Após a execução do processo que configura a integração será iniciado o processo que Executa a integração ConTotvsAppExecutorProcess. 

Aviso
titleAtenção

Pelo fato da integração ser executada através de processos, os processos  ConTotvsAppActivatorProcess e ConTotvsAppExecutorProcess NÃO podem ser manipulados pelo usuário. 

Os menus Iniciar / Parar / Desabilitar / Reiniciar da visão de gerenciamento de Job's foram desativados. 

Os WebServices que Executam / Agendam processos também foram bloqueados.

A atividade ExecutarProcesso da Fórmula Visual também foi bloqueada para não permitir a execução dos processos citados.

Caso o ambiente utilizado seja um ambiente 3 Camadas, o processo será recorrente de acordo com a configuração de periodicidade utilizada no arquivo totvsAppConfig.json.

Caso o ambiente utilizado seja um ambiente Local, o processo não será recorrente porém o serviço de monitoramento da saúde do processo irá executar o processo em horários bem próximo da configuração de periodicidade.

Informações
titleAtenção

Para entender como funciona o serviço de Monitoramento da saúde do processo acesse : https://tdn.totvs.com/x/27EMIg

05. VISUALIZAÇÃO DE LOG

Na tabela GTOTVSAPPLOG é armazenado o log detalhado de cada execução dos conceitos e envios cadastrados na tabela GTOTVSAPPCONCEITOHST.

Informações relevantes sobre a tabela GTOTVSAPPLOG:

Na coluna Tipo, deve ser considerado:
0 - Mensagem de Sucesso
1 - Mensagem de Alerta
2 - Mensagem de Erro

Caso ocorram erros, as colunas Mensagem e Descrição podem ser consultadas para obter mais detalhes.

Exemplo de mensagens de erro:
Mensagem: Erro ao enviar dados para serviço de api!| {"code":404,"message":"HTTP 404 Not Found"}
Mensagem: Pacote '1' de '1' enviado com erro!|Erro ao enviar dados para serviço de api!| {"code":405,"message":"HTTP 405 Method Not Allowed"}

Para analisar os registros da tabela de log, é importante filtrar a busca pela chave GTOTVSAPPLOG.IDTOTVSAPPCONCEITOHST, relacionada a coluna GTOTVSAPPCONCEITOHST.ID

06. HABILITAR TLS

Em alguns casos é necessário ajustar o TLS do servidor.

Para ajustar basta criar um arquivo com a extensão .Reg e escrever o comando abaixo. Após criar o arquivo basta executar.

Aviso
titleAtenção

Antes de Executar é importante fazer o backup das todas as chaves descritas no bloco de código.

Bloco de código
Windows Registry Editor Version 5.00
; 0x00000008	Habilitar o SSL 2.0 por padrão
; 0x00000020	Habilitar SSL 3.0 por padrão
; 0x00000080	Habilitar TLS 1.0 por padrão
; 0x00000200	Habilita o TLS 1.1 por padrão
; 0x00000800	Habilitar TLS 1.2 por padrão


[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp]
     "DefaultSecureProtocols"=dword:0x00000800

[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Internet Settings\WinHttp]
     "DefaultSecureProtocols"=dword:0x00000800 

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Client] 
 "DisabledByDefault"=dword:00000000
 
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\Protocols\TLS 1.2\Server] 
 "DisabledByDefault"=dword:00000000

Ferramentas de teste de solução de problemas de conexão SSL / TLS (Totvs App precisa de TLS1.2 handshake)

Existe um troubleshooting da Microsoft abordando diversas formas de se testar a comunicação:

https://techcommunity.microsoft.com/t5/azure-paas-blog/ssl-tls-connection-issue-troubleshooting-test-tools/ba-p/2240059

Utilizando CURL

curl -v https://endpoint-registry.dev.totvs.app/api/v1/services --tlsv1.0

curl -v https://endpoint-registry.dev.totvs.app/api/v1/services --ciphers ECDHE-RSA-NULL-SHA --tlsv1.2

Image Removed

Resposta esperada (Sucesso)

Image Removed

07. MECANISMO DE EXECUÇÃO DA INTERGRAÇÃO COM O TLC

      O campo "IDJOB" já existente na tabela "GTOTVSAPP" é responsável em fazer a subida dos dados para Carol (busca do RM e envia para Carol).

      Com a nova implantação do TLC, após realizar a ativação do TotvsApp, será criado um novo IDJOB na tabela "GTOTVSAPP", mas que estará disponível no novo campo "JOIBIDTLC". Ele é o responsável por enviar mensagens localizadas na tabela "GTOTVSLINKMESSAGE" para o TotvsLinkClient. 

      A cada exportação realizada com sucesso, o dado das métricas será localizado na tabela "GTOTVSLINKMESSAGE", após o envio das métricas serem realizados com sucesso, os dados serão enviados para a tabela "GTOTVSLINKMESSAGEEXEC" e excluído na tabela "GTOTVSLINKMESSAGE"

Aviso
titleImportante

Os dois Jobs criados "IDJOB" e "JOIBIDTLC" não poderão ser desabilitados na tela "Gerenciamento de Jobs" localizado no RM.exe, visto que são Jobs de integração. Os mesmo somente serão desabilitados pelo processo de "Desativação da integração".

Image Removed

Image Removed

      Na tabela "GJOBXEXECUCAO", deve existir dois Jobs recorrentes (de acordo com a configuração de periodicidade utilizada), referentes aos Ids criados no campos "JOBID" e "JOIBIDTLC" da tabela "GTOTVSAPP".

      E ao realizar a desativação do processo, o Jobs devem ser desabilitados (status = 8).

Image Removed Image Removed

      As métricas, estão sendo enviadas para o Grafana (Uma aplicação web de análise, onde são fornecidos tabelas, gráficos, etc). 

      A execução do "JOBIDTLC", fará uma leitura na tabela "GTOTVSLINKMESSAGEe realizará o envio das métrica para o Grafana.
      Segue abaixo o exemplo da visualização das métricas dentro da aplicação:

...