diff --git a/.env.cec-prd b/.env.cec-prd index 51200bb..b9a9c92 100644 --- a/.env.cec-prd +++ b/.env.cec-prd @@ -2,5 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=https://gw.cec-prd.fiskerinc.com/compute_auth REACT_APP_CERT_SERVICE_URL=https://gw.cec-prd.fiskerinc.com/certificate REACT_APP_UPLOAD_SERVICE_URL=https://gw.cec-prd.fiskerinc.com/ota_update REACT_APP_AUTH_CALLBACK_URL=https://ota-admin.cec-prd.fiskerinc.com -REACT_APP_SUPERSET_URL=https://dev-superset-new.cloud.fiskerinc.com -REACT_APP_SUPERSET_KEYS_LIST=0b01ac72-9ef4-4a5f-be34-b1ac0bf972cc +REACT_APP_SUPERSET_URL=https://superset.cec-prd.fiskerinc.com diff --git a/.env.dev b/.env.dev index d342cd3..fb00db4 100644 --- a/.env.dev +++ b/.env.dev @@ -2,5 +2,4 @@ REACT_APP_CERT_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/certificate REACT_APP_AUTH_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/compute_auth REACT_APP_UPLOAD_SERVICE_URL=https://dev-gw.cloud.fiskerinc.com/ota_update REACT_APP_AUTH_CALLBACK_URL=https://dev-ota-admin.cloud.fiskerinc.com -REACT_APP_SUPERSET_URL=https://dev-superset-new.cloud.fiskerinc.com -REACT_APP_SUPERSET_KEYS_LIST=0b01ac72-9ef4-4a5f-be34-b1ac0bf972cc \ No newline at end of file +REACT_APP_SUPERSET_URL=https://dev-superset-new.cloud.fiskerinc.com \ No newline at end of file diff --git a/.env.prd b/.env.prd index 16c180c..d8ce7fe 100644 --- a/.env.prd +++ b/.env.prd @@ -2,5 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=https://gw.cloud.fiskerinc.com/compute_auth REACT_APP_CERT_SERVICE_URL=https://gw.cloud.fiskerinc.com/certificate REACT_APP_UPLOAD_SERVICE_URL=https://gw.cloud.fiskerinc.com/ota_update REACT_APP_AUTH_CALLBACK_URL=https://ota-admin.cloud.fiskerinc.com -REACT_APP_SUPERSET_URL=https://dev-superset-new.cloud.fiskerinc.com -REACT_APP_SUPERSET_KEYS_LIST=0b01ac72-9ef4-4a5f-be34-b1ac0bf972cc +REACT_APP_SUPERSET_URL=https://superset.cloud.fiskerinc.com diff --git a/.env.stg b/.env.stg index ebe7bc6..8d545cf 100644 --- a/.env.stg +++ b/.env.stg @@ -2,5 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/compute_auth REACT_APP_CERT_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/certificate REACT_APP_UPLOAD_SERVICE_URL=https://stg-gw.cloud.fiskerinc.com/ota_update REACT_APP_AUTH_CALLBACK_URL=https://stg-ota-admin.cloud.fiskerinc.com -REACT_APP_SUPERSET_URL=https://dev-superset-new.cloud.fiskerinc.com -REACT_APP_SUPERSET_KEYS_LIST=0b01ac72-9ef4-4a5f-be34-b1ac0bf972cc +REACT_APP_SUPERSET_URL=https://stg-superset.cloud.fiskerinc.com \ No newline at end of file diff --git a/.env.template b/.env.template index e79b9ab..eb5a173 100644 --- a/.env.template +++ b/.env.template @@ -2,5 +2,4 @@ REACT_APP_AUTH_SERVICE_URL=http://localhost/compute_auth REACT_APP_UPLOAD_SERVICE_URL=http://localhost/ota_update REACT_APP_AUTH_CALLBACK_URL=http://localhost:3000 REACT_APP_SUPERSET_URL=https://dev-superset.cloud.fiskerinc.com -REACT_APP_CERT_SERVICE_URL=http://localhost/certificate -REACT_APP_SUPERSET_KEYS_LIST=0b01ac72-9ef4-4a5f-be34-b1ac0bf972cc +REACT_APP_CERT_SERVICE_URL=http://localhost/certificate \ No newline at end of file diff --git a/src/components/App/App.test.js b/src/components/App/App.test.js index f5de9f0..24f282c 100644 --- a/src/components/App/App.test.js +++ b/src/components/App/App.test.js @@ -5,8 +5,10 @@ jest.mock("../Contexts/ManifestsContext"); jest.mock("../Contexts/UserContext"); jest.mock("../../services/monitoring"); jest.mock("../../services/vehiclesAPI"); +jest.mock("../../services/superset") import { + act, render, screen, cleanup, @@ -30,7 +32,10 @@ const renderRoute = async (route) => { }; const check = async (path, selector, compare) => { - const container = await renderRoute(path); + let container + await act(async () => { + container = await renderRoute(path); + }) expect(container.querySelector(selector).innerHTML).toEqual(compare); expect(container).toMatchSnapshot(); }; diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index 666d7a0..4d7e87c 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -210,10 +210,9 @@ exports[`App Route / authenticated 1`] = `
  • - @@ -243,7 +242,33 @@ exports[`App Route / authenticated 1`] = ` - + +
  • @@ -744,10 +769,9 @@ exports[`App Route /home authenticated 1`] = `
  • - @@ -777,7 +801,33 @@ exports[`App Route /home authenticated 1`] = ` - + +
  • @@ -1314,10 +1364,9 @@ exports[`App Route /package-deploy authenticated 1`] = `
  • - @@ -1347,7 +1396,33 @@ exports[`App Route /package-deploy authenticated 1`] = ` - + +
  • @@ -2248,10 +2323,9 @@ exports[`App Route /package-status authenticated 1`] = `
  • - @@ -2281,7 +2355,33 @@ exports[`App Route /package-status authenticated 1`] = ` - + +
  • @@ -2900,10 +3000,9 @@ exports[`App Route /packages authenticated 1`] = `
  • - @@ -2933,7 +3032,33 @@ exports[`App Route /packages authenticated 1`] = ` - + +
  • @@ -3779,10 +3904,9 @@ exports[`App Route /page-not-found authenticated 1`] = `
  • - @@ -3812,7 +3936,33 @@ exports[`App Route /page-not-found authenticated 1`] = ` - + +
  • @@ -4276,10 +4426,9 @@ exports[`App Route /tools/certificates/add authenticated 1`] = `
  • - @@ -4309,7 +4458,33 @@ exports[`App Route /tools/certificates/add authenticated 1`] = ` - + +
  • @@ -5006,10 +5181,9 @@ exports[`App Route /tools/sms/send authenticated 1`] = `
  • - @@ -5039,7 +5213,33 @@ exports[`App Route /tools/sms/send authenticated 1`] = ` - + +
  • @@ -5602,10 +5802,9 @@ exports[`App Route /vehicle-add authenticated 1`] = `
  • - @@ -5635,7 +5834,33 @@ exports[`App Route /vehicle-add authenticated 1`] = ` - + +
  • @@ -6756,10 +6981,9 @@ exports[`App Route /vehicle-status authenticated 1`] = `
  • - @@ -6789,7 +7013,33 @@ exports[`App Route /vehicle-status authenticated 1`] = ` - + +
  • @@ -7541,10 +7791,9 @@ exports[`App Route /vehicles authenticated 1`] = `
  • - @@ -7574,7 +7823,33 @@ exports[`App Route /vehicles authenticated 1`] = ` - + +
  • diff --git a/src/components/Dashboard/index.jsx b/src/components/Dashboard/index.jsx index 3570980..107d6c5 100644 --- a/src/components/Dashboard/index.jsx +++ b/src/components/Dashboard/index.jsx @@ -4,10 +4,12 @@ import { useStatusContext } from "../Contexts/StatusContext"; import { useUserContext } from "../Contexts/UserContext"; import './index.css' import { embedDashboard } from "@superset-ui/embedded-sdk"; +import { useHistory } from "react-router-dom"; const Dashboard = () => { const { setTitle, setSitePath } = useStatusContext(); + const history = useHistory() const { token: { @@ -16,17 +18,19 @@ const Dashboard = () => { } = useUserContext(); useEffect(() => { + const urlsplit = window.location.href.split("/") + const id = urlsplit[urlsplit.length - 1] setTitle("Datascope"); setSitePath([]); embedDashboard({ - id: api.SupersetDashboardID(), // given by the Superset embedding UI + id: id, // given by the Superset embedding UI supersetDomain: api.SupersetDashboardURL(), mountPoint: document.getElementById("my-superset-container"), // any html element that can contain an iframe fetchGuestToken: () => api.getGuestToken(token), dashboardUiConfig: { hideTab: true, hideTitle: true }, // dashboard UI config: hideTitle, hideTab, hideChartControls (optional) }); // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, [history.location]); return (
    { + return ( +
  • + {item.label && (<> + {item.url ? + + : } + {item.component && + + } + )} + {children} +
  • + ); +}; + +const ExpandableSideMenuItem = ({ item }) => ( + <> + + + +
      + {item.submenus.map((subitem, index) => ( + + ))} +
    + +); + +export {ExpandableSideMenuItem, MenuItem} \ No newline at end of file diff --git a/src/components/Layouts/SideMenu.jsx b/src/components/Layouts/SideMenu.jsx index 10e0b9e..214bdf3 100644 --- a/src/components/Layouts/SideMenu.jsx +++ b/src/components/Layouts/SideMenu.jsx @@ -8,10 +8,10 @@ import AssessmentIcon from "@material-ui/icons/Assessment"; import BuildIcon from "@material-ui/icons/Build"; import SettingsInputCompositeIcon from "@material-ui/icons/SettingsInputComposite"; -import ListItemLink from "../ListItemLink"; -import ListItemExternalLink from "../ListItemExternalLink"; import { useUserContext } from "../Contexts/UserContext"; import { Roles, hasRole } from "../../utils/roles"; +import { ExpandableSideMenuItem, MenuItem } from "./MenuItem"; +import SupersetDashboardList from "../SupersetDashboardList/SupersetDashboardList"; const menuData = [ { @@ -40,9 +40,10 @@ const menuData = [ }, { label: "Datascope", - to: "/datascope", + to: null, icon: , roles: [Roles.READ, Roles.CREATE], + component: SupersetDashboardList }, { label: "Suppliers", @@ -70,39 +71,10 @@ const menuData = [ }, ]; -const MenuItem = ({ item, children }) => { - return ( -
  • - {item.to && ( - - )} - {item.url && ( - - )} - {children} -
  • - ); -}; - -const ExpandableSideMenuItem = ({ item }) => ( - <> - - - -
      - {item.submenus.map((subitem, index) => ( - - ))} -
    - -); const SideMenu = () => { const { groups } = useUserContext(); + const menu = menuData.reduce((result, item) => { if (hasRole(item.roles, groups)) { result.push(item); @@ -111,6 +83,7 @@ const SideMenu = () => { return result; }, []); + return ( {menu.map((item, index) => { @@ -123,4 +96,4 @@ const SideMenu = () => { ); }; -export default SideMenu; +export default SideMenu; \ No newline at end of file diff --git a/src/components/Layouts/SideMenu.test.jsx b/src/components/Layouts/SideMenu.test.jsx index fb0cdc2..476a22a 100644 --- a/src/components/Layouts/SideMenu.test.jsx +++ b/src/components/Layouts/SideMenu.test.jsx @@ -1,6 +1,7 @@ jest.mock("../Contexts/UserContext"); +jest.mock("../../services/superset") -import { render, waitFor } from "@testing-library/react"; +import { render, waitFor, screen } from "@testing-library/react"; import { BrowserRouter } from "react-router-dom"; import { UserProvider, setToken } from "../Contexts/UserContext"; import { TEST_AUTH_OBJECT } from "../../utils/testing"; @@ -22,17 +23,15 @@ const renderMenu = async () => { describe("SideMenu", () => { beforeAll(() => { addSnapshotSerializer(expect); - }); - it("Unauthenticated", async () => { - setToken(null); - const container = await renderMenu(); - expect(container).toMatchSnapshot(); }); it("Authenticated", async () => { setToken(TEST_AUTH_OBJECT); const container = await renderMenu(); + await waitFor(() => { + expect(screen.getByText('Datascope')).toBeInTheDocument() + }) expect(container).toMatchSnapshot(); }); }); diff --git a/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap b/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap index 9e9c905..c804955 100644 --- a/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap +++ b/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap @@ -153,10 +153,9 @@ exports[`SideMenu Authenticated 1`] = `
  • - @@ -186,7 +185,33 @@ exports[`SideMenu Authenticated 1`] = ` - + +
  • @@ -278,52 +303,3 @@ exports[`SideMenu Authenticated 1`] = ` `; - -exports[`SideMenu Unauthenticated 1`] = ` - -`; diff --git a/src/components/ListItemLink.jsx b/src/components/ListItemLink.jsx index b30901b..34eaa8c 100644 --- a/src/components/ListItemLink.jsx +++ b/src/components/ListItemLink.jsx @@ -9,10 +9,13 @@ function ListItemLink(props) { const { icon, primary, to } = props; const renderLink = React.useMemo( - () => - React.forwardRef((itemProps, ref) => ( - - )), + () => { + if (to) { + return React.forwardRef((itemProps, ref) => ( + + )) + } + }, [to] ); @@ -27,7 +30,7 @@ function ListItemLink(props) { ListItemLink.propTypes = { icon: PropTypes.element, primary: PropTypes.string.isRequired, - to: PropTypes.string.isRequired, + to: PropTypes.string, }; export default ListItemLink; diff --git a/src/components/SupersetDashboardList/SupersetDashboardList.jsx b/src/components/SupersetDashboardList/SupersetDashboardList.jsx new file mode 100644 index 0000000..cce2712 --- /dev/null +++ b/src/components/SupersetDashboardList/SupersetDashboardList.jsx @@ -0,0 +1,45 @@ +import React, { useState, useEffect } from "react"; +import { useUserContext } from "../Contexts/UserContext"; + +import supersetAPI from "../../services/superset"; +import { MenuItem } from "../Layouts/MenuItem"; + +const SupersetDashboardList = () => { + const [dashboardList, setDashboardList] = useState([]) + const { groups } = useUserContext(); + const { + token: { + idToken: { jwtToken: token }, + }, + } = useUserContext(); + + useEffect(() => { + if (groups && token) { + const internalEffect = async (token) => { + const embeddedDashboards = await await supersetAPI.getEmbeddedDashboards(token) + const submenus = embeddedDashboards.map((dashboard) => { + return { + label: dashboard.title, + to: "/datascope/" + dashboard.embedded_id, + roles: [], + } + }) + setDashboardList(submenus) + } + + internalEffect(token) + } + + }, [groups, token]) + + + return ( +
      + {dashboardList.map((subitem, index) => ( + + ))} +
    + ) +} + +export default SupersetDashboardList \ No newline at end of file diff --git a/src/services/__mocks__/superset.js b/src/services/__mocks__/superset.js new file mode 100644 index 0000000..cd2af34 --- /dev/null +++ b/src/services/__mocks__/superset.js @@ -0,0 +1,13 @@ +const SupersetAPI = { + getEmbeddedDashboards: async () => { + return [{ + title: "test title", + embedded_id: "00000000-0000-0000-0000-000000000000" + }] + }, + SupersetDashboardID: () => { + return "11111100-0000-1111-1111-000000000000" + } +} + +export default SupersetAPI \ No newline at end of file diff --git a/src/services/superset.js b/src/services/superset.js index 3c13b81..4d13078 100644 --- a/src/services/superset.js +++ b/src/services/superset.js @@ -21,15 +21,32 @@ const supersetAPI = { let q = r["token"] return q }, + getEmbeddedDashboards: async(token) => { + const u = addQueryParams(`${API_ENDPOINT}/dashboard/embedded-dashboards`); + let res = await fetch(u, { + method: "GET", + headers: Object.assign( + getAuthHeaderOptions(token) + ), + }) + if(res.status !== 200){ + return [{title: "dashboard", embedded_id: GetSupersetDashboardID()}] + } + let r = await res.json() + return r + }, SupersetDashboardURL: () => { const SUPERSET_BASE_URL = process.env.REACT_APP_SUPERSET_URL; return SUPERSET_BASE_URL }, SupersetDashboardID: () => { - const SUPERSET_BASE_ID = process.env.REACT_APP_SUPERSET_KEYS_LIST; - return SUPERSET_BASE_ID + return GetSupersetDashboardID() } +} +const GetSupersetDashboardID = () => { + const SUPERSET_BASE_ID = process.env.REACT_APP_SUPERSET_KEYS_LIST; + return SUPERSET_BASE_ID } export default supersetAPI; \ No newline at end of file diff --git a/src/services/superset.test.js b/src/services/superset.test.js new file mode 100644 index 0000000..c5fb037 --- /dev/null +++ b/src/services/superset.test.js @@ -0,0 +1,15 @@ +import superset from "./superset" + +describe("Superset tests", () => { + it("Outdated API doesn't cause problems", async () => { + process.env.REACT_APP_SUPERSET_KEYS_LIST = "11111100-0000-1111-1111-000000000000" + global.fetch = jest.fn(() => { + return { status: 404 } + } + ); + + const res = await superset.getEmbeddedDashboards() + expect(res).toHaveLength(1) + expect(res[0].embedded_id).toBe("11111100-0000-1111-1111-000000000000") + }) +}) \ No newline at end of file