Hello,
My team has a web application with QuickSight dashboards embedded in it. I’m trying to add support for Bookmarks per Amazon QuickSight now supports State Persistence and Bookmarks for embedded dashboards, but I haven’t gotten it to work. Below is the code that I believe is relevant for this. Why are bookmarks not displaying? (If I need to expand on any // ...
parts, let me know.) Thanks in advance for the help.
1. Set up lambda using boto3
Our lambda_function.py lambda handler uses boto3.QuickSight.Client.generate_embed_url_for_registered_user() to get the embed URL, like so:
import boto3
from botocore.exceptions import ClientError
# ...
response = qs.generate_embed_url_for_registered_user(
AwsAccountId=self.aws_account_id,
SessionLifetimeInMinutes=135,
UserArn=user_arn,
ExperienceConfiguration={
'Dashboard': {
'InitialDashboardId': dashboard_id,
'FeatureConfigurations': {
'StatePersistence': {
'Enabled': True
},
'Bookmarks': {
'Enabled': True
}
}
},
},
)
# ...
return {
'statusCode': 200,
'headers': {"Access-Control-Allow-Origin": "*"},
'body': json.dumps(response, cls=DateTimeEncoder),
'isBase64Encoded': bool('false')
}
2. Call API in React code
We have an API Gateway that calls this lambda. In our React code, this API is called in a file named retrieveQSDashboardURL.js like so:
import Axios from "axios";
import { v4 as uuidv4 } from "uuid";
const uuid = uuidv4();
export async function retrieveQSDashboardURL(userEmailAddress, DashboardId, token) {
try {
const accessTokenValue = async () => {
const response = await token;
const data = await response;
return data;
};
const value = await accessTokenValue();
const tokenValue = value.accessToken;
const headers = {
Authorization: tokenValue,
};
const params = {
dashboard_id: DashboardId,
correlationId: uuid,
};
const quicksightAPIResponse = await new Axios.get(
process.env.REACT_APP_GET_DASHBOARD_EMBED_URL, // the API url
{
headers,
params,
}
);
return quicksightAPIResponse;
} catch (error) {
console.log(error);
}
}
3. Process and dispatch the API response
The retrieveQSDashboardURL()
function is called in a file named retrieveDashboard.js:
import { retrieveQSDashboardURL } from "../../../services/api/aws/retrieveQSDashboardURL";
// ...
const execute = (userEmailAddress, DashboardId, qsDashboardEmbedState, token) =>
async (dispatch, getState) => {
try {
dispatch({ type: RETRIEVE_DASHBOARD_LOADING });
const quicksightAPIResponse = await retrieveQSDashboardURL(
userEmailAddress,
DashboardId,
token
);
EmbedObj = quicksightAPIResponse.data
EmbedObj.BaseUrl = // edit the base url of quicksightAPIResponse.data for page organizing later
EmbedObj.EmbedUrl.substr(0, EmbedObj.EmbedUrl.indexOf("/dashboards/")) + "/dashboards/";
dispatch({
type: RETRIEVE_DASHBOARD,
payload: quicksightAPIResponse.data,
});
} catch (err) {
dispatch({ type: RETRIEVE_DASHBOARD_FAILURE, payload: err });
}
};
export default execute;
4. Set state data with the API response
This RETRIEVE_DASHBOARD
dispatch type is used in a file named dashboardEmbed.js:
import { initialState } from "./initialState";
// ...
const quicksightDashboardEmbed = (
state = initialState.quicksightDashboardEmbed,
action
) => {
const payload = action.payload;
switch (action.type) {
case RETRIEVE_DASHBOARD:
return {
data: payload,
loading: false,
rendered: false,
rendering: false,
loaded: true,
error: null,
retrieveDateTime: new Date().getTime(),
};
// ...other cases...
default:
return state;
}
};
export default quicksightDashboardEmbed;
5. Use that state data to render the embedded QuickSight dashboard (bookmarks not working)
Finally, quicksightDashboardEmbed
is used in the file named Dashboard.js, which renders the embedded dashboard in HTML via JSX:
import { useEffect } from "react";
import Grid from "@mui/material/Grid";
import CircularProgress from "@mui/material/CircularProgress";
import { useSelector, useDispatch } from "react-redux";
import { setRenderedTrue, setRenderedFalse } from "../../redux/actions/aws/setRenderDashboard";
import retrieveDashboard from "../../redux/actions/aws/retrieveDashboard";
import Iframe from "react-iframe";
import { useMsal } from "@azure/msal-react";
import { loginRequest } from "../../services/auth/microsoft/authConfig";
import AppBarDashboard from "../common/AppBarDashboard";
function Dashboard(props) {
const dispatch = useDispatch();
const { accounts, instance } = useMsal();
const token = instance.acquireTokenSilent({
...loginRequest,
account: accounts[0],
});
const qsDashboardState = props.dashboard;
const userEmailAddressState = useSelector(
(state) => state.microsoftUserData.data.userEmailAddress
);
const qsDashboardListState = useSelector(
(state) => state.quicksightDashboards
);
const qsDashboardEmbedState = useSelector(
(state) => state.quicksightDashboardEmbed
);
useEffect(() => {
// ...
return function cleanup() {
if (
qsDashboardEmbedState.loaded &&
!qsDashboardEmbedState.loading &&
qsDashboardListState.loaded &&
!qsDashboardListState.loading
) {
dispatch(setRenderedTrue());
}
};
}, [
qsDashboardEmbedState,
qsDashboardState,
dispatch,
qsDashboardListState.loaded,
qsDashboardListState.loading,
userEmailAddressState,
token,
]);
return (
<div data-testid="appDashboard">
<AppBarDashboard title={qsDashboardState.title} />
{qsDashboardEmbedState.loading && (
// ...
)}
{qsDashboardEmbedState.loaded && !qsDashboardEmbedState.loading && (
<>
{" "}
<Grid container justifyContent="center" data-testid="dashboardLoaded">
<Grid item lg={12} md={12} sm={12} xs={12}>
<Iframe
url={
qsDashboardEmbedState.rendered
?
qsDashboardEmbedState.data.BaseUrl +
qsDashboardState.DashboardId
: qsDashboardEmbedState.data.EmbedUrl
}
width="100%"
height="1000px"
id="dashboardContainer"
className="QuicksightEmbedding"
display="initial"
position="relative"
loading={"LOADING"}
/>
</Grid>
</Grid>
</>
)}
</div>
);
}
export default Dashboard;