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 |
---|
|
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 |
...
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 |
---|
|
title | Importante - Desenvolvimento de APIS específicas de clientes (Fábrica de Software) |
---|
|
- 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/wurth_do_brasil_pecas_de_fixacao_ltd/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
vdpe_v1_tributos_post() >> Função acionada pelo método POST (definida na função de rotas)
apontamento_horas.4gl | man_v1_apontamento_horas |
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 |
---|
title | Clique para expandir e ver o exemplo... |
---|
|
Bloco de código |
---|
language | ruby |
---|
theme | Confluence |
---|
| 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=* |
...
Através desta mensagem, o desenvolvedor poderá efetuar os devidos filtros e lógicas necessárias.
EXEMPLO DE MENSAGEM JSON
Bloco de código |
---|
language | js |
---|
theme | Confluence |
---|
|
{
uri: valor,
method: GET,
headers: {},
pathParams: [ "param1", "param2" ],
queryParams: { query1: valor1, query2: valor1},
payload: {}
} |
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:
...
Acesse a documentação referente ao componente LOGIX LRESTLOGIXRESPONSE para saber mais detalhes a respeito das propriedades SET e GET disponíveis.
...
07. Exemplo de montagem do JSON de retorno
EXEMPLO MONTAGEM DO JSON DE RETORNO DA REQUISIÇÃO
Expandir |
---|
title | Clique para expandir e ver o exemplo... |
---|
|
Bloco de código |
---|
language | ruby |
---|
theme | Confluence |
---|
title | Criação de Response |
---|
linenumbers | true |
---|
| #-----------------------------------------------#
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 |
---|
title | Clique para expandir e ver o exemplo... |
---|
|
Bloco de código |
---|
language | ruby |
---|
theme | Confluence |
---|
title | Carregando um Array de Record |
---|
linenumbers | true |
---|
| 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 |
---|
title | Clique para expandir e ver o exemplo... |
---|
|
Bloco de código |
---|
language | ruby |
---|
theme | Confluence |
---|
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 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 |
|