BigQuery-Writebacks mit Looker-Aktionen auf Cloud Run-Funktionen

Viele Looker-Kunden möchten ihren Nutzern die Möglichkeit geben, nicht nur Berichte zu Daten in ihrem Data Warehouse zu erstellen, sondern auch Daten in das Data Warehouse zurückzuschreiben und zu aktualisieren.

Über die Action API unterstützt Looker diesen Anwendungsfall für jedes Data Warehouse oder Ziel. Auf dieser Dokumentationsseite wird beschrieben, wie Kunden, die die Google Cloud -Infrastruktur verwenden, eine Lösung in Cloud Run-Funktionen bereitstellen, um Daten in BigQuery zurückzuschreiben. Auf dieser Seite werden die folgenden Themen behandelt:

Überlegungen zur Lösung

Anhand dieser Liste können Sie prüfen, ob die Lösung Ihren Anforderungen entspricht.

  • Cloud Run Functions
    • Warum Cloud Run-Funktionen? Cloud Run Functions ist das serverlose Angebot von Google und daher eine gute Wahl, wenn Sie Wert auf einfache Bedienung und Wartung legen. Ein wichtiger Aspekt ist, dass die Latenz, insbesondere bei Kaltaufrufen, länger sein kann als bei einer Lösung, die auf einem dedizierten Server basiert.
    • Sprache und Laufzeit: Cloud Run Functions unterstützt mehrere Sprachen und Laufzeiten. Auf dieser Dokumentationsseite wird ein Beispiel in JavaScript und Node.js beschrieben. Die Konzepte lassen sich jedoch direkt auf die anderen unterstützten Sprachen und Runtimes übertragen.
  • BigQuery
    • Warum BigQuery? Auf dieser Dokumentationsseite wird davon ausgegangen, dass Sie BigQuery bereits verwenden. BigQuery ist jedoch generell eine gute Wahl für ein Data Warehouse. Beachten Sie dabei Folgendes:
      • BigQuery Storage Write API:BigQuery bietet mehrere Schnittstellen zum Aktualisieren von Daten in Ihrem Data Warehouse, z. B. DML-Anweisungen (Data Manipulation Language) in SQL-basierten Jobs. Die beste Option für Schreibvorgänge mit hohem Volumen ist jedoch die BigQuery Storage Write API.
      • Anhängen statt Aktualisieren:Bei dieser Lösung werden zwar nur Zeilen angehängt und nicht aktualisiert, Sie können aber jederzeit Tabellen mit dem aktuellen Status zur Abfragezeit aus einem Nur-Anhängen-Log ableiten und so Aktualisierungen simulieren.
  • Unterstützende Dienste
    • Secret Manager:Secret Manager enthält Secret-Werte, damit sie nicht an zu leicht zugänglichen Orten wie direkt in der Konfiguration der Funktion gespeichert werden.
    • Identity and Access Management (IAM): IAM autorisiert die Funktion, zur Laufzeit auf das erforderliche Secret zuzugreifen und in die vorgesehene BigQuery-Tabelle zu schreiben.
    • Cloud Build:Cloud Build wird auf dieser Seite nicht ausführlich behandelt. Cloud Run Functions verwendet es jedoch im Hintergrund. Sie können Cloud Build verwenden, um kontinuierlich bereitgestellte Updates für Ihre Funktionen zu automatisieren, wenn sich Ihr Quellcode in einem Git-Repository ändert.
  • Aktions- und Nutzerauthentifizierung
    • Cloud Run-Dienstkonto: Die primäre und einfachste Methode, Looker-Aktionen für die Integration in die selbst erhobenen Assets und Ressourcen Ihrer Organisation zu verwenden, besteht darin, Anfragen als von Ihrer Looker-Instanz stammend zu authentifizieren. Dazu nutzen Sie den tokenbasierten Authentifizierungsmechanismus der Looker Action API. Anschließend autorisieren Sie die Funktion, Daten in BigQuery mit einem Dienstkonto zu aktualisieren.
    • OAuth:Eine weitere Option, die auf dieser Seite nicht behandelt wird, ist die Verwendung der OAuth-Funktion der Looker Action API. Dieser Ansatz ist komplexer und in der Regel nicht erforderlich. Er kann jedoch verwendet werden, wenn Sie den Schreibzugriff von Endnutzern auf die Tabelle mit IAM definieren müssen, anstatt ihren Zugriff in Looker oder Ad-hoc-Logik in Ihrem Funktionscode zu verwenden.

