Compartir a través de


TripPin, parte 2: conector de datos para un servicio REST

En este tutorial de varias partes se describe la creación de una nueva extensión de origen de datos para Power Query. El tutorial está diseñado para realizarse secuencialmente: cada lección se basa en el conector creado en las lecciones anteriores, agregando incrementalmente nuevas funcionalidades al conector.

En esta lección:

  • Creación de una función base que llama a una API REST mediante Web.Contents
  • Aprenda a establecer encabezados de solicitud y a procesar una respuesta JSON
  • Utiliza Power BI Desktop para transformar la respuesta en un formato fácil de usar.

En esta lección se convierte el conector basado en OData para el servicio TripPin (creado en la lección anterior) en un conector similar a algo que se crearía para cualquier API RESTful. OData es una API RESTful, pero una con un conjunto fijo de convenciones. La ventaja de OData es que proporciona un esquema, un protocolo de recuperación de datos y un lenguaje de consulta estándar. Quitar el uso de OData.Feed requiere que usted mismo cree estas funcionalidades en el conector.

Resumen del conector de OData

Antes de quitar las funciones de OData del conector, vamos a revisar rápidamente lo que hace actualmente (principalmente en segundo plano) para recuperar datos del servicio.

Abra el proyecto de extensión TripPin de la parte 1 en Visual Studio Code. Abra el archivo De consulta y pegue la consulta siguiente:

TripPin.Feed("https://services.odata.org/v4/TripPinService/Me")

Abra Fiddler y, a continuación, evalúe el archivo de Power Query actual en Visual Studio Code.

En Fiddler, hay tres solicitudes al servidor:

Captura de pantalla de la pantalla de salida de Fiddler con las tres solicitudes de OData mostradas.

  • /Me: la dirección URL real que solicita.
  • /$metadata: una llamada realizada automáticamente por la función OData.Feed para determinar la información de esquema y tipo sobre la respuesta.
  • /Me/BestFriend: uno de los campos que se extrajo (diligentemente) al enumerar el singleton /Me. En este caso, la llamada dio como resultado un estado de 204 No Content.

La evaluación de M es principalmente perezosa. En la mayoría de los casos, los valores de datos solo se recuperan o extraen cuando son necesarios. Hay escenarios (como el caso /Me/BestFriend) donde un valor se extrae diligentemente. Suele ocurrir este comportamiento cuando se necesita información de tipo para un miembro, y el motor no tiene otra forma de determinar el tipo, que recuperar el valor e inspeccionarlo. Hacer que las cosas sean perezosas (es decir, evitar extracciones anticipadas) es uno de los aspectos clave para que un conector M tenga un buen rendimiento.

Eche un vistazo a los encabezados de solicitud que se enviaron junto con las solicitudes y el formato JSON de la respuesta de la solicitud /Me.

{
  "@odata.context": "https://services.odata.org/v4/TripPinService/$metadata#Me",
  "UserName": "aprilcline",
  "FirstName": "April",
  "LastName": "Cline",
  "MiddleName": null,
  "Gender": "Female",
  "Age": null,
  "Emails": [ "April@example.com", "April@contoso.com" ],
  "FavoriteFeature": "Feature1",
  "Features": [ ],
  "AddressInfo": [
    {
      "Address": "P.O. Box 555",
      "City": {
        "Name": "Lander",
        "CountryRegion": "United States",
        "Region": "WY"
      }
    }
  ],
  "HomeAddress": null
}

Cuando la consulta termine de evaluarse, la ventana de resultados de PQTest debe mostrar el valor Record para el singleton Me.

Captura de pantalla del resultado de PQTest que muestra el valor record del singleton Me.

Si compara los campos de la ventana de salida con los campos devueltos en la respuesta JSON sin procesar, verá un error de coincidencia. El resultado de la consulta tiene otros campos (Friends, Trips, GetFriendsTrips) que no aparecen en ninguna parte de la respuesta JSON. La función OData.Feed anexó automáticamente estos campos al registro en función del esquema devuelto por $metadata. Esta diferencia es un buen ejemplo de cómo un conector podría aumentar o volver a formatear la respuesta del servicio para proporcionar una mejor experiencia de usuario.

