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 |
---|
|
Versão / Release | Funcionalidade |
---|
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. |
|
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: /restlogix/wms/v1/dimensoes
Aviso |
---|
A URL definida pelo Padrão de API TOTVS segue o seguinte formato: /restlogix/<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 |
---|
|
Os programas 4GL disponibilizados, deverão seguir o padrão de localização abaixo:
Exemplos: Objeto de Negócio | Função de roteamento |
---|
\suprimentos\obrigacoes_fiscais\api\v1\transportadoras.4gl | obf_v1_transportadoras | \suprimentos\suprimentos\api\v1\estoque.4gl | sup_v1_estoque | \adm_producao\manufatura\api\v1\apontamento_horas.4gl | man_v1_apontamento_horas |
Nota |
---|
| 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ção | Requisição |
---|
FUNCTION obf_v1_transportadoras()
| GET/POST/PUT/DELETE /restlogix/obf/v1/transportadoras
|
FUNCTION sup_v1_estoque() | GET/POST/PUT/DELETE /restlogix/sup/v1/estoque
|
FUNCTION man_v1_apontamento_horas() | GET/POST/PUT/DELETE/restlogix/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 |
---|
|
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 |
---|
|
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 |
---|
|
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 |
---|
|
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 |
---|
|
"/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 (*).
Bloco de código |
---|
|
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") e um parâmetro Query "order" com valor "dimensao" será direcionada para a função wms_v1_get_dimensoes_ordenado.
Bloco de código |
---|
|
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 |
---|
|
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 determinada posição e o sinal "*" (asterisco) para um conjunto de caracteres variáveis. 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=* |
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 |
---|
|
{
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 |
---|
language | erl |
---|
title | Criaçã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',"status")
#--# 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 |
---|
language | erl |
---|
title | Carregando 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 |
---|
language | erl |
---|
title | Carregar 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: '{"queryParam1":["codigo","valor"]}'
LET l_query_param = _ADVPL_get_property(l_json_reference,"VALUE","queryparams[1][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),'"}'
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',"status")
RETURN _ADVPL_get_property(l_logix_response,"GENERATE")
END FUNCTION |