Schritt-für-Schritt-Anleitung zum Democode

Wir haben eine einzelne Datei mit der gesamten Logik unserer Demo-Aktion, die auf GitHub verfügbar ist. In diesem Abschnitt werden die wichtigsten Elemente des Codes erläutert.

Einrichtungscode

Der erste Abschnitt enthält einige Demokonstanten, die die Tabelle identifizieren, in die die Aktion Daten schreibt. Im Abschnitt Bereitstellungsleitfaden weiter unten auf dieser Seite werden Sie aufgefordert, die Projekt-ID durch Ihre eigene zu ersetzen. Das ist die einzige Änderung, die Sie am Code vornehmen müssen.

/*** Demo constants */
const projectId = "your-project-id"
const datasetId = "demo_dataset"
const tableId = "demo_table"

Im nächsten Abschnitt werden einige Codeabhängigkeiten deklariert und initialisiert, die von Ihrer Aktion verwendet werden. Wir stellen ein Beispiel bereit, in dem über das Secret Manager-Node.js-Modul „im Code“ auf Secret Manager zugegriffen wird. Sie können diese Codeabhängigkeit jedoch auch vermeiden, indem Sie die integrierte Funktion von Cloud Run-Funktionen verwenden, um während der Initialisierung ein Secret abzurufen.

/*** Code Dependencies ***/
const crypto = require("crypto")
const {SecretManagerServiceClient} = require('@google-cloud/secret-manager')
const secrets = new SecretManagerServiceClient()
const BigqueryStorage = require('@google-cloud/bigquery-storage')
const BQSManagedWriter = BigqueryStorage.managedwriter

Die referenzierten @google-cloud-Abhängigkeiten werden auch in unserer package.json-Datei deklariert, damit die Abhängigkeiten vorab geladen werden können und für unsere Node.js-Laufzeit verfügbar sind. crypto ist ein integriertes Node.js-Modul und wird nicht in package.json deklariert.

Verarbeitung und Routing von HTTP-Anfragen

Die Hauptschnittstelle, die Ihr Code für die Cloud Run-Funktionslaufzeit bereitstellt, ist eine exportierte JavaScript-Funktion, die den Konventionen des Node.js Express-Webservers folgt. Ihre Funktion erhält insbesondere zwei Argumente: Das erste stellt die HTTP-Anfrage dar, aus der Sie verschiedene Anfrageparameter und -werte lesen können. Das zweite stellt ein Antwortobjekt dar, an das Sie Ihre Antwortdaten ausgeben. Der Name der Funktion kann beliebig sein. Sie müssen ihn jedoch später für Cloud Run Functions angeben, wie im Abschnitt Bereitstellungsleitfaden beschrieben.

/*** Entry-point for requests ***/
exports.httpHandler = async function httpHandler(req,res) {

Im ersten Abschnitt der Funktion httpHandler werden die verschiedenen Routen deklariert, die von unserer Aktion erkannt werden. Sie spiegeln die erforderlichen Endpunkte der Action API für eine einzelne Aktion und die Funktionen, die jede Route verarbeiten, die später in der Datei definiert werden.

Bei einigen Beispielen für Aktionen + Cloud Run Functions wird für jede Route eine separate Funktion bereitgestellt, um die Standardweiterleitung von Cloud Run Functions zu nutzen. Funktionen können jedoch auch zusätzliche „Unterrouten“ in ihrem Code anwenden, wie hier gezeigt. Das ist letztendlich eine Frage der Präferenz, aber durch dieses zusätzliche Routing im Code wird die Anzahl der Funktionen, die wir bereitstellen müssen, minimiert und wir können einen einheitlichen Codezustand für alle Endpunkte der Actions beibehalten.

    const routes = {
        "/": [hubListing],
        "/status": [hubStatus], // Debugging endpoint. Not required.
        "/action-0/form": [
            requireInstanceAuth,
            action0Form
            ], 
        "/action-0/execute": [
            requireInstanceAuth,
            processRequestBody,
            action0Execute
            ]
        }

Der Rest der HTTP-Handler-Funktion implementiert die Verarbeitung der HTTP-Anfrage anhand der vorhergehenden Routendeklarationen und verbindet die Rückgabewerte dieser Handler mit dem Antwortobjekt.

    try {
        const routeHandlerSequence = routes[req.path] || [routeNotFound]
        for(let handler of routeHandlerSequence) {
            let handlerResponse = await handler(req)
            if (!handlerResponse) continue 
            return res
                .status(handlerResponse.status || 200)
                .json(handlerResponse.body || handlerResponse)
            }
        }
    catch(err) {
        console.error(err)
        res.status(500).json("Unhandled error. See logs for details.")
        }
    }

