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 seguirse secuencialmente: cada lección se basa en el conector creado en las lecciones anteriores, agregando incrementalmente nuevas funcionalidades al conector.
En esta lección, aprenderá lo siguiente:
- 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
- Uso de Power BI Desktop para transformar la respuesta en un formato descriptivo
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 al que se crearía para cualquier API RESTful. OData es una API RESTful, pero 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. Para eliminar el uso de OData.Feed deberemos incorporar estas funcionalidades en el conector nosotros mismos.
Resumen del conector de OData
Antes de eliminar 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 la 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:
/Me
: la dirección URL real que solicita./$metadata
: una llamada realizada automáticamente por la funciónOData.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 estado204 No Content
.
La evaluación de M es principalmente diferida. 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) en los que un valor se extrae diligentemente. Esto tiende a producirse cuando se necesita información de tipo para un miembro y el motor no tiene otra manera de determinar el tipo que recuperar el valor e inspeccionarlo. El funcionamiento diferido (es decir, evitar extracciones diligentes) es uno de los aspectos clave para conseguir que un conector M tenga un buen rendimiento.
Observe 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 del singleton Me.
Si compara los campos de la ventana de salida con los campos devueltos en la respuesta JSON sin procesar, observará una discrepancia. El resultado de la consulta tiene otros campos (Friends
, Trips
, GetFriendsTrips
) que no aparecen en ninguna parte en la respuesta JSON. La función OData.Feed anexó automáticamente estos campos al registro en función del esquema devuelto por $metadata. Este es un buen ejemplo de cómo un conector podría aumentar o volver a formatear la respuesta del servicio para ofrecer una mejor experiencia de usuario.
Creación de un conector REST básico
Ahora agregará una nueva función exportada al conector que llama a Web.Contents.
Sin embargo, para poder realizar solicitudes web correctas al servicio OData, tendrá que establecer algunos encabezados estándar de OData. Para ello, deberá definir 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
];
Cambiará la implementación de la función TripPin.Feed
para que, en lugar de usar OData.Feed
, use Web.Contents para realizar una solicitud web y analizar 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 /Me ahora es similar al JSON sin formato que vio en la solicitud de Fiddler.
Si observa Fiddler al ejecutar la nueva función, también observará que la evaluación ahora lleva a cabo una única solicitud web, en lugar de tres. ¡Enhorabuena: ha logrado un aumento del rendimiento del 300 %! Ahora ha perdido 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 por ejemplo:
https://services.odata.org/v4/TripPinService/Airlines
https://services.odata.org/v4/TripPinService/Airports
https://services.odata.org/v4/TripPinService/Me/Trips
Observará que las rutas de acceso que solían devolver tablas con formato correcto ahora devuelven un campo de nivel superior "value" con un elemento [List] insertado. Deberá realizar algunas transformaciones en el resultado para que se pueda usar en escenarios de consumo de usuario final.
Creación de transformaciones en Power Query
Aunque es posible crear sus transformaciones M manualmente, la mayoría de las personas prefieren usar Power Query para dar forma a sus datos. Abrirá la extensión en Power BI Desktop y la usará para diseñar consultas para convertir la salida en un formato más fácil de usar. Vuelva a compilar 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 el contenido siguiente 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 se parezca a la fuente de OData original, una tabla con dos columnas: AirlineCode y Name.
La consulta resultante deberá ser 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 función TripPin.Feed
para acceder a la entidad /Airports. Aplique transformaciones hasta que obtenga algo similar al recurso compartido que se muestra a continuación. La consulta coincidente también se puede encontrar a continuación: asígnele también un nombre ("Airports").
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 otras rutas de acceso en el servicio. Cuando esté listo, vaya al siguiente paso para crear una tabla de navegación (simulada).
Simulación de una tabla de navegación
Ahora va a crear una tabla (con código M) que presenta las entidades TripPin con formato correcto.
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 ha establecido la opción Niveles de privacidad en "Ignorar siempre la configuración del nivel de privacidad" (que también se conoce como "Combinación rápida"), verá un aviso de privacidad.
Los avisos de privacidad aparecen cuando se combinan datos de varios orígenes y aún no se ha especificado 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.
Seleccione Guardar y aparecerá la tabla. Aunque aún no es una tabla de navegación, proporciona la funcionalidad básica necesaria para convertirla en una tabla de navegación en una lección posterior.
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 al origen de datos realizadas desde dentro de la extensión heredan el mismo contexto de autorización, se supone se pueden combinar de forma "segura". La extensión siempre se tratará 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 normales al combinar el origen con otros orígenes de M.
Si ejecuta Fiddler y selecciona el botón Actualizar vista previa en el Editor de consultas, observará que hay solicitudes web distintas para cada elemento de la tabla de navegación. Esto indica que se está produciendo una evaluación diligente, lo que no es lo ideal al crear tablas de navegación con numerosos elementos. En lecciones posteriores se muestra cómo crear una tabla de navegación adecuada que admita la evaluación diferida.
Conclusión
En esta lección se ha visto 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 de REST estándar (con Web.Contents), pero los mismos conceptos son válidos si está creando 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.