Partilhar via


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).

QueryWithoutPaging.

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:

  1. Importar a função comum Table.GenerateByPage
  2. Adicionar uma GetAllPagesByNextLink função que usa Table.GenerateByPage para colar todas as páginas juntas
  3. Adicionar uma GetPage função que possa ler uma única página de dados
  4. Adicionar uma GetNextLink função para extrair o próximo URL da resposta
  5. 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.

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];

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.

QueryWithPaging.

Se você olhar para as solicitações no fiddler, agora você deve ver solicitações separadas para cada página de dados.

Violinista.

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.

Próximos passos

TripPin Parte 6 - Esquema