Questa pagina fornisce esempi di codice scritti in React e JavaScript per le funzioni comuni che potresti voler utilizzare nelle tue estensioni.
Utilizzare l'SDK Looker Extension
Le estensioni devono stabilire una connessione con l'host Looker. In React, questa operazione viene eseguita aggregando l'estensione in un componente ExtensionProvider40
. Questo componente stabilisce una connessione con l'host di Looker e rende disponibili per l'estensione l'SDK di Looker Extension e l'SDK di Looker.
import React from 'react'
import { ExtensionProvider40 } from '@looker/extension-sdk-react'
import { DemoCoreSDK } from './DemoCoreSDK'
export const App = () => {
return (
<ExtensionProvider40 chattyTimeout={-1}>
<DemoCoreSDK />
</ExtensionProvider40>
)
}
Informazioni sui fornitori di estensioni
I provider di estensioni espongono alle estensioni l'SDK delle estensioni di Looker e l'API SDK. Da quando è stato creato il framework dell'estensione, sono state create versioni diverse del provider di estensioni. Questa sezione spiega la cronologia dei provider di estensioni e perché ExtensionProvider40 è il provider consigliato.
Il primo provider di estensioni era ExtensionProvider
, che esponeva entrambi gli SDK di Looker, nelle versioni 3.1 e 4.0. Lo svantaggio era che l'inclusione di entrambi gli SDK aumentava le dimensioni del bundle di produzione finale.
È stata quindi creata la pagina ExtensionProvider2
. È stato creato perché non aveva senso che un'estensione utilizzasse entrambi gli SDK e costringe lo sviluppatore a scegliere uno o l'altro. Purtroppo, per questo motivo, entrambi gli SDK sono stati inclusi nelle dimensioni del bundle di produzione finale.
Quando l'SDK 4.0 è passato a GA, è stato creato ExtensionProvider40
. Il vantaggio di ExtensionProvider40
è che lo sviluppatore non deve scegliere quale SDK utilizzare, perché l'SDK 4.0 è l'unica versione disponibile. Poiché l'SDK 3.1 non è incluso nel bundle finale, questo ha il vantaggio di ridurre le dimensioni del bundle.
Per aggiungere funzioni dall'SDK dell'estensione Looker, devi prima ottenere un riferimento all'SDK, che può essere eseguito dal provider o a livello globale. A questo punto puoi chiamare le funzioni dell'SDK come faresti in qualsiasi applicazione JavaScript.
- Per accedere all'SDK dal provider:
import { ExtensionContext40 } from '@looker/extension-sdk-react'
export const Comp1 = () => {
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK, coreSDK } = extensionContext
- Per accedere all'SDK a livello globale (l'estensione deve essere inizializzata prima della chiamata), segui questi passaggi:
const coreSDK = getCoreSDK()
Ora puoi utilizzare l'SDK come faresti in qualsiasi applicazione JavaScript:
const GetLooks = async () => {
try {
const looks = await sdk.ok(sdk.all_looks('id'))
// process looks
. . .
} catch (error) {
// do error handling
. . .
}
}
Navigazione in un altro punto dell'istanza di Looker
Poiché l'estensione viene eseguita in un iframe con sandbox, non puoi spostarti altrove all'interno dell'istanza di Looker aggiornando l'oggetto window.location
dell'elemento principale. È possibile navigare utilizzando l'SDK dell'estensione Looker.
Questa funzione richiede il diritto navigation
.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
extensionSDK.updateLocation('/browse')
Apertura di una nuova finestra del browser
Poiché l'estensione viene eseguita in un iframe con sandbox, non puoi utilizzare la finestra principale per aprire una nuova finestra del browser. È possibile aprire una finestra del browser utilizzando l'SDK di estensione di Looker.
Questa funzione richiede il dirietto new_window
per aprire una nuova finestra in una posizione nell'istanza Looker corrente o il dirietto new_window_external_urls
per aprire una nuova finestra che viene eseguita su un host diverso.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
extensionSDK.openBrowserWindow('/browse', '_blank')
. . .
extensionSDK.openBrowserWindow('https://docs.looker.com/reference/manifest-params/application#entitlements', '_blank')
Routing e link diretti
Quanto segue si applica alle estensioni basate su React.
I componenti ExtensionProvider
, ExtensionProvider2
e ExtensionProvider40
creano automaticamente un router React chiamato MemoryRouter
che puoi utilizzare. Non tentare di creare un BrowserRouter
, in quanto non funziona negli iframe con sandbox. Non tentare di creare un HashRouter
, in quanto non funziona negli iframe con sandbox per la versione non basata su Chromium del browser Microsoft Edge.
Se utilizzi MemoryRouter
e nell'estensione usi react-router
, il framework di estensione sincronizzerà automaticamente il router dell'estensione con il router host di Looker. Ciò significa che quando la pagina viene ricaricata, l'estensione riceverà una notifica relativa ai clic sui pulsanti avanti e indietro nel browser e del percorso corrente. Ciò significa anche che l'estensione dovrebbe supportare automaticamente i link diretti. Consulta gli esempi di estensioni per scoprire come utilizzare react-router
.
Dati contesto estensione
I dati di contesto del framework di estensioni non devono essere confusi con i contesti di React.
Le estensioni hanno la possibilità di condividere i dati di contesto tra tutti gli utenti di un'estensione. I dati di contesto possono essere utilizzati per i dati che non cambiano di frequente e che non hanno requisiti di sicurezza speciali. È necessario prestare attenzione durante la scrittura dei dati, in quanto non è previsto il blocco dei dati e l'ultima scrittura ha la precedenza. I dati di contesto sono disponibili per l'estensione immediatamente all'avvio. L'SDK dell'estensione Looker fornisce funzioni per consentire l'aggiornamento e l'aggiornamento dei dati di contesto.
La dimensione massima dei dati di contesto è di circa 16 MB. I dati di contesto verranno serializzati in una stringa JSON, quindi questo aspetto deve essere preso in considerazione anche se utilizzi i dati di contesto per l'estensione.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
// Get loaded context data. This will reflect any updates that have
// been made by saveContextData.
let context = await extensionSDK.getContextData()
. . .
// Save context data to Looker server.
context = await extensionSDK.saveContextData(context)
. . .
// Refresh context data from Looker server.
context = await extensionSDK.refreshContextData()
Attributi utente
L'SDK Looker Extension fornisce un'API per accedere agli attributi utente di Looker. Esistono due tipi di accesso agli attributi utente:
Con ambito: associato all'estensione. Un attributo utente basato su ambito è associato allo spazio dei nomi dell'estensione e deve essere definito nell'istanza di Looker prima di poter essere utilizzato. Per assegnare lo spazio dei nomi a un attributo utente, fai precedere il nome dell'attributo dal nome dell'estensione. Eventuali trattini e i caratteri "::" nel nome dell'estensione devono essere sostituiti da un trattino basso, poiché i trattini e i due punti non possono essere utilizzati nei nomi degli attributi utente.
Ad esempio, un attributo utente con ambito denominato
my_value
utilizzato con un ID estensionemy-extension::my-extension
deve avere un nome dell'attributo utentemy_extension_my_extension_my_value
definito. Una volta definito, l'attributo utente può essere letto e aggiornato dall'estensione.Globali: si tratta di attributi utente globali e sono di sola lettura. Un esempio è l'attributo utente
locale
.
Di seguito è riportato un elenco di chiamate API degli attributi utente:
userAttributeGetItem
: legge un attributo utente. È possibile definire un valore predefinito che verrà utilizzato se non esiste un valore dell'attributo utente per l'utente.userAttributeSetItem
: salva un attributo utente per l'utente corrente. Non riuscirà per gli attributi utente globali. Il valore salvato è visibile solo all'utente corrente.userAttributeResetItem
: reimposta un attributo utente per l'utente corrente sul valore predefinito. Non avrà esito positivo per gli attributi utente globali.
Per accedere agli attributi utente, devi specificare i nomi degli attributi nei diritti global_user_attributes
e/o scoped_user_attributes
. Ad esempio, nel file manifest del progetto LookML, aggiungi:
entitlements: {
scoped_user_attributes: ["my_value"]
global_user_attributes: ["locale"]
}
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
// Read global user attribute
const locale = await extensionSDK.userAttributeGetItem('locale')
// Read scoped user attribute
const value = await extensionSDK.userAttributeGetItem('my_value')
// Update scoped user attribute
const value = await extensionSDK.userAttributeSetItem('my_value', 'abcd1234')
// Reset scoped user attribute
const value = await extensionSDK.userAttributeResetItem('my_value')
Conservazione locale
Gli iframe con sandbox non consentono l'accesso allo spazio di archiviazione locale del browser. L'SDK di estensione di Looker consente a un'estensione di leggere e scrivere nello spazio di archiviazione locale della finestra principale. Lo spazio di archiviazione locale ha uno spazio dei nomi all'estensione, il che significa che non può leggere lo spazio di archiviazione locale creato dalla finestra principale o da altre estensioni.
Per utilizzare lo spazio di archiviazione locale è necessario il diritto local_storage
.
L'API localhost dell'estensione è asincrona, al contrario dell'API di archiviazione locale del browser sincrono.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
// Read from local storage
const value = await extensionSDK.localStorageGetItem('my_storage')
// Write to local storage
await extensionSDK.localStorageSetItem('my_storage', 'abcedefh')
// Delete item from local storage
await extensionSDK.localStorageRemoveItem('my_storage')
Aggiornamento del titolo della pagina
Le estensioni potrebbero aggiornare il titolo della pagina corrente. Per eseguire questa azione non sono necessari diritti.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
extensionSDK.updateTitle('My Extension Title')
Scrittura negli appunti di sistema in corso...
Gli iframe con sandbox non consentono l'accesso agli appunti di sistema. L'SDK di estensione Looker consente a un'estensione di scrivere testo negli appunti di sistema. Per motivi di sicurezza, l'estensione non è autorizzata a leggere dagli appunti di sistema.
Per scrivere negli appunti di sistema, devi disporre del diririttto use_clipboard
.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
// Write to system clipboard
try {
await extensionSDK.clipboardWrite(
'My interesting information'
)
. . .
} catch (error) {
. . .
}
Incorporamento di dashboard, Look ed esplorazioni
Il framework di estensione supporta l'incorporamento di dashboard, Look ed esplorazioni.
Il diritto use_embeds
è obbligatorio. Ti consigliamo di utilizzare l'SDK di incorporamento JavaScript di Looker per incorporare i contenuti. Per ulteriori informazioni, consulta la documentazione relativa all'incorporamento dell'SDK.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
const canceller = (event: any) => {
return { cancel: !event.modal }
}
const updateRunButton = (running: boolean) => {
setRunning(running)
}
const setupDashboard = (dashboard: LookerEmbedDashboard) => {
setDashboard(dashboard)
}
const embedCtrRef = useCallback(
(el) => {
const hostUrl = extensionContext?.extensionSDK?.lookerHostData?.hostUrl
if (el && hostUrl) {
el.innerHTML = ''
LookerEmbedSDK.init(hostUrl)
const db = LookerEmbedSDK.createDashboardWithId(id as number)
.withNext()
.appendTo(el)
.on('dashboard:loaded', updateRunButton.bind(null, false))
.on('dashboard:run:start', updateRunButton.bind(null, true))
.on('dashboard:run:complete', updateRunButton.bind(null, false))
.on('drillmenu:click', canceller)
.on('drillmodal:explore', canceller)
.on('dashboard:tile:explore', canceller)
.on('dashboard:tile:view', canceller)
.build()
.connect()
.then(setupDashboard)
.catch((error: Error) => {
console.error('Connection error', error)
})
}
},
[]
)
return (<EmbedContainer ref={embedCtrRef} />)
Gli esempi di estensioni utilizzano componenti con stili per fornire uno stile semplice all'iframe generato. Ad esempio:
import styled from "styled-components"
export const EmbedContainer = styled.div`
width: 100%;
height: 95vh;
& > iframe {
width: 100%;
height: 100%;
}
Accesso agli endpoint API esterni
Il framework di estensione fornisce due metodi per accedere agli endpoint API esterni:
- Il proxy del server: accede all'endpoint tramite il server Looker. Questo meccanismo consente al server Looker di impostare in modo sicuro ID client e chiavi private.
- Il proxy di recupero: accede all'endpoint dal browser dell'utente. Il proxy è l'interfaccia utente di Looker.
In entrambi i casi, devi specificare l'endpoint dell'API esterna nel diritto di external_api_urls
dell'estensione.
Server proxy
L'esempio seguente mostra l'utilizzo del proxy del server per ottenere un token di accesso da utilizzare dal proxy di recupero. L'ID client e il secret devono essere definiti come attributi utente per l'estensione. In genere, quando l'attributo utente è configurato, il valore predefinito è impostato sull'ID client o sul secret.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
const requestBody = {
client_id: extensionSDK.createSecretKeyTag('my_client_id'),
client_secret: extensionSDK.createSecretKeyTag('my_client_secret'),
},
try {
const response = await extensionSDK.serverProxy(
'https://myaccesstokenserver.com/access_token',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(requestBody),
}
)
const { access_token, expiry_date } = response.body
. . .
} catch (error) {
// Error handling
. . .
}
Il nome dell'attributo utente deve essere mappato all'estensione. I trattini devono essere sostituiti da trattini bassi e i caratteri ::
devono essere sostituiti da un trattino basso.
Ad esempio, se il nome della tua estensione è my-extension::my-extension
, gli attributi utente da definire per l'esempio precedente sono i seguenti:
my_extension_my_extension_my_client_id
my_extension_my_extension_'my_client_secret'
Recupera proxy
L'esempio seguente mostra l'utilizzo del proxy di recupero. Utilizza il token di accesso dell'esempio di server proxy precedente.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
try {
const response = await extensionSDK.fetchProxy(
'https://myaccesstokenserver.com/myendpoint',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${accessToken}`,
},
body: JSON.stringify({
some_value: someValue,
another_value: anotherValue,
}),
}
)
// Handle success
. . .
} catch (error) {
// Handle failure
. . .
}
Integrazione OAuth
Il framework di estensione supporta l'integrazione con i provider OAuth. È possibile utilizzare OAuth per ottenere un token di accesso per accedere a una determinata risorsa, ad esempio un documento di Fogli Google.
Dovrai specificare l'endpoint del server OAuth nel diritto extension oauth2_urls
. Potresti anche dover specificare altri URL nel diritto external_api_urls
.
I framework di estensione supportano i seguenti flussi:
- Flusso implicito
- Tipo di concessione del codice di autorizzazione con chiave segreta
- Verificatore e richiesta di codice PKCE
Il flusso generale prevede l'apertura di una finestra secondaria che carica una pagina del server OAuth. Il server OAuth autentica l'utente e reindirizza nuovamente al server Looker con ulteriori dettagli che possono essere utilizzati per ottenere un token di accesso.
Flusso implicito:
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
const response = await extensionSDK.oauth2Authenticate(
'https://accounts.google.com/o/oauth2/v2/auth',
{
client_id: GOOGLE_CLIENT_ID!,
scope: GOOGLE_SCOPES,
response_type: 'token',
}
)
const { access_token, expires_in } = response
Tipo di concessione del codice di autorizzazione con chiave segreta:
const authenticateParameters: Record<string, string> = {
client_id: GITHUB_CLIENT_ID!,
response_type: 'code',
}
const response = await extensionSDK.oauth2Authenticate(
'https://github.com/login/oauth/authorize',
authenticateParameters,
'GET'
)
const exchangeParameters: Record<string, string> = {
client_id: GITHUB_CLIENT_ID!,
code: response.code,
client_secret: extensionSDK.createSecretKeyTag('github_secret_key'),
}
const codeExchangeResponse = await extensionSDK.oauth2ExchangeCodeForToken(
'https://github.com/login/oauth/access_token',
exchangeParameters
)
const { access_token, error_description } = codeExchangeResponse
Verifica e verifica del codice PKCE:
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
const authRequest: Record<string, string> = {
client_id: AUTH0_CLIENT_ID!,
response_type: 'code',
scope: AUTH0_SCOPES,
code_challenge_method: 'S256',
}
const response = await extensionSDK.oauth2Authenticate(
'https://sampleoauthserver.com/authorize',
authRequest,
'GET'
)
const exchangeRequest: Record<string, string> = {
grant_type: 'authorization_code',
client_id: AUTH0_CLIENT_ID!,
code: response.code,
}
const codeExchangeResponse = await extensionSDK.oauth2ExchangeCodeForToken(
'https://sampleoauthserver.com/login/oauth/token',
exchangeRequest
)
const { access_token, expires_in } = codeExchangeResponse
Spartan
Spartan si riferisce a un metodo per utilizzare l'istanza di Looker come ambiente per esporre le estensioni e solo le estensioni a un insieme designato di utenti. Un utente spartano che accede a un'istanza di Looker visualizzerà il flusso di accesso configurato dall'amministratore di Looker. Una volta autenticato l'utente, gli verrà presentata un'estensione in base al suo attributo utente landing_page
, come mostrato di seguito. L'utente può accedere solo alle estensioni; non possono accedere a nessun'altra parte di Looker. Se l'utente ha accesso a più estensioni, queste controllano la sua capacità di passare alle altre estensioni utilizzando extensionSDK.updateLocation
. Esiste un metodo specifico di SDK estensione Looker per consentire all'utente di uscire dall'istanza di Looker.
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
// Navigate to another extension
extensionSDK.updateLocation('/spartan/another::extension')
. . .
// Logout
extensionSDK.spartanLogout()
Definire gli utenti spartani
Per definire un utente spartano, devi creare un gruppo denominato "Solo estensioni".
Dopo aver selezionato "Solo estensioni" un gruppo creato, vai alla pagina Attributi utente nella sezione Amministratore di Looker e modifica l'attributo utente landing_page
. Seleziona la scheda Valori del gruppo e aggiungi "Solo estensioni" gruppo. Il valore deve essere impostato su /spartan/my_extension::my_extension/
, dove my_extension::my_extension
è l'ID dell'estensione. Ora, quando l'utente accede, viene indirizzato all'estensione designata.
Suddivisione del codice
La suddivisione del codice è una tecnica in cui il codice viene richiesto solo quando necessario. In genere, i chunk di codice sono associati alle route di React, dove ogni route ha il proprio chunk di codice. In React, questo viene fatto con i componenti Suspense
e React.lazy
. Il componente Suspense
mostra un componente di fallback mentre viene caricato il blocco di codice. React.lazy
è responsabile del caricamento del frammento di codice.
Configurazione della suddivisione in codice:
import { AsyncComp1 as Comp1 } from './Comp1.async'
import { AsyncComp1 as Comp2 } from './Comp2.async'
. . .
<Suspense fallback={<div>Loading...</div>}>
<Switch>
<Route path="/comp1">
<Comp1 />
</Route>
<Route path="/comp2">
<Comp2 />
</Route>
</Switch>
<Suspense>
Il componente caricato tramite caricamento lento viene implementato come segue:
import { lazy } from 'react'
const Comp1 = lazy(
async () => import(/* webpackChunkName: "comp1" */ './Comp1')
)
export const AsyncComp1 = () => <Home />
Il componente viene implementato come segue. Il componente deve essere esportato come componente predefinito:
const Comp1 = () => {
return (
<div>Hello World</div>
)
}
export default Comp1
Movimento di alberi
Sebbene gli SDK di Looker supportino attualmente il tree-shaking, questa funzione deve ancora essere migliorata. Modifichiamo continuamente i nostri SDK per migliorare il supporto dell'effetto "tree shaking". Alcune di queste modifiche potrebbero richiedere il refactoring del codice per trarne vantaggio, ma quando necessario, queste modifiche saranno documentate nelle note di rilascio.
Per utilizzare la funzione di scuotimento ad albero, il modulo utilizzato deve essere esportato come esmodule e le funzioni importate devono essere prive di effetti collaterali. L'SDK Looker per TypeScript/JavaScript, la libreria di runtime dell'SDK Looker, i componenti dell'interfaccia utente di Looker, l'SDK di estensioni di Looker e l'SDK di estensioni per React soddisfano tutti questi requisiti.
In un'estensione, utilizza l'SDK Looker 4.0 e il componente ExtensionProvider2
o ExtensionProvider40
dell'SDK Extension for React.
Il seguente codice configura il provider dell'estensione. Dovrai indicare al provider quale SDK vuoi:
import { MyExtension } from './MyExtension'
import { ExtensionProvider40 } from '@looker/extension-sdk-react'
import { Looker40SDK } from '@looker/sdk/lib/4.0/methods'
import { hot } from 'react-hot-loader/root'
export const App = hot(() => {
return (
<ExtensionProvider2 type={Looker40SDK}>
<MyExtension />
</ExtensionProvider2>
)
})
Non utilizzare il seguente stile di importazione nell'estensione:
import * as lookerComponents from `@looker/components`
L'esempio precedente include tutti gli elementi del modulo. Importa solo i componenti di cui hai effettivamente bisogno. Ad esempio:
import { Paragraph } from `@looker/components`
Glossario
- Suddivisione del codice: una tecnica per il caricamento differito di JavaScript finché non è effettivamente necessario. Idealmente, dovresti mantenere le dimensioni del bundle JavaScript caricato inizialmente. Ciò può essere ottenuto utilizzando la suddivisione del codice. Qualsiasi funzionalità non immediatamente richiesta non viene caricata finché non è effettivamente necessaria.
- IDE - Ambiente di sviluppo integrato. Un editor utilizzato per creare e modificare un'estensione. Alcuni esempi sono Visual Studio Code, Intellij e WebStorm.
- Scena: in genere una visualizzazione di pagina in Looker. Le scene vengono associate alle principali strade. A volte una scena contiene scene secondarie che mappano a sottopercorsi all'interno del percorso principale.
- Transpile : il processo di acquisizione del codice sorgente scritto in un linguaggio e di trasformarlo in un altro linguaggio con un livello di astrazione simile. Un esempio è TypeScript in JavaScript.