Coder Social home page Coder Social logo

amplify-quicksight-dashboard-embedded's Introduction

Embedding Amazon QuickSight Dashboards in a React Application using Amplify

Diagram

Requirements

Configure your Cloud9 environment

Inside the Cloud9 environment, in the bash terminal we are going to configure the AWS CLI with your IAM credentials as follows.

aws configure
  • In AWS Cloud9 configure the AWS CLI as follows.
    • AWS Access Key ID: ** (Type your Access key ID)**
    • AWS Secret Access Key: (Type your Secret access key)
    • Default region name [us-east-1]: us-east-1
    • Default output format [json]: json

Remove aws_session_token variable from aws credentials.

sed -i 's/aws_session_token =//g' ~/.aws/credentials

Install dependencies and create the React project

Install Amplify CLI tool https://github.com/aws-amplify/amplify-cli

npm install -g @aws-amplify/cli

Create the React project.

npx create-react-app amplify-quicksight-dashboard-embedded
cd amplify-quicksight-dashboard-embedded
npm install amazon-quicksight-embedding-sdk
npm install aws-amplify aws-amplify-react @aws-amplify/ui-react @material-ui/core

Initialize your project with Amplify

amplify init

? Enter a name for the project amplifyquicksightdas

? Enter a name for the environment dev

? Choose your default editor: Sublime Text

? Choose the type of app that you're building javascript

Please tell us about your project

? What javascript framework are you using react

? Source Directory Path: src

? Distribution Directory Path: build

? Build Command: npm run-script build

? Start Command: npm run-script start

Using default provider awscloudformation

For more information on AWS Profiles, see:

https://docs.aws.amazon.com/cli/latest/userguide/cli-multiple-profiles.html

? Do you want to use an AWS profile? Yes

? Please choose the profile you want to use default

Authentication with Amazon Cognito

Add authentication with the following command using the default values.

amplify auth add

? Do you want to use the default authentication and security configuration? Default configuration

Warning: you will not be able to edit these selections.

? How do you want users to be able to sign in? Username

? Do you want to configure advanced settings? No, I am done.

Add Lambda Function

amplify add function

? Select which capability you want to add: Lambda function (serverless function)

? Provide a friendly name for your resource to be used as a label for this category in the project: getQuickSightDashboardEmbedURL

? Provide the AWS Lambda function name: getQuickSightDashboardEmbedURL

? Choose the runtime that you want to use: NodeJS

? Choose the function template that you want to use: Serverless ExpressJS function (Integration with API Gateway)

? Do you want to access other resources in this project from your Lambda function? No

? Do you want to invoke this function on a recurring schedule? No

? Do you want to configure Lambda layers for this function? No

? Do you want to edit the local lambda function now? No

Add API REST

amplify add api

? Please select from one of the below mentioned services: REST

? Provide a friendly name for your resource to be used as a label for this category in the project: quicksight

? Provide a path (e.g., /book/{isbn}): /getQuickSightDashboardEmbedURL

? Choose a Lambda source Use a Lambda function already added in the current Amplify project

? Choose the Lambda function to invoke by this path getQuickSightDashboardEmbedURL

? Restrict API access Yes

? Who should have access? Authenticated users only

? What kind of access do you want for Authenticated users? read

? Do you want to add another path? No

Build all your local backend resources and provision it in the cloud with the following command.

amplify push

Configure the React application

For the file src/App.js, replace the content with the following lines.

import Typography from '@material-ui/core/Typography';
import Container from '@material-ui/core/Container';
import Amplify, { Auth } from 'aws-amplify';
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react';
import awsconfig from './aws-exports';
import Embed from './Embed';
import { makeStyles } from '@material-ui/core/styles';

Amplify.configure(awsconfig);

const useStyles = makeStyles((theme) => ({
  title: {
    paddingTop: theme.spacing(2)
  },
}));

function App() {
  
  const classes = useStyles();
  
  return (
    <div>
      <AmplifySignOut />
      <Container maxWidth="md">
        <Typography variant="h4" component="h1" align="center" color="textPrimary" className={classes.title} gutterBottom>
          Amazon QuickSight Embed
        </Typography>
        <Embed />
      </Container>
    </div>
  );
}

