Páginas filhas
  • Desenvolvimento de APIs para o produto Logix

Versões comparadas

Chave

  • Esta linha foi adicionada.
  • Esta linha foi removida.
  • A formatação mudou.
Informações
titleÍndice

Índice


Introdução


     O desenvolvimento de APIs permite a exposição e o consumo de dados para integração (front-end, portais, customizações, etc) com o back-end do produto Logix, de maneira segura e padronizada.

     A estrutura de integração de APIs Logix suporta o envio de requisições no estilo de arquitetura REST com o desenvolvimento da regra de negócio em 4GL e está disponível para utilização conforme apresentado no quadro abaixo:

Informações
titleMatriz de Evolução
Versão / ReleaseFuncionalidade
12.1.22

Requisições através dos verbos GET / POST / PUT / DELETE utilizando o contexto /restlogix

Classe utilitária para tratamento das respostas da requisição (response) em 4GL.

12.1.24

Requisições através dos verbos GET / POST / PUT / DELETE utilizando o contexto /api

Classe utilitária para tratamento das respostas da requisição (response) em 4GL.

O contexto /restlogix está em processo de depreciação.


Formato URL


      O Guia de Implementação de API TOTVS define que o formato das URIs dos endpoints devem conter o nome do produto, o módulo, a versão da API e o recurso alvo da funcionalidade em questão. Tomando como exemplo o endpoint de integração do recurso de "Dimensões" do módulo de "WMS" do produto "Logix", a URI básica deste serviço deve ser: /api/wms/v1/dimensoes


Aviso

A URL definida pelo Padrão de API TOTVS segue o seguinte formato:

/api/<módulo>/<versão API>/<recurso>/

Informações que forem passadas após o recurso, serão tratadas como parâmetros PATH.


Serviço 4GL


     Para "publicar" a funcionalidade 4GL basta criar o programa (.4gl) com o seguinte caminho: <módulo>/api/<versão API>/<recurso>.4gl. 

     A sub-pasta "api" passa a concentrar todas as funcionalidades de integração dos módulos existentes. Outros caminhos e parâmetros podem ser adicionados a URL, mas sempre de acordo com o Guia de Implementação de APIs.

Informações
titleImportante

Os programas 4GL disponibilizados, deverão seguir o padrão de localização abaixo:

Exemplos:

Objeto de NegócioFunção de roteamento
\suprimentos\obrigacoes_fiscais\api\v1\transportadoras.4globf_v1_transportadoras
\suprimentos\suprimentos\api\v1\estoque.4glsup_v1_estoque
\adm_producao\manufatura\api\v1\apontamento_horas.4glman_v1_apontamento_horas
Nota
titleImportante

Para desenvolvimentos específicos é preciso adicionar o sufixo "_espec" ao nome do recurso para que não gere conflitos com as APIs padrões disponibilizadas pelo produto.

Exemplo: obf_v1_transportadoras_espec.4gl


       Dentro do programa 4GL a definição da função principal (roteadora) é de fundamental importância, pois é ela que será primeiramente chamada e que definirá como será a execução das outras funções com base na requisição solicitada. Segue abaixo um exemplo de definição:

Exemplo de definição de funções e como será realizada a requisição web de execução destas funções:

FunçãoRequisição

FUNCTION obf_v1_transportadoras()

GET/POST/PUT/DELETE /api/obf/v1/transportadoras

FUNCTION sup_v1_estoque()GET/POST/PUT/DELETE /api/sup/v1/estoque
FUNCTION man_v1_apontamento_horas()GET/POST/PUT/DELETE /api/man/v1/apontamento_horas


Abaixo um exemplo de recurso desenvolvido em 4GL para ser utilizado junto ao serviço de API:

Bloco de código
languageerl
DATABASE Logix

#------------------------------#
 FUNCTION wms_v1_dimensoes()
