Esempi di reazioni del framework JavaScript e codice JavaScript

Il framework dell'estensione Looker presto utilizzerà un nuovo meccanismo di caricamento. Il nuovo caricatore potrebbe causare errori quando vengono caricate le estensioni esistenti. Per istruzioni su come testare le estensioni con il nuovo caricatore prima dell'attivazione ufficiale negli ambienti Looker, consulta l'articolo Testing the new extension framework loader nel Centro assistenza Looker.

Questa pagina fornisce esempi di codice scritti in React e JavaScript per le funzioni comuni che potresti utilizzare nelle tue estensioni.

Utilizzo dell'SDK dell'estensione Looker

Per aggiungere funzioni dall'SDK dell'estensione Looker, devi prima ottenere un riferimento all'SDK, che può essere effettuato dal fornitore o a livello globale. Dopodiché puoi chiamare le funzioni SDK come faresti in qualsiasi applicazione JavaScript.

  • Per accedere all'SDK dal provider:
  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 in qualsiasi applicazione JavaScript:

  const GetLooks = async () => {
    try {
      const looks = await sdk.ok(sdk.all_looks('id'))
      // process looks
      . . .
    } catch (error) {
      // do error handling
      . . .
    }
}

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 padre. Puoi navigare utilizzando l'SDK dell'estensione Looker.

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. Con l'SDK dell'estensione Looker è possibile aprire una finestra del browser.

Questa funzione richiede il diritto new_window per aprire una nuova finestra in una posizione nell'istanza di Looker corrente o il diritto new_window_external_urls per aprire una nuova finestra in esecuzione su un host diverso.

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

Le seguenti estensioni si applicano alle estensioni basate su reazioni.

I componenti ExtensionProvider e ExtensionProvider2 creano automaticamente un router reagito chiamato MemoryRouter. Non tentare di creare un BrowserRouter, poiché non funziona negli iframe con sandbox. Non tentare di creare un HashRouter poiché 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 all'estensione verranno segnalati i clic sul pulsante Avanti e Indietro del browser e la notifica del percorso attuale quando la pagina viene ricaricata. Questo significa anche che l'estensione deve supportare automaticamente i link diretti. Consulta gli esempi di estensione su come utilizzare react-router.

Dati contesto estensione

I dati relativi al contesto del framework dell'estensione non devono essere confusi con i contesti di reazione.

Le estensioni possono condividere i dati di contesto tra tutti gli utenti di un'estensione. I dati contestuali possono essere utilizzati per dati che non cambiano di frequente e che non hanno requisiti di sicurezza speciali. Prestare attenzione quando si scrivono i dati, poiché non esiste alcun blocco dei dati e vince l'ultima scrittura. I dati contestuali sono disponibili per l'estensione subito dopo l'avvio. L'SDK dell'estensione Looker fornisce funzioni che consentono di aggiornare e aggiornare i dati contestuali.

La dimensione massima dei dati di contesto è di circa 16 MB. I dati di contesto verranno serializzati su una stringa JSON, quindi devi anche tenerne conto se utilizzi i dati di contesto per la tua 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 dell'estensione Looker 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 sarà sottoposto a uno spazio dei nomi rispetto all'estensione, che deve essere definita nell'istanza di Looker prima di poter essere utilizzata. Per aggiungere uno spazio dei nomi a un attributo utente, aggiungi il nome dell'estensione prima del nome. Qualsiasi trattino e i caratteri '::' nel nome dell'estensione devono essere sostituiti da un trattino basso, visto che 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 ID estensione my-extension::my-extension deve avere un nome attributo utente pari a my_extension_my_extension_my_value definito. Una volta definito, l'attributo utente potrà essere letto e aggiornato dall'estensione.

  • Globale: sono attributi utente globali e sono 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 utente. È possibile definire un valore predefinito che verrà utilizzato in assenza di un valore di attributo utente per l'utente.
  • userAttributeSetItem: salva un attributo utente per l'utente corrente. Errore per gli attributi utente globali. Il valore salvato è visibile solo all'utente corrente.
  • userAttributeResetItem: reimposta il valore predefinito di un attributo utente per l'utente corrente. Errore 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, devi aggiungere:

  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 dell'estensione Looker consente a un'estensione di leggere e scrivere nella memoria locale della finestra principale. Lo spazio di archiviazione locale è associato allo spazio dei nomi dell'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 dell'estensione localhost è asincrona, anziché l'API sincrona di archiviazione locale del browser.

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')

Aggiornare il titolo della pagina

Le estensioni possono aggiornare il titolo della pagina corrente. Non è necessario disporre di 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 dell'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 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) {
      . . .
    }

Incorporare dashboard, Look ed esplorazioni

