本頁面提供以 React 和 JavaScript 編寫的程式碼範例,供您在擴充功能中使用常見函式。
使用 Looker Extension SDK
擴充功能必須與 Looker 主機建立連線。在 React 中,您可以將擴充功能包裝在 ExtensionProvider40
元件中來完成這項操作。這個元件會與 Looker 主機建立連線,並讓擴充功能使用 Looker 擴充功能 SDK 和 Looker SDK。
import React from 'react'
import { ExtensionProvider40 } from '@looker/extension-sdk-react'
import { DemoCoreSDK } from './DemoCoreSDK'
export const App = () => {
return (
<ExtensionProvider40 chattyTimeout={-1}>
<DemoCoreSDK />
</ExtensionProvider40>
)
}
擴充功能供應器的背景資訊
擴充功能供應商會將 Looker 擴充功能 SDK 和 SDK API 公開給擴充功能。自從擴充功能架構建立以來,已建立不同版本的擴充功能供應器。本節將說明擴充功能供應器的歷史沿革,以及為何建議使用 ExtensionProvider40。
第一個擴充功能供應器是 ExtensionProvider
,它同時公開了 Looker SDK 3.1 和 4.0 版本。缺點是,加入這兩個 SDK 會增加最終正式版套件的大小。
接著建立 ExtensionProvider2
。之所以建立這個選項,是因為擴充功能不應同時使用兩個 SDK,也不能強制開發人員選擇其中一個。但不幸的是,這仍會導致兩個 SDK 都包含在最終正式版套件的大小中。
當 SDK 4.0 移至 GA 時,ExtensionProvider40
就會建立。ExtensionProvider40
的優點在於開發人員不必選擇要使用的 SDK,因為 SDK 4.0 是唯一可用的版本。由於 SDK 3.1 未納入最終套件,因此這項做法可縮減套件大小。
如要從 Looker Extension SDK 新增函式,您必須先取得 SDK 參照,這可以透過供應商或全域方式完成。接著,您可以呼叫 SDK 函式,就像在任何 JavaScript 應用程式中一樣。
- 如要從供應器存取 SDK,請按照下列步驟操作:
import { ExtensionContext40 } from '@looker/extension-sdk-react'
export const Comp1 = () => {
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK, coreSDK } = extensionContext
- 如要全域存取 SDK (在呼叫擴充功能前「必須」初始化),請按照下列步驟操作:
const coreSDK = getCoreSDK()
您現在可以像在任何 JavaScript 應用程式中一樣使用 SDK:
const GetLooks = async () => {
try {
const looks = await sdk.ok(sdk.all_looks('id'))
// process looks
. . .
} catch (error) {
// do error handling
. . .
}
}
在 Looker 執行個體中瀏覽其他位置
由於外掛程式是在沙箱 iframe 中執行,因此您無法透過更新父項的 window.location
物件,在 Looker 例項中瀏覽其他位置。您可以使用 Looker Extension SDK 進行導覽。
這個函式需要 navigation
授權。
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
. . .
extensionSDK.updateLocation('/browse')
開啟新的瀏覽器視窗
由於擴充功能是在沙箱 iframe 中執行,因此您無法使用父項視窗開啟新的瀏覽器視窗。您可以使用 Looker Extension SDK 開啟瀏覽器視窗。
這個函式需要 new_window
授權,才能開啟至目前 Looker 例項位置的新視窗,或是 new_window_external_urls
授權,才能開啟在不同主機上執行的新視窗。
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')
路由和深層連結
以下說明適用於 React 擴充功能的情況。
ExtensionProvider
、ExtensionProvider2
和 ExtensionProvider40
元件會自動建立名為 MemoryRouter
的 React Router,供您使用。請勿嘗試建立 BrowserRouter
,因為這在沙箱 iframe 中無法運作。請勿嘗試建立 HashRouter
,因為在非以 Chromium 為基礎的 Microsoft Edge 瀏覽器沙箱 iframe 中無法運作。
如果使用 MemoryRouter
,且在擴充功能中使用 react-router
,擴充功能架構會自動將擴充功能的路由器同步至 Looker 主機路由器。也就是說,擴充功能會在瀏覽器返回和前進按鈕點擊時收到通知,以及在網頁重新載入時收到目前路徑的通知。這也表示擴充功能應自動支援深層連結。請參閱擴充功能範例,瞭解如何使用 react-router
。
擴充功能內容資料
請勿將擴充功能架構情境資料與 React 情境混淆。
擴充功能可在所有擴充功能使用者之間共用背景資料。您可以將背景資料用於不常變更且沒有特殊安全性需求的資料。請注意,寫入資料時不會鎖定資料,因此最後寫入的資料會覆寫先前寫入的資料。擴充功能一啟動,就會立即取得背景資料。Looker Extension SDK 提供的函式可讓您更新及重新整理內容資料。
情境資料的大小上限約為 16 MB。內容資料會序列化為 JSON 字串,因此如果您要為擴充功能使用內容資料,也必須考量這點。
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()
使用者屬性
Looker Extension SDK 提供 API,可存取 Looker 使用者屬性。使用者屬性存取權有兩種:
受限 — 與擴充功能相關聯。範圍限定的使用者屬性會以擴充功能命名空間,且使用者屬性必須在 Looker 例項中定義,才能使用。如要為使用者屬性指定命名空間,請在屬性名稱前方加上擴充功能名稱。擴充功能名稱中的任何破折號和 '::' 字元必須以底線取代,因為破折號和冒號不得用於使用者屬性名稱。
舉例來說,如果您使用範圍限定的使用者屬性 (名稱為
my_value
) 搭配擴充功能 IDmy-extension::my-extension
,則必須定義使用者屬性名稱my_extension_my_extension_my_value
。定義完成後,擴充功能就可以讀取及更新使用者屬性。全域:全域使用者屬性,僅供讀取。例如
locale
使用者屬性。
以下列出使用者屬性 API 呼叫:
userAttributeGetItem
:讀取使用者屬性。您可以定義預設值,如果使用者沒有使用者屬性值,系統就會使用該預設值。userAttributeSetItem
:為目前使用者儲存使用者屬性。全域使用者屬性會失敗。只有目前使用者能看到已儲存的值。userAttributeResetItem
:將目前使用者的使用者屬性重設為預設值。全域使用者屬性會失敗。
如要存取使用者屬性,您必須在 global_user_attributes
和/或 scoped_user_attributes
授權中指定屬性名稱。舉例來說,您可以在 LookML 專案資訊清單檔案中新增下列內容:
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')
本機儲存空間
沙箱 iframe 不允許存取瀏覽器本機儲存空間。Looker Extension SDK 可讓擴充功能讀取及寫入父窗格的本機儲存空間。本機儲存空間的命名空間為擴充功能,因此無法讀取由父系視窗或其他擴充功能建立的本機儲存空間。
您必須具備 local_storage
授權,才能使用本機存放區。
擴充功能 localhost API 是非同步的,而瀏覽器本機儲存空間 API 則是同步的。
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')
更新頁面標題
擴充功能可能會更新目前的網頁標題。執行這項操作不需要授權。
import { ExtensionContext40 } from '@looker/extension-sdk-react'
. . .
const extensionContext = useContext(
ExtensionContext40
)
const { extensionSDK } = extensionContext
extensionSDK.updateTitle('My Extension Title')
寫入系統剪貼簿
沙箱 iframe 不允許存取系統剪貼簿。Looker Extension SDK 可讓擴充功能將文字寫入系統剪貼簿。基於安全考量,擴充功能不得讀取系統剪貼簿。
如要寫入系統剪貼簿,您必須具備 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) {
. . .
}
嵌入資訊主頁、Look 和探索
擴充功能架構支援嵌入資訊主頁、Look 和探索項目。
必須具備 use_embeds
授權。建議您使用 Looker JavaScript 嵌入 SDK 嵌入內容。詳情請參閱 Embed 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} />)
擴充功能範例會使用樣式化元件,為產生的 iframe 提供簡單的樣式。例如:
import styled from "styled-components"
export const EmbedContainer = styled.div`
width: 100%;
height: 95vh;
& > iframe {
width: 100%;
height: 100%;
}
存取外部 API 端點
擴充功能架構提供兩種方法,可存取外部 API 端點:
- 伺服器 Proxy:透過 Looker 伺服器存取端點。這項機制可讓 Looker 伺服器安全地設定用戶端 ID 和密鑰。
- 擷取 Proxy:從使用者的瀏覽器存取端點。代理程式是 Looker UI。
無論是哪種情況,您都必須在擴充功能 external_api_urls
授權中指定外部 API 端點。
伺服器 Proxy
以下範例說明如何使用伺服器 Proxy 取得存取權方代碼,供擷取 Proxy 使用。用戶端 ID 和密鑰必須定義為擴充功能的使用者屬性。通常在設定使用者屬性時,預設值會設為用戶端 ID 或密鑰。
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
. . .
}
使用者屬性名稱必須對應至擴充功能。連字號必須替換為底線,::
字元則必須替換為單底線。
舉例來說,如果擴充功能的名稱是 my-extension::my-extension
,則上例所需定義的使用者屬性如下:
my_extension_my_extension_my_client_id
my_extension_my_extension_'my_client_secret'
擷取 Proxy
以下範例說明如何使用擷取 Proxy。這個例子會使用先前伺服器 Proxy 範例中的存取權杖。
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
. . .
}
OAuth 整合
擴充功能架構支援與 OAuth 供應器整合。OAuth 可用於取得存取權杖,以存取特定資源,例如 Google 試算表文件。
您必須在 extension oauth2_urls
授權中指定 OAuth 伺服器端點。您可能還需要在 external_api_urls
授權中指定其他網址。
擴充功能架構支援下列流程:
- 隱含流程
- 含密式金鑰的授權碼授權類型
- PKCE 驗證碼挑戰和驗證器
一般流程是開啟子視窗,載入 OAuth 伺服器頁面。OAuth 伺服器會驗證使用者,並將其他可用於取得存取權杖的詳細資料重新導向至 Looker 伺服器。
隱含流程:
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
含密式金鑰的授權碼授權類型:
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
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 是指使用 Looker 例項做為環境的方法,可將擴充功能 (僅限擴充功能) 公開給指定的使用者。當簡易版使用者前往 Looker 執行個體時,系統會顯示 Looker 管理員設定的登入流程。使用者完成驗證後,系統會根據使用者的 landing_page
使用者屬性,向使用者顯示擴充功能,如下所示。使用者只能存取擴充功能,無法存取 Looker 的任何其他部分。如果使用者有權存取多個擴充功能,則擴充功能會控制使用者是否能使用 extensionSDK.updateLocation
前往其他擴充功能。有一個特定的 Looker Extension SDK 方法,可讓使用者登出 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()
定義 Spartan 使用者
如要定義 Spartan 使用者,您必須建立名為「Extensions Only」的群組。
建立「Extensions Only」群組後,請前往 Looker 的「Admin」部分,然後在「User Attributes」頁面編輯 landing_page
使用者屬性。選取「群組值」分頁,然後新增「僅限擴充功能」群組。值應設為 /spartan/my_extension::my_extension/
,其中 my_extension::my_extension
是擴充功能的 ID。這樣一來,使用者登入時就會被導向指定的擴充功能。
程式碼分割
程式碼分割是一種技術,可在需要時才要求程式碼。通常,程式碼片段會與 React 路徑相關聯,每個路徑都會取得自己的程式碼片段。在 React 中,這項操作是透過 Suspense
和 React.lazy
元件完成。載入程式碼片段時,Suspense
元件會顯示備用元件。React.lazy
負責載入程式碼區塊。
設定程式碼分割:
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>
延遲載入元件的實作方式如下:
import { lazy } from 'react'
const Comp1 = lazy(
async () => import(/* webpackChunkName: "comp1" */ './Comp1')
)
export const AsyncComp1 = () => <Home />
元件的實作方式如下。元件必須匯出為預設元件:
const Comp1 = () => {
return (
<div>Hello World</div>
)
}
export default Comp1
樹狀圖移除
雖然 Looker SDK 目前支援樹狀圖搖晃,但這項功能仍需要改善。我們會持續修改 SDK,以改善樹狀圖移除支援功能。其中部分變更可能需要重構程式碼才能發揮效用,但如果需要這麼做,我們會在版本資訊中記錄相關資訊。
如要使用樹狀圖搖動,您使用的模組必須匯出為 esmodule,且匯入的函式不得有副作用。Looker SDK for TypeScript/Javascript、Looker SDK 執行階段程式庫、Looker UI 元件、Looker Extension SDK 和 Extension SDK for React 都符合這些要求。
在擴充功能中使用 Looker SDK 4.0,並使用 Extension SDK for React 中的 ExtensionProvider2
或 ExtensionProvider40
元件。
以下程式碼會設定擴充功能提供者。您必須告訴供應商您要哪個 SDK:
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>
)
})
請勿在擴充功能中使用下列匯入樣式:
import * as lookerComponents from `@looker/components`
先前的範例會從模組中匯入所有內容。請只匯入實際需要的元件。例如:
import { Paragraph } from `@looker/components`
詞彙
- 程式碼分割:一種延後載入 JavaScript 的技術,直到實際需要時才載入。理想情況下,您應該盡可能將初始載入的 JavaScript 套件保持在最小狀態。您可以利用程式碼分割功能達成這項目標。系統只會在實際需要時載入非立即必要的功能。
- IDE:整合式開發環境。用於建立及修改擴充功能的編輯器。例如 Visual Studio Code、IntelliJ 和 WebStorm。
- 情境:通常是 Looker 中的網頁瀏覽。場景會對應至主要路線。有時,場景會包含子場景,這些子場景會對應至主要路線中的子路線。
- 轉譯 — 將以一種語言編寫的原始碼轉換為具有類似抽象層級的另一種語言。例如將 TypeScript 轉換為 JavaScript。