Nachdem wir uns mit dem HTTP-Handler und den Routendeklarationen befasst haben, sehen wir uns die drei wichtigsten Aktionsendpunkte an, die wir implementieren müssen:

Endpunkt für die Aktionsliste

Wenn ein Looker-Administrator eine Looker-Instanz zum ersten Mal mit einem Aktionsserver verbindet, ruft Looker die angegebene URL auf, die als Endpunkt für die Aktionsliste bezeichnet wird, um Informationen zu den Aktionen abzurufen, die über den Server verfügbar sind.

In den Routendeklarationen, die wir zuvor gezeigt haben, haben wir diesen Endpunkt unter dem Stammverzeichnis (/) unter der URL unserer Funktion verfügbar gemacht und angegeben, dass er von der Funktion hubListing verarbeitet wird.

Wie Sie an der folgenden Funktionsdefinition sehen, ist nicht viel „Code“ erforderlich – es werden einfach jedes Mal dieselben JSON-Daten zurückgegeben. Dabei wird die „eigene“ URL dynamisch in einige der Felder eingefügt, sodass die Looker-Instanz spätere Anfragen an dieselbe Funktion senden kann.

async function hubListing(req){
    return {
        integrations: [
            {
                name: "demo-bq-insert",
                label: "Demo BigQuery Insert",
                supported_action_types: ["cell", "query", "dashboard"],
                form_url:`${process.env.CALLBACK_URL_PREFIX}/action-0/form`,
                url: `${process.env.CALLBACK_URL_PREFIX}/action-0/execute`,
                icon_data_uri: "data:image/png;base64,...",
                supported_formats:["inline_json"],
                supported_formattings:["unformatted"],
                required_fields:[
                    // You can use this to make your action available
                    // for specific queries/fields
                    // {tag:"user_id"}
                    ],
                params: [
                    // You can use this to require parameters, either
                    // from the Action's administrative configuration,
                    // or from the invoking user's user attributes. 
                    // A common use case might be to have the Looker
                    // instance pass along the user's identification to
                    // allow you to conditionally authorize the action:
                    {name: "email", label: "Email", user_attribute_name: "email", required: true}
                    ]
                }
            ]
        }
    }

Zu Demozwecken ist für den Abruf dieses Eintrags in unserem Code keine Authentifizierung erforderlich. Wenn Sie die Metadaten Ihrer Aktion jedoch als vertraulich betrachten, können Sie für diesen Pfad auch eine Authentifizierung erforderlich machen, wie im nächsten Abschnitt beschrieben.

Beachten Sie außerdem, dass unsere Cloud Run-Funktion mehrere Aktionen verfügbar machen und verarbeiten kann. Das erklärt unsere Routenkonvention /action-X/.... Unsere Demo-Cloud Run-Funktion implementiert jedoch nur eine Aktion.

Endpunkt für das Aktionsformular

Nicht für alle Anwendungsfälle ist ein Formular erforderlich, aber es passt gut zum Anwendungsfall von Datenbank-Writebacks, da Nutzer Daten in Looker prüfen und dann Werte für das Einfügen in die Datenbank angeben können. Da unsere Aktionsliste einen form_url-Parameter enthält, ruft Looker diesen Aktionsformular-Endpunkt auf, wenn ein Nutzer mit Ihrer Aktion interagiert, um zu ermitteln, welche zusätzlichen Daten vom Nutzer erfasst werden sollen.

In unseren Routendeklarationen haben wir diesen Endpunkt unter dem Pfad /action-0/form verfügbar gemacht und zwei Handler damit verknüpft: requireInstanceAuth und action0Form.

Wir haben unsere Routendeklarationen so eingerichtet, dass mehrere Handler wie dieser möglich sind, da einige Logiken für mehrere Endpunkte wiederverwendet werden können.