#------------------------------#
 	CALL _ADVPL_create_rest_logix_routes()
 	
 	CALL _ADVPL_add_rest_logix_routes("GET",                      #--# Método #--#
 	                                  "/normal/*/",               #--# Path Param a ser filtrado #--#
 	                                  "fields=*",                 #--# QueryParam a ser filtrado #--#
 	                                  "wms_v1_get_dimensoes_normal")  #--# Função a ser executada #--#
 	                                  
    CALL _ADVPL_add_rest_logix_routes("GET",                           
 	                                  "/*",                            
 	                                  "order=dimensao",                
 	                                  "wms_v1_get_dimensoes_ordenado")  
 	
 	CALL _ADVPL_add_rest_logix_routes("GET",                          
 	                                  "/*",                           
 	                                  "",                             
 	                                  "wms_v1_get_dimensoes")        
 	                             
 	CALL _ADVPL_add_rest_logix_routes("POST",                        
 	                                  "/*",                         
 	                                  "",                           
 	                                  "wms_v1_update_dimensoes")       
 	                                  
 	CALL _ADVPL_add_rest_logix_routes("PUT",                           
 	                                  "/*",                            
 	                                  "",                             
 	                                  "wms_v1_update_parc_dimensoes")  
 	                                  
    CALL _ADVPL_add_rest_logix_routes("DELETE",                       
 	                                  "/*",                          
 	                                  "",                            
 	                                  "wms_v1_delete_dimensoes")   
END FUNCTION

#------------------------------------------------------#
 FUNCTION wms_v1_get_dimensoes_normal(l_json_reference)
#------------------------------------------------------#
	DEFINE l_json_reference VARCHAR(10)
	DEFINE l_json VARCHAR(1000)
	
	.
	.
	.

	RETURN l_json
END FUNCTION


#-------------------------------------------------------#
 FUNCTION wms_v1_get_dimensoes_ordenado(l_json_reference)
#-------------------------------------------------------#
	DEFINE l_json_reference VARCHAR(10)
	DEFINE l_json VARCHAR(1000)
	
	.
	.
	.

	RETURN l_json
END FUNCTION


    No exemplo acima, temos os seguintes pontos:


Bloco de código
languageerl
FUNCTION wms_v1_dimensoes()

    É a função roteadora, que será executada pelo Logix REST quando feito uma requisição que combine com o módulo, a versão e o recurso da função.


Bloco de código
languageerl
CALL _ADVPL_create_rest_logix_routes()

    Inicia a técnica para definição das rotas de execução das funções conforme a requisição recebida.


Bloco de código
languageerl
CALL _ADVPL_add_rest_logix_routes("GET", #--# Método #--#
 	                              "/normal/*/", #--# Path Param #--#
 	                              "fields=*",   #--# QueryParam #--#
 	                              "wms_v1_get_dimensoes_normal") 

    Neste exemplo está sendo definida uma rota onde toda requisição de método GET, que contenha os filtros informados, será direcionada para função wms_v1_get_dimensao_normal.


Bloco de código
languageerl
"/normal/*/",               #--# Path Param a ser filtrado #--#
"fields=*",                 #--# QueryParam a ser filtrado #--#

    Em relação ao filtro informado, serão capturadas todas as requisições que possuírem um parâmetro Path "/normal" e um parâmetro Query "fields" com qualquer conteúdo (*). 

Dica
titleDICA

Para PATH com valor obrigatório informe ?* (interrogação seguido de asterisco), que determina que o PATH tem ao menos 1 caracter qualquer (?), seguido de 0 ou mais caracteres (*).

Exemplo: "/normal/?*/", #--# Path Param obrigatório a ser filtrado #--#



Bloco de código
languageerl
CALL _ADVPL_add_rest_logix_routes("GET",                           
 	                              "/*",                            
 	                              "order=dimensao",                
 	                              "wms_v1_get_dimensoes_ordenado")

    Neste exemplo, está sendo definido uma outra rota, sendo que toda requisição de método GET, que contenha qualquer parâmetro Path ("/*"  indica "Todos Paths" ou "nenhum") e um parâmetro Query "order" com valor "dimensao" será direcionada para a função wms_v1_get_dimensoes_ordenado.