export default withAuthenticator(App);

Create the file src/Embed.js and add the following lines.

import React from 'react';
import { API, Auth } from 'aws-amplify';
import CircularProgress from '@material-ui/core/CircularProgress';
import { withStyles } from '@material-ui/core/styles';

var QuickSightEmbedding = require("amazon-quicksight-embedding-sdk");

const useStyles = theme => ({
  loading: {
    alignContent: 'center',
    justifyContent: 'center',
    display: 'flex',
    marginTop: theme.spacing(4),
  },
});

class Embed extends React.Component {

    constructor(props){
        super(props);
        this.state = {
            loader: true
        };
    }
    
    componentDidMount() {
        this.getQuickSightDashboardEmbedURL();
    }
    
    getQuickSightDashboardEmbedURL = async () => {
        const data = await Auth.currentSession();
        const jwtToken = data.idToken.jwtToken;
        const payloadSub = data.idToken.payload.sub;
        const email = data.idToken.payload.email;
        
        const params = { 
            headers: {},
            response: true,
            queryStringParameters: {
                jwtToken: jwtToken,
                payloadSub: payloadSub,
                email: email
            }
        }
        const quicksight = await API.get('quicksight', '/getQuickSightDashboardEmbedURL', params);
        console.log(quicksight);
        const containerDiv = document.getElementById("dashboardContainer");
        
        const options = {
            url: quicksight.data.data.EmbedUrl,
            container: containerDiv,
            parameters: {
                country: "United States"
            },
            scrolling: "no",
            height: "800px",
            width: "912px",
            footerPaddingEnabled: true,
        };
        const dashboard = QuickSightEmbedding.embedDashboard(options);
        this.setState({ loader: false });
    };
    
    render() {
        const { classes } = this.props;
        return (
            <div>
                { this.state.loader && (
                    <div className={classes.loading}> <CircularProgress /> </div>
                )}
                <div id="dashboardContainer"></div>
            </div>
        );
    }
}

export default withStyles(useStyles)(Embed);

Update your Lambda function and permissions required

In the file amplify/backend/function/getQuickSightDashboardEmbedURL/getQuickSightDashboardEmbedURL-cloudformation-template.json add the following lines to the lambdaexecutionpolicy resource in the PolicyDocument property (line 130).

						{
							"Effect": "Allow",
							"Action": [
								"sts:AssumeRoleWithWebIdentity"
							],
							"Resource": "*"
						},
						{
							"Effect": "Allow",
							"Action": [
							    "cognito-identity:GetId",
							    "cognito-identity:GetOpenIdToken"
							],
							"Resource": "*"
						},

Install the dependencie amazon-cognito-identity-js for your backend function getQuickSightDashboardEmbedURL.

cd amplify/backend/function/getQuickSightDashboardEmbedURL/src/
npm install amazon-cognito-identity-js
cd ../../../../../

For the file amplify/backend/function/getQuickSightDashboardEmbedURL/src/app.js replace the content with the following lines.

Inside the code replace the following values with your own values that can be found in your CloudFormation stack outputs:

  • <cognito-authenticated-role-arn>
  • <identity-pool-id>
  • <user-pool-id>
  • <account-id>
  • <dashboard-id>
var express = require('express')
var bodyParser = require('body-parser')
var awsServerlessExpressMiddleware = require('aws-serverless-express/middleware')

var AWS = require('aws-sdk');
var AmazonCognitoIdentity = require('amazon-cognito-identity-js');
const https = require('https');

// declare a new express app
var app = express()
app.use(bodyParser.json())
app.use(awsServerlessExpressMiddleware.eventContext())

// Enable CORS for all methods
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*")
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")
  next()
});

/**********************
 * getQuickSightDashboardEmbedURL get method *
 **********************/

