Writeback BigQuery menggunakan tindakan Looker pada fungsi Cloud Run

Banyak pelanggan Looker ingin memberdayakan pengguna mereka untuk tidak hanya membuat laporan data di data warehouse, tetapi juga menulis kembali dan memperbarui data warehouse tersebut.

Melalui Action API, Looker mendukung kasus penggunaan ini untuk data warehouse atau tujuan apa pun. Halaman dokumentasi ini memandu pelanggan yang menggunakan Google Cloud infrastruktur untuk men-deploy solusi di Cloud Run Functions guna menulis kembali ke BigQuery. Halaman ini membahas beberapa topik berikut:

Pertimbangan solusi

Gunakan daftar pertimbangan ini untuk memvalidasi bahwa solusi ini sesuai dengan kebutuhan Anda.

  • Cloud Run Functions
    • Mengapa Cloud Run Functions? Sebagai penawaran "serverless" Google, Cloud Run Functions adalah pilihan yang tepat untuk kemudahan operasi dan pemeliharaan. Satu hal yang perlu diingat adalah latensi, terutama untuk pemanggilan dingin, mungkin lebih lama daripada solusi yang mengandalkan server khusus.
    • Bahasa dan Runtime Cloud Run Functions mendukung beberapa bahasa dan runtime. Halaman dokumentasi ini akan berfokus pada contoh dalam JavaScript dan Node.js. Namun, konsepnya dapat langsung diterjemahkan ke bahasa dan runtime lain yang didukung.
  • BigQuery
    • Mengapa BigQuery? Meskipun halaman dokumentasi ini mengasumsikan bahwa Anda sudah menggunakan BigQuery, BigQuery adalah pilihan yang tepat untuk data warehouse secara umum. Perhatikan pertimbangan berikut:
      • BigQuery Storage Write API (gRPC): BigQuery menawarkan beberapa antarmuka untuk memperbarui data di data warehouse Anda, termasuk, misalnya, pernyataan Bahasa Manipulasi Data (DML) dalam tugas berbasis SQL. Namun, opsi terbaik untuk penulisan volume tinggi adalah BigQuery Storage Write API (gRPC).
      • Menambahkan, bukan memperbarui: Meskipun solusi ini hanya akan menambahkan baris, bukan memperbarui baris, Anda selalu dapat memperoleh tabel "status saat ini" pada waktu kueri dari log yang hanya ditambahkan, sehingga mensimulasikan pembaruan.
  • Layanan pendukung
    • Secret Manager: Secret Manager menyimpan nilai rahasia untuk memastikan nilai tersebut tidak disimpan di tempat yang terlalu mudah diakses seperti langsung dalam konfigurasi fungsi.
    • Identity and Access Management (IAM): IAM mengotorisasi fungsi untuk mengakses rahasia yang diperlukan saat runtime dan untuk menulis ke tabel BigQuery yang diinginkan.
    • Cloud Build: Meskipun Cloud Build tidak akan dibahas secara mendalam di halaman ini, Cloud Run Functions menggunakannya di latar belakang, dan Anda dapat menggunakan Cloud Build untuk mengotomatiskan pembaruan yang di-deploy secara berkelanjutan ke fungsi Anda dari perubahan pada kode sumber di repositori Git.
  • Autentikasi tindakan dan pengguna
    • Akun Layanan Cloud Run Cara utama dan termudah untuk menggunakan tindakan Looker untuk integrasi dengan aset dan resource pihak pertama organisasi Anda sendiri adalah mengautentikasi permintaan yang berasal dari instance Looker Anda menggunakan mekanisme autentikasi berbasis token Looker Action API, lalu mengotorisasi fungsi untuk memperbarui data di BigQuery menggunakan Akun Layanan.
    • OAuth: Opsi lain, yang tidak dibahas di halaman ini, adalah menggunakan fitur OAuth Looker Action API. Pendekatan ini lebih kompleks dan umumnya tidak diperlukan, tetapi dapat digunakan jika Anda perlu menentukan akses pengguna akhir untuk menulis ke tabel menggunakan IAM, bukan menggunakan akses mereka di Looker atau logika ad hoc dalam kode fungsi Anda.

Panduan kode demo

Kami memiliki satu file yang berisi seluruh logika tindakan demo kami tersedia di GitHub. Di bagian ini, kita akan membahas elemen utama kode.

Kode penyiapan

Bagian pertama memiliki beberapa konstanta demo yang mengidentifikasi tabel yang akan ditulis oleh tindakan tersebut. Di bagian Panduan deployment nanti di halaman ini, Anda akan diberi petunjuk untuk mengganti project ID dengan project ID Anda sendiri, yang akan menjadi satu-satunya modifikasi yang diperlukan pada kode.

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

Bagian berikutnya mendeklarasikan dan menginisialisasi beberapa dependensi kode yang akan digunakan oleh tindakan Anda. Kami memberikan contoh yang mengakses Secret Manager "dalam kode" menggunakan modul Secret Manager Node.js; namun, Anda juga dapat menghilangkan dependensi kode ini dengan menggunakan fitur bawaan Cloud Run Functions untuk mengambil rahasia bagi Anda selama inisialisasinya.

/*** 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

Perhatikan bahwa dependensi @google-cloud yang direferensikan juga dideklarasikan dalam file package.json kami untuk memungkinkan dependensi dimuat sebelumnya dan tersedia untuk runtime Node.js kami. crypto adalah modul Node.js bawaan dan tidak dideklarasikan di package.json.

Penanganan dan perutean permintaan HTTP

Antarmuka utama yang diekspos kode Anda ke runtime Cloud Run Functions adalah fungsi JavaScript yang diekspor yang mengikuti konvensi server web Node.js Express. Secara khusus, fungsi Anda menerima dua argumen: yang pertama mewakili permintaan HTTP, yang dapat Anda gunakan untuk membaca berbagai parameter dan nilai permintaan; dan yang kedua mewakili objek respons, yang dapat Anda gunakan untuk mengeluarkan data respons. Meskipun nama fungsi dapat berupa apa pun yang Anda inginkan, Anda harus memberikan nama tersebut ke Cloud Run Functions nanti, seperti yang dijelaskan di bagian Panduan deployment.

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

Bagian pertama fungsi httpHandler mendeklarasikan berbagai rute yang akan dikenali oleh tindakan kami, yang sangat mirip dengan Action API endpoint yang diperlukan untuk satu tindakan, dan fungsi yang akan menangani setiap rute, yang ditentukan nanti dalam file.

Meskipun beberapa contoh tindakan + Cloud Run Functions men-deploy fungsi terpisah untuk setiap rute tersebut agar selaras satu per satu dengan perutean default Cloud Run Functions, fungsi dapat menerapkan "sub-perutean" tambahan dalam kodenya seperti yang ditunjukkan di sini. Hal ini pada akhirnya merupakan masalah preferensi, tetapi melakukan perutean tambahan dalam kode akan meminimalkan jumlah fungsi yang harus kita deploy dan membantu kita mempertahankan satu status kode yang koheren di semua endpoint tindakan.

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

Bagian lain dari fungsi pengendali HTTP mengimplementasikan penanganan permintaan HTTP terhadap deklarasi rute sebelumnya dan menghubungkan nilai yang ditampilkan dari pengendali tersebut ke objek respons.

    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.")
        }
    }

Setelah deklarasi pengendali dan rute HTTP selesai, kita akan membahas tiga endpoint tindakan utama yang harus kita implementasikan:

Endpoint Daftar Tindakan

Saat administrator Looker pertama kali menghubungkan instance Looker ke server Tindakan, Looker akan memanggil URL yang diberikan, yang disebut sebagai "endpoint Daftar Tindakan," untuk mendapatkan informasi tentang tindakan yang tersedia melalui server.

Dalam deklarasi rute yang kami tunjukkan sebelumnya, kami menyediakan endpoint ini di jalur root (/) di URL fungsi kami, dan menunjukkan bahwa endpoint ini akan ditangani oleh fungsi hubListing.

Seperti yang dapat Anda lihat dari definisi fungsi berikut, tidak ada terlalu banyak "kode" di dalamnya sama sekali - kode tersebut hanya menampilkan data JSON yang sama setiap kali. Satu hal yang perlu diperhatikan adalah bahwa kode tersebut secara dinamis menyertakan URL "sendiri" ke dalam beberapa kolom, sehingga memungkinkan instance Looker mengirim permintaan berikutnya kembali ke fungsi yang sama.

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}
                    ]
                }
            ]
        }
    }

Untuk tujuan demo, kode kami tidak memerlukan autentikasi untuk mengambil listingan ini. Namun, jika Anda menganggap metadata tindakan Anda sensitif, Anda juga dapat mewajibkan autentikasi untuk rute ini, seperti yang ditunjukkan di bagian berikutnya.

Perhatikan juga bahwa fungsi Cloud Run kami dapat mengekspos dan menangani beberapa tindakan, yang menjelaskan konvensi rute /action-X/... kami. Namun, fungsi Cloud Run demo kami hanya akan mengimplementasikan satu tindakan.

Endpoint Formulir Tindakan

Meskipun tidak semua kasus penggunaan memerlukan formulir, formulir sangat cocok dengan kasus penggunaan penulisan kembali database, karena pengguna dapat memeriksa data di Looker, lalu memberikan nilai yang akan dimasukkan ke dalam database. Karena Daftar Tindakan kami menyediakan parameter form_url, Looker akan memanggil endpoint Formulir Tindakan ini saat pengguna mulai berinteraksi dengan tindakan Anda, untuk menentukan data tambahan yang akan diambil dari pengguna.

Dalam deklarasi rute, kami menyediakan endpoint ini di jalur /action-0/form, dan mengaitkan dua pengendali dengannya: requireInstanceAuth dan action0Form.

Kami menyiapkan deklarasi rute untuk mengizinkan beberapa pengendali seperti ini karena beberapa logika dapat digunakan kembali untuk beberapa endpoint.

Misalnya, kita dapat melihat bahwa requireInstanceAuth digunakan untuk beberapa rute. Kami menggunakan pengendali ini di mana pun kami ingin mewajibkan permintaan berasal dari instance Looker kami. Pengendali mengambil nilai token rahasia yang diharapkan dari Secret Manager dan menolak permintaan apa pun yang tidak memiliki nilai token yang diharapkan tersebut.

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;
        }
    }

Perhatikan bahwa kami menggunakan implementasi timingSafeEqual, bukan pemeriksaan kesetaraan standar (==), untuk mencegah kebocoran informasi waktu side-channel yang akan memungkinkan penyerang dengan cepat mengetahui nilai rahasia kami.

Dengan asumsi bahwa permintaan lulus pemeriksaan autentikasi instance, permintaan tersebut kemudian ditangani oleh pengendali action0Form.

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"}
        ]
    }

Meskipun contoh demo kami sangat statis, kode formulir dapat lebih interaktif untuk kasus penggunaan tertentu. Misalnya, bergantung pada pilihan pengguna di drop-down awal, kolom yang berbeda dapat ditampilkan.

Endpoint Eksekusi Tindakan

Endpoint Eksekusi Tindakan adalah tempat sebagian besar logika tindakan berada, dan tempat kita akan membahas logika khusus untuk kasus penggunaan penyisipan BigQuery.

Dalam deklarasi rute, kami menyediakan endpoint ini di jalur /action-0/execute, dan mengaitkan tiga pengendali dengannya: requireInstanceAuth, processRequestBody, dan action0Execute.

Kita sudah membahas requireInstanceAuth, dan pengendali processRequestBody menyediakan pra-pemrosesan yang sebagian besar tidak menarik untuk membuat kolom yang tidak praktis tertentu di isi permintaan Looker menjadi format yang lebih praktis, tetapi Anda dapat merujuknya di file kode lengkap.

Fungsi action0Execute dimulai dengan menampilkan contoh cara mengekstrak informasi dari beberapa bagian permintaan tindakan yang mungkin berguna. Dalam praktiknya, perhatikan bahwa elemen permintaan yang dirujuk kode kami sebagai formParams dan actionParams dapat berisi kolom yang berbeda, bergantung pada apa yang Anda deklarasikan di endpoint Listing dan Formulir.

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,
            }

Kode tersebut kemudian beralih ke beberapa kode BigQuery standar untuk benar-benar menyisipkan data. Perhatikan bahwa BigQuery Storage Write API menawarkan variasi lain yang lebih kompleks dan lebih cocok untuk koneksi streaming persisten atau penyisipan massal banyak data; tetapi, untuk merespons interaksi pengguna individual dalam konteks Cloud Run Functions, ini adalah variasi yang paling langsung.

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()}
        }
    }

Kode demo juga menyertakan endpoint "status" untuk tujuan pemecahan masalah, tetapi endpoint ini tidak diperlukan untuk integrasi Action API.

Panduan deployment

Terakhir, kami akan memberikan panduan langkah demi langkah untuk men-deploy demo bagi Anda sendiri, yang mencakup prasyarat, deployment Cloud Run Functions, konfigurasi BigQuery, dan konfigurasi Looker.

Prasyarat project dan layanan

Sebelum mulai mengonfigurasi hal-hal spesifik, tinjau daftar ini untuk memahami layanan dan kebijakan yang akan diperlukan oleh solusi ini:

  1. Project baru: Anda akan memerlukan project baru untuk menampung resource dari contoh kami.
  2. Layanan: Saat pertama kali menggunakan BigQuery dan Cloud Run Functions di UI Konsol Cloud, Anda akan diminta untuk mengaktifkan API yang diperlukan untuk layanan yang diperlukan, termasuk BigQuery, Artifact Registry, Cloud Build, Cloud Functions, Cloud Logging, Pub/Sub, Admin Cloud Run, dan Secret Manager.
  3. Kebijakan untuk pemanggilan yang tidak diautentikasi: Kasus penggunaan ini mengharuskan kita men-deploy Cloud Run Functions yang "mengizinkan akses publik", karena kita akan menangani autentikasi untuk permintaan masuk dalam kode kita sesuai dengan Action API, bukan menggunakan IAM. Meskipun diizinkan secara default, kebijakan organisasi sering kali membatasi penggunaan ini. Secara khusus, kebijakan constraints/iam.allowedPolicyMemberDomains membatasi siapa yang dapat diberikan izin IAM, dan Anda mungkin perlu menyesuaikannya untuk mengizinkan akun utama allUsers untuk akses yang tidak diautentikasi. Lihat panduan ini, Cara membuat layanan Cloud Run publik saat Fitur Berbagi yang Dibatasi Domain diterapkan untuk mengetahui informasi selengkapnya jika Anda tidak dapat mengizinkan akses publik.
  4. Kebijakan lainnya: Perlu diingat bahwa Google Cloud batasan kebijakan organisasi lainnya juga dapat mencegah deployment layanan yang diizinkan secara default.

Men-deploy Cloud Run Functions

Setelah membuat project baru, ikuti langkah-langkah berikut untuk men-deploy Cloud Run Functions

  1. Di Cloud Run Functions, klik Create Function.
  2. Pilih nama apa pun untuk fungsi Anda (misalnya, "demo-bq-insert-action").
  3. Di bagian setelan Trigger :
    1. Jenis pemicu seharusnya sudah "HTTPS".
    2. Tetapkan Authentication ke Allow unauthenticated invocations.
    3. Salin nilai URL ke papan klip Anda.
  4. Di bagian setelan Runtime > Runtime environment variables :
    1. Klik Add variable.
    2. Tetapkan nama variabel ke CALLBACK_URL_PREFIX.
    3. Tempel URL dari langkah sebelumnya sebagai nilai.
  5. Klik Next.
  6. Klik file package.json, lalu tempel kontennya.
  7. Klik file index.js, lalu tempel kontennya.
  8. Tetapkan variabel projectId di bagian atas file ke project ID Anda sendiri.
  9. Tetapkan Entry Point ke httpHandler.
  10. Klik Deploy.
  11. Berikan izin yang diminta (jika ada) ke Akun Layanan build.
  12. Tunggu hingga deployment selesai.
  13. Jika, pada langkah berikutnya, Anda mendapatkan error yang mengarahkan Anda untuk meninjau log Google Cloud , perhatikan bahwa Anda dapat mengakses log untuk fungsi ini dari tab Logs di halaman ini.
  14. Sebelum keluar dari halaman Cloud Run Functions, di tab Details, temukan dan catat Service Account yang dimiliki fungsi tersebut. Kita akan menggunakannya pada langkah berikutnya untuk memastikan bahwa fungsi tersebut memiliki izin yang diperlukan.
  15. Uji deployment fungsi Anda langsung di browser dengan membuka URL. Anda akan melihat respons JSON yang berisi listingan integrasi Anda.
  16. Jika Anda mendapatkan error 403, upaya Anda untuk menetapkan Allow unauthenticated invocations mungkin gagal tanpa pemberitahuan sebagai akibat dari kebijakan organisasi. Periksa apakah fungsi Anda mengizinkan pemanggilan yang tidak diautentikasi, tinjau setelan kebijakan organisasi Anda, dan coba perbarui setelan tersebut.

Akses ke tabel tujuan BigQuery

Dalam praktiknya, tabel tujuan yang akan disisipkan dapat berada di project Google Cloud yang berbeda; tetapi, untuk tujuan demonstrasi, kita akan membuat tabel tujuan baru di project yang sama. Dalam kedua kasus tersebut, Anda harus memastikan bahwa Akun Layanan Cloud Run Functions Anda memiliki izin untuk menulis ke tabel.

  1. Buka konsol BigQuery.
  2. Buat tabel demo:

    1. Di panel Explorer, gunakan menu elipsis di samping project Anda, lalu pilih Create dataset.
    2. Beri set data Anda ID demo_dataset, lalu klik Create dataset.
    3. Gunakan menu elipsis di set data yang baru Anda buat, lalu pilih Create table.
    4. Beri tabel Anda nama demo_table.
    5. Di bagian Schema, pilih Edit as text, gunakan skema berikut, lalu klik Create table.

      [
       {"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. Tetapkan izin:

    1. Di panel Explorer, klik set data Anda.
    2. Di halaman dataset, klik Sharing > Permissions.
    3. Klik Add Principal.
    4. Tetapkan New Principal ke Akun Layanan untuk fungsi Anda, yang dicatat sebelumnya di halaman ini.
    5. Tetapkan peran BigQuery Data Editor.
    6. Klik Save.

Menghubungkan ke Looker

Setelah fungsi Anda di-deploy, kita akan menghubungkan Looker ke fungsi tersebut.

  1. Kita akan memerlukan rahasia bersama agar tindakan Anda dapat mengautentikasi bahwa permintaan berasal dari instance Looker Anda. Buat string acak yang panjang dan jaga keamanannya. Kita akan menggunakannya pada langkah berikutnya sebagai nilai Looker secret.
  2. Di Konsol Cloud, buka Secret Manager.
    1. Klik Create Secret.
    2. Tetapkan Name ke LOOKER_SECRET. (Nama ini dikodekan secara permanen dalam kode untuk demo ini, tetapi Anda dapat memilih nama apa pun saat menggunakan kode Anda sendiri.)
    3. Tetapkan Secret Value ke nilai rahasia yang Anda buat.
    4. Klik Create Secret.
    5. Di halaman Secret, klik tab Permissions.
    6. Klik Grant Access.
    7. Tetapkan New Principals ke Akun Layanan untuk fungsi Anda, yang dicatat sebelumnya.
    8. Tetapkan peran Secret Manager Secret Accessor.
    9. Klik Save.
    10. Anda dapat mengonfirmasi bahwa fungsi Anda berhasil mengakses rahasia dengan membuka rute /status yang ditambahkan ke URL fungsi Anda.
  3. Di instance Looker Anda:
    1. Buka Admin > Platform > Actions.
    2. Buka bagian bawah halaman untuk mengklik Add Action Hub.
    3. Berikan URL fungsi Anda (misalnya, https://your-region-your-project.cloudfunctions.net/demo-bq-insert-action), lalu konfirmasi dengan mengklik Add Action Hub.
    4. Anda akan melihat entri Action Hub baru dengan satu tindakan bernama Demo BigQuery Insert.
    5. Di entri Action Hub, klik Configure Authorization.
    6. Masukkan Looker Secret yang Anda buat ke kolom Authorization Token, lalu klik Update Token.
    7. Pada tindakan Demo BigQuery Insert, klik Enable.
    8. Alihkan tombol Enabled ke aktif.
    9. Pengujian tindakan akan otomatis berjalan, yang mengonfirmasi bahwa fungsi Anda menerima permintaan Looker dan merespons endpoint formulir dengan benar.
    10. Klik Save.

Pengujian menyeluruh

Sekarang kita dapat menggunakan tindakan baru kita. Tindakan ini dikonfigurasi untuk berfungsi dengan kueri apa pun, jadi pilih Eksplorasi apa pun (misalnya, Eksplorasi Aktivitas Sistem bawaan), tambahkan beberapa kolom ke kueri baru, eksekusi, lalu pilih Send dari menu roda gigi. Anda akan melihat tindakan tersebut sebagai salah satu tujuan yang tersedia, dan Anda akan diminta untuk memberikan beberapa input kolom:

Screenshot modal 'Kirim' Looker dengan tindakan baru kami dipilih

Setelah menekan Send, Anda akan memiliki baris baru yang disisipkan ke dalam tabel BigQuery (dan memiliki email akun pengguna Looker Anda yang diidentifikasi di kolom invoked_by)!