Bloco de código
languageerl
CALL _ADVPL_add_rest_logix_routes("GET",                          
 	                              "/*",                           
 	                              "",                             
 	                              "wms_v1_get_dimensoes")

    Neste exemplo, todas as requisições de método GET, que possuírem quaisquer parâmetros (Query e/ou Path) informados, serão direcionadas para a função wms_v1_get_dimensoes.


Bloco de código
languageerl
CALL _ADVPL_add_rest_logix_routes("POST",                        
 	                              "/*",                         
 	                              "",                           
 	                              "wms_v1_update_dimensoes")

    Neste exemplo, todas as requisições de método POST, que possuírem quaisquer parâmetros (Query e/ou Path) informados, serão direcionadas para a função wms_v1_update_dimensoes.


Informações

Algumas considerações sobre o uso de roteamento através da função _ADVPL_add_rest_logix_routes():

  • Os roteamentos devem ser definidos sempre do mais específico (detalhado) para o mais genérico (simples).
  • O Logix REST utiliza a função Match() do ADVPL, que basicamente permite o uso do sinal "?" (interrogação) como coringa para uma determina um único caracter obrigatório e o sinal "*" (asterisco) para um conjunto de caracteres variáveis (zero ou mais caracteres). Neste caso, quando houver necessidade de ter ao menos 1 caracter obrigatório, deve-se informar "?*" que determinará "1 ou mais caracteres" e quando informar "*" (apenas asterisco) determinará "0 ou mais caracteres". Para mais detalhes acesse a documentação da função Match().
  • Podem ser definidos um ou mais parâmetros de pesquisa utilizando a "," (vírgula) como separador e a pesquisa sempre será realizada utilizando o operador AND.
    Exemplo:
    CALL _ADVPL_add_rest_logix_routes("GET",                           
     	                              "/*",                            
     	                              "order=dimensao,cliente=*",                
     	                              "wms_v1_get_dimensoes_ordenado")

    Veja que o parâmetro QUERY foi repassado como "order=dimensao,cliente=*" onde existem 2 filtros separados por uma vírgula, sendo:

FILTRO 1: order=dimensao

FILTRO 2: cliente=*

  • Atente aos valores repassados como QUERY e PATH pois são processados como CaseSensitve.


Formato mensagem JSON


      A varíavel de referência de um objeto LJSONOBJECT, recebida pela requisição na função 4GL conterá informações completas da requisição, desde informações do HEADER, QUERY PARAMs, PATH PARAMs e o próprio PAYLOAD.

      Através desta mensagem, o desenvolvedor poderá efetuar os devidos filtros e lógicas necessárias. 


Exemplo de mensagem:

Bloco de código
languagejs
{ 
    uri: valor,
	method: GET,
    headers: {},
	pathParams: [ "param1", "param2" ],
	queryParams: { query1: valor1, query2: valor1},
	payload: {}
}


Classes utilitárias


      Com o objetivo de facilitar a manipulação dos objetos JsonObject recebidos e enviados pela API 4GL, foram desenvolvidas algumas classes de utilitários:

LJSONObject

Permite manipular o JSON recebido como parâmetro pela função. 

Acesse a documentação referente ao componente Logix LJSONOBJECT para saber mais detalhes de como manipular informações recebidas num formato JSON.


LRestLogixResponse

Trata a criação do JSON de response da requisição.

Acesse a documentação referente ao componente Logix LRESTLOGIXRESPONSE para saber mais detalhes a respeito das propriedades SET e GET disponíveis.


Exemplos de utilização

Bloco de código
languageerl
titleCriação de Response
#----------------------------------------------#
 FUNCTION wms_v1_get_dimensao(l_json_reference)