app.get('/getQuickSightDashboardEmbedURL', function(req, res) {

    var roleArn = '<cognito-authenticated-role-arn>'; // your cognito authenticated role arn here
  
    AWS.config.region = 'us-east-1';
  
    var sessionName = req.query.payloadSub;
    var cognitoIdentity = new AWS.CognitoIdentity();
    var stsClient = new AWS.STS();
    var params = {
        IdentityPoolId: '<identity-pool-id>', // your identity pool id here
        Logins: {
            // your logins here
            'cognito-idp.us-east-1.amazonaws.com/<user-pool-id>': req.query.jwtToken
        }
    };
    
    cognitoIdentity.getId(params, function(err, data) {
        if (err) console.log(err, err.stack);
        else {
            data.Logins = {
                // your logins here
                'cognito-idp.us-east-1.amazonaws.com/<user-pool-id>': req.query.jwtToken
            };

            cognitoIdentity.getOpenIdToken(data, function(err, openIdToken) {
                if (err) {
                    console.log(err, err.stack);
                    //callback(err);
                    res.json({
                      err
                    })
                } else {
                    let stsParams = {
                        RoleSessionName: sessionName,
                        WebIdentityToken: openIdToken.Token,
                        RoleArn: roleArn
                    }
                    stsClient.assumeRoleWithWebIdentity(stsParams, function(err, data) {
                        if (err) {
                            console.log(err, err.stack);
                            //callback(err);
                            res.json({
                              err
                            })
                        } else {
                            AWS.config.update({
                                region: 'us-east-1',
                                credentials: {
                                    accessKeyId: data.Credentials.AccessKeyId,
                                    secretAccessKey: data.Credentials.SecretAccessKey,
                                    sessionToken: data.Credentials.SessionToken,
                                    expiration: data.Credentials.Expiration
                                }
                            });
                            var registerUserParams = {
                                // required
                                AwsAccountId: "<account-id>",
                                // can be passed in from api-gateway call
                                Email: req.query.email,
                                // can be passed in from api-gateway call
                                IdentityType: 'IAM',
                                // can be passed in from api-gateway call
                                Namespace: 'default',
                                // can be passed in from api-gateway call
                                UserRole: 'READER',
                                IamArn: roleArn,
                                SessionName: sessionName
                            };
                            var quicksight = new AWS.QuickSight();
                            quicksight.registerUser(registerUserParams, function(err, data) {
                                if (err) {
                                    console.log("3");
                                    console.log(err, err.stack); // an error occurred
                                    if (err.code && err.code === 'ResourceExistsException') {
                                      var getDashboardParams = {
                                            // required
                                            AwsAccountId: "<account-id>",
                                            // required
                                            DashboardId: "<dashboard-id>",
                                            // required
                                            IdentityType: 'IAM',
                                            ResetDisabled: false, // can be passed in from api-gateway call
                                            SessionLifetimeInMinutes: 100, // can be passed in from api-gateway call
                                            UndoRedoDisabled: false // can be passed in from api-gateway call
                                        };
                                        var quicksightGetDashboard = new AWS.QuickSight();
                                        quicksightGetDashboard.getDashboardEmbedUrl(getDashboardParams, function(err, data) {
                                            if (err) {
                                                console.log(err, err.stack); // an error occurred
                                                  res.json({
                                                    err
                                                  })
                                            } else {
                                                console.log(data);
                                                res.json({
                                                  data
                                                })
                                            }
                                        });
                                    } else {
                                      res.json({
                                        err
                                      })
                                    }
                                } else {
                                    // successful response
                                    setTimeout(function() {
                                    var getDashboardParams = {
                                          // required
                                          AwsAccountId: "<account-id>",
                                          // required
                                          DashboardId: "<dashboard-id>",
                                          // required
                                          IdentityType: 'IAM',
                                          ResetDisabled: false, // can be passed in from api-gateway call
                                          SessionLifetimeInMinutes: 100, // can be passed in from api-gateway call
                                          UndoRedoDisabled: false // can be passed in from api-gateway call
                                      };
                                  
                                      var quicksightGetDashboard = new AWS.QuickSight();
                                      quicksightGetDashboard.getDashboardEmbedUrl(getDashboardParams, function(err, data) {
                                          if (err) {
                                              console.log(err, err.stack); // an error occurred
                                                res.json({
                                                  err
                                                })
                                          } else {
                                              console.log(data);
                                              res.json({
                                                data
                                              })
                                          }
                                      });
                                        
                                    }, 2000);
                                    
                                }
                            });
                            
                        }
                    });
                }
            });
        }
    });

});

app.listen(3000, function() {
    console.log("App started")
});

module.exports = app

