Paginação de API com OpenEdge
É mais do que conhecida a importância de enviar registros em lotes do servidor para o cliente por meio de APIs. Uma das vantagens é um menor tempo de espera por parte do cliente enquanto carrega os dados, menor tráfico de dados desnecessários saindo do servidor, além de que é uma boa prática.
Não sou a melhor pessoa para explicar o que é uma API com paginação, nem o porquê você deveria adotar esta prática. O objetivo aqui é mostrar uma maneira de fazer isso usando Progress OpenEdge e Progress Application Server for OpenEdge (PASOE ou OEPAS).
Se você conhece uma maneira diferente de paginar suas APIs com Progress OpenEdge , compartilhe nos comentários! E antes de ir aos códigos, é válido mencionar o curso gratuito da Progress para se tornar um desenvolvedor OpenEdge, confira!
Antes do código
Neste artigo não entrarei em detalhes sobre configuração da IDE PDSOE, nem sobre criar um projeto server. Neste momento meu PDSOE já está configurado com uma conexão ao banco de dados sports2020, e um projeto OpenEdge com server e REST transport for PASOE, chamado ws.
O exemplo para a paginação da API é retornar registros da tabela item com um limite máximo de 200 registros por requisição, e opção de apontar o id para o próximo lote de registros. Ambos passados como parâmetros na URL do endpoint.
Seções do artigo:
1. Código (o arquivo .p)
Começo definindo os objetos, como temp-table, datasource, dataset, e query:
DEFINE TEMP-TABLE ttItem NO-UND
FIELD ItemNum LIKE Item.ItemNum
FIELD ItemName LIKE Item.ItemName
FIELD Price LIKE Item.Price
INDEX ItemNum IS PRIMARY IS UNIQUE ItemNum.
DEFINE DATA-SOURCE srcItem FOR Item.
DEFINE DATASET dsItem FOR ttItem.
DEFINE QUERY qItem FOR ttItem.
Em seguida, os parâmetros de INPUT e OUTPUT. Estes parâmetros serão usados na definição do endpoint no REST Resource URI Editor.
DEFINE INPUT PARAM iQty AS INTEGER NO-UNDO
DEFINE INPUT-OUTPUT PARAM pRowId AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAM cStatus AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAM DATASET FOR dsItem.
O bloco principal, com toda a lógica e comentários em cada etapa:
/* Limpa o dataset */
DATASET dsItem:EMPTY-DATASET ().
/* Validações iniciais */
IF iQty = 0 THEN RETURN.
IF iQty > 200 THEN
DO:
ASSIGN
cStatus = "Not alowed! Maximum of 200 records per request!".
RETURN.
END.
ASSIGN
cStatus = "Unknown error!".
/* Vincula o datasource srcItem ao buffer ttItem */
BUFFER ttItem:ATTACH-DATA-SOURCE(DATA-SOURCE srcItem:HANDLE).
/* Configur o RESTART-ROWID usando o pRowId informado */
IF LENGTH(pRowId) > 0 THEN
DATA-SOURCE srcItem:RESTART-ROWID = TO-ROWID(pRowId).
/* Configura quantidade de registros por lote */
BUFFER ttItem:BATCH-SIZE = iQty.
/* Limpa a tabela antes da operação FILL (preencher) iniciar */
BUFFER ttItem:FILL-MODE = "EMPTY".
/* Preenche dataset */
DATASET dsItem:FILL().
/* Obtém próximo NEXT-ROWID do DATA-SOURCE para retornar */
pRowId = STRING(DATA-SOURCE srcItem:NEXT-ROWID).
/* Desvincula o datasource */
BUFFER ttItem:DETACH-DATA-SOURCE().
ASSIGN
cStatus = "Success!".
Adicionando o CATCH usando Progress.Lang.Error, para capturar qualquer condição não esperada ocorrida durante a execução:
CATCH err AS Progress.Lang.Error
DEFINE VARIABLE iMessage AS INTEGER NO-UNDO.
DO WHILE iMessage < err:NumMessages:
ASSIGN
cStatus = SUBSTITUTE ("&1~n&2", cStatus, err:GetMessage(iMessage))
iMessage = iMessage + 1.
END.
IF err:CallStack <> ? THEN DO:
ASSIGN
cStatus = SUBSTITUTE ("&1~n~nCall Stack:~n&2", cStatus, err:CallStack).
END.
END CATCH.:
O código completo você pode encontrar aqui.
Uma vez que o código está pronto, o próximo passo é mapear o programa (.p) como um endpoint no REST Resource URI Editor.
Recomendados pelo LinkedIn
2. Mapeando o .p como um endpoint
No REST Resource URI Editor você pode adicionar um novo recurso (endpoint) e associar os programas aos verbos (GET, PUT, POST e DELETE).
Criei um novo endpoint (chamado item), e selecionei o arquivo item.p para ser vinculado ao verbo GET:
Então fiz a vinculação dos parâmetros de entrada (input) como Query String Parameters, os parâmetros que serão informados na URL do endpoint:
Finalmente, vinculei os parâmetros de saída (output) ao Response Body:
Como já tenho uma instância do PASOE rodando localmente, e o projeto já está publicado no OpenEdge Explorer, a próxima etapa é testar o endpoint.
3. Testando o endpoint em uma instância local PASOE
Testando o limite de registros chamando http://localhost:8810/ws/rest/wsService/item?iQty=300:
Ótimo! A primeira validação executou com sucesso!
Agora, testando com limite de 3 registros em duas etapas:
Primeiro, chamando o endpoint sem passar o parâmetro pRowId, o que deve resultar nos três primeiros registros da tabela (http://localhost:8810/ws/rest/wsService/item?iQty=3).
E em seguida, passando o pRowId, obtido da requisição anterior, como parâmetro para a próxima requisição (http://localhost:8810/ws/rest/wsService/item?iQty=3&pRowId=0x0000000000005c04)
Veja os resultados abaixo:
Ótimo! Tudo funcionou como esperado! Enquanto o artigo foca em um simples e específico escopo, existem muitas possibilidades de melhorias futuras e validações.
Se você estiver interessado em explorar o código completo, você pode encontrá-lo clicando aqui.
Espero que tenha gostado! Comente se isso é uma novidade para você ou se você desenvolve paginações em suas APIs de uma maneira diferente!
IT Projects | Digital Transformation |IT Leader| ERP | TOTVS Datasul | Salesforce | Field Service| 1x Certified Salesforce| AWS Certified | Microsoft Certified|
1 aSensacional Lucas. Tks por compartilhar