#----------------------------------------------#
	DEFINE l_json_reference VARCHAR(10)
    DEFINE l_logix_response VARCHAR(10)
	DEFINE l_json CHAR(1000)
	
    #--# Utilização do método SERIALIZE da classe LJSONOBJECT  #--#
	LET l_json = _ADVPL_get_property(l_json_reference,"SERIALIZE")
	
	#--# Criação da resposta padronizada utilizando a classe LRestLogixResponse  #--#
	LET l_logix_response = _ADVPL_create_component(NULL,"LRestLogixResponse")
    CALL _ADVPL_set_property(l_logix_response,"PAYLOAD",l_json,"payload")

	#--# Propriedades opcionais #--#
    CALL _ADVPL_set_property(l_logix_response,"MESSAGE","Erro Msg","Erro Detail", "10")
    CALL _ADVPL_set_property(l_logix_response,"STATUS",'200')

	#--# Opcional, utilizada quando o conteúdo de retorno for um JSONArray #--#
	CALL _ADVPL_set_property(l_logix_response,"HAS_NEXT",TRUE)
    
	RETURN _ADVPL_get_property(l_logix_response,"GENERATE")
END FUNCTION
Bloco de código
languageerl
titleCarregando um Array de Record
   DEFINE l_json_data          CHAR(30000)
   DEFINE l_json_reference     VARCHAR(10)    
   DEFINE l_length_ajusts      SMALLINT
   DEFINE l_status             SMALLINT
   DEFINE l_ind                SMALLINT 
   
   DEFINE ma_ajust_bxa_adt_integ  ARRAY[500] OF RECORD
	                                               cod_tip_val      LIKE ad_valores.cod_tip_val,
	                                               valor            LIKE ad_valores.valor,
	                                               num_ad_nf_orig   LIKE adiant.num_ad_nf_orig,
	                                               ser_nf           LIKE adiant.ser_nf,
	                                               ssr_nf           LIKE adiant.ssr_nf,
	                                               cod_fornecedor   LIKE adiant.cod_fornecedor,
	                                               dat_mov          LIKE mov_adiant.dat_mov      
	                                            END RECORD                  
                                                                
    //Esta informação da variável l_json_data abaixo só é atribuida manualmente 
    //como um exemplo do conteúdo que já é recebido via REST.
    LET l_json_data = 
   '{
   		"payload": {
   		    "ajustBxaAdtInteg": [
	   		    {
	   		       "codTipVal": "1",
	   		       "valor": 1000,
	   		       "numAdNfOrig": 123456,
	   		       "serNf": "AD",
	   		       "ssrNF": "A",
	   		       "codFornecedor": "12",
	   		       "datMov": "10/10/2019"
	   		    },
	   		    {
	   		       "codTipVal": "2",
	   		       "valor": 3000,
	   		       "numAdNfOrig": 654321,
	   		       "serNf": "AF",
	   		       "ssrNF": "B",
	   		       "codFornecedor": "13",
	   		       "datMov": "01/12/2018"
	   		    },
	   		    {
	   		       "codTipVal": "3",
	   		       "valor": 2000,
	   		       "numAdNfOrig": 555555,
	   		       "serNf": "AJ",
	   		       "ssrNF": "C",
	   		       "codFornecedor": "14",
	   		       "datMov": "31/10/2019"
	   		    }
   		    ]
   		}
   }'
   
   LET l_json_reference = _ADVPL_create_component(NULL, "LJSONOBJECT")
   LET l_status         = _ADVPL_get_property(l_json_reference,"ACTIVATE",l_json_data CLIPPED)
   
   LET l_length_ajusts = _ADVPL_get_property(l_json_reference,"LENGTH","payload/ajustBxaAdtInteg")
    
   FOR l_ind = 1 TO l_length_ajusts
       
       LET ma_ajust_bxa_adt_integ[l_ind].cod_tip_val    = _ADVPL_get_property(l_json_reference, "VALUE", "payload/ajustBxaAdtInteg["||l_ind||"]/codTipVal")
       LET ma_ajust_bxa_adt_integ[l_ind].valor          = _ADVPL_get_property(l_json_reference, "VALUE", "payload/ajustBxaAdtInteg["||l_ind||"]/valor")
       LET ma_ajust_bxa_adt_integ[l_ind].num_ad_nf_orig = _ADVPL_get_property(l_json_reference, "VALUE", "payload/ajustBxaAdtInteg["||l_ind||"]/numAdNfOrig")
       LET ma_ajust_bxa_adt_integ[l_ind].ser_nf         = _ADVPL_get_property(l_json_reference, "VALUE", "payload/ajustBxaAdtInteg["||l_ind||"]/serNf")
       LET ma_ajust_bxa_adt_integ[l_ind].ssr_nf         = _ADVPL_get_property(l_json_reference, "VALUE", "payload/ajustBxaAdtInteg["||l_ind||"]/ssrNF")
       LET ma_ajust_bxa_adt_integ[l_ind].cod_fornecedor = _ADVPL_get_property(l_json_reference, "VALUE", "payload/ajustBxaAdtInteg["||l_ind||"]/codFornecedor")
       LET ma_ajust_bxa_adt_integ[l_ind].dat_mov        = _ADVPL_get_property(l_json_reference, "VALUE", "payload/ajustBxaAdtInteg["||l_ind||"]/datMov")
       
       CALL CONOUT("------------------- Exibindo os valores --------------------")
       CALL CONOUT(ma_ajust_bxa_adt_integ[l_ind].cod_tip_val)
       CALL CONOUT(ma_ajust_bxa_adt_integ[l_ind].valor)
       CALL CONOUT(ma_ajust_bxa_adt_integ[l_ind].num_ad_nf_orig)
       CALL CONOUT(ma_ajust_bxa_adt_integ[l_ind].ser_nf)
       CALL CONOUT(ma_ajust_bxa_adt_integ[l_ind].ssr_nf)
       CALL CONOUT(ma_ajust_bxa_adt_integ[l_ind].cod_fornecedor)
       CALL CONOUT(ma_ajust_bxa_adt_integ[l_ind].dat_mov)
   
   END FOR