Identify your AuthRole assigned to your Cognito Identity Pool and add the following inline policy.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": "quicksight:RegisterUser",
            "Resource": "*",
            "Effect": "Allow"
        },
        {
            "Action": "quicksight:GetDashboardEmbedUrl",
            "Resource": "*",
            "Effect": "Allow"
        }
    ]
}

Update your lambda function and permissions in the cloud with the following command.

amplify push

Testing your Application inside Cloud9

Inside your amplify project start the React application.

npm start

Use the Preview Running Application option inside the Cloud9 environment.

React Start

Create a new new account following the proccess and login.

Cloud9 HTTPS

Cloud9 expose a HTTPS URL, copy the URL provided and in Manage QuickSight add the domain in Domains and Embedding section.

https://docs.aws.amazon.com/quicksight/latest/user/approve-domain-for-dashboard-embedding.html

Now you can test your application and see the dashboard with the following message "Not authorized or not found".

QuickSight Dashboard Not Authorized

Update the dashboard permissions, you will find a new user added to Quicksight related to Cognito, just add the user to the Dashboard and show the dashboard again.

Managed dashboard permissions for sharing: https://docs.aws.amazon.com/quicksight/latest/user/sharing-a-dashboard.html#share-a-dashboard

QuickSight Dashboard

Hosting with Amazon S3 and Amazon CloudFront (Optional)

amplify add hosting

? Select the plugin module to execute Amazon CloudFront and S3

? Select the environment setup: PROD (S3 with CloudFront using HTTPS)

? hosting bucket name (use default name or personalized)

amplify publish

Add the Hosting endpoint to your domains in QuickSight.

Use the Hosting endpoint to browse inside your React application to visualize your dashboard.

QuickSight Dashboard

amplify-quicksight-dashboard-embedded's People

Contributors

aurbac avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

amplify-quicksight-dashboard-embedded's Issues

Dashboard embedding failing

Hi,

I did the dashboard embedding successfully for the first time.

But after this, when I repeat the same procedure for embedding a new dashboard, it fails. I've used a unique API, pathname, dashboard. I am getting the error of failing Get request error 404. What can be the solution?

Authentication error due to incorrect package versions for aws-amplify and @aws-amplify/ui-react

[ERROR] 06:59.609 AuthError - 
            Error: Amplify has not been configured correctly. 
            The configuration object is missing required auth properties.
            This error is typically caused by one of the following scenarios:

            1. Did you run `amplify push` after adding auth via `amplify add auth`?
                See https://aws-amplify.github.io/docs/js/authentication#amplify-project-setup for more information

            2. This could also be caused by multiple conflicting versions of amplify packages, see (https://docs.amplify.aws/lib/troubleshooting/upgrading/q/platform/js) for help upgrading Amplify packages.

Tried updating App.js like this (source):

import Typography from '@material-ui/core/Typography';
import Container from '@material-ui/core/Container';
import Amplify, { Auth } from 'aws-amplify';
import { withAuthenticator, AmplifySignOut } from '@aws-amplify/ui-react';
import awsconfig from './aws-exports';
import Embed from './Embed';
import { makeStyles } from '@material-ui/core/styles';

Amplify.configure(awsconfig);

Auth.configure(awsconfig); // added this

Then I ran into this error on compile:

'AmplifySignOut' is not exported from '@aws-amplify/ui-react'

So I tried using older package versions (source):

"dependencies": {
    "@aws-amplify/ui-react": "^1.2.5",
   ...
}

I also tried uninstalling and reinstalling aws-amplify and @aws-amplify/ui-react 1.2.5 but no luck.

This did not work, but I still may not be using the correct package versions. There is an issue on this here:
aws-amplify/amplify-js#4315

Would be helpful to know which package versions this needs to run correctly.

Unhandled Rejection (TypeError): Cannot read property 'EmbedUrl' of undefined

Thank you firstly for this walkthrough. I was able to follow this but when running npm start at the end and previewing the application, I'm hit with the error: Unhandled Rejection (TypeError): Cannot read property 'EmbedUrl' of undefined.

I'm fairly confident I entered my ID pool and ARN etc. correctly, do you know why this could be?

Screenshot 2021-01-15 at 10 15 49

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.