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
stylecircle

Conceito

...

Uma API (acrônimo de Application Programming Interface ou Interface de Programação de Aplicação em português) é um conjunto de rotinas e padrões estabelecidos por um software para a utilização de seus recursos por aplicativos que não pretendem envolver-se em detalhes da implementação do software, mas apenas usar seus serviços. De modo geral, uma API é composta por uma série de funções acessíveis somente por programação e que permitem utilizar características do software menos evidentes ao usuário tradicional.

Informações

No desenvolvimento do produto Logix uma API antigamente chamava-se RNL acrônimo de Regra de Negócio Logix.

Desenvolvimento

...

Para o correto desenvolvimento é preciso ter em mente alguns cuidados que devem ser sempre considerados na construção de uma API:

...

Nos próximos itens deste documento, serão detalhadas as técnicas que devem ser seguidas na construção do código fonte da API.

Clique AQUI para obter o código 4GL base de uma API para desenvolvimento e complemente o código conforme a necessidade da sua API.

Aviso
titlePara específicos

Utilize sempre como base para a criação de uma API TOTVS o Guia de Implementação de API TOTVS, disponível em: http://tdn.totvs.com/x/nDUxE

...

  • Para desenvolvimento de API específica de cliente é preciso adicionar o sufixo "_espec" no nome do fonte para que não gere conflitos com os fontes de APIs disponibilizadas pelo produto padrão e a compilação de fonte específico acabe sobrepondo funções do fonte padão.  Exemplo: transportadoras_espec.4gl
  • No TFS os arquivos com o código fonte das APIs específicas devem ser armazenados respeitando o mesmo padrão de pastas utilizado para as APIs do produto padrão, ou seja:

Exemplo:

API PADRÃO     → $/Logix/Fontes_Doc/Sustentacao/V12/V12/suprimentos/suprimentos/api/v1/Request.4gl

API ESPECÍFICA → $/Logix/Fontes_Doc/Customizacao/V12/<cliente>/suprimentos/suprimentos/api/v1/Tributos_espec.4gl  (sufixo "_espec" no final do nome do arquivo)

  • As funções internas do fonte de API específica deverá ter nome de funções acrescentando a letra "e" (específico) ao final do nome do módulo, assim como já é muito utilizado no nome de fontes específicos atualmente. Neste todas funções internas da API específica, utilizando como exemplo a API específica "Tributos" citada logo acima, deverão ficar definidas com o seguinte prefixo vdpe_v1_tributos.

Exemplos:

vdpe_v1_tributos()          >> função principal para definição de rotas

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 - Desenvolvimento de APIS específicas de clientes (Fábrica de Software)
apontamento_horas.4glman_v1_apontamento_horas

vdpe_v1_tributos_post() >> Função acionada pelo método POST (definida na função de rotas)

vdpe_v1_tributos_get()   >> Função acionada pelo método GET (definida na função de rotas)


     Dentro do código fonte 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:

...

  • Função principal roteadora. Esta função não possui parâmetros e tem como objetivo a definição das rotas.
  • Funções definidas na função roteadora para cada uma das rotas. Estas funções devem ter sempre a definição de 1 parâmetro do tipo VARCHAR(10) que será a referência do objeto de classe LJsonObject que é enviado no ato de toda requisição. Também sempre devem ter um retorno no formato JSON que será o retorno da requisição, utilizando para isso a classe LRestLogixResponse. Ambas classes estão detalhadas em Classes Utilitárias.


EXEMPLO DE IMPLEMENTAÇÃO

Expandir
titleClique para expandir e ver o exemplo...
Bloco de código
languageruby
themeConfluence
DATABASE Logix