Wir sehen beispielsweise, dass requireInstanceAuth für mehrere Routen verwendet wird. Wir verwenden diesen Handler immer dann, wenn wir verlangen möchten, dass eine Anfrage von unserer Looker-Instanz stammt. Der Handler ruft den erwarteten Tokenwert des Secrets aus Secret Manager ab und lehnt alle Anfragen ab, die diesen erwarteten Tokenwert nicht enthalten.

async function requireInstanceAuth(req) {
    const lookerSecret = await getLookerSecret()
    if(!lookerSecret){return}
    const expectedAuthHeader = `Token token="${lookerSecret}"`
    if(!timingSafeEqual(req.headers.authorization,expectedAuthHeader)){
        return {
            status:401,
            body: {error: "Looker instance authentication is required"}
            }
        }
    return

    function timingSafeEqual(a, b) {
        if(typeof a !== "string"){return}
        if(typeof b !== "string"){return}
        var aLen = Buffer.byteLength(a)
        var bLen = Buffer.byteLength(b)
        const bufA = Buffer.allocUnsafe(aLen)
        bufA.write(a)
        const bufB = Buffer.allocUnsafe(aLen) //Yes, aLen
        bufB.write(b)

        return crypto.timingSafeEqual(bufA, bufB) && aLen === bLen;
        }
    }

Wir verwenden eine timingSafeEqual-Implementierung anstelle der standardmäßigen Gleichheitsprüfung (==), um zu verhindern, dass Informationen zum Timing von Seitenkanälen preisgegeben werden, die es einem Angreifer ermöglichen würden, den Wert unseres Geheimnisses schnell herauszufinden.

Wenn eine Anfrage die Instanzauthentifizierungsprüfung besteht, wird sie vom action0Form-Handler verarbeitet.

async function action0Form(req){
    return [
        {name: "choice",  label: "Choose", type:"select", options:[
            {name:"Yes", label:"Yes"},
            {name:"No", label:"No"},
            {name:"Maybe", label:"Maybe"}
            ]},
        {name: "note", label: "Note", type: "textarea"}
        ]
    }

Obwohl unser Demobeispiel sehr statisch ist, kann der Formularcode für bestimmte Anwendungsfälle interaktiver sein. Je nach Auswahl eines Nutzers in einem ersten Drop-down-Menü können beispielsweise unterschiedliche Felder angezeigt werden.

Endpunkt für die Ausführung von Aktionen

Der Großteil der Logik einer Aktion befindet sich im Action Execute-Endpunkt. Dort werden wir uns auch die Logik ansehen, die speziell für den BigQuery-Einfüge-Anwendungsfall gilt.

In unseren Routendeklarationen haben wir diesen Endpunkt unter dem Pfad /action-0/execute verfügbar gemacht und drei Handler damit verknüpft: requireInstanceAuth, processRequestBody und action0Execute.

Wir haben requireInstanceAuth bereits behandelt. Der processRequestBody-Handler führt hauptsächlich uninteressante Vorverarbeitungen durch, um bestimmte unpraktische Felder im Anfragetext von Looker in ein praktischeres Format zu konvertieren. Sie können ihn jedoch in der vollständigen Codedatei aufrufen.

Die Funktion action0Execute beginnt mit Beispielen für das Extrahieren von Informationen aus verschiedenen Teilen der Aktionsanfrage, die nützlich sein könnten. In der Praxis können die Anfrageelemente, auf die sich unser Code als formParams und actionParams bezieht, je nachdem, was Sie in Ihren Listing- und Formular-Endpunkten deklarieren, unterschiedliche Felder enthalten.

