Security News
PyPI Introduces Digital Attestations to Strengthen Python Package Security
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
bryans99-extension-sdk
Advanced tools
An SDK that may be used by Looker extensions to make API calls or otherwise interact with their host context.
A Looker extension is JavaScript code that code that runs inside of the Looker UI and exposes an interface for custom functionality. (In this setup, the extension itself can be thought of as a client, while Looker can be thought of as a host.) Via this Extension SDK, the extension may request that the Looker host perform various tasks to enhance your extension. This allows the host to take care of complex functionality, including API authentication, so your extension does not need to perform any setup and does not need to deal with any credentials.
Extensions are implemented as a sandboxed <iframe>
and communication with Looker is accomplished through this SDK. Internally, the SDK will translate function calls into messages passed safely across the iframe boundary. The specific format of those messages is considered private, and this SDK is the only supported interface for interacting with the host from an extension.
React bindings for extensions are also available, as well as a template project in React and TypeScript to help you get started.
Add dependency to your project using yarn or npm
yarn add @looker/extension-sdk
or
npm install @looker/extension-sdk
The Extension SDK must establish a connection with its host before further functionality will be available.
import { LookerExtensionSDK, connectExtensionHost } from '@looker/extension-sdk'
import { Looker40SDK } from '@looker/sdk/dist/sdk/4.0/methods'
let extensionSDK
let coreSDK
// Establish connection
connectExtensionHost()
.then((host) => {
// This `extensionSDK` can perform extension-specific actions
extensionSDK = host
// This `coreSDK` is an automatically credentialed variant of the standard Looker Core SDK for performing API calls
coreSDK = LookerExtensionSDK.create40Client(extensionSDK)
})
.catch((error) => console.error())
The following create methods are also available
LookerExtensionSDK.createClient(extensionSDK)
creates a Looker31SDK instance.LookerExtensionSDK.create31Client(extensionSDK)
creates a Looker31SDK instance.connectExtensionHost()
can also accept a configuration object as an argument.
initializedCallback
- (optional) an initialization callback that is called after communicating with the looker host. The callback routine accepts an error message argument that will be populated should an error occur during initialization (for example a looker version issue).ExtensionSDK lookerHostData
property and as such this callback is likely to be removed in a future release.Looker host data is made available once the host connection has been established
lookerVersion
- host Looker version. Test this value if the extension depends on a particular version of Looker.route
- if routes are tracked, route is the last active route tracked by the host. On initialization the extension may set its route to this value.When your extension is run inside Looker, and the connection process is completed, you can use coreSDK
to make API calls.
async function runLook(coreSDK) {
var result = await coreSDK.run_look({
look_id: look_id,
result_format: 'json',
})
if (result.ok) {
// do something with result
} else {
console.error('Something went wrong:', result.error)
}
}
extensionSDK.updateTitle('Change the window title')
To navigate the host context to a new page:
extensionSDK.updateLocation('/dashboards/37')
Or to open a new tab:
extensionSDK.openBrowserWindow('/dashboards/37', '_blank')
The Looker host may overlay the extension with it's popovers. These will not automatically close when the extension is active. The extension may request that the Looker host close any open popovers.
extensionSDK.closeHostPopovers()
Extensions may read/write data from/to the Looker host local storage. Note that the storage is namespaced to the extension. The extension may not read/write data from other extensions or the Looker host. Note that the extension localstorage API is asynchronous which is different from the regular browser localstorage API which is synchronous.
// Save to localstorage
await extensionSDK.localStorageSetItem('data', JSON.stringify(myDataObj))
// Read from localstorage
const value = await extensionSDK.localStorageGetItem('data')
const myDataObj = JSON.parse(value)
// Remove from localstorage
await extensionSDK.localStorageRemoveItem('data')
The extension framework supports the OAUTH2 implicit and PKCE authentication flows. The OAUTH2 flow opens a separate window above the extension where the user goes through the authentication flow with the OAUTH2 provider. Upon completion, the OAUTH2 provider's response is made available to the extension OR an error is returned should the user fail to authenticate.
try {
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
// The user is authenticated, it does not mean the user is authorized.
// There may be further work for the extension to do.
} catch (error) {
// The user failed to authenticate
}
Note that the PKCE flow requires that the code exchange be proxied through the Looker server. The PKCE flow also requires that a secret key be defined. This secret key MUST NOT be exposed in code in the exension either at runtime OR compile time. The extension framework proxy running in the Looker server has the capability of replacing secret key tags identified in the request with actual secret ket values stored securely in the Looker server. The secret key is name spaced to the Looker extension. The createSecretKetTag will format the secret key name such that the proxy can replace it with the correct value. See the section on secret keys for more information.
try {
const response = await extensionSDK.oauth2Authenticate(
'https://github.com/login/oauth/authorize',
{
client_id: GITHUB_CLIENT_ID,
response_type: 'code',
},
'GET'
)
const codeExchangeResponse = await extensionSDK.oauth2ExchangeCodeForToken(
'https://github.com/login/oauth/access_token',
{
client_id: GITHUB_CLIENT_ID,
client_secret: extensionSDK.createSecretKeyTag('github_secret_key'),
code: response.code,
}
)
const { access_token } = codeExchangeResponse
// The user is authenticated, it does not mean the user is authorized.
// There may be further work for the extension to do.
} catch (error) {
// The user failed to authenticate
}
Note on entitlements: OAUTH2 authentication urls must be defined in the extensions entitlements (see the Entitlements
section for more details).
The external API allows the extension to call third party API in the context of the Looker host. Note that entitlements must be defined for the Extension in order to use the external API feature. The Extension SDK fetchProxy method is a proxy to the Looker host window fetch API.
// Get
const response: any = await extensionSDK.fetchProxy(`${postsServer}/posts`)
if (response.ok) {
// Do something with response.body
} else {
// error handling
}
// Post
const response = await extensionSDK.fetchProxy(
`${postsServer}/posts`,
{
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(myDataObj)
if (response.ok) {
// Continue
} else {
// Error handling
}
A fetch proxy object can also be created with a base URL and a pre initialized init object. In the example below { credentials: 'include' }
will be used as the init object or merged with the init object if provided.
const dataServerFetchProxy = extensionSDK.createFetchProxy(postsServer, { credentials: 'include' })
// Get
const response: any = await dataServerFetchProxy.fetchProxy('/posts')
if (response.ok) {
// Do something with response.body
} else {
// error handling
}
// Post
const response = await dataServerFetchProxy.fetchProxy(
'/posts',
{
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify(myDataObj)
if (response.ok) {
// Continue
} else {
// Error handling
}
Note on cookies returned by the external API: The credentials
property of the init
argument needs to be set to include
in order for any cookies associated with the external API server to be sent with the request. Use the createFetchProxy
to populate the credentials value for all requests.
A server proxy mechanism has been provided purely to allow secret keys to be stored safely and securely in the Looker server. The server proxy should only be used to get ephemeral access tokens. The OAUTH2 PKCE flow uses the server proxy to get an access token but the implementation is flexible enough to support non OAUTH2 flows. For instance, an external API endpoint could be created that given a secret key and some kind of identification of the extension user, it will return an access token. That access token could be a JWT token and can subsequently be used with the fetchProxy functionality. Note that it is down to the external APIs to enforce and verify that the access tokens are valid.
const response = await extensionSDK.serverProxy(
`${postsServer}/authorize`,
{
method: 'POST',
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
extension_secret_key: extensionSDK.createSecretKeyTag("extension_secret_key"),
extension_userid: extensionSDK.createSecretKeyTag("extension_userid"),
})
if (response.ok) {
// Continue - at this point the response should contain the access token
} else {
// Error handling
}
The extension framework server proxy has the capability to recognize and replace special tags in a request with values stored in user attributes in the Looker server. The createSecretKeyTag
method will format the tag such that it will be recognized by the proxy (it will handle the name spacing and attaching of special characters to indicate that it is to be replaced). The tags can be placed any where in the header or body of the request. An object property can contain multiple secret key tags.
Secret keys should be defined in the Looker as user attributes. The key should name spaced as follows
{modifed extension id}::{key name}
A modified extension id
means replacing the ::
with an underscore, _
. Example:
Extension kichensink::kitchensink
secret key name github_secret_key
becomes kitchensink_kitchensink_github_secret_key
.
The user attribute value should be hidden and the domain whitelist should include the url used in the server proxy call.
Note on entitlements: External API urls must be defined in the extensions entitlements (see the Entitlements
section for more details).
If an extension is installed from the marketplace entitlements are required in order to use some of the APIs. Eventually, entitlements will be required for all extensions.
local_storage
- required to access local storage apis.navigation
- required to access navigation and link apis.new_window
- required to open a new window.allow_forms
- required if extension uses html form submission. Note that some @looker/componenents
use html forms underneath the covers. Note that a message similar to the following will be displayed on the browser console if a form is used without the allow forms entitlement: Blocked form submission to '' because the form's frame is sandboxed and the 'allow-forms' permission is not set.
allow_same_origin
- required if the extension uses the @looker/embed-sdk
. Note that a message similar to the following will be displayed on the browser console if the emded sdk used without the allow same origin entitlement: Access to script at ... from origin 'null' has been blocked by CORS policy ...
.core_api_methods
- list of Looker api methods used by the extension.external_api_urls
- list of external apis used by the extension. Only the protocol, domain and top level domain need be defined. A wild card of *.
may be used for sub domains. External api urls must be defined for both fetch proxy and server proxy calls.oauth2_urls
- list of OAUTH2 authentication URLs used by the extension. Note that these are URLs that do the authentication and code exchange..An extension can be viewed in full screen mode (no Looker chrome) at /spartan/{extension_project::application_id}. If a Looker user is assigned to the group "Extensions Only" the only parts of Looker the user can access are extensions under the /spartan/ path. As there is no Looker chrome in this mode, the extension should provide a Logout mechanism.
Note: this call only works when the extension is running in /spartan mode.
extensionSDK.spartanLogout()
FAQs
Looker Extension SDK
The npm package bryans99-extension-sdk receives a total of 0 weekly downloads. As such, bryans99-extension-sdk popularity was classified as not popular.
We found that bryans99-extension-sdk demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
PyPI now supports digital attestations, enhancing security and trust by allowing package maintainers to verify the authenticity of Python packages.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.