Hello,
I’m attempting to embed a LookML dashboard using the Looker Embed SDK and signed embedding.
I’ve already configured the following:
The front-end domain where the LookML is embedded has been whitelisted in the Looker admin panel (the Looker domain is different from the embedding domain).
“Embed SSO Authentication” is enabled.
An embed secret has been successfully generated.
API credentials for my admin user are correctly generated.
The URL created by my backend is validated by the Embed URI Validator, and the LookML dashboard displays correctly within my application.
However, the handlers for the Embed SDK are not functional; specifically, the connect() promise does not resolve.
Here is my code:
#######################
FRONT: EMBED LOOKML
#######################
import ‘./bootstrap’;
import { getEmbedSDK } from ‘@looker/embed-sdk’;
// Initialize the Looker Embed SDK
getEmbedSDK().init(‘xxxxxxx.cloud.looker.com’, ‘/getUrl’);
// Function to setup connection interactions
const setupConnection = (connection) => {
console.log(‘Setting up connection interactions for:’, connection.getPageType());
if (connection.getPageType() === ‘dashboards’) {
const dashboardConnection = connection.asDashboardConnection();
}
};
// Initialize when DOM is ready
document.addEventListener(‘DOMContentLoaded’, () => {
const dashboardBuilder = getEmbedSDK()
.createDashboardWithId(‘mrl_temp_data::mrl_sales’)
.withAllowAttr(‘fullscreen’)
.withDialogScroll()
.appendTo(‘#dashboard’)
.withClassName(‘looker-dashboard’);
// Attach event handlers
dashboardBuilder
.on(‘dashboard:loaded’, (event) => {
console.log(’
Dashboard loaded’, event);
console.log(‘Dashboard ID:’, event.dashboard?.id);
console.log(‘Dashboard Title:’, event.dashboard?.title);
})
.on(‘dashboard:filters:changed’, (event) => {
console.log(’
Filters updated’, event);
console.log(‘Changed filters:’, event.filters);
})
.on(‘dashboard:run:start’, (event) => {
console.log(’
Dashboard run started’, event);
document.querySelector(‘#dashboard’)?.classList.add(‘loading’);
})
.on(‘dashboard:run:complete’, (event) => {
console.log(’
Dashboard run completed’, event);
document.querySelector(‘#dashboard’)?.classList.remove(‘loading’);
})
.on(‘page:changed’, (event) => {
console.log(’
Page changed’, event);
console.log(‘New page:’, event.page);
})
.on(‘session:status’, (event) => {
console.log(’
Session status:‘, event);
})
.on(‘dashboard:delete’, (event) => {
console.log(’
Dashboard deleted’, event);
})
.on(‘dashboard:save’, (event) => {
console.log(’
Dashboard saved’, event);
});
// Build and connect
dashboardBuilder
.build()
.connect()
.then(setupConnection)
.catch(error => {
console.error(’
Error connecting to dashboard:', error);
if (error.message.includes(‘authentication’)) {
console.error(‘Authentication issue - check your SSO token’);
} else if (error.message.includes(‘network’)) {
console.error(‘Network issue - check your connection’);
}
});
});
#############################################
BACKEND GET EMBED URL
#############################################
Création du token pour la connection à looker
public function getAccessToken()
{
if (!$this->apiHost || !$this->clientId || !$this->clientSecret) {
Log::error(‘Looker API credentials or host not configured.’);
return null;
}
$endpoint = $this->apiHost . $this->apiVersion . ‘/login’;
try {
$response = Http::withOptions([
‘verify’ => false
])->asForm()->post($endpoint, [
‘client_id’ => $this->clientId,
‘client_secret’ => $this->clientSecret,
]);
$responseData = $response->json();
if (!is_array($responseData) || !isset($responseData[‘access_token’])) {
Log::error(‘Access token missing in Looker API response’, [
‘status’ => $response->status(),
‘response’ => $response->body(),
]);
throw new \RuntimeException(‘Access token missing in API response.’);
}
if (!is_string($responseData[‘access_token’])) {
Log::error(‘Access token must be a string’, [
‘status’ => $response->status(),
‘response’ => $response->body(),
]);
throw new \RuntimeException(‘Access token must be a string.’);
}
$this->accessToken = $responseData[‘access_token’];
return $this->accessToken;
} catch (\Exception $e) {
Log::error(‘Error with getAccessToken Looker:’ . $e->getMessage());
throw new \RuntimeException('An error occurred while getting access token: ’ . $e->getMessage());
}
}
/**
- @return array|mixed
- @throws ConnectionException
*/
public function getEmbedUrl(): array
{
$token = $this->getAccessToken();
$endpoint = $this->apiHost . ‘api/4.0/embed/sso_url’;
try {
$response = Http::withOptions([
‘verify’ => false
])->withToken($token)->post($endpoint, [
‘target_url’ => “https://xxxxxxx.cloud.looker.com/embed/dashboards/{$this->lookMlId}”,
‘session_length’ => 3600,
‘force_logout_login’ => false,
‘external_user_id’ => (string)auth()->id(),
‘first_name’ => ‘John’, //fake value
‘last_name’ => ‘Doe’, //fake value
‘permissions’ => [
‘access_data’,
‘see_lookml_dashboards’,
‘explore’,
‘see_looks’,
‘see_user_dashboards’,
‘embed_browse_spaces’,
‘embed_save_shared_space’,
],
‘models’ => [$this->lookMLModel],
‘group_ids’ => ,
‘external_group_id’ => null,
‘embed_domain’ => ‘https://xxxxxxxxxxxx-front.xxxx.me:3000’,
]);
if ($response->failed()) {
Log::error(‘Failed to get embed URL from Looker’, [
‘status’ => $response->status(),
‘response’ => $response->body(),
‘dashboard_id’ => $this->lookMlId,
]);
throw new \RuntimeException(‘Failed to get embed URL from Looker.’);
}
return $response->json();
} catch (\Exception $e) {
Log::error('Error with getEmbedUrl Looker: ’ . $e->getMessage());
throw new \RuntimeException('An error occurred while getting embed URL: ’ . $e->getMessage());
}
}