async function action0Execute (req){
    try{
        // Prepare some data that we will insert
        const scheduledPlanId = req.body.scheduled_plan && req.body.scheduled_plan.scheduled_plan_id
        const formParams = req.body.form_params || {}
        const actionParams = req.body.data || {}
        const queryData = req.body.attachment.data //If using a standard "push" action

        /*In case any fields require datatype-specific preparation, check this example:
        https://github.com/googleapis/nodejs-bigquery-storage/blob/main/samples/append_rows_proto2.js
        */

        const newRow = {
            invoked_at: new Date(),
            invoked_by: actionParams.email,
            scheduled_plan_id: scheduledPlanId || null,
            query_result_size: queryData.length,
            choice: formParams.choice,
            note: formParams.note,
            }

Der Code geht dann in Standard-BigQuery-Code über, um die Daten tatsächlich einzufügen. Die BigQuery Storage Write APIs bieten auch andere, komplexere Varianten, die sich besser für eine persistente Streamingverbindung oder Bulk-Insert-Vorgänge mit vielen Datensätzen eignen. Für die Reaktion auf einzelne Nutzerinteraktionen im Kontext einer Cloud Run-Funktion ist dies jedoch die direkteste Variante.

await bigqueryConnectAndAppend(newRow)

...

async function bigqueryConnectAndAppend(row){   
    let writerClient
    try{
        const destinationTablePath = `projects/${projectId}/datasets/${datasetId}/tables/${tableId}`
        const streamId = `${destinationTablePath}/streams/_default`
        writerClient = new BQSManagedWriter.WriterClient({projectId})
        const writeMetadata = await writerClient.getWriteStream({
            streamId,
            view: 'FULL',
            })
        const protoDescriptor = BigqueryStorage.adapt.convertStorageSchemaToProto2Descriptor(
            writeMetadata.tableSchema,
            'root'
            )
        const connection = await writerClient.createStreamConnection({
            streamId,
            destinationTablePath,
            })
        const writer = new BQSManagedWriter.JSONWriter({
            streamId,
            connection,
            protoDescriptor,
            })

        let result
        if(row){
            // The API expects an array of rows, so wrap the single row in an array
            const rowsToAppend = [row]
            result = await writer.appendRows(rowsToAppend).getResult()
            }
        return {
            streamId: connection.getStreamId(),
            protoDescriptor,
            result
            }
        }
    catch (e) {throw e}
    finally{
        if(writerClient){writerClient.close()}
        }
    }

Der Democode enthält auch einen „status“-Endpunkt für die Fehlerbehebung. Dieser Endpunkt ist jedoch für die Action API-Integration nicht erforderlich.

Bereitstellungsanleitung

Schließlich stellen wir eine Schritt-für-Schritt-Anleitung für die Bereitstellung der Demo bereit, die Voraussetzungen, die Bereitstellung von Cloud Run-Funktionen, die BigQuery-Konfiguration und die Looker-Konfiguration abdeckt.

Projekt- und Dienstvoraussetzungen

Bevor Sie mit der Konfiguration beginnen, sollten Sie sich diese Liste ansehen, um zu verstehen, welche Dienste und Richtlinien für die Lösung erforderlich sind:

  1. Neues Projekt:Sie benötigen ein neues Projekt, in dem die Ressourcen aus unserem Beispiel untergebracht werden.
  2. Dienste:Wenn Sie BigQuery und Cloud Run-Funktionen zum ersten Mal in der Cloud Console-UI verwenden, werden Sie aufgefordert, die erforderlichen APIs für die erforderlichen Dienste zu aktivieren, darunter BigQuery, Artifact Registry, Cloud Build, Cloud Functions, Cloud Logging, Pub/Sub, Cloud Run Admin und Secret Manager.
  3. Richtlinie für nicht authentifizierte Aufrufe:Für diesen Anwendungsfall müssen wir Cloud Run-Funktionen bereitstellen, die nicht authentifizierte Aufrufe zulassen, da wir die Authentifizierung für eingehende Anfragen in unserem Code gemäß der Action API und nicht mit IAM verarbeiten. Dies ist zwar standardmäßig zulässig, wird aber häufig durch Organisationsrichtlinien eingeschränkt. Insbesondere schränkt die constraints/iam.allowedPolicyMemberDomains-Richtlinie ein, wem IAM-Berechtigungen gewährt werden können. Möglicherweise müssen Sie sie anpassen, damit das allUsers-Hauptkonto für nicht authentifizierten Zugriff zugelassen wird. Weitere Informationen finden Sie in diesem Leitfaden: Öffentliche Cloud Run-Dienste erstellen, wenn die Freigabe der Domain beschränkt ist.
  4. Andere Richtlinien:Denken Sie daran, dass andere Google Cloud Einschränkungen für Organisationsrichtlinien auch die Bereitstellung von Diensten verhindern können, die ansonsten standardmäßig zulässig sind.

Cloud Run-Funktion bereitstellen

Nachdem Sie ein neues Projekt erstellt haben, gehen Sie so vor, um die Cloud Run-Funktion bereitzustellen:

  1. Klicken Sie unter Cloud Run Functions auf Funktion erstellen.
  2. Wählen Sie einen beliebigen Namen für Ihre Funktion aus, z. B. „demo-bq-insert-action“.
  3. Unter Trigger-Einstellungen:
    1. Der Triggertyp sollte bereits „HTTPS“ sein.
    2. Stellen Sie Authentifizierung auf Nicht authentifizierte Aufrufe zulassen ein.
    3. Kopieren Sie den Wert unter URL in die Zwischenablage.
  4. Unter den Einstellungen Laufzeit > Umgebungsvariablen der Laufzeit:
    1. Klicken Sie auf Variable hinzufügen.
    2. Legen Sie CALLBACK_URL_PREFIX als Variablennamen fest.
    3. Fügen Sie die URL aus dem vorherigen Schritt als Wert ein.
  5. Klicken Sie auf Weiter.
  6. Klicken Sie auf die Datei package.json und fügen Sie den Inhalt ein.
  7. Klicken Sie auf die Datei index.js und fügen Sie den Inhalt ein.
  8. Weisen Sie der Variablen projectId oben in der Datei Ihre eigene Projekt-ID zu.
  9. Legen Sie den Einstiegspunkt auf httpHandler fest.
  10. Klicken Sie auf Bereitstellen.
  11. Gewähren Sie dem Build-Dienstkonto die angeforderten Berechtigungen (falls vorhanden).
  12. Warten Sie, bis die Bereitstellung abgeschlossen ist.
  13. Wenn Sie in einem der folgenden Schritte eine Fehlermeldung erhalten, in der Sie aufgefordert werden, die Google Cloud -Logs zu prüfen, können Sie auf dieser Seite über den Tab Logs auf die Logs für diese Funktion zugreifen.
  14. Bevor Sie die Seite Ihrer Cloud Run-Funktion verlassen, suchen Sie auf dem Tab Details nach dem Dienstkonto der Funktion und notieren Sie es. Wir verwenden diese in späteren Schritten, um sicherzustellen, dass die Funktion die erforderlichen Berechtigungen hat.
  15. Testen Sie die Bereitstellung Ihrer Funktion direkt in Ihrem Browser, indem Sie die URL aufrufen. Sie sollten eine JSON-Antwort mit Ihrem Integrationsangebot sehen.
  16. Wenn Sie einen 403-Fehler erhalten, ist Ihr Versuch, Nicht authentifizierte Aufrufe zulassen festzulegen, möglicherweise aufgrund einer Organisationsrichtlinie fehlgeschlagen. Prüfen Sie, ob Ihre Funktion nicht authentifizierte Aufrufe zulässt, sehen Sie sich die Einstellung der Organisationsrichtlinie an und versuchen Sie, die Einstellung zu aktualisieren.

Zugriff auf die BigQuery-Zieltabelle

In der Praxis kann sich die Zieltabelle, in die eingefügt werden soll, in einem anderen Google Cloud Projekt befinden. Zu Demonstrationszwecken erstellen wir jedoch eine neue Zieltabelle in unserem Projekt. In beiden Fällen müssen Sie dafür sorgen, dass das Dienstkonto Ihrer Cloud Run-Funktion die Berechtigung zum Schreiben in die Tabelle hat.

  1. Rufen Sie die BigQuery-Konsole auf.
  2. Erstellen Sie die Demotabelle:

    1. Klicken Sie in der Explorer-Leiste neben Ihrem Projekt auf das Dreipunkt-Menü und wählen Sie Dataset erstellen aus.
    2. Geben Sie Ihrem Dataset die ID demo_dataset und klicken Sie auf Dataset erstellen.
    3. Klicken Sie im neu erstellten Dataset auf das Dreipunkt-Menü und wählen Sie Tabelle erstellen aus.
    4. Geben Sie der Tabelle den Namen demo_table.
    5. Wählen Sie unter Schema die Option Als Text bearbeiten aus, verwenden Sie das folgende Schema und klicken Sie dann auf Tabelle erstellen.

      [
       {"name":"invoked_at","type":"TIMESTAMP"},
       {"name":"invoked_by","type":"STRING"},
       {"name":"scheduled_plan_id","type":"STRING"},
       {"name":"query_result_size","type":"INTEGER"},
       {"name":"choice","type":"STRING"},
       {"name":"note","type":"STRING"}
      ]
      
  3. Berechtigungen zuweisen:

    1. Klicken Sie in der Leiste Explorer auf Ihr Dataset.
    2. Klicken Sie auf der Seite Dataset auf Freigabe > Berechtigungen.
    3. Klicken Sie auf Hauptkonto hinzufügen.
    4. Legen Sie das Neue Hauptkonto auf das Dienstkonto für Ihre Funktion fest, das Sie zuvor auf dieser Seite notiert haben.
    5. Weisen Sie die Rolle BigQuery-Datenbearbeiter zu.
    6. Klicken Sie auf Speichern.

Verbindung zu Looker herstellen

Nachdem Ihre Funktion bereitgestellt wurde, verbinden wir Looker damit.

  1. Wir benötigen ein gemeinsames Secret für Ihre Aktion, um zu authentifizieren, dass Anfragen von Ihrer Looker-Instanz stammen. Generieren Sie einen langen zufälligen String und bewahren Sie ihn sicher auf. Wir verwenden ihn in den folgenden Schritten als Looker-Secret-Wert.
  2. Rufen Sie in der Cloud Console Secret Manager auf.
    1. Klicken Sie auf Secret erstellen.
    2. Legen Sie als Name LOOKER_SECRET fest. (Dieser Name ist im Code für diese Demo hartcodiert, Sie können aber bei der Arbeit mit Ihrem eigenen Code einen beliebigen Namen auswählen.)
    3. Legen Sie den Secret Value auf den von Ihnen generierten geheimen Wert fest.
    4. Klicken Sie auf Secret erstellen.
    5. Klicken Sie auf der Seite Secret auf den Tab Berechtigungen.
    6. Klicken Sie auf Zugriff gewähren.
    7. Legen Sie Neue Hauptkonten auf das Dienstkonto für Ihre Funktion fest, das Sie sich zuvor notiert haben.
    8. Weisen Sie die Rolle Secret Manager Secret Accessor zu.
    9. Klicken Sie auf Speichern.
    10. Sie können bestätigen, dass Ihre Funktion erfolgreich auf das Secret zugreift, indem Sie die Route /status an die URL Ihrer Funktion anhängen.
  3. In Ihrer Looker-Instanz:
    1. Rufen Sie „Verwaltung“ > „Plattform“ > „Aktionen“ auf.
    2. Klicken Sie unten auf der Seite auf Aktionshub hinzufügen.
    3. Geben Sie die URL Ihrer Funktion an (z. B. https://your-region-your-project.cloudfunctions.net/demo-bq-insert-action) und bestätigen Sie dies, indem Sie auf Action Hub hinzufügen klicken.
    4. Sie sollten nun einen neuen Action Hub-Eintrag mit einer Aktion namens Demo BigQuery Insert sehen.
    5. Klicken Sie im Action Hub-Eintrag auf Autorisierung konfigurieren.
    6. Geben Sie Ihr generiertes Looker Secret in das Feld Authorization Token (Autorisierungstoken) ein und klicken Sie auf Update Token (Token aktualisieren).
    7. Klicken Sie bei der Aktion Demo BigQuery Insert auf Aktivieren.
    8. Stellen Sie den Schalter Aktiviert auf „Ein“.
    9. Ein Test der Aktion sollte automatisch ausgeführt werden, um zu bestätigen, dass Ihre Funktion die Anfrage von Looker akzeptiert und korrekt auf den Formular-Endpunkt antwortet.
    10. Klicken Sie auf Speichern.

End-to-End-Test

Wir sollten unsere neue Aktion jetzt verwenden können. Diese Aktion ist für jede Abfrage konfiguriert. Wählen Sie also ein beliebiges Explore aus (z. B. ein integriertes Systemaktivitäts-Explore), fügen Sie einer neuen Abfrage einige Felder hinzu, führen Sie sie aus und wählen Sie dann im Zahnradmenü Senden aus. Die Aktion sollte als eines der verfügbaren Ziele angezeigt werden. Sie werden aufgefordert, einige Felder auszufüllen:

Screenshot des modalen Dialogfelds „Senden“ in Looker mit der neuen Aktion

Wenn Sie auf Senden klicken, sollte eine neue Zeile in Ihre BigQuery-Tabelle eingefügt werden und die E-Mail-Adresse Ihres Looker-Nutzerkontos in der Spalte invoked_by angezeigt werden.