TripPin, parte 4: rutas de origen de datos
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:
- Simplificación de la lógica de conexión para el conector
- Mejora de la experiencia de la tabla de navegación
En esta lección se simplifica el conector integrado en la lección anterior quitando sus parámetros de función necesarios y mejorando la experiencia del usuario pasando a una tabla de navegación generada dinámicamente.
Para obtener una explicación detallada de cómo se identifican las credenciales, consulte la sección Rutas de origen de datos de Control de la autenticación.
Rutas de origen de datos
Al invocar una función de origen de datos, el motor de M identifica las credenciales que se deben usar durante una evaluación mediante una búsqueda basada en los valores de Tipo de origen de datos y Ruta del origen de datos.
En la lección anterior compartió dos funciones de origen de datos, ambas con un único parámetro Uri.Type.
[DataSource.Kind="TripPin"]
shared TripPin.Feed = Value.ReplaceType(TripPinImpl, type function (url as Uri.Type) as any);
[DataSource.Kind="TripPin", Publish="TripPin.Publish"]
shared TripPin.Contents = Value.ReplaceType(TripPinNavTable, type function (url as Uri.Type) as any);
La primera vez que ejecute una consulta que use una de las funciones, recibirá una solicitud de credenciales con listas desplegables que le permiten seleccionar una ruta de acceso y un tipo de autenticación.
Si vuelve a ejecutar la misma consulta, con los mismos parámetros, el motor de M puede localizar las credenciales almacenadas en caché y no se muestra ninguna solicitud de credenciales. Si modifica el argumento url
para la función de manera que la ruta de acceso base ya no coincida, se muestra una nueva solicitud de credenciales para la nueva ruta de acceso.
Puede ver las credenciales almacenadas en caché en la tabla Credenciales en la ventana Salida de consulta M.
Según el tipo de cambio, la modificación de los parámetros de la función probablemente producirá un error de credencial.
Simplificación del conector
Ahora simplificará el conector quitando los parámetros de la función de origen de datos (TripPin.Contents
). También quitará el calificador shared
de TripPin.Feed
y lo dejará como una función solo interna.
Una de las filosofías de diseño de Power Query es que el cuadro de diálogo inicial del origen de datos sea lo más sencillo posible. Si es posible, debe proporcionar al usuario opciones en el nivel del Navegador, en lugar de hacerlo en el cuadro de diálogo de conexión. Si un valor proporcionado por el usuario se puede determinar mediante programación, considere la posibilidad de agregarlo como el nivel superior de la tabla de navegación en lugar de como un parámetro de función.
Por ejemplo, al conectarse a una base de datos relacional, es posible que necesite nombres de servidor, base de datos y tabla.
Cuando sepa cuál es el servidor al que debe conectarse y se hayan proporcionado credenciales, puede usar la API de la base de datos para obtener una lista de bases de datos y una lista de tablas contenidas en cada base de datos.
En este caso, para que el cuadro de diálogo de conexión inicial sea lo más sencillo posible, solo debe ser un parámetro obligatorio el nombre del servidor: Database
y Table
serían niveles de la tabla de navegación.
Dado que el servicio TripPin tiene un punto de conexión de dirección URL fijo, no es necesario solicitar al usuario ningún valor. Quitará el parámetro url de la función y definirá una variable BaseUrl en el conector.
BaseUrl = "https://services.odata.org/v4/TripPinService/";
[DataSource.Kind="TripPin", Publish="TripPin.Publish"]
shared TripPin.Contents = () => TripPinNavTable(BaseUrl) as table;
Conservará la función TripPin.Feed
, pero ya no la tendrá compartida, ya no la asociará con un tipo de origen de datos y simplificará su declaración. A partir de este momento, solo la usará internamente en este documento de sección.
TripPin.Feed = (url as text) =>
let
source = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
json = Json.Document(source)
in
json;
Si actualiza la llamada a TripPin.Contents()
en el archivo TripPin.query.pq
y la ejecuta en Visual Studio Code, verá una nueva solicitud de credenciales. Tenga en cuenta que ahora hay un único valor de ruta de acceso del origen de datos: TripPin.
Mejora de la tabla de navegación
En el primer tutorial usó las funciones OData
integradas para conectarse al servicio TripPin.
Esto le proporcionó una tabla de navegación de aspecto muy agradable, basada en el documento de servicio TripPin, sin código adicional por su parte.
La función OData.Feed se ocupó automáticamente del trabajo pesado.
Puesto que está "subsistiendo" usando Web.Contents en lugar de OData.Feed, deberá volver a crear esta tabla de navegación usted mismo.
Va a realizar los siguientes cambios:
- Definir una lista de elementos que se van a mostrar en la tabla de navegación
- Eliminar las funciones específicas de la entidad (
GetAirlineTables
yGetAirportsTable
)
Generación de una tabla de navegación a partir de una lista
Enumerará las entidades que desea exponer en la tabla de navegación y creará la dirección URL adecuada para acceder a ellas. Dado que todas las entidades están en la misma ruta de acceso raíz, podrá crear estas direcciones URL dinámicamente.
Para simplificar el ejemplo, solo expondrá los tres conjuntos de entidades (Airlines, Airports, People), que se expondrían como tablas en M y omitirá el singleton (Me) que se expondría como un registro. Omitirá la adición de las funciones hasta una lección posterior.
RootEntities = {
"Airlines",
"Airports",
"People"
};
A continuación, actualice la función TripPinNavTable
para crear la tabla columna por columna.
La columna [Data] de cada entidad se recupera llamando a TripPin.Feed
con la dirección URL completa a la entidad.
TripPinNavTable = (url as text) as table =>
let
entitiesAsTable = Table.FromList(RootEntities, Splitter.SplitByNothing()),
rename = Table.RenameColumns(entitiesAsTable, {{"Column1", "Name"}}),
// Add Data as a calculated column
withData = Table.AddColumn(rename, "Data", each TripPin.Feed(Uri.Combine(url, [Name])), Uri.Type),
// Add ItemKind and ItemName as fixed text values
withItemKind = Table.AddColumn(withData, "ItemKind", each "Table", type text),
withItemName = Table.AddColumn(withItemKind, "ItemName", each "Table", type text),
// Indicate that the node should not be expandable
withIsLeaf = Table.AddColumn(withItemName, "IsLeaf", each true, type logical),
// Generate the nav table
navTable = Table.ToNavigationTable(withIsLeaf, {"Name"}, "Name", "Data", "ItemKind", "ItemName", "IsLeaf")
in
navTable;
Al crear dinámicamente rutas de acceso de direcciones URL, asegúrese de que quede claro dónde están las barras diagonales (/). Tenga en cuenta que Uri.Combine usa las siguientes reglas al combinar rutas de acceso:
- Cuando el parámetro
relativeUri
comience por /, reemplazará toda la ruta de acceso del parámetrobaseUri
- Si el parámetro
relativeUri
no comienza con / ybaseUri
termina con /, se anexa la ruta de acceso - Si el parámetro
relativeUri
no comienza por / ybaseUri
no termina con /, se reemplaza el último segmento de la ruta de acceso
En la imagen siguiente se muestran ejemplos de esto:
Eliminación de las funciones específicas de la entidad
Para que el mantenimiento del conector sea más sencillo, eliminará las funciones de formato específicas de la entidad que usó en la lección anterior: GetAirlineTables
y GetAirportsTable
.
En su lugar, actualizará TripPin.Feed
para procesar la respuesta JSON de manera que funcione para todas las entidades.
En concreto, toma el campo value
de la carga de JSON de OData devuelta y lo convierte de una lista de registros a una tabla.
TripPin.Feed = (url as text) =>
let
source = Web.Contents(url, [ Headers = DefaultRequestHeaders ]),
json = Json.Document(source),
// The response is a JSON record - the data we want is a list of records in the "value" field
value = json[value],
asTable = Table.FromList(value, Splitter.SplitByNothing()),
// expand all columns from the record
fields = Record.FieldNames(Table.FirstValue(asTable, [Empty = null])),
expandAll = Table.ExpandRecordColumn(asTable, "Column1", fields)
in
expandAll;
Nota:
Un inconveniente de usar un enfoque genérico para procesar las entidades es que se pierde la información de formato y tipo que hace que las entidades tengan un mejor aspecto. En una sección posterior de este tutorial se muestra cómo aplicar el esquema en las llamadas a la API REST.
Conclusión
En este tutorial, ha limpiado y simplificado el conector mediante la corrección del valor de ruta de acceso del origen de datos y el cambio a un formato más flexible para la tabla de navegación. Después de completar estos pasos (o mediante el código de ejemplo de este directorio), la función TripPin.Contents
devuelve una tabla de navegación en Power BI Desktop.