Il framework delle estensioni supporta l'incorporamento di dashboard, visualizzazioni ed esplorazioni. È possibile incorporare sia le dashboard normali sia quelle precedenti.

Il diritto use_embeds è obbligatorio. Ti consigliamo di utilizzare l'SDK Embed di JavaScript di Looker per incorporare i contenuti. Per scoprire di più, consulta la documentazione sull'incorporamento dell'SDK.

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. 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 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 ID client e chiavi secret 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 devi specificare l'endpoint API esterno nell'estensione diritto external_api_urls.

Proxy server

L'esempio seguente mostra l'utilizzo del proxy server per ottenere un token di accesso che deve essere utilizzato 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 sul client secret o sull'ID client.

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 i caratteri :: devono essere sostituiti da un singolo 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. in quanto utilizza il token di accesso dell'esempio di proxy 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 con 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 concessione del codice di autorizzazione con chiave segreta
  • Verifica del codice PKCE e verifica

Il flusso generale è che viene aperta una finestra secondaria che carica una pagina di server OAuth. Il server OAuth autentica l'utente e lo reindirizza al server Looker con ulteriori dettagli che possono essere utilizzati 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 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 del codice PKCE e verifica:

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 per utilizzare l'istanza di Looker come un ambiente per esporre le estensioni, e le estensioni solo, a un insieme designato di utenti. A un utente essenziale che passa a un'istanza di Looker verrà visualizzato il flusso di accesso configurato dall'amministratore di Looker. Dopo che l'utente è stato autenticato, gli verrà mostrata un'estensione in base al suo attributo utente landing_page, come mostrato di seguito. L'utente può accedere solo alle estensioni e non ad altre parti di Looker. Se l'utente ha accesso a più estensioni, queste controllano la capacità dell'utente di passare alle altre estensioni utilizzando extensionSDK.updateLocation. Esiste un metodo specifico dell'SDK dell'estensione Looker 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 di utenti essenziali

Per definire un utente essenziale, devi creare un gruppo chiamato "Solo estensioni".

Una volta 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. Ora, quando l'utente esegue l'accesso, verrà indirizzato all'estensione designata.

Suddivisione codice

La suddivisione del codice è la tecnica in cui il codice viene richiesto solo quando è necessario. In genere, i blocchi di codice sono associati a route di reazione, in cui ogni percorso riceve il proprio blocco di codice. In React, esegui questa operazione 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.

Impostazione della suddivisione del 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 Caricamento lento è implementato come segue:

import { lazy } from 'react'

const Comp1 = lazy(
 async () => import(/* webpackChunkName: "comp1" */ './Comp1')
)

export const AsyncComp1 = () => &#60;Home />

Il componente viene implementato come segue. Il componente deve essere esportato come componente predefinito:

const Comp1 = () => {
  return (
    &#60;div&#62;Hello World&#60;/div&#62;
  )
}

export default Comp1

Scuotimento di alberi

Gli SDK Looker supportano lo scuotimento degli alberi, ma non sono ancora perfetti. Modifichiamo continuamente i nostri SDK per migliorare il supporto della scossa degli alberi. Alcune di queste modifiche potrebbero richiedere il refactoring del codice per sfruttarlo, ma quando è necessario, sarà documentato nelle note di rilascio.

Per utilizzare l'albero tremolio del modulo in uso, devi esportare i dati 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 interfaccia utente di Looker, l'SDK dell'estensione Looker e l'SDK dell'estensione per la reazione fanno tutti questo.

In un'estensione, dovresti scegliere uno degli SDK Looker, 3.1 o 4.0 e utilizzare il componente ExtensionProvider2 dall'SDK dell'estensione per la reazione. Se richiedi entrambi gli SDK, continua a utilizzare il componente ExtensionProvider, ma noterai un aumento delle dimensioni finali del bundle.

Il seguente codice configura il provider dell'estensione. 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 (
    &#60;ExtensionProvider2 type={Looker40SDK}&#62;
      &#60;MyExtension /&#62;
    &#60;/ExtensionProvider2&#62;
  )
})

Non utilizzare il seguente stile di importazione nell'estensione:

import * as lookerComponents from `@looker/components`

L'esempio precedente introduce tutto dal modulo. Importa invece solo i componenti di cui hai effettivamente bisogno. Ad esempio:

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. Questo 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.
  • Scene: in genere una visualizzazione di pagina in Looker. Le scene mappano i percorsi principali. A volte una scena avrà scene secondarie mappate a percorsi secondari all'interno del percorso principale.
  • Transpile - Il processo di acquisizione del codice sorgente in una lingua e di trasformazione in un'altra lingua con un livello di astrazione simile. Un esempio è TypeScript in JavaScript.