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.

Credenciales con rutas de acceso.

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.

Pestaña Credenciales.

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.

Credenciales sin ruta de acceso.

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.

Navegador de OData.

Va a realizar los siguientes cambios:

  1. Definir una lista de elementos que se van a mostrar en la tabla de navegación
  2. Eliminar las funciones específicas de la entidad (GetAirlineTables y GetAirportsTable)

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ámetro baseUri
  • Si el parámetro relativeUri no comienza con / y baseUri termina con /, se anexa la ruta de acceso
  • Si el parámetro relativeUri no comienza por / y baseUri no termina con /, se reemplaza el último segmento de la ruta de acceso

En la imagen siguiente se muestran ejemplos de esto:

Ejemplo de Uri.Combine.

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.

Navegador.

Pasos siguientes

TripPin, parte 5: paginación