diff --git a/backend/dataall/api/Objects/Tenant/mutations.py b/backend/dataall/api/Objects/Tenant/mutations.py index ac4e87be5..7f57a5050 100644 --- a/backend/dataall/api/Objects/Tenant/mutations.py +++ b/backend/dataall/api/Objects/Tenant/mutations.py @@ -1,6 +1,6 @@ from ... import gql from .input_types import UpdateGroupTenantPermissionsInput -from .resolvers import update_group_permissions +from .resolvers import * updateGroupPermission = gql.MutationField( name='updateGroupTenantPermissions', @@ -12,3 +12,22 @@ type=gql.Boolean, resolver=update_group_permissions, ) + +createQuicksightDataSourceSet = gql.MutationField( + name='createQuicksightDataSourceSet', + args=[ + gql.Argument(name='vpcConnectionId', type=gql.NonNullableType(gql.String)) + ], + type=gql.String, + resolver=create_quicksight_data_source_set, +) + +updateSSMParameter = gql.MutationField( + name='updateSSMParameter', + args=[ + gql.Argument(name='name', type=gql.NonNullableType(gql.String)), + gql.Argument(name='value', type=gql.NonNullableType(gql.String)) + ], + type=gql.String, + resolver=update_ssm_parameter, +) diff --git a/backend/dataall/api/Objects/Tenant/queries.py b/backend/dataall/api/Objects/Tenant/queries.py index 4c4d6f089..62cac727f 100644 --- a/backend/dataall/api/Objects/Tenant/queries.py +++ b/backend/dataall/api/Objects/Tenant/queries.py @@ -16,3 +16,33 @@ type=gql.Ref('GroupSearchResult'), resolver=list_tenant_groups, ) + +getMonitoringDashboardId = gql.QueryField( + name='getMonitoringDashboardId', + type=gql.String, + resolver=get_monitoring_dashboard_id, +) + +getMonitoringVpcConnectionId = gql.QueryField( + name='getMonitoringVPCConnectionId', + type=gql.String, + resolver=get_monitoring_vpc_connection_id, +) + +getPlatformAuthorSession = gql.QueryField( + name='getPlatformAuthorSession', + args=[ + gql.Argument(name='awsAccount', type=gql.NonNullableType(gql.String)), + ], + type=gql.String, + resolver=get_quicksight_author_session, +) + +getPlatformReaderSession = gql.QueryField( + name='getPlatformReaderSession', + args=[ + gql.Argument(name='dashboardId', type=gql.NonNullableType(gql.String)), + ], + type=gql.String, + resolver=get_quicksight_reader_session, +) diff --git a/backend/dataall/api/Objects/Tenant/resolvers.py b/backend/dataall/api/Objects/Tenant/resolvers.py index 2fafa76a2..23eed371e 100644 --- a/backend/dataall/api/Objects/Tenant/resolvers.py +++ b/backend/dataall/api/Objects/Tenant/resolvers.py @@ -1,4 +1,10 @@ +import os + from .... import db +from ....aws.handlers.sts import SessionHelper +from ....aws.handlers.parameter_store import ParameterStoreManager +from ....aws.handlers.quicksight import Quicksight +from ....db import exceptions def update_group_permissions(context, source, input=None): @@ -32,3 +38,95 @@ def list_tenant_groups(context, source, filter=None): data=filter, check_perm=True, ) + + +def update_ssm_parameter(context, source, name: str = None, value: str = None): + current_account = SessionHelper.get_account() + region = os.getenv('AWS_REGION', 'eu-west-1') + print(value) + print(name) + response = ParameterStoreManager.update_parameter(AwsAccountId=current_account, region=region, parameter_name=f'/dataall/{os.getenv("envname", "local")}/quicksightmonitoring/{name}', parameter_value=value) + return response + + +def get_monitoring_dashboard_id(context, source): + current_account = SessionHelper.get_account() + region = os.getenv('AWS_REGION', 'eu-west-1') + dashboard_id = ParameterStoreManager.get_parameter_value(AwsAccountId=current_account, region=region, parameter_path=f'/dataall/{os.getenv("envname", "local")}/quicksightmonitoring/DashboardId') + if not dashboard_id: + raise exceptions.AWSResourceNotFound( + action='GET_DASHBOARD_ID', + message='Dashboard Id could not be found on AWS Parameter Store', + ) + return dashboard_id + + +def get_monitoring_vpc_connection_id(context, source): + current_account = SessionHelper.get_account() + region = os.getenv('AWS_REGION', 'eu-west-1') + vpc_connection_id = ParameterStoreManager.get_parameter_value(AwsAccountId=current_account, region=region, parameter_path=f'/dataall/{os.getenv("envname", "local")}/quicksightmonitoring/VPCConnectionId') + if not vpc_connection_id: + raise exceptions.AWSResourceNotFound( + action='GET_VPC_CONNECTION_ID', + message='Dashboard Id could not be found on AWS Parameter Store', + ) + return vpc_connection_id + + +def create_quicksight_data_source_set(context, source, vpcConnectionId: str = None): + current_account = SessionHelper.get_account() + region = os.getenv('AWS_REGION', 'eu-west-1') + user = Quicksight.register_user(AwsAccountId=current_account, UserName=context.username, UserRole='AUTHOR') + + datasourceId = Quicksight.create_data_source_vpc(AwsAccountId=current_account, region=region, UserName=context.username, vpcConnectionId=vpcConnectionId) + # Data sets are not created programmatically. Too much overhead for the value added. However, an example API is provided: + # datasets = Quicksight.create_data_set_from_source(AwsAccountId=current_account, region=region, UserName='dataallTenantUser', dataSourceId=datasourceId, tablesToImport=['organization', 'environment', 'dataset', 'datapipeline', 'dashboard', 'share_object']) + + return datasourceId + + +def get_quicksight_author_session(context, source, awsAccount: str = None): + with context.engine.scoped_session() as session: + admin = db.api.TenantPolicy.is_tenant_admin(context.groups) + + if not admin: + raise db.exceptions.TenantUnauthorized( + username=context.username, + action=db.permissions.TENANT_ALL, + tenant_name=context.username, + ) + region = os.getenv('AWS_REGION', 'eu-west-1') + + url = Quicksight.get_author_session( + AwsAccountId=awsAccount, + region=region, + UserName=context.username, + UserRole='AUTHOR', + ) + + return url + + +def get_quicksight_reader_session(context, source, dashboardId: str = None): + with context.engine.scoped_session() as session: + admin = db.api.TenantPolicy.is_tenant_admin(context.groups) + + if not admin: + raise db.exceptions.TenantUnauthorized( + username=context.username, + action=db.permissions.TENANT_ALL, + tenant_name=context.username, + ) + + region = os.getenv('AWS_REGION', 'eu-west-1') + current_account = SessionHelper.get_account() + + url = Quicksight.get_reader_session( + AwsAccountId=current_account, + region=region, + UserName=context.username, + UserRole='READER', + DashboardId=dashboardId + ) + + return url diff --git a/backend/dataall/aws/handlers/parameter_store.py b/backend/dataall/aws/handlers/parameter_store.py index 7b20c741d..c67197ab6 100644 --- a/backend/dataall/aws/handlers/parameter_store.py +++ b/backend/dataall/aws/handlers/parameter_store.py @@ -23,7 +23,7 @@ def client(AwsAccountId, region): @staticmethod def get_parameter_value(AwsAccountId, region, parameter_path): if not parameter_path: - raise Exception('Secret name is None') + raise Exception('Parameter name is None') try: parameter_value = ParameterStoreManager.client( AwsAccountId, region @@ -31,3 +31,18 @@ def get_parameter_value(AwsAccountId, region, parameter_path): except ClientError as e: raise Exception(e) return parameter_value + + @staticmethod + def update_parameter(AwsAccountId, region, parameter_name, parameter_value): + if not parameter_name: + raise Exception('Parameter name is None') + if not parameter_value: + raise Exception('Parameter value is None') + try: + response = ParameterStoreManager.client( + AwsAccountId, region + ).put_parameter(Name=parameter_name, Value=parameter_value, Overwrite=True)['Version'] + except ClientError as e: + raise Exception(e) + else: + return str(response) diff --git a/backend/dataall/aws/handlers/quicksight.py b/backend/dataall/aws/handlers/quicksight.py index edde347a2..d3a1d6f5b 100644 --- a/backend/dataall/aws/handlers/quicksight.py +++ b/backend/dataall/aws/handlers/quicksight.py @@ -1,9 +1,13 @@ import logging import re +import os +import ast from botocore.exceptions import ClientError from .sts import SessionHelper +from .secrets_manager import SecretsManager +from .parameter_store import ParameterStoreManager logger = logging.getLogger('QuicksightHandler') logger.setLevel(logging.DEBUG) @@ -262,3 +266,154 @@ def can_import_dashboard(AwsAccountId, region, UserName, DashboardId): return True return False + + @staticmethod + def create_data_source_vpc(AwsAccountId, region, UserName, vpcConnectionId): + client = Quicksight.get_quicksight_client(AwsAccountId, region) + identity_region = 'us-east-1' + + user = Quicksight.register_user(AwsAccountId, UserName, UserRole='AUTHOR') + try: + response = client.describe_data_source( + AwsAccountId=AwsAccountId, DataSourceId="dataall-metadata-db" + ) + + except client.exceptions.ResourceNotFoundException: + aurora_secret_arn = ParameterStoreManager.get_parameter_value(AwsAccountId=AwsAccountId, region=region, parameter_path=f'/dataall/{os.getenv("envname", "local")}/aurora/secret_arn') + aurora_params = SecretsManager.get_secret_value( + AwsAccountId=AwsAccountId, region=region, secretId=aurora_secret_arn + ) + aurora_params_dict = ast.literal_eval(aurora_params) + response = client.create_data_source( + AwsAccountId=AwsAccountId, + DataSourceId="dataall-metadata-db", + Name="dataall-metadata-db", + Type="AURORA_POSTGRESQL", + DataSourceParameters={ + 'AuroraPostgreSqlParameters': { + 'Host': aurora_params_dict["host"], + 'Port': aurora_params_dict["port"], + 'Database': aurora_params_dict["dbname"] + } + }, + Credentials={ + "CredentialPair": { + "Username": aurora_params_dict["username"], + "Password": aurora_params_dict["password"], + } + }, + Permissions=[ + { + "Principal": f"arn:aws:quicksight:{region}:{AwsAccountId}:group/default/dataall", + "Actions": [ + "quicksight:UpdateDataSourcePermissions", + "quicksight:DescribeDataSource", + "quicksight:DescribeDataSourcePermissions", + "quicksight:PassDataSource", + "quicksight:UpdateDataSource", + "quicksight:DeleteDataSource" + ] + } + ], + VpcConnectionProperties={ + 'VpcConnectionArn': f"arn:aws:quicksight:{region}:{AwsAccountId}:vpcConnection/{vpcConnectionId}" + } + ) + + return "dataall-metadata-db" + + @staticmethod + def create_data_set_from_source(AwsAccountId, region, UserName, dataSourceId, tablesToImport): + client = Quicksight.get_quicksight_client(AwsAccountId, region) + user = Quicksight.describe_user(AwsAccountId, UserName) + if not user: + return False + + data_source = client.describe_data_source( + AwsAccountId=AwsAccountId, + DataSourceId=dataSourceId + ) + + if not data_source: + return False + + for table in tablesToImport: + + response = client.create_data_set( + AwsAccountId=AwsAccountId, + DataSetId=f"dataall-imported-{table}", + Name=f"dataall-imported-{table}", + PhysicalTableMap={ + 'string': { + 'RelationalTable': { + 'DataSourceArn': data_source.get('DataSource').get('Arn'), + 'Catalog': 'string', + 'Schema': 'dev', + 'Name': table, + 'InputColumns': [ + { + 'Name': 'string', + 'Type': 'STRING' + }, + ] + } + }}, + ImportMode='DIRECT_QUERY', + Permissions=[ + { + 'Principal': user.get('Arn'), + 'Actions': [ + "quicksight:DescribeDataSet", + "quicksight:DescribeDataSetPermissions", + "quicksight:PassDataSet", + "quicksight:DescribeIngestion", + "quicksight:ListIngestions" + ] + }, + ], + ) + + return True + + @staticmethod + def create_analysis(AwsAccountId, region, UserName): + client = Quicksight.get_quicksight_client(AwsAccountId, region) + user = Quicksight.describe_user(AwsAccountId, UserName) + if not user: + return False + + response = client.create_analysis( + AwsAccountId=AwsAccountId, + AnalysisId='dataallMonitoringAnalysis', + Name='dataallMonitoringAnalysis', + Permissions=[ + { + 'Principal': user.get('Arn'), + 'Actions': [ + 'quicksight:DescribeAnalysis', + 'quicksight:DescribeAnalysisPermissions', + 'quicksight:UpdateAnalysisPermissions', + 'quicksight:UpdateAnalysis' + ] + }, + ], + SourceEntity={ + 'SourceTemplate': { + 'DataSetReferences': [ + { + 'DataSetPlaceholder': 'environment', + 'DataSetArn': f"arn:aws:quicksight:{region}:{AwsAccountId}:dataset/" + }, + ], + 'Arn': 'Linking environment section. + + +**2) Create Quicksight VPC connection** + +Follow the steps in the +documentation + and make sure that you are in the same region as the infrastructure of data.all. For example, in this case Ireland region. + +![quicksight](pictures/monitoring/vpc1.png#zoom#shadow) + +To complete the set-up you will need the following information: + +- VPC_ID of the RDS Aurora database, which is the same as the data.all created one. If you have more than one VPC in the account, you can always check this value in +AWS SSM Parameters or in the Aurora database as appears in the picture: + + +![quicksight](pictures/monitoring/vpc2.png#zoom#shadow) + +- Security group created for Quicksight: In the VPC console, under security groups, look for a group called `--quicksight-monitoring-sg` +For example using the default resource prefix, in an environment called prod, look for `dataall-prod-quicksight-monitoring-sg`. + +**3) Create Aurora data source** +We have automated this step for you! As a tenant user, a user that belongs to `DAADministrators` group, sign in to data.all. +In the UI navigate to the **Admin Settings** window by clicking in the top-right corner. You will appear in a window with 2 tabs: Teams and Monitoring. +In the Monitoring tab, introduce the VPC connection name that you created in step 2 and click on the *Save* button. Then, click on the +*Create Quicksight data source* button. Right now, a connection between the RDS database and Quicksight has been established. + +![quicksight](pictures/monitoring/qs1.png#zoom#shadow) + +**4) Customize your analyses and share your dashboards** +Go to Quicksight to start building your analysis by clicking on the *Start Quicksight session* button. +First, you need to create a dataset. Use the **dataall-metadata-db** data source, this is our connection with RDS. + +![quicksight](pictures/monitoring/qs2.png#zoom#shadow) + +Use this dataset in an analysis (check the docs customization of analyses) +and publish it as a dashboard (docs in publish dashboards) + +!!! abstract "Not only RDS" + With Quicksight you can go one step further and communicate with other AWS services and data sources. Explore the documentation + for cost analyses in AWS with Quicksight or AWS CloudWatch Logs collection and visualization with Quicksight. + + +**5) Bring your dashboard back to data.all** +Once your dashboard is ready, copy its ID (you can find it in the URL as appears in the below picture) + + +![quicksight](pictures/monitoring/qs3.png#zoom#shadow) + +Back in the data.all Monitoring tab, introduce this dashbaord ID. Now, other tenants can see your dashboard directly from data.all UI! + +![quicksight](pictures/monitoring/qs4.png#zoom#shadow) \ No newline at end of file diff --git a/documentation/userguide/docs/pictures/monitoring/qs1.png b/documentation/userguide/docs/pictures/monitoring/qs1.png new file mode 100644 index 000000000..b69d5fb4f Binary files /dev/null and b/documentation/userguide/docs/pictures/monitoring/qs1.png differ diff --git a/documentation/userguide/docs/pictures/monitoring/qs2.png b/documentation/userguide/docs/pictures/monitoring/qs2.png new file mode 100644 index 000000000..ee1f1219b Binary files /dev/null and b/documentation/userguide/docs/pictures/monitoring/qs2.png differ diff --git a/documentation/userguide/docs/pictures/monitoring/qs3.png b/documentation/userguide/docs/pictures/monitoring/qs3.png new file mode 100644 index 000000000..5fc75069c Binary files /dev/null and b/documentation/userguide/docs/pictures/monitoring/qs3.png differ diff --git a/documentation/userguide/docs/pictures/monitoring/qs4.png b/documentation/userguide/docs/pictures/monitoring/qs4.png new file mode 100644 index 000000000..db70aad10 Binary files /dev/null and b/documentation/userguide/docs/pictures/monitoring/qs4.png differ diff --git a/documentation/userguide/docs/pictures/monitoring/secret2.png b/documentation/userguide/docs/pictures/monitoring/secret2.png new file mode 100644 index 000000000..8419c4abc Binary files /dev/null and b/documentation/userguide/docs/pictures/monitoring/secret2.png differ diff --git a/documentation/userguide/docs/pictures/monitoring/secrets.png b/documentation/userguide/docs/pictures/monitoring/secrets.png new file mode 100644 index 000000000..a29167de3 Binary files /dev/null and b/documentation/userguide/docs/pictures/monitoring/secrets.png differ diff --git a/documentation/userguide/docs/pictures/monitoring/vpc1.png b/documentation/userguide/docs/pictures/monitoring/vpc1.png new file mode 100644 index 000000000..66c4b2df5 Binary files /dev/null and b/documentation/userguide/docs/pictures/monitoring/vpc1.png differ diff --git a/documentation/userguide/docs/pictures/monitoring/vpc2.png b/documentation/userguide/docs/pictures/monitoring/vpc2.png new file mode 100644 index 000000000..0161a9733 Binary files /dev/null and b/documentation/userguide/docs/pictures/monitoring/vpc2.png differ diff --git a/documentation/userguide/mkdocs.yml b/documentation/userguide/mkdocs.yml index 5f97051c7..13373a05a 100644 --- a/documentation/userguide/mkdocs.yml +++ b/documentation/userguide/mkdocs.yml @@ -18,6 +18,7 @@ nav: - Dashboards: dashboards.md # - Warehouses: redshift.md - Security: security.md + - Monitoring: monitoring.md - Labs: - Data access management: lab1.md #- Data ingestion and transformation with pipelines: lab_template.md diff --git a/frontend/src/api/Tenant/createQuicksightDataSourceSet.js b/frontend/src/api/Tenant/createQuicksightDataSourceSet.js new file mode 100644 index 000000000..f7be6e031 --- /dev/null +++ b/frontend/src/api/Tenant/createQuicksightDataSourceSet.js @@ -0,0 +1,14 @@ +import { gql } from 'apollo-boost'; + +const createQuicksightDataSourceSet = ({vpcConnectionId}) => ({ + variables: { + vpcConnectionId + }, + mutation: gql` + mutation createQuicksightDataSourceSet ($vpcConnectionId: String!) { + createQuicksightDataSourceSet(vpcConnectionId: $vpcConnectionId) + } + ` +}); + +export default createQuicksightDataSourceSet; diff --git a/frontend/src/api/Tenant/getMonitoringDashboardId.js b/frontend/src/api/Tenant/getMonitoringDashboardId.js new file mode 100644 index 000000000..d7a074124 --- /dev/null +++ b/frontend/src/api/Tenant/getMonitoringDashboardId.js @@ -0,0 +1,11 @@ +import { gql } from 'apollo-boost'; + +const getMonitoringDashboardId = () => ({ + query: gql` + query getMonitoringDashboardId { + getMonitoringDashboardId + } + ` +}); + +export default getMonitoringDashboardId; diff --git a/frontend/src/api/Tenant/getMonitoringVPCConnectionId.js b/frontend/src/api/Tenant/getMonitoringVPCConnectionId.js new file mode 100644 index 000000000..568e5b713 --- /dev/null +++ b/frontend/src/api/Tenant/getMonitoringVPCConnectionId.js @@ -0,0 +1,11 @@ +import { gql } from 'apollo-boost'; + +const getMonitoringVPCConnectionId = () => ({ + query: gql` + query getMonitoringVPCConnectionId { + getMonitoringVPCConnectionId + } + ` +}); + +export default getMonitoringVPCConnectionId; diff --git a/frontend/src/api/Tenant/getPlatformAuthorSession.js b/frontend/src/api/Tenant/getPlatformAuthorSession.js new file mode 100644 index 000000000..c2b7728ac --- /dev/null +++ b/frontend/src/api/Tenant/getPlatformAuthorSession.js @@ -0,0 +1,16 @@ +import { gql } from 'apollo-boost'; + +const getPlatformAuthorSession = (awsAccount) => ({ + variables: { + awsAccount + }, + query: gql` + query getPlatformAuthorSession($awsAccount: String) { + getPlatformAuthorSession( + awsAccount: $awsAccount + ) + } + ` +}); + +export default getPlatformAuthorSession; diff --git a/frontend/src/api/Tenant/getPlatformReaderSession.js b/frontend/src/api/Tenant/getPlatformReaderSession.js new file mode 100644 index 000000000..206bf2194 --- /dev/null +++ b/frontend/src/api/Tenant/getPlatformReaderSession.js @@ -0,0 +1,14 @@ +import { gql } from 'apollo-boost'; + +const getPlatformReaderSession = (dashboardId) => ({ + variables: { + dashboardId + }, + query: gql` + query getPlatformReaderSession($dashboardId: String) { + getPlatformReaderSession(dashboardId: $dashboardId) + } + ` +}); + +export default getPlatformReaderSession; diff --git a/frontend/src/api/Tenant/updateSSMParameter.js b/frontend/src/api/Tenant/updateSSMParameter.js new file mode 100644 index 000000000..731c3465f --- /dev/null +++ b/frontend/src/api/Tenant/updateSSMParameter.js @@ -0,0 +1,15 @@ +import { gql } from 'apollo-boost'; + +const updateSSMParameter = ({name,value}) => ({ + variables: { + name, + value + }, + mutation: gql` + mutation updateSSMParameter ($name: String!, $value: String!) { + updateSSMParameter(name: $name, value: $value) + } + ` +}); + +export default updateSSMParameter; diff --git a/frontend/src/views/Administration/AdministrationView.js b/frontend/src/views/Administration/AdministrationView.js index 23300fa06..db2be01a3 100644 --- a/frontend/src/views/Administration/AdministrationView.js +++ b/frontend/src/views/Administration/AdministrationView.js @@ -15,8 +15,10 @@ import { import useSettings from '../../hooks/useSettings'; import ChevronRightIcon from '../../icons/ChevronRight'; import AdministrationTeams from './AdministrationTeams'; +import DashboardViewer from './AdministratorDashboardViewer' -const tabs = [{ label: 'Teams', value: 'teams' }]; + +const tabs = [{ label: 'Teams', value: 'teams' },{ label: 'Monitoring', value: 'dashboard' }]; const AdministrationView = () => { const { settings } = useSettings(); @@ -87,6 +89,7 @@ const AdministrationView = () => { {currentTab === 'teams' && } + {currentTab === 'dashboard' && } @@ -95,3 +98,4 @@ const AdministrationView = () => { }; export default AdministrationView; + diff --git a/frontend/src/views/Administration/AdministratorDashboardViewer.js b/frontend/src/views/Administration/AdministratorDashboardViewer.js new file mode 100644 index 000000000..ba9888b4d --- /dev/null +++ b/frontend/src/views/Administration/AdministratorDashboardViewer.js @@ -0,0 +1,416 @@ +import { createRef, useCallback, useEffect, useState } from 'react'; +import * as Yup from 'yup'; +import { Formik, useFormik } from 'formik'; +import * as ReactIf from 'react-if'; +import { + Box, + Grid, + Card, + CardActions, + CardContent, + CardHeader, + Container, + Divider, + TextField, + Typography, + Button, + CircularProgress +} from '@mui/material'; +import { FaCheckCircle } from 'react-icons/fa'; +import { AddOutlined, CopyAllOutlined, ArrowLeft, ArrowRightAlt, ChevronRight } from '@mui/icons-material'; +import { CopyToClipboard } from 'react-copy-to-clipboard/lib/Component'; +import { LoadingButton } from '@mui/lab'; +import getMonitoringDashboardId from '../../api/Tenant/getMonitoringDashboardId'; +import getMonitoringVPCConnectionId from '../../api/Tenant/getMonitoringVPCConnectionId'; +import updateSSMParameter from "../../api/Tenant/updateSSMParameter"; +import getTrustAccount from '../../api/Environment/getTrustAccount'; +import createQuicksightDataSourceSet from '../../api/Tenant/createQuicksightDataSourceSet'; +import getPlatformAuthorSession from '../../api/Tenant/getPlatformAuthorSession'; +import getPlatformReaderSession from '../../api/Tenant/getPlatformReaderSession'; +import { useDispatch } from '../../store'; +import useClient from '../../hooks/useClient'; +import { SET_ERROR } from '../../store/errorReducer'; +import useSettings from '../../hooks/useSettings'; + +const QuickSightEmbedding = require('amazon-quicksight-embedding-sdk'); + +const DashboardViewer = () => { + const dispatch = useDispatch(); + const client = useClient(); + const { settings } = useSettings(); + const [dashboardId, setDashboardId] = useState(''); + const [vpcConnectionId, setVpcConnectionId] = useState(''); + const [trustedAccount, setTrustedAccount] = useState(null); + const [dashboardRef] = useState(createRef()); + const [sessionUrl, setSessionUrl] = useState(null); + const [isOpeningSession, setIsOpeningSession] = useState(false); + const [isCreatingDataSource, setIsCreatingDataSource] = useState(false); + + const fetchTrustedAccount = useCallback(async () => { + const response = await client.query(getTrustAccount()); + if (!response.errors) { + setTrustedAccount(response.data.getTrustAccount); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + }, [client, dispatch]); + + const fetchMonitoringVPCConnectionId = useCallback( async () => { + const response = await client.query(getMonitoringVPCConnectionId()); + if (!response.errors) { + setVpcConnectionId(response.data.getMonitoringVPCConnectionId); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + }, [client, dispatch]); + + const fetchMonitoringDashboardId = useCallback( async () => { + const response = await client.query(getMonitoringDashboardId()); + if (!response.errors) { + setDashboardId(response.data.getMonitoringDashboardId); + if (response.data.getMonitoringDashboardId != "updateme"){ + const resp = await client.query(getPlatformReaderSession(response.data.getMonitoringDashboardId)); + if (!resp.errors){ + setSessionUrl(resp.data.getPlatformReaderSession) + const options = { + url: resp.data.getPlatformReaderSession, + scrolling: 'no', + height: '700px', + width: '100%', + locale: 'en-US', + footerPaddingEnabled: true, + sheetTabsDisabled: false, + printEnabled: false, + maximize: true, + container: dashboardRef.current + }; + QuickSightEmbedding.embedDashboard(options); + }else{ + dispatch({ type: SET_ERROR, error: resp.errors[0].message }); + } + } + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + }, [client, dispatch]); + + useEffect(() => { + if (client) { + fetchMonitoringDashboardId().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + fetchMonitoringVPCConnectionId().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + fetchTrustedAccount().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + } + }, [client, dispatch,fetchMonitoringDashboardId, fetchMonitoringVPCConnectionId, fetchTrustedAccount]); + + async function submitVpc(values, setStatus, setSubmitting, setErrors){ + try { + setVpcConnectionId(values.vpc) + const response = await client.mutate(updateSSMParameter({name:"VPCConnectionId", value:values.vpc})); + if (!response.errors) { + setStatus({success: true}); + setSubmitting(false); + }else{ + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (err) { + console.error(err); + setStatus({ success: false }); + setErrors({ submit: err.message }); + setSubmitting(false); + dispatch({ type: SET_ERROR, error: err.message }); + } + }; + + async function submitDash(values, setStatus, setSubmitting, setErrors){ + try { + setDashboardId(values.dash) + const response = await client.mutate(updateSSMParameter({name:"DashboardId", value:values.dash})); + if (!response.errors) { + setStatus({success: true}); + setSubmitting(false); + const resp = await client.query(getPlatformReaderSession(values.dash)); + if (!resp.errors){ + setSessionUrl(resp.data.getPlatformReaderSession) + const options = { + url: resp.data.getPlatformReaderSession, + scrolling: 'no', + height: '700px', + width: '100%', + locale: 'en-US', + footerPaddingEnabled: true, + sheetTabsDisabled: false, + printEnabled: false, + maximize: true, + container: dashboardRef.current + }; + QuickSightEmbedding.embedDashboard(options); + }else{ + dispatch({ type: SET_ERROR, error: resp.errors[0].message }); + } + }else{ + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + } catch (err) { + console.error(err); + setStatus({ success: false }); + setErrors({ submit: err.message }); + setSubmitting(false); + dispatch({ type: SET_ERROR, error: err.message }); + } + }; + + async function createQuicksightdata () { + setIsCreatingDataSource(true) + const response = await client.mutate(createQuicksightDataSourceSet({vpcConnectionId})); + if (response.errors) { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + setIsCreatingDataSource(false) + } + + const startAuthorSession = async () => { + setIsOpeningSession(true); + const response = await client.query(getPlatformAuthorSession(trustedAccount)); + if (!response.errors) { + window.open(response.data.getPlatformAuthorSession, '_blank'); + } else { + dispatch({ type: SET_ERROR, error: response.errors[0].message }); + } + setIsOpeningSession(false); + }; + + + return ( + + + + + + + + + + 1. Enable Quicksight Enterprise Edition in AWS Account = {trustedAccount}. Check the user guide for more details. + + + + + 2. Create a VPC Connection between Quicksight and RDS VPC. Check the user guide for more details. + + + + + + + + + + + + + + 3. Introduce or Update the VPC Connection ID value in the following box: + + + + + { + await submitVpc(values, setStatus, setSubmitting, setErrors); + }} + > + {({ + errors, + handleBlur, + handleChange, + handleSubmit, + isSubmitting, + setFieldValue, + touched, + values + }) => ( +
+ + + + + + + Save + + + +
+ )} +
+
+
+
+ + + + 4. Click on the button to automatically create the data source connecting our RDS Aurora database with Quicksight + + + + + } + sx={{ mt: 1, mb: 2, ml: 2 }} + variant="outlined" + onClick={() => { + createQuicksightdata().catch((e) => + dispatch({ type: SET_ERROR, error: e.message }) + ); + }} + > + Create Quicksight data source + + + + +
+
+
+ + + + + + + + + 5. Go to Quicksight to build your Analysis and publish a Dashboard. Check the user guide for more details. + + + + + } + variant="outlined" + onClick={startAuthorSession} + sx={{ mt: 1, mb: 2, ml: 2 }} + > + Start Quicksight session + + + + + + + + 6. Introduce or update your Dashboard ID + + + + + { + await submitDash(values, setStatus, setSubmitting, setErrors); + }} + > + {({ + errors, + handleBlur, + handleChange, + handleSubmit, + isSubmitting, + setFieldValue, + touched, + values + }) => ( +
+ + + + + + + Save + + + +
+ )} +
+
+
+
+
+
+
+ + + +
+ + + + + + ); +}; + +export default DashboardViewer;