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..89272a9 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -209,42 +209,6 @@ exports[`App Route / authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -743,42 +771,6 @@ exports[`App Route /home authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -1313,42 +1433,6 @@ exports[`App Route /package-deploy authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -2247,42 +2715,6 @@ exports[`App Route /package-status authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -2899,42 +3651,6 @@ exports[`App Route /packages authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -3778,42 +4750,6 @@ exports[`App Route /page-not-found authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -4275,42 +5403,6 @@ exports[`App Route /tools/certificates/add authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -5005,42 +6737,6 @@ exports[`App Route /tools/sms/send authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -5601,42 +8001,6 @@ exports[`App Route /vehicle-add authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -6755,42 +9567,6 @@ exports[`App Route /vehicle-status authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + @@ -7540,42 +10892,6 @@ exports[`App Route /vehicles authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + + +
  • + +
    + +
    +
    + + Datascope + +
    + +
    +
  • +
    + 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 (
    , roles: [Roles.READ, Roles.CREATE], }, - { - label: "Datascope", - to: "/datascope", - icon: , - roles: [Roles.READ, Roles.CREATE], - }, { label: "Suppliers", to: "/suppliers", @@ -103,13 +99,30 @@ const ExpandableSideMenuItem = ({ item }) => ( const SideMenu = () => { const { groups } = useUserContext(); - const menu = menuData.reduce((result, item) => { - if (hasRole(item.roles, groups)) { - result.push(item); + const [menu, setMenu] = useState(menuData) + const { + token: { + idToken: { jwtToken: token }, + }, + } = useUserContext(); + + useEffect(() => { + if (groups && token) { + const internalEffect = async (token) => { + const datasscopeitem = await SupersetItemList(token) + menuData.push(datasscopeitem) + FilterAccessible(groups, setMenu) + } + + internalEffect(token) } - return result; - }, []); + }, [groups, token]) + + useEffect(() => { + FilterAccessible(groups, setMenu) + }, [groups]) + return ( @@ -123,4 +136,36 @@ const SideMenu = () => { ); }; -export default SideMenu; +const FilterAccessible = (groups, setMenu) => { + + const filteredMenu = menuData.reduce((result, item) => { + if (hasRole(item.roles, groups)) { + result.push(item); + } + return result; + }, []) + setMenu(filteredMenu) +} + +// Will get the set of superset embeddable dashboards, and put it in the submenu style +const SupersetItemList = async (token) => { + const embeddedDashboards = await supersetAPI.getEmbeddedDashboards(token) + + const submenus = embeddedDashboards.map((dashboard) => { + return { + label: dashboard.title, + to: "/datascope/" + dashboard.embedded_id, + roles: [], + } + }) + + return { + label: "Datascope", + to: "/datascope", + icon: , + roles: [Roles.READ, Roles.CREATE], + submenus: submenus + } +} + +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..ac71be0 100644 --- a/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap +++ b/src/components/Layouts/__snapshots__/SideMenu.test.jsx.snap @@ -152,42 +152,6 @@ exports[`SideMenu Authenticated 1`] = ` /> -
  • - -
    - -
    -
    - - Datascope - -
    - -
    -
  • - -
    - -`; - -exports[`SideMenu Unauthenticated 1`] = ` -
    -
    -
    -
    + +
    +
    + + Datascope + +
    +
    + + +
    + + + + 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