{#
 #Função roteadora principal 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.
 #}
#---------------------------#
 FUNCTION wms_v1_dimensoes()
#---------------------------#
{DEFINIÇÃO DAS ROTAS}

    #Inicia a técnica para definição das rotas de execução das funções conforme a requisição recebida.
 	CALL _ADVPL_create_rest_logix_routes()
 	
    #Definição de rota onde toda requisição de método GET, que contenha o filtro a seguir,
    #será direcionada para função wms_v1_dimensoes_get_normal().
    #FILTRO:
    # - 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 (*). 
 	CALL _ADVPL_add_rest_logix_routes("GET",                      #--# Método #--#
 	                                  "/normal/*/",               #--# Path Param a ser filtrado #--#
 	                                  "fields=*",                 #--# QueryParam a ser filtrado #--#
 	                                  "wms_v1_dimensoes_get_normal")  #--# Função a ser executada #--#
 	 
    #Definição de outra rota, onde toda requisição de método GET, que contenha o filtro a seguir,
    #será direcionada para função wms_v1_dimensoes_get_ordenado().
    #FILTRO:
    # - Serão capturadas todas requisições que contenha qualquer parâmetro Path ("/*"  indica "Todos Paths" ou "nenhum") 
    #   e um parâmetro Query "order" com valor "dimensao"
    CALL _ADVPL_add_rest_logix_routes("GET",                           
 	                                  "/*",                            
 	                                  "order=dimensao",                
 	                                  "wms_v1_dimensoes_get_ordenado")  
 	
    #Definição de outra rota, onde 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_dimensoes_get().
 	CALL _ADVPL_add_rest_logix_routes("GET",                          
 	                                  "/*",                           
 	                                  "",                             
 	                                  "wms_v1_dimensoes_get")        
 	                             
    #Definição de rota onde 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_dimensoes_post().
	CALL _ADVPL_add_rest_logix_routes("POST",                        
 	                                  "/*",                         
 	                                  "",                           
 	                                  "wms_v1_dimensoes_post")       
 	                                  
    #Definição de rota onde todas as requisições de método PUT (update), que possuírem quaisquer parâmetros (Query e/ou Path) informados, 
    #serão direcionadas para a função wms_v1_dimensoes_put().
 	CALL _ADVPL_add_rest_logix_routes("PUT",                           
 	                                  "/*",                            
 	                                  "",                             
 	                                  "wms_v1_dimensoes_put")  
 	                                  
    #Definição de rota onde todas as requisições de método DELETE, que possuírem quaisquer parâmetros (Query e/ou Path) informados, 
    #serão direcionadas para a função wms_v1_dimensoes_delete().
    CALL _ADVPL_add_rest_logix_routes("DELETE",                       
 	                                  "/*",                          
 	                                  "",                            
 	                                  "wms_v1_dimensoes_delete")   
END FUNCTION

#------------------------------------------------------#
 FUNCTION wms_v1_dimensoes_get_normal(l_json_reference)
#------------------------------------------------------#
#FUNÇÃO GET COM PATH PARAM "NORMAL" ADICIONADA COMO ROTA NA FUNÇÃO wms_v1_dimensoes()

	DEFINE l_json_reference VARCHAR(10)
	DEFINE l_json VARCHAR(1000)
	
	.
	.
	.

	RETURN l_json
END FUNCTION


#-------------------------------------------------------#
 FUNCTION wms_v1_dimensoes_get_ordenado(l_json_reference)
#-------------------------------------------------------#
#FUNÇÃO GET COM PATH PARAM "ORDENADO" ADICIONADA COMO ROTA NA FUNÇÃO wms_v1_dimensoes()

	DEFINE l_json_reference VARCHAR(10)
	DEFINE l_json VARCHAR(1000)
	
	.
	.
	.

	RETURN l_json
END FUNCTION

IMPORTANTE

Dica

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 #--#

OBSERVAÇÕES

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.

...

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


EXEMPLO DE MENSAGEM JSON

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

Âncora
classes
classes

06.

...

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:

...

07. Exemplo de montagem do JSON de retorno


EXEMPLO MONTAGEM DO JSON DE RETORNO DA REQUISIÇÃO

Expandir
titleClique para expandir e ver o exemplo...
Bloco de código
languageruby
themeConfluence
titleCriação de Response
linenumberstrue
#-----------------------------------------------#
 FUNCTION wms_v1_dimensoes_get(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 (mensagem de erro, detalhamento do erro, código do erro #--#
    CALL _ADVPL_set_property(l_logix_response,"MESSAGE","Mensagem de erro","Detalhamento do erro", "10")
	#--# Definição do status de retorno da requisição
    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)
    
    #Retorno do conteúdo do componente LRestLogixResponse no formato JSON
	RETURN _ADVPL_get_property(l_logix_response,"GENERATE")
END FUNCTION


08. Exemplo de leitura dos dados da requisição


EXEMPLO DE LEITURA DOS DADOS ENVIADOS NO JSON DA REQUISIÇÃO

A leitura dos dados do JSON da requisição é realizada utilizando a função _ADVPL_get_property(l_json_reference, "VALUE",<campo>)

Expandir
titleClique para expandir e ver o exemplo...
Bloco de código
languageruby
themeConfluence
titleCarregando um Array de Record
linenumberstrue
   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 é apenas um exemplo do conteúdo JSON que já é recebido pelas requisições 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")

   #Leitura dos itens da lista "ajustBxaAdtInteg" existentes no JSON da requisição   
   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


09. Exemplo de leitura dos filtros da requisição (QueryParam e PathParam)


EXEMPLO DA LEITURA DOS FILTROS QueryParam E PathParam ENVIADOS NO JSON DA REQUISIÇÃO

Expandir
titleClique para expandir e ver o exemplo...
Bloco de código
languageruby
themeConfluence
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