Bloco de código
languageerl
titleCarregar QueryParam e PathParam
#--------------------------------------------------#
 FUNCTION vdp_v1_clientes_nacionais(l_json_reference)
#--------------------------------------------------#
    DEFINE l_json_reference VARCHAR(10)
    DEFINE l_logix_response VARCHAR(10)
	DEFINE l_query_param CHAR(200)
	DEFINE l_path_param  CHAR(200)
	DEFINE l_json_retorno CHAR(1000)
	
    #--# Carrega o valor da primeira entrada do Query Param #--# 
    #--# Exemplo de QueryParams ?order=dimensao&cliente=1234: '[["order","dimensao"],["cliente","1234"]]'
    #--# No exemplo abaixo, a busca acontece no primeiro QueryParam (order) buscando o nome dele (order) #--#
	LET l_query_param = _ADVPL_get_property(l_json_reference,"VALUE","queryparams[1][1]") 
    #--# Neste exemplo, a busca acontece no primeiro QueryParam (order) buscando o valor dele (dimensao) #--#
	LET l_query_param = _ADVPL_get_property(l_json_reference,"VALUE","queryparams[1][2]") 
    #--# No exemplo abaixo, a busca acontece no segundo QueryParam (cliente) buscando o nome dele (cliente) #--#
	LET l_query_param = _ADVPL_get_property(l_json_reference,"VALUE","queryparams[2][1]") 
    #--# Neste exemplo, a busca acontece no primeiro QueryParam (cliente) buscando o valor dele (1234) #--#
	LET l_query_param = _ADVPL_get_property(l_json_reference,"VALUE","queryparams[2][2]")
	
	#--# Carrega o primeiro Path Param #--# 
    #--# Exemplo: '{"pathparams":["path1","path2"]}'
	LET l_path_param = _ADVPL_get_property(l_json_reference,"VALUE","pathparams[1]") 
	
	LET l_json_retorno = '{"queryParam":"',LOG_alltrim(l_query_param),'","pathParam":"',LOG_alltrim(l_path_param),'"}'
	
	#--# Executar a regra de negócio neste ponto #--# 
	LET l_logix_response = _ADVPL_create_component(NULL,"LRestLogixResponse")
    CALL _ADVPL_set_property(l_logix_response,"PAYLOAD",l_json_retorno,"payload")
    CALL _ADVPL_set_property(l_logix_response,"STATUS",'200')
    
	RETURN _ADVPL_get_property(l_logix_response,"GENERATE")
END FUNCTION