Creación de un conector REST básico

Ahora agregue una nueva función exportada al conector que llama a Web.Contents.

Sin embargo, para que las solicitudes web al servicio OData sean exitosas, debe establecer algunos encabezados OData estándar. Para ello, defina un conjunto común de encabezados como una nueva variable en el conector:

DefaultRequestHeaders = [
    #"Accept" = "application/json;odata.metadata=minimal",  // column name and values only
    #"OData-MaxVersion" = "4.0"                             // we only support v4
];

Cambie la implementación de la TripPin.Feed función para que, en lugar de usar OData.Feed, use Web.Contents para realizar una solicitud web y analice el resultado como un documento JSON.

TripPinImpl = (url as text) =>
    let
        source = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
        json = Json.Document(source)
    in
        json;

Recuerde compilar el conector ahora que ha realizado cambios en el archivo del conector. A continuación, puede evaluar el archivo de consulta (TripPin.query.pq). El resultado del registro de /Me ahora es similar al JSON sin formato que se encontraba en la solicitud Fiddler.

Si ve Fiddler al ejecutar la nueva función, también observará que la evaluación ahora realiza una única solicitud web, en lugar de tres. Enhorabuena: ha logrado un aumento de rendimiento de 300%! Ahora perdió toda la información de tipo y esquema, pero aún no es necesario centrarse en esa parte.

Actualice la consulta para acceder a algunas de las entidades o tablas de TripPin, como:

  • https://services.odata.org/v4/TripPinService/Airlines
  • https://services.odata.org/v4/TripPinService/Airports
  • https://services.odata.org/v4/TripPinService/Me/Trips

Observe que las rutas de acceso que solían devolver tablas con formato correcto ahora devuelven un campo de nivel superior "valor" con una [Lista] insertada. Debe realizar algunas transformaciones en el resultado para que sea utilizable para escenarios de consumo del usuario final.

Captura de pantalla del resultado de PQTest con un campo de valor que contiene una lista incrustada.

Creación de transformaciones en Power Query

Aunque es posible crear las transformaciones de M a mano, la mayoría de las personas prefieren usar Power Query para dar forma a sus datos. Abra la extensión en Power BI Desktop y úsela para diseñar consultas para convertir la salida en un formato más fácil de usar. Recompile la solución, copie el nuevo archivo de extensión en el directorio De conectores de datos personalizados y vuelva a iniciar Power BI Desktop.

Inicie una nueva consulta en blanco y pegue la siguiente línea en la barra de fórmulas:

= TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines")

Asegúrese de incluir el signo = .

Manipule la salida hasta que parezca la fuente OData original, una tabla con dos columnas: AirlineCode y Name.

Captura de pantalla de una tabla de Power Query que contiene las columnas AirlineCode y Name.

La consulta resultante debe tener un aspecto similar al siguiente:

let
    Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airlines"),
    value = Source[value],
    toTable = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    expand = Table.ExpandRecordColumn(toTable, "Column1", {"AirlineCode", "Name"}, {"AirlineCode", "Name"})
in
    expand

Asigne un nombre a la consulta ("Airlines").

Cree una nueva consulta en blanco. Esta vez, use la TripPin.Feed función para acceder a la entidad /Airports. Aplique transformaciones hasta que obtenga algo similar a la tabla siguiente. La consulta coincidente sigue la tabla, asígnele un nombre a esta consulta ("Aeropuertos").

Captura de pantalla de una tabla de Power Query que contiene la tabla transformada.

let
    Source = TripPin.Feed("https://services.odata.org/v4/TripPinService/Airports"),
    value = Source[value],
    #"Converted to Table" = Table.FromList(value, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
    #"Expanded Column1" = Table.ExpandRecordColumn(#"Converted to Table", "Column1", {"Name", "IcaoCode", "IataCode", "Location"}, {"Name", "IcaoCode", "IataCode", "Location"}),
    #"Expanded Location" = Table.ExpandRecordColumn(#"Expanded Column1", "Location", {"Address", "Loc", "City"}, {"Address", "Loc", "City"}),
    #"Expanded City" = Table.ExpandRecordColumn(#"Expanded Location", "City", {"Name", "CountryRegion", "Region"}, {"Name.1", "CountryRegion", "Region"}),
    #"Renamed Columns" = Table.RenameColumns(#"Expanded City",{{"Name.1", "City"}}),
    #"Expanded Loc" = Table.ExpandRecordColumn(#"Renamed Columns", "Loc", {"coordinates"}, {"coordinates"}),
    #"Added Custom" = Table.AddColumn(#"Expanded Loc", "Latitude", each [coordinates]{1}),
    #"Added Custom1" = Table.AddColumn(#"Added Custom", "Longitude", each [coordinates]{0}),
    #"Removed Columns" = Table.RemoveColumns(#"Added Custom1",{"coordinates"}),
    #"Changed Type" = Table.TransformColumnTypes(#"Removed Columns",{{"Name", type text}, {"IcaoCode", type text}, {"IataCode", type text}, {"Address", type text}, {"City", type text}, {"CountryRegion", type text}, {"Region", type text}, {"Latitude", type number}, {"Longitude", type number}})
in
    #"Changed Type"

Puede repetir este proceso para obtener más rutas de acceso en el servicio. Una vez que esté listo, vaya al siguiente paso para crear una tabla de navegación (simulada).

Simulación de una tabla de navegación

Ahora vas a crear una tabla (usando código M) que presenta tus entidades TripPin bien formateadas.

Inicie una nueva consulta en blanco y abra el editor avanzado.

Pegue la consulta siguiente:

let
    source = #table({"Name", "Data"}, {
        { "Airlines", Airlines },
        { "Airports", Airports }
    })
in
    source

Si no estableció la configuración de niveles de privacidad en "Omitir siempre la configuración del nivel de privacidad" (también conocida como "Combinación rápida"), se muestra un mensaje de privacidad.

Captura de pantalla del mensaje de privacidad de Power Query.

Los avisos de privacidad aparecen cuando se combinan datos de varios orígenes y no se especifica un nivel de privacidad para uno o varios orígenes. Seleccione el botón Continuar y establezca el nivel de privacidad del origen superior en Público.

Captura de pantalla del cuadro de diálogo Niveles de privacidad donde se establece el nivel de privacidad del origen superior en Público.

Seleccione Guardar y aparecerá la tabla. Aunque esta tabla aún no es una tabla de navegación, proporciona la funcionalidad básica que necesita para convertirla en una en una lección posterior.

Captura de pantalla de la tabla que contiene los datos de TripPin.

Las comprobaciones de combinación de datos no se producen al acceder a varios orígenes de datos desde dentro de una extensión. Dado que todas las llamadas de origen de datos realizadas desde dentro de la extensión heredan el mismo contexto de autorización, la suposición es que son "seguras" para combinar. La extensión siempre se trata como un único origen de datos cuando se trata de reglas de combinación de datos. Los usuarios seguirán recibiendo las solicitudes de privacidad habituales al combinar su fuente con otras fuentes M.

Si ejecuta Fiddler y selecciona el botón Actualizar vista previa en el editor de Power Query, anote las solicitudes web independientes para cada elemento de la tabla de navegación. Estas solicitudes independientes indican que se está produciendo una evaluación diligente, lo que no es ideal al crear tablas de navegación con muchos elementos. Las lecciones posteriores muestran cómo crear una tabla de navegación adecuada que admita la evaluación diferida.

Conclusión

En esta lección se muestra cómo crear un conector sencillo para un servicio REST. En este caso, ha convertido una extensión de OData existente en una extensión REST estándar (con Web.Contents), pero se aplican los mismos conceptos si va a crear una nueva extensión desde cero.

En la siguiente lección, tomará las consultas creadas en esta lección mediante Power BI Desktop y las convertirá en una tabla de navegación verdadera dentro de la extensión.

Pasos siguientes

TripPin, parte 3: tablas de navegación