TripPin parte 5 - Paging
Este tutorial com várias partes aborda a criação de uma nova extensão de fonte de dados para o Power Query. O tutorial deve ser feito sequencialmente — cada lição se baseia no conector criado nas lições anteriores, adicionando incrementalmente novos recursos ao seu conector.
Nesta lição, você irá:
- Adicionar suporte de paginação ao conector
Muitas APIs Rest retornam dados em "páginas", exigindo que os clientes façam várias solicitações para unir os resultados. Embora existam algumas convenções comuns para paginação (como RFC 5988), geralmente varia de API para API. Felizmente, o TripPin é um serviço OData e o padrão OData define uma maneira de fazer paginação usando valores odata.nextLink retornados no corpo da resposta.
Para simplificar iterações anteriores do conector, a TripPin.Feed
função não estava ciente da página. Ele simplesmente analisava qualquer JSON retornado da solicitação e o formatava como uma tabela. Aqueles familiarizados com o protocolo OData podem ter notado que muitas suposições incorretas foram feitas no formato da resposta (como assumir que há um value
campo contendo uma matriz de registros).
Nesta lição, você melhora sua lógica de manipulação de respostas tornando-a ciente da página. Tutoriais futuros tornam a lógica de manipulação de página mais robusta e capaz de lidar com vários formatos de resposta (incluindo erros do serviço).
Nota
Você não precisa implementar sua própria lógica de paginação com conectores baseados em OData.Feed, pois ele lida com tudo para você automaticamente.
Lista de verificação de paginação
Ao implementar o suporte de paginação, você precisará saber as seguintes coisas sobre sua API:
- Como solicitar a próxima página de dados?
- O mecanismo de paginação envolve o cálculo de valores ou você extrai o URL da próxima página da resposta?
- Como saber quando parar a paginação?
- Existem parâmetros relacionados à paginação que você deve estar ciente? (como "tamanho da página")
A resposta a essas perguntas afeta a maneira como você implementa sua lógica de paginação. Embora haja alguma quantidade de reutilização de código em implementações de paginação (como o uso de Table.GenerateByPage), a maioria dos conectores acabará exigindo lógica personalizada.
Nota
Esta lição contém lógica de paginação para um serviço OData, que segue um formato específico. Verifique a documentação da sua API para determinar as alterações que você precisará fazer no conector para suportar seu formato de paginação.
Visão geral da paginação OData
A paginação OData é orientada por anotações nextLink contidas na carga útil de resposta. O valor nextLink contém a URL para a próxima página de dados. Você saberá se há outra página de dados procurando por um odata.nextLink
campo no objeto mais externo na resposta. Se não houver nenhum odata.nextLink
campo, você leu todos os seus dados.
{
"odata.context": "...",
"odata.count": 37,
"value": [
{ },
{ },
{ }
],
"odata.nextLink": "...?$skiptoken=342r89"
}
Alguns serviços OData permitem que os clientes forneçam uma preferência de tamanho máximo de página, mas cabe ao serviço honrá-la ou não. O Power Query deve ser capaz de lidar com respostas de qualquer tamanho, para que não tenha de se preocupar em especificar uma preferência de tamanho de página — pode suportar tudo o que o serviço lhe lançar.
Mais informações sobre a paginação controlada por servidor podem ser encontradas na especificação OData.
Testando o TripPin
Antes de corrigir sua implementação de paginação, confirme o comportamento atual da extensão do tutorial anterior. A consulta de teste a seguir recupera a tabela Pessoas e adiciona uma coluna de índice para mostrar sua contagem de linhas atual.
let
source = TripPin.Contents(),
data = source{[Name="People"]}[Data],
withRowCount = Table.AddIndexColumn(data, "Index")
in
withRowCount
Ative o Fiddler e execute a consulta no SDK do Power Query. Observe que a consulta retorna uma tabela com oito linhas (índice de 0 a 7).
Se você olhar para o corpo da resposta do violinista, verá que ele de fato contém um @odata.nextLink
campo, indicando que há mais páginas de dados disponíveis.
{
"@odata.context": "https://services.odata.org/V4/TripPinService/$metadata#People",
"@odata.nextLink": "https://services.odata.org/v4/TripPinService/People?%24skiptoken=8",
"value": [
{ },
{ },
{ }
]
}
Implementando paginação para o TripPin
Agora você vai fazer as seguintes alterações na sua extensão:
- Importar a função comum
Table.GenerateByPage
- Adicionar uma
GetAllPagesByNextLink
função que usaTable.GenerateByPage
para colar todas as páginas juntas - Adicionar uma
GetPage
função que possa ler uma única página de dados - Adicionar uma
GetNextLink
função para extrair o próximo URL da resposta - Atualizar
TripPin.Feed
para usar as novas funções de leitor de página
Nota
Como dito anteriormente neste tutorial, a lógica de paginação varia entre as fontes de dados. A implementação aqui tenta dividir a lógica em funções que devem ser reutilizáveis para fontes que usam os próximos links retornados na resposta.
Table.GenerateByPage
Para combinar as (potencialmente) várias páginas retornadas pela fonte em uma única tabela, usaremos Table.GenerateByPage
. Esta função toma como argumento uma getNextPage
função que deve fazer exatamente o que seu nome sugere: buscar a próxima página de dados. Table.GenerateByPage
chamará repetidamente a getNextPage
função, cada vez que lhe passarem os resultados produzidos da última vez que foi chamada, até que volte null
a sinalizar de volta que não há mais páginas disponíveis.
Uma vez que esta função não faz parte da biblioteca padrão do Power Query, terá de copiar o respetivo código-fonte para o ficheiro .pq.
Implementando GetAllPagesByNextLink
O corpo da sua GetAllPagesByNextLink
função implementa o getNextPage
argumento function para Table.GenerateByPage
. Ele chamará a GetPage
função e recuperará o URL para a próxima página de dados do NextLink
campo do meta
registro da chamada anterior.
// Read all pages of data.
// After every page, we check the "NextLink" record on the metadata of the previous request.
// Table.GenerateByPage will keep asking for more pages until we return null.
GetAllPagesByNextLink = (url as text) as table =>
Table.GenerateByPage((previous) =>
let
// if previous is null, then this is our first page of data
nextLink = if (previous = null) then url else Value.Metadata(previous)[NextLink]?,
// if NextLink was set to null by the previous call, we know we have no more data
page = if (nextLink <> null) then GetPage(nextLink) else null
in
page
);
Implementando o GetPage
Sua GetPage
função usará Web.Contents para recuperar uma única página de dados do serviço TripPin e converter a resposta em uma tabela. Ele passa a resposta de Web.Contents para a GetNextLink
função para extrair a URL da próxima página e a define no meta
registro da tabela retornada (página de dados).
Esta implementação é uma versão ligeiramente modificada da TripPin.Feed
chamada dos tutoriais anteriores.
GetPage = (url as text) as table =>
let
response = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
body = Json.Document(response),
nextLink = GetNextLink(body),
data = Table.FromRecords(body[value])
in
data meta [NextLink = nextLink];
Implementando GetNextLink
Sua GetNextLink
função simplesmente verifica o corpo da resposta para um @odata.nextLink
campo e retorna seu valor.
// In this implementation, 'response' will be the parsed body of the response after the call to Json.Document.
// Look for the '@odata.nextLink' field and simply return null if it doesn't exist.
GetNextLink = (response) as nullable text => Record.FieldOrDefault(response, "@odata.nextLink");
Juntar tudo
A etapa final para implementar sua lógica de paginação é atualizar TripPin.Feed
para usar as novas funções. Por enquanto, você está simplesmente ligando para o GetAllPagesByNextLink
, mas nos tutoriais subsequentes, você adicionará novos recursos (como a imposição de um esquema e a lógica do parâmetro de consulta).
TripPin.Feed = (url as text) as table => GetAllPagesByNextLink(url);
Se você executar novamente a mesma consulta de teste do início do tutorial, verá o leitor de página em ação. Você também deve ver que você tem 24 linhas na resposta em vez de oito.
Se você olhar para as solicitações no fiddler, agora você deve ver solicitações separadas para cada página de dados.
Nota
Você notará solicitações duplicadas para a primeira página de dados do serviço, o que não é o ideal. A solicitação extra é resultado do comportamento de verificação de esquema do mecanismo M. Ignore esse problema por enquanto e resolva-o no próximo tutorial, onde você aplicará um esquema explícito.
Conclusão
Esta lição mostrou como implementar o suporte de paginação para uma API Rest. Embora a lógica provavelmente varie entre APIs, o padrão estabelecido aqui deve ser reutilizável com pequenas modificações.
Na próxima lição, você verá como aplicar um esquema explícito aos seus dados, indo além dos tipos de dados simples text
e number
que você obtém do Json.Document
.