Embed Analytical Designer Using Iframes
To embed a Analytical Designer into your web application, copy the following iframe snippet into your web app and subtitute your own values for <host_url>
, <workspace_id>
and <visualization_id>
:
<iframe
title="My Embedded GoodData Analytical Designer"
id="embedded-app-frame"
src="<host_url>/analyze/embedded/#/<workspace_id>/<visualization_id>"
height="500px"
width="100%"
frameborder="0"
></iframe>
Height and Width Limitations
Iframe height: Ensure the iframe height is no larger than the browser window height to prevent modal dialogs from stretching incorrectly.
Iframe width: We recommend a minimum width of 1100 px to avoid having a horizontal scrolling bar.
You may be prompted to authenticate yourself. Once authenticated, you should be able to see your embded Analytical Designer:
To hide or display different parts of the Analytical Designer, see Configuration Options.
To use alternative ways of authentication, see Authentication.
To monitor and control the behaviour of the Analytical Designer using JavaScript, see PostMessages.
Configuration Options
You can configure your embedded Analytical Designer by adding one or more of the following URL parameters:
URL Parameter | Description |
---|---|
?hideControl=[topBar] | Hide the top bar |
?includeObjectsWithTags=[tag1,tag2] | Display only objects that contain a tag listed in this parameter |
?excludeObjectsWithTags=[tag1,tag2] | Hide objects that contain a tag listed in this parameter |
For example to embed a Analytical Designer without the top bar, you would use the following URL:
<host_url>/analyze/embedded/#/<workspace_id>/<visualization_id>?hideControl=[topBar]
Authentication
If you are using an OpenID Connect (OIDC) identity provider for authentication, the authentication process should be automatic. However, if you want to use API tokens for authentication, you will need to handle this type of authentication manually.
Use API Token
Security Risk
Using API token authentication in a production environment can lead to some security issues, such as unintentionally exposing your token to someone else. We strongly recommend to use context deferred authentication for UI applications.
To set up authentication using API token for an embedded Analytical Designer:
Embed the Analytical Designer using the iframe.
Add URL parameter
?apiTokenAuthentication=true
.Implement a message listener in the embedding page to listen to events sent by the Analytical Designer. The listener must wait for a
listeningForApiToken
event.When the event is received, the Analytical Designer is ready to receive the API token.
Send a
postMessage
command to the iframe with the API token value.The Analytical Designer will continue with initialization and use the token for authentication of all API calls from now on.
See the Example below for implementation details.
Browser Same-origin Policy
For maximum compatibility, GoodData and your application should be the same hostname and port, otherwise you may need to enable CORS to get around the same-origin browser restrictions.
Example of Authentication Using Injected API Token
You may reuse the following code snippet, do not forget to replace <host_url>
, <workspace_id>
, <visualization_id>
, and <api_token_value>
with your own values:
<iframe
title="Embed GD Application"
id="embedded-app-frame"
src="<host_url>/analyze/embedded/#/<workspace_id>/<visualization_id>?apiTokenAuthentication=true"
width="1300"
height="800"
></iframe>
<script>
console.log("Setup parent frame message listener")
window.addEventListener(
"message",
function (event) {
console.log("Post message received", event);
if (event.data.gdc?.event.name === "listeningForApiToken") {
const postMessageStructure = {
gdc: {
product: "analyticalDesigner",
event: {
name: "setApiToken",
data: {
token: "<api_token_value>"
}
}
}
};
console.log("Sending token to embedded window");
const origin = "*";
const iframe = document.getElementById("embedded-app-frame").contentWindow;
iframe.postMessage(postMessageStructure, origin);
}
},
false
);
</script>
Use JSON Web Token
If you are using JSON Web Token (JWT) instead of an API token for authentication edit your event data payload in the following way:
event: {
name: "setApiToken",
data: {
token: "<jwt_token_value>",
type: "jwt",
secondsBeforeTokenExpirationToEmitReminder: 60
}
}
When the token is 60 seconds from expiring, the apiTokenIsAboutToExpire
event is emitted to notify you and give you a chance to post a fresh token. Note that 60 seconds is the default value, but you can change it.
JWT Expiration Grace Period
There is an additional 60 second grace period after the JWT expires when GoodData backend will still accept the JWT as valid. This is to avoid various issues related to potential desync between GoodData backend and the service that manages the JWTs.
We recommend you check out the Create JWK and JWT in JavaScript to get started on handling JWTs inside JavaScript.
Example of Authentication Using Injected JWT
This is a modified example of the API token injection example shown above that has been modified to better suit the JWT use case:
<iframe
title="Embed GD Application"
id="embedded-app-frame"
src="<host_url>/analyze/embedded/#/<workspace_id>/<visualization_id>?apiTokenAuthentication=true"
width="1300"
height="800"
></iframe>
<script>
console.log("Setup parent frame message listener")
window.addEventListener(
"message",
function (event) {
console.log("Post message received", event);
const eventName = event.data.gdc?.event.name;
// Send new token when embedded application ask for it for the first time and then subsequently
// each 60 seconds before the previous token expires.
if (eventName === "listeningForApiToken" || eventName === "apiTokenIsAboutToExpire") {
// Fetch token from JWT generator endpoint, for example Node.JS Express application.
// The application must ensure that the call is authenticated, for example via request cookie
// or through SSO
fetch(`/api/jwt-generator?user=john.doe`, {
method: "GET"
}).then(function (response) {
if (response.ok) {
return response.json();
}
throw response;
}).then(function (data) {
const postMessageStructure = {
gdc: {
product: "analyticalDesigner",
event: {
name: "setApiToken",
data: {
token: data.jwt, // example expects the response is {"jwt": "<jwt_value>"}
type: "jwt",
secondsBeforeTokenExpirationToEmitReminder: 60,
}
}
}
};
console.log("Sending JWT to embedded window");
const origin = "*";
const iframe = document.getElementById("embedded-app-frame").contentWindow;
iframe.postMessage(postMessageStructure, origin);
});
}
},
false
);
</script>
PostMessages
When you embed the Analytical Designer, you can set up receiving messages from the Analytical Designer to your application.
The Use of ContextID in postMessages
ContextId
is used to correlate events and commands. It identifies the input and output of the communication.
Events
The following events are supported:
insightOpened
Triggered when a visualization is opened within the analytical designer.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "insightOpened",
"data" : {...}
"contextId": "context123"
}
}
}
insightChanged
Triggered when the content of a visualization is modified within the analytical designer.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "insightChanged",
"data" : {...},
"contextId": "context123"
}
}
}
insightRendered
Triggered when a visualization is fully rendered in the analytical designer.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "insightRendered",
"data" : {...},
"contextId": "context123"
}
}
}
setFilterContextFinished
Triggered when the process of setting the filter context is completed in the analytical designer.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "setFilterContextFinished",
"contextId": "context123"
}
}
}
filterContextChanged
Triggered when the filter context is modified within the analytical designer.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "filterContextChanged",
"data": {
"filters": [
{
"relativeDateFilter": {
"dataSet": {
"identifier": "identifier 123",
"type": "dataSet"
},
"granularity": "GDC.time.date",
"from": -6,
"to": 0
},
"localIdentifier": "id123"
}
]
},
"contextId": "context123"
}
}
}
Commands
The following commands are supported:
setApiToken
Set the API token you want to use for authentication. See the Example in the parent article.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "setApiToken",
"contextId": "<context_id>",
"data": {
"token": "<api_token_value>"
}
}
}
}
undo
Reverts the most recent change made to the visualization.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "undo",
"contextId": "<context_id>"
}
}
}
redo
Reapplies the most recently undone edit to the visualization.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "redo",
"contextId": "<context_id>"
}
}
}
clear
Removes all items and information from the visualization.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "clear",
"contextId": "<context_id>"
}
}
}
clearInsight
Removes the following items from the insight:
- Attributes
- Facts
- Metrics
- Filters
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "clearInsight",
"contextId": "<context_id>"
}
}
}
openInsight
Opens an existing visualization.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "openInsight",
"data": {
"reportId": "<reportId>",
"projectId": "<projectId>"
},
"contextId": "<context_id>"
}
}
}
setFilterContext
Applies a set of filters to the embedded Analytical Designer. You can define, one or more filters in the filter definition.
Limitation When Using Multiple Date Filters
You are currently not able to change filter contexts with postMessages if the Analytical Designer contains multiple date filters. We intend to implement a fix for this in the future.
Example: Attribute filter with all values
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "setFilterContext",
"data": {
"filters": [
{
"negativeAttributeFilter": {
"displayForm": {
"identifier": "county_name"
},
"notIn":[]
}
}
]
},
"contextId": "<context_id>"
}
}
}
Example: Negative attribute filter
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "setFilterContext",
"data": {
"filters": [
{
"positiveAttributeFilter": {
"displayForm": {
"identifier": "ls__id__nm_stagename"
},
"in": ["Closed Lost","Closed Won","Conviction","Discovery"]
}
}
]
},
"contextId": "<context_id>"
}
}
}
Example: Relative date filter
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "setFilterContext",
"data": {
"filters": [
{
"relativeDateFilter": {
"dataSet": {
"identifier": "dt_closedate_timestamp",
"type": "dataSet"
},
"granularity": "GDC.time.year",
"from": -8,
"to": 0
}
}
]
},
"contextId": "<context_id>"
}
}
}
Example: Absolute date filter
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "setFilterContext",
"data": {
"filters": [
{
"absoluteDateFilter": {
"dataSet": {
"identifier": "dt_closedate_timestamp", "type": "dataSet"
},
"from": "2010-05-24",
"to": "2010-05-24"
}
}
]
},
"contextId": "<context_id>"
}
}
}
Example: Ranking filter
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "setFilterContext",
"data": {
"filters": [
{
"rankingFilter": {
"measure": {
"localIdentifier": "{local_identifier}"
},
"operator": "TOP",
"value": 1
}
}
]
},
"contextId": "<context_id>"
}
}
}
removeFilterContext
Removes filters from the embedded analytical designer.
{
"gdc": {
"product": "analyticalDesigner",
"event": {
"name": "removeFilterContext",
"data": {
"filters": [
// attribute filter
{
"displayForm": {
"identifier": "identifier123"
}
},
// date filter
{
"dataSet": {
"identifier": "identifier123"
}
},
// Ranking filter
{
"removeRankingFilter": {}
}
],
},
"contextId": "<context_id>"
}
}
}