In questa pagina vengono forniti esempi di codice scritti in React e JavaScript per le funzioni comuni che ti consigliamo di utilizzare nelle estensioni.
Utilizzo dell'SDK dell'estensione di Looker
Per aggiungere funzioni dall'SDK Looker Extension, devi prima ottenere un riferimento all'SDK, che può essere fatto dal provider o a livello globale. A questo punto, puoi chiamare le funzioni dell'SDK come faresti con qualsiasi applicazione JavaScript.
- Per accedere all'SDK dal fornitore:
import { ExtensionContext2 } from '@looker/extension-sdk-react'
export const Comp1 = () => {
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK, coreSDK } = extensionContext
- Per accedere all'SDK a livello globale (l'estensione deve essere inizializzata prima che venga richiamata):
const coreSDK = getCoreSDK2()
Ora puoi utilizzare l'SDK come faresti con qualsiasi applicazione JavaScript:
const GetLooks = async () => {
try {
const looks = await sdk.ok(sdk.all_looks('id'))
// process looks
. . .
} catch (error) {
// do error handling
. . .
}
}
Navigazione altrove nell'istanza di Looker
Poiché l'estensione viene eseguita in un iframe con sandbox, non puoi spostarti altrove nell'istanza di Looker aggiornando l'oggetto window.location
dell'elemento padre. È possibile navigare utilizzando l'SDK Looker Extension.
Questa funzione richiede il diritto navigation
.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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 Looker Extension.
Questa funzione richiede il diritto new_window
per aprire una nuova finestra in una posizione nell'istanza di Looker corrente oppure il diritto new_window_external_urls
per aprire una nuova finestra che viene eseguita su un altro host.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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 reazioni.
I componenti ExtensionProvider
e ExtensionProvider2
creano automaticamente un router React chiamato MemoryRouter
. Non tentare di creare un elemento BrowserRouter
perché non funziona negli iframe con sandbox. Non tentare di creare un elemento HashRouter
perché non funziona negli iframe con sandbox per la versione non basata su Chromium del browser Microsoft Edge.
Se viene utilizzato MemoryRouter
e utilizzi react-router
nell'estensione, il framework dell'estensione sincronizzerà automaticamente il router dell'estensione con il router host di Looker. Ciò significa che l'estensione riceverà una notifica dei clic sul pulsante Indietro e Avanti del browser e la route corrente quando la pagina viene ricaricata. Ciò significa anche che l'estensione dovrebbe supportare automaticamente i link diretti. Guarda gli esempi di estensione su come utilizzare react-router
.
Dati contesto estensione
I dati contesto del contesto dell'estensione non devono essere confusi con i contesti reazione.
Le estensioni possono condividere i dati contestuali tra tutti gli utenti di un'estensione. I dati contesto possono essere utilizzati per dati che non cambiano spesso e che non hanno requisiti di sicurezza speciali. Presta attenzione quando scrivi i dati, poiché non esistono blocchi dei dati e vince l'ultima scrittura. I dati relativi al contesto sono disponibili per l'estensione all'avvio. L'SDK Looker Extension fornisce funzioni per consentire l'aggiornamento e l'aggiornamento dei dati relativi al contesto.
La dimensione massima dei dati di contesto è di circa 16 MB. I dati di contesto verranno serializzati su una stringa JSON, quindi devi prendere in considerazione anche questi dati se utilizzi dati di contesto per l'estensione.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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:
Ambito: associato all'estensione. Un attributo utente con ambito è dotato di spazio dei nomi rispetto all'estensione e deve essere definito nell'istanza di Looker prima di poter essere utilizzato. Per lo spazio dei nomi di un attributo utente, anteponi al nome dell'estensione il nome dell'attributo. Qualsiasi trattino 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 attributo utente definitomy_extension_my_extension_my_value
. Una volta definito, l'attributo utente può essere letto e aggiornato dall'estensione.Globale: si tratta di attributi utente globali di sola lettura. Un esempio è l'attributo utente
locale
.
Di seguito è riportato un elenco di chiamate API per gli attributi utente:
userAttributeGetItem
: legge un attributo dell'utente. È possibile definire un valore predefinito, che verrà utilizzato se non esiste un valore per l'attributo utente per l'utente.userAttributeSetItem
: salva un attributo dell'utente per l'utente corrente. Non riusciranno per gli attributi utente globali. Il valore salvato è visibile soltanto all'utente corrente.userAttributeResetItem
: reimposta il valore predefinito di un attributo utente per l'utente corrente. Non riusciranno 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 { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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')
Archiviazione locale
Gli iframe con sandbox non consentono l'accesso allo spazio di archiviazione locale del browser. L'SDK Looker Extension 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 per l'estensione, vale a dire che non può leggere lo spazio di archiviazione locale creato dalla finestra principale o da altre estensioni.
Per utilizzare lo spazio di archiviazione locale è richiesto il diritto local_storage
.
L'API localhost dell'estensione è asincrona anziché all'API sincrona del browser sincrono.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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 in corso...
Le estensioni potrebbero aggiornare il titolo della pagina corrente. Non sono necessari diritti per eseguire questa azione.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
extensionSDK.updateTitle('My Extension Title')
Scrittura negli appunti di sistema
Gli iframe con sandbox non consentono l'accesso agli appunti di sistema. L'SDK Looker Extension 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 avere il diritto use_clipboard
.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
// Write to system clipboard
try {
await extensionSDK.clipboardWrite(
'My interesting information'
)
. . .
} catch (error) {
. . .
}
Incorporamento di dashboard, Look ed esplorazioni
Il framework dell'estensione supporta l'incorporamento di dashboard, Look ed esplorazioni.
Il diritto use_embeds
è obbligatorio. Per incorporare i contenuti, ti consigliamo di utilizzare l'SDK Embed JavaScript di Looker. Per saperne di più, consulta la documentazione relativa all'SDK per l'incorporamento.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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 estensione utilizzano componenti con stile per fornire uno stile semplice all'iframe generato. Ecco alcuni esempi:
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 dell'estensione offre due metodi per accedere agli endpoint API esterni:
- Il proxy server: accede all'endpoint tramite il server Looker. Questo meccanismo consente di impostare in modo sicuro gli ID client e le chiavi segrete dal server Looker.
- Il proxy di recupero: accede all'endpoint dal browser dell'utente. Il proxy è l'interfaccia utente di Looker.
In entrambi i casi è necessario specificare l'endpoint API esterno nell'estensione external_api_urls
diritto.
Proxy server
L'esempio seguente mostra l'utilizzo del proxy server per ottenere un token di accesso da utilizzare per il 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 { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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 associato all'estensione. I trattini devono essere sostituiti da trattini bassi e il carattere ::
deve essere sostituito con un solo trattino basso.
Ad esempio, se il nome dell'estensione è my-extension::my-extension
, gli attributi utente che devono essere definiti per l'esempio precedente saranno:
my_extension_my_extension_my_client_id
my_extension_my_extension_'my_client_secret'
Recupero proxy
L'esempio seguente mostra l'utilizzo del proxy di recupero. Utilizza il token di accesso dell'esempio di proxy del server precedente.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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 dell'estensione supporta l'integrazione con i provider OAuth. OAuth può essere utilizzato 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 URL aggiuntivi nel diritto external_api_urls
.
I framework delle estensioni supportano i seguenti flussi:
- Flusso implicito
- Tipo di autorizzazione del codice di autorizzazione con chiave segreta
- Verifica e verifica del codice PKCE
Il flusso generale è costituito dall'apertura di una finestra secondaria che carica una pagina del server OAuth. Il server OAuth autentica l'utente e lo reindirizza al server Looker con ulteriori dettagli utilizzabili per ottenere un token di accesso.
Flusso implicito:
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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 autorizzazione 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 { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
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
Spartano
Spartan si riferisce a un metodo di utilizzo dell'istanza di Looker come ambiente per esporre estensioni, ed estensioni, solo a un insieme definito di utenti. Un utente spartan che raggiunge un'istanza di Looker visualizza il flusso di accesso configurato dall'amministratore di Looker. Una volta che l'utente è stato autenticato, gli verrà presentata un'estensione in base al suo landing_page
attributo utente come mostrato di seguito. L'utente può accedere solo alle estensioni e non può accedere ad altre parti di Looker. Se l'utente ha accesso a più estensioni, può controllare la capacità dell'utente di passare alle altre estensioni utilizzando extensionSDK.updateLocation
. Esiste un metodo specifico dell'SDK Looker Extension per consentire all'utente di uscire dall'istanza di Looker.
import { ExtensionContext2 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext2
)
const { extensionSDK } = extensionContext
. . .
// Navigate to another extension
extensionSDK.updateLocation('/spartan/another::extension')
. . .
// Logout
extensionSDK.spartanLogout()
Definizione degli utenti spartani
Per definire un utente spartano, devi creare un gruppo denominato "Solo estensioni".
Dopo aver creato il gruppo "Solo estensioni", 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 il gruppo "Solo estensioni". Il valore deve essere impostato su /spartan/my_extension::my_extension/
, dove my_extension::my_extension
è l'ID della tua estensione. Quando questo utente esegue l'accesso, viene reindirizzato all'estensione designata.
Suddivisione del codice
La suddivisione del codice è la tecnica in cui il codice viene richiesto solo quando è necessario. In genere, i blocchi di codice vengono associati a route di reazione in cui ogni percorso riceve un proprio blocco di codice. In Reazione, ciò viene fatto con i componenti Suspense
e React.lazy
. Il componente Suspense
mostra un componente di riserva durante il caricamento del blocco di codice. React.lazy
è responsabile del caricamento del blocco di codice.
Configurazione della suddivisione del codice in corso:
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 di caricamento lento viene implementato nel seguente modo:
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
Scuotimento degli alberi
Gli SDK Looker supportano l'tree shaking, ma non sono ancora perfetti. Modificano continuamente i nostri SDK per migliorare il supporto tree shaking. Alcune di queste modifiche potrebbero richiedere il refactoring del codice per trarre vantaggio, ma quando questo è necessario, sarà documentato nelle note di rilascio.
Per utilizzare tree shaking, il modulo in uso 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 dell'estensione Looker e l'SDK dell'estensione per la reazione fanno tutti questo.
In un'estensione, devi scegliere uno degli SDK Looker, 3.1 o 4.0 e utilizzare il componente ExtensionProvider2
dell'SDK dell'estensione per la reazione. Se hai bisogno di entrambi gli SDK, continua a utilizzare il componente ExtensionProvider
, ma noterai un aumento delle dimensioni finali del bundle.
Il codice seguente configura il provider di estensioni. Dovrai indicare al provider quale SDK vuoi:
import { MyExtension } from './MyExtension'
import { ExtensionProvider2 } 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 di sopra include tutti gli aspetti del modulo. Importa invece solo i componenti necessari. Ecco alcuni esempi:
import { Paragraph } from `@looker/components`
Glossario
- Suddivisione del codice: una tecnica per il caricamento lento di JavaScript finché non è effettivamente necessaria. Idealmente, dovresti mantenere il bundle JavaScript caricato inizialmente il più piccolo possibile. Ciò può essere ottenuto utilizzando la suddivisione del codice. Qualsiasi funzionalità non immediatamente necessaria 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: generalmente una visualizzazione di pagina in Looker. Le scene mappano i percorsi principali. A volte possono essere presenti scene secondarie mappate a percorsi secondari all'interno del percorso principale.
- Transpile: il processo di accettazione del codice sorgente scritto in una lingua e della trasformazione in un'altra lingua con un livello di astrazione simile. Un esempio è TypeScript in JavaScript.