diff --git a/example/src/App.tsx b/example/src/App.tsx index 7608a3a74..e38b7183f 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,20 +1,21 @@ import { - ApplicationContext, + AuthContext, + DMApplicationProvider, + DmssAPI, EntityView, - FSTreeProvider, - Loading, - RoleProvider, + ErrorResponse, TApplication, - useDocument, } from '@development-framework/dm-core' import '@development-framework/dm-core/dist/main.css' import { Button, Card, Icon, Typography } from '@equinor/eds-core-react' import { refresh } from '@equinor/eds-icons' import './main.css' +import { AxiosError } from 'axios' +import { useContext, useEffect, useState } from 'react' import { RouterProvider, createBrowserRouter } from 'react-router-dom' import ViewPage from './ViewPage' - +import plugins from './plugins' const appNotReadyPage = () => (
(idReference) + const [application, setApplication] = useState() + const [error, setError] = useState() + const [isLoading, setIsLoading] = useState(true) + const { token } = useContext(AuthContext) - if (isLoading) return + useEffect(() => { + setIsLoading(true) + const dmssAPI = new DmssAPI(token, import.meta.env.VITE_DMSS_URL) + dmssAPI + .documentGet({ address: idReference }) + .then((response: any) => { + setApplication(response.data) + setError(null) + }) + .catch((error: AxiosError) => { + console.error(error) + setError(error.response?.data || { message: error.name, data: error }) + }) + .finally(() => setIsLoading(false)) + }, []) - if (error || !application) { + if (isLoading) return <> + if (error || !application || !application.type) { console.error(error) return appNotReadyPage() } + const router = createBrowserRouter([ { path: '/', @@ -98,15 +114,19 @@ function App() { ), }, ]) + const enableBlueprintCache = + import.meta.env.VITE_BLUEPRINT_CACHE_ENABLED === '1' || true return ( - - - - - - - + + + ) } diff --git a/example/src/index.tsx b/example/src/index.tsx index 11b73e02a..23c6ecc05 100644 --- a/example/src/index.tsx +++ b/example/src/index.tsx @@ -1,19 +1,9 @@ -import { - DMJobProvider, - DMSSProvider, - UiPluginProvider, -} from '@development-framework/dm-core' import React from 'react' -import { ToastContainer } from 'react-toastify' import 'react-toastify/dist/ReactToastify.min.css' import ReactDOM from 'react-dom/client' import { AuthProvider } from 'react-oauth2-code-pkce' import App from './App' -import plugins from './plugins' - -const fullCurrentURL = () => - `${window.location.pathname}${window.location.search}${window.location.hash}` const authEnabled = import.meta.env.VITE_AUTH_ENABLED === '1' const authConfig = { @@ -31,34 +21,16 @@ const authConfig = { }/oauth2/logout`, } -const Content = () => { - const enableBlueprintCache = - import.meta.env.VITE_BLUEPRINT_CACHE_ENABLED === '1' || true - return ( - - - - - - - - - ) -} - const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement) root.render( {authEnabled ? ( - + ) : ( - + )} ) diff --git a/packages/dm-core-plugins/src/blueprint-hierarchy/BlueprintHierarchyPlugin.tsx b/packages/dm-core-plugins/src/blueprint-hierarchy/BlueprintHierarchyPlugin.tsx index 9a2c45604..51a6230dc 100644 --- a/packages/dm-core-plugins/src/blueprint-hierarchy/BlueprintHierarchyPlugin.tsx +++ b/packages/dm-core-plugins/src/blueprint-hierarchy/BlueprintHierarchyPlugin.tsx @@ -4,7 +4,7 @@ import { DmssAPI, IUIPlugin, Loading, - useDMSS, + useApplication, useDocument, } from '@development-framework/dm-core' import MermaidWrapper from './MermaidWrapper' @@ -74,7 +74,7 @@ function useExplorer(dmssAPI: DmssAPI) { export const BlueprintHierarchyPlugin = (props: IUIPlugin) => { const { idReference } = props - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const explorer = useExplorer(dmssAPI) const [chart, setChart] = useState(undefined) diff --git a/packages/dm-core-plugins/src/blueprint-hierarchy/loader.tsx b/packages/dm-core-plugins/src/blueprint-hierarchy/loader.tsx index ab6b6b6af..306bad35b 100644 --- a/packages/dm-core-plugins/src/blueprint-hierarchy/loader.tsx +++ b/packages/dm-core-plugins/src/blueprint-hierarchy/loader.tsx @@ -1,4 +1,4 @@ -import { useDMSS } from '@development-framework/dm-core' +import { useApplication } from '@development-framework/dm-core' import { IBlueprintType, TAttributeType } from './types' export class Node { @@ -58,7 +58,7 @@ const nonPrimitiveAttributes = (blueprint: IBlueprintType): TAttributeType[] => ) const search = async (query: any) => { - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const response = await dmssAPI.search({ dataSources: ['WorkflowDS'], diff --git a/packages/dm-core-plugins/src/data-grid/DataGridPlugin.tsx b/packages/dm-core-plugins/src/data-grid/DataGridPlugin.tsx index 820af43a1..c7e4d289b 100644 --- a/packages/dm-core-plugins/src/data-grid/DataGridPlugin.tsx +++ b/packages/dm-core-plugins/src/data-grid/DataGridPlugin.tsx @@ -3,8 +3,8 @@ import { Stack, TAttribute, TGenericObject, + useApplication, useBlueprint, - useDMSS, useDocument, } from '@development-framework/dm-core' import { Button, Icon, Tooltip } from '@equinor/eds-core-react' @@ -17,7 +17,7 @@ import { getFunctionalityVariables, reverseData } from './utils' export function DataGridPlugin(props: IUIPlugin) { const { idReference, config: userConfig, type, onChange } = props const config: DataGridConfig = { ...defaultConfig, ...userConfig } - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const [data, setData] = useState() const [initialData, setInitialData] = useState() const [loading, setLoading] = useState(false) diff --git a/packages/dm-core-plugins/src/explorer/ExplorerPlugin.tsx b/packages/dm-core-plugins/src/explorer/ExplorerPlugin.tsx index 75855f7c8..49d83606e 100644 --- a/packages/dm-core-plugins/src/explorer/ExplorerPlugin.tsx +++ b/packages/dm-core-plugins/src/explorer/ExplorerPlugin.tsx @@ -1,22 +1,17 @@ import { EBlueprint, EntityView, - FSTreeContext, - Tree, TreeNode, TreeView, + useApplication, } from '@development-framework/dm-core' import { Progress } from '@equinor/eds-core-react' -import { useContext, useState } from 'react' +import { useState } from 'react' import Sidebar from './components/Sidebar' import NodeRightClickMenu from './components/context-menu/NodeRightClickMenu' export default () => { - const { treeNodes, loading } = useContext<{ - tree: null | Tree - treeNodes: TreeNode[] - loading: boolean - }>(FSTreeContext) + const { treeNodes, loading } = useApplication() const [selectedType, setSelectedType] = useState() const [selectedEntity, setSelectedEntity] = useState() const [nodeDimensions, setNodeDimensions] = useState( diff --git a/packages/dm-core-plugins/src/explorer/components/dialogs/DeleteDialog.tsx b/packages/dm-core-plugins/src/explorer/components/dialogs/DeleteDialog.tsx index f27bf0745..8c2753517 100644 --- a/packages/dm-core-plugins/src/explorer/components/dialogs/DeleteDialog.tsx +++ b/packages/dm-core-plugins/src/explorer/components/dialogs/DeleteDialog.tsx @@ -2,7 +2,7 @@ import { Dialog, ErrorResponse, TreeNode, - useDMSS, + useApplication, } from '@development-framework/dm-core' import { Button, Progress } from '@equinor/eds-core-react' import { AxiosError } from 'axios' @@ -22,7 +22,7 @@ type TProps = { const DeleteDialog = (props: TProps) => { const { setDialogId, node } = props const [loading, setLoading] = useState(false) - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const handleDelete = () => { setLoading(true) diff --git a/packages/dm-core-plugins/src/explorer/components/dialogs/NewEntityDialog.tsx b/packages/dm-core-plugins/src/explorer/components/dialogs/NewEntityDialog.tsx index 88c5f9dce..0189f2b36 100644 --- a/packages/dm-core-plugins/src/explorer/components/dialogs/NewEntityDialog.tsx +++ b/packages/dm-core-plugins/src/explorer/components/dialogs/NewEntityDialog.tsx @@ -1,16 +1,15 @@ import { - ApplicationContext, BlueprintPicker, Dialog, ErrorResponse, TAttribute, TBlueprint, TreeNode, - useDMSS, + useApplication, } from '@development-framework/dm-core' import { Button, Progress, TextField } from '@equinor/eds-core-react' import { AxiosError } from 'axios' -import { useContext, useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { toast } from 'react-toastify' import { EDialog } from '../../types' import { @@ -30,9 +29,7 @@ const NewEntityDialog = (props: TProps) => { const [blueprint, setBlueprint] = useState() const [newName, setNewName] = useState('') const [loading, setLoading] = useState(false) - const dmssAPI = useDMSS() - - const { name } = useContext(ApplicationContext) + const { dmssAPI, name } = useApplication() useEffect(() => { if (!blueprintName) return diff --git a/packages/dm-core-plugins/src/explorer/components/dialogs/NewFolderDialog.tsx b/packages/dm-core-plugins/src/explorer/components/dialogs/NewFolderDialog.tsx index 4ef5fcdac..51bda68fe 100644 --- a/packages/dm-core-plugins/src/explorer/components/dialogs/NewFolderDialog.tsx +++ b/packages/dm-core-plugins/src/explorer/components/dialogs/NewFolderDialog.tsx @@ -3,7 +3,7 @@ import { ErrorResponse, INPUT_FIELD_WIDTH, TreeNode, - useDMSS, + useApplication, } from '@development-framework/dm-core' import { Button, Progress, TextField } from '@equinor/eds-core-react' import { AxiosError } from 'axios' @@ -26,7 +26,7 @@ const NewFolderDialog = (props: TProps) => { const { setDialogId, node, setNodeOpen, isRoot } = props const [folderName, setFolderName] = useState('') const [loading, setLoading] = useState(false) - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const handleCreate = () => { const newFolder = { diff --git a/packages/dm-core-plugins/src/file/DownloadFileButton.tsx b/packages/dm-core-plugins/src/file/DownloadFileButton.tsx index 4ada2b696..67d673fdf 100644 --- a/packages/dm-core-plugins/src/file/DownloadFileButton.tsx +++ b/packages/dm-core-plugins/src/file/DownloadFileButton.tsx @@ -2,7 +2,7 @@ import { ErrorResponse, Loading, TFileEntity, - useDMSS, + useApplication, } from '@development-framework/dm-core' import { Button } from '@equinor/eds-core-react' import { AxiosError, AxiosRequestConfig, AxiosResponse } from 'axios' @@ -20,7 +20,7 @@ export const DownloadFileButton = (props: DownloadButtonProps) => { const [loading, setLoading] = useState(true) const [error, setError] = useState(null) - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() useEffect(() => { setError(null) diff --git a/packages/dm-core-plugins/src/file/UploadFileButton.tsx b/packages/dm-core-plugins/src/file/UploadFileButton.tsx index 6b9efdf7d..0bd13a510 100644 --- a/packages/dm-core-plugins/src/file/UploadFileButton.tsx +++ b/packages/dm-core-plugins/src/file/UploadFileButton.tsx @@ -1,4 +1,7 @@ -import { TStorageReference, useDMSS } from '@development-framework/dm-core' +import { + TStorageReference, + useApplication, +} from '@development-framework/dm-core' import { Button, Progress } from '@equinor/eds-core-react' import { AxiosError } from 'axios' import { ChangeEvent, useEffect, useRef, useState } from 'react' @@ -14,7 +17,7 @@ export const UploadFileButton = (props: UploadButtonProps) => { const textInput = useRef(null) const [error, setError] = useState() const [loading, setLoading] = useState(false) - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() useEffect(() => setError(undefined)) diff --git a/packages/dm-core-plugins/src/form/components/AddObjectButton.tsx b/packages/dm-core-plugins/src/form/components/AddObjectButton.tsx index 752986185..841622ba3 100644 --- a/packages/dm-core-plugins/src/form/components/AddObjectButton.tsx +++ b/packages/dm-core-plugins/src/form/components/AddObjectButton.tsx @@ -1,4 +1,4 @@ -import { ErrorResponse, useDMSS } from '@development-framework/dm-core' +import { ErrorResponse, useApplication } from '@development-framework/dm-core' import { AxiosError, AxiosResponse } from 'axios' import { useFormContext } from 'react-hook-form' import { useRegistryContext } from '../context/RegistryContext' @@ -12,7 +12,7 @@ const AddObject = (props: { }) => { const { type, namePath, defaultValue, onAdd } = props const { setValue } = useFormContext() - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const { idReference } = useRegistryContext() const handleAdd = () => { if (!defaultValue) { diff --git a/packages/dm-core-plugins/src/form/components/AddObjectBySearchButton.tsx b/packages/dm-core-plugins/src/form/components/AddObjectBySearchButton.tsx index a718c74d7..d9cf3d584 100644 --- a/packages/dm-core-plugins/src/form/components/AddObjectBySearchButton.tsx +++ b/packages/dm-core-plugins/src/form/components/AddObjectBySearchButton.tsx @@ -1,8 +1,4 @@ -import { - useApplication, - useDMSS, - useSearch, -} from '@development-framework/dm-core' +import { useApplication, useSearch } from '@development-framework/dm-core' import { EdsProvider, Menu } from '@equinor/eds-core-react' import { add } from '@equinor/eds-icons' import { useState } from 'react' @@ -19,17 +15,16 @@ const AddObjectBySearchButton = (props: { }) => { const { type, namePath, onAdd } = props const { setValue } = useFormContext() - const dmssAPI = useDMSS() + const { dmssAPI, visibleDataSources } = useApplication() const { idReference } = useRegistryContext() const [isOpen, setIsOpen] = useState(false) const [anchorEl, setAnchorEl] = useState(null) - const application = useApplication() const [searchResult] = useSearch( { type: 'dmss://system/SIMOS/Blueprint', extends: [type], }, - application.dataSources + visibleDataSources ) const openMenu = () => { diff --git a/packages/dm-core-plugins/src/form/components/AddStorageUncontained.tsx b/packages/dm-core-plugins/src/form/components/AddStorageUncontained.tsx index 0330e8ced..dab0f96a3 100644 --- a/packages/dm-core-plugins/src/form/components/AddStorageUncontained.tsx +++ b/packages/dm-core-plugins/src/form/components/AddStorageUncontained.tsx @@ -1,4 +1,4 @@ -import { splitAddress, useDMSS } from '@development-framework/dm-core' +import { splitAddress, useApplication } from '@development-framework/dm-core' import { AxiosResponse } from 'axios' import { useFormContext } from 'react-hook-form' import { useRegistryContext } from '../context/RegistryContext' @@ -19,7 +19,7 @@ const AddStorageUncontained = (props: { const { idReference } = useRegistryContext() const { setValue } = useFormContext() - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const addDocument = () => { const options = { shouldDirty: true, diff --git a/packages/dm-core-plugins/src/form/components/Form.test.tsx b/packages/dm-core-plugins/src/form/components/Form.test.tsx index b881e80aa..55bacfb6f 100644 --- a/packages/dm-core-plugins/src/form/components/Form.test.tsx +++ b/packages/dm-core-plugins/src/form/components/Form.test.tsx @@ -37,7 +37,7 @@ describe('Form', () => { expect(container.querySelector(`input[id="bar"]`)).toBeTruthy() expect(container.querySelector(`input[id="baz"]`)).toBeNull() // Should only call get blueprint once - expect(mock).toHaveBeenCalledWith({ typeRef: 'Root' }) + expect(mock).toHaveBeenCalledWith({ typeRef: 'Root', context: 'test' }) expect(mock).toHaveBeenCalledTimes(1) }) }) @@ -134,7 +134,7 @@ describe('Form', () => { await waitFor(() => { expect(container.querySelector(`input[id="foo"]`)).toBeTruthy() // Should only call get blueprint once - expect(mock).toHaveBeenCalledWith({ typeRef: 'Root' }) + expect(mock).toHaveBeenCalledWith({ typeRef: 'Root', context: 'test' }) expect(mock).toHaveBeenCalledTimes(1) }) }) diff --git a/packages/dm-core-plugins/src/form/components/Form.tsx b/packages/dm-core-plugins/src/form/components/Form.tsx index a6ffa2b86..7827a44a2 100644 --- a/packages/dm-core-plugins/src/form/components/Form.tsx +++ b/packages/dm-core-plugins/src/form/components/Form.tsx @@ -1,15 +1,14 @@ -import { useContext, useState } from 'react' +import { useState } from 'react' import { - ApplicationContext, EBlueprint, Loading, TAttribute, TGenericObject, TUiRecipe, findRecipe, + useApplication, useBlueprint, - useDMSS, } from '@development-framework/dm-core' import { Button, EdsProvider, Icon } from '@equinor/eds-core-react' import { undo } from '@equinor/eds-icons' @@ -36,8 +35,7 @@ export const defaultConfig: TFormConfig = { export const Form = (props: TFormProps) => { const { type, formData, onSubmit, idReference, onOpen } = props const { blueprint, storageRecipes, isLoading, error } = useBlueprint(type) - const dmssAPI = useDMSS() - const { name } = useContext(ApplicationContext) + const { dmssAPI, name } = useApplication() const [reloadCounter, setReloadCounter] = useState(0) const showSubmitButton = props.showSubmitButton ?? true // Every react hook form controller needs to have a unique name diff --git a/packages/dm-core-plugins/src/form/components/RemoveObjectButton.tsx b/packages/dm-core-plugins/src/form/components/RemoveObjectButton.tsx index 3c7f9f01b..a1962a315 100644 --- a/packages/dm-core-plugins/src/form/components/RemoveObjectButton.tsx +++ b/packages/dm-core-plugins/src/form/components/RemoveObjectButton.tsx @@ -1,7 +1,7 @@ import { DeleteHardButton, ErrorResponse, - useDMSS, + useApplication, } from '@development-framework/dm-core' import { AxiosError } from 'axios' import { useFormContext } from 'react-hook-form' @@ -19,7 +19,7 @@ const RemoveObject = (props: { props const { unregister } = useFormContext() const { idReference } = useRegistryContext() - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const onClick = () => { dmssAPI diff --git a/packages/dm-core-plugins/src/form/components/SelectReference.tsx b/packages/dm-core-plugins/src/form/components/SelectReference.tsx index 24b470549..eb7fb9709 100644 --- a/packages/dm-core-plugins/src/form/components/SelectReference.tsx +++ b/packages/dm-core-plugins/src/form/components/SelectReference.tsx @@ -4,7 +4,7 @@ import { ErrorResponse, TEntityPickerReturn, TLinkReference, - useDMSS, + useApplication, } from '@development-framework/dm-core' import { AxiosError } from 'axios/index' import { useState } from 'react' @@ -19,7 +19,7 @@ export const SelectReference = (props: { }) => { const [showModal, setShowModal] = useState(false) const { setValue, watch } = useFormContext() - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const { idReference } = useRegistryContext() const value = watch(props.namePath) diff --git a/packages/dm-core-plugins/src/form/fields/BinaryField.tsx b/packages/dm-core-plugins/src/form/fields/BinaryField.tsx index fc5a86b0a..67aecb617 100644 --- a/packages/dm-core-plugins/src/form/fields/BinaryField.tsx +++ b/packages/dm-core-plugins/src/form/fields/BinaryField.tsx @@ -2,7 +2,7 @@ import { ErrorResponse, TGenericObject, splitAddress, - useDMSS, + useApplication, } from '@development-framework/dm-core' import { Button, Label } from '@equinor/eds-core-react' import { AxiosError, AxiosResponse } from 'axios' @@ -29,7 +29,7 @@ const DownloadBinary = (props: { initialValue: TGenericObject & { address: string } }) => { const { namePath, initialValue, displayLabel } = props - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const [data_source_id, blob_id] = getTarget(initialValue) const handleDownload = () => { diff --git a/packages/dm-core-plugins/src/form/test-utils.tsx b/packages/dm-core-plugins/src/form/test-utils.tsx index 5eb82392e..ac51445fd 100644 --- a/packages/dm-core-plugins/src/form/test-utils.tsx +++ b/packages/dm-core-plugins/src/form/test-utils.tsx @@ -1,8 +1,7 @@ import { - DMSSProvider, + DMApplicationProvider, DmssAPI, TUiPluginMap, - UiPluginProvider, } from '@development-framework/dm-core' import React from 'react' import { FormPlugin } from './FormPlugin' @@ -42,7 +41,12 @@ const plugins = { } as TUiPluginMap export const wrapper = (props: { children: React.ReactNode }) => ( - - {props.children} - + + {props.children} + ) diff --git a/packages/dm-core-plugins/src/header/HeaderPlugin.tsx b/packages/dm-core-plugins/src/header/HeaderPlugin.tsx index b6454bb84..bc25961f9 100644 --- a/packages/dm-core-plugins/src/header/HeaderPlugin.tsx +++ b/packages/dm-core-plugins/src/header/HeaderPlugin.tsx @@ -4,9 +4,9 @@ import { TApplication, TGenericObject, TUiRecipe, + useApplication, useBlueprint, useDocument, - useUiPlugins, } from '@development-framework/dm-core' import { Icon, TopBar } from '@equinor/eds-core-react' import React, { useEffect, useState } from 'react' @@ -74,7 +74,7 @@ export default (props: IUIPlugin): React.ReactElement => { const { uiRecipes, isLoading: isBlueprintLoading } = useBlueprint(type) const [aboutOpen, setAboutOpen] = useState(false) const [visibleUserInfo, setVisibleUserInfo] = useState(false) - const { getUiPlugin } = useUiPlugins() + const { getUiPlugin } = useApplication() const [selectedRecipe, setSelectedRecipe] = useState({ component: () =>
, config: {}, diff --git a/packages/dm-core-plugins/src/header/components/UserInfoDialog.tsx b/packages/dm-core-plugins/src/header/components/UserInfoDialog.tsx index 2a30b1e58..5d75237da 100644 --- a/packages/dm-core-plugins/src/header/components/UserInfoDialog.tsx +++ b/packages/dm-core-plugins/src/header/components/UserInfoDialog.tsx @@ -1,9 +1,8 @@ import { Dialog, - RoleContext, TApplication, TRole, - useDMSS, + useApplication, } from '@development-framework/dm-core' import { Button, Icon, Radio, Typography } from '@equinor/eds-core-react' import { close } from '@equinor/eds-icons' @@ -51,8 +50,7 @@ export const UserInfoDialog = (props: UserInfoDialogProps) => { const { isOpen, setIsOpen } = props const [apiKey, setAPIKey] = useState(null) const { tokenData, token, logOut } = useContext(AuthContext) - const dmssAPI = useDMSS() - const { role, setRole, roles } = useContext(RoleContext) + const { dmssAPI, role, setRole, roles } = useApplication() const [selectedRole, setSelectedRole] = useState(role) return ( diff --git a/packages/dm-core-plugins/src/job/JobControl.tsx b/packages/dm-core-plugins/src/job/JobControl.tsx index 66c2b2369..04f88b4be 100644 --- a/packages/dm-core-plugins/src/job/JobControl.tsx +++ b/packages/dm-core-plugins/src/job/JobControl.tsx @@ -11,7 +11,7 @@ import { TSchedule, TTemplate, TemplateMenu, - useDMSS, + useApplication, useDocument, useJob, } from '@development-framework/dm-core' @@ -47,7 +47,7 @@ const defaultConfig: TJobControlConfig = { export const JobControl = (props: IUIPlugin) => { const { idReference, config } = props - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const { tokenData }: IAuthContext = useContext(AuthContext) const internalConfig: TJobControlConfig = { ...defaultConfig, ...config } diff --git a/packages/dm-core-plugins/src/job/JobCreate.tsx b/packages/dm-core-plugins/src/job/JobCreate.tsx index 988718bcc..002d9e6b6 100644 --- a/packages/dm-core-plugins/src/job/JobCreate.tsx +++ b/packages/dm-core-plugins/src/job/JobCreate.tsx @@ -1,4 +1,7 @@ -import { resolveRelativeAddressSimplified } from '@development-framework/dm-core' +import { + resolveRelativeAddressSimplified, + useApplication, +} from '@development-framework/dm-core' import { useContext, useMemo } from 'react' import { AuthContext } from 'react-oauth2-code-pkce' @@ -12,7 +15,6 @@ import { TSchedule, TTemplate, TemplateMenu, - useDMSS, useDocument, useJob, } from '@development-framework/dm-core' @@ -54,7 +56,7 @@ export const JobCreate = (props: IUIPlugin & { config: TJobPluginConfig }) => { config, idReference, }: { config: TJobPluginConfig; idReference: string } = props - const dmssApi = useDMSS() + const { dmssAPI } = useApplication() const { tokenData } = useContext(AuthContext) const username = tokenData?.preferred_username ?? 'unknown user' @@ -109,7 +111,7 @@ export const JobCreate = (props: IUIPlugin & { config: TJobPluginConfig }) => { } // Get template - dmssApi + dmssAPI .documentGet({ address: templateAddress, }) @@ -137,7 +139,7 @@ export const JobCreate = (props: IUIPlugin & { config: TJobPluginConfig }) => { updateDocument(newJob, false).then(() => start()) } else { // Add template to job container - dmssApi + dmssAPI .documentAdd({ address: jobTargetAddress, document: JSON.stringify(newJob), diff --git a/packages/dm-core-plugins/src/mediaViewer/MediaViewerPlugin.tsx b/packages/dm-core-plugins/src/mediaViewer/MediaViewerPlugin.tsx index 65c85be6e..8ae4c6ef9 100644 --- a/packages/dm-core-plugins/src/mediaViewer/MediaViewerPlugin.tsx +++ b/packages/dm-core-plugins/src/mediaViewer/MediaViewerPlugin.tsx @@ -5,7 +5,7 @@ import { MediaContent, mimeTypes, splitAddress, - useDMSS, + useApplication, useDocument, } from '@development-framework/dm-core' import { AxiosRequestConfig } from 'axios' @@ -39,7 +39,7 @@ export const MediaViewerPlugin = ( const { idReference, config } = props const [blobUrl, setBlobUrl] = useState() const [contentType, setContentType] = useState('') - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const { document, isLoading, diff --git a/packages/dm-core-plugins/src/pdf/PDFViewer.tsx b/packages/dm-core-plugins/src/pdf/PDFViewer.tsx index f5f74255a..bf99c0bea 100644 --- a/packages/dm-core-plugins/src/pdf/PDFViewer.tsx +++ b/packages/dm-core-plugins/src/pdf/PDFViewer.tsx @@ -4,7 +4,7 @@ import { Loading, TGenericObject, formatBytes, - useDMSS, + useApplication, } from '@development-framework/dm-core' import { AxiosError } from 'axios' import { useEffect, useState } from 'react' @@ -40,7 +40,7 @@ export const ViewerPDFPlugin = (props: { const [blobUrl, setBlobUrl] = useState('') const [loading, setLoading] = useState(true) const [error, setError] = useState(null) - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() useEffect(() => { setError(null) diff --git a/packages/dm-core-plugins/src/role_filter/RoleFilterPlugin.tsx b/packages/dm-core-plugins/src/role_filter/RoleFilterPlugin.tsx index 07ed4a1a8..75d1f56bd 100644 --- a/packages/dm-core-plugins/src/role_filter/RoleFilterPlugin.tsx +++ b/packages/dm-core-plugins/src/role_filter/RoleFilterPlugin.tsx @@ -1,11 +1,11 @@ import { IUIPlugin, - RoleContext, ViewCreator, + useApplication, } from '@development-framework/dm-core' import { Banner, Icon } from '@equinor/eds-core-react' import { thumbs_down } from '@equinor/eds-icons' -import React, { useContext, useEffect, useState } from 'react' +import React, { useEffect, useState } from 'react' type FilteredView = { type: string @@ -22,7 +22,7 @@ export const RoleFilterPlugin = (props: IUIPlugin): React.ReactElement => { ) const [openViewConfigs, setOpenViewConfigs] = useState([]) const [allowedRoles, setAllowedRoles] = useState([]) - const { role } = useContext(RoleContext) + const { role } = useApplication() useEffect(() => { let roles: string[] = [] diff --git a/packages/dm-core/src/ApplicationContext.tsx b/packages/dm-core/src/ApplicationContext.tsx new file mode 100644 index 000000000..4e316a855 --- /dev/null +++ b/packages/dm-core/src/ApplicationContext.tsx @@ -0,0 +1,166 @@ +import React, { + Dispatch, + ReactNode, + SetStateAction, + useContext, + useEffect, + useState, +} from 'react' +import { AuthContext } from 'react-oauth2-code-pkce' +import { ToastContainer } from 'react-toastify' +import { Tree, TreeNode } from './domain/Tree' +import { DmJobAPI } from './services' +import DmssAPI from './services/api/DmssAPI' +import { IUIPlugin, TApplication, TRole, TUiPluginMap } from './types' +import { ErrorGroup } from './utils/ErrorBoundary' + +const DEFAULT_ROLE: TRole = { + name: 'anonymous', + authServerRoleName: 'anonymous', + label: 'Anonymous', +} +export const ApplicationContext = React.createContext< + | { + name: string + dmssAPI: DmssAPI + dmJobApi: DmJobAPI + getUiPlugin: ( + pluginName: string + ) => (props: IUIPlugin) => React.ReactElement + visibleDataSources: string[] + role: TRole + setRole: Dispatch> + roles: TRole[] + tree: null | Tree + treeNodes: TreeNode[] + loading: boolean + } + | undefined +>(undefined) + +function capitalizeFirstLetter(v: string): string { + return v.charAt(0).toUpperCase() + v.slice(1) +} + +function findFirstCommonRole(appRoles: TRole[], actualRoles: string[]): TRole { + const matchingRole = appRoles.find((r: TRole) => + actualRoles.includes(r.authServerRoleName) + ) + return matchingRole ?? appRoles[0] +} + +function generateFallbackRoles(roles: string[] | undefined): TRole[] { + if (!roles) { + console.warn('No roles has been configured for the application') + return [DEFAULT_ROLE] + } + return roles.map((r) => ({ + name: r, + label: capitalizeFirstLetter(r), + authServerRoleName: r, + })) +} + +export const DMApplicationProvider = (props: { + children: ReactNode + application: TApplication + dmJobPath: string + dmssBasePath?: string + enableBlueprintCache: boolean + plugins: TUiPluginMap +}) => { + const [role, setRole] = useState(DEFAULT_ROLE) + const [roles, setRoles] = useState(props.application.roles || []) + const [loading, setLoading] = useState(true) + const [treeNodes, setTreeNodes] = useState([]) + const { token, tokenData } = useContext(AuthContext) + const dmJobApi = new DmJobAPI(token, props.dmJobPath) + + const dmssAPIOriginal = new DmssAPI(token, props.dmssBasePath) + const dmssAPI = new DmssAPI(token, props.dmssBasePath) + const tree: Tree = new Tree(dmssAPI, (t: Tree) => setTreeNodes([...t])) + + // @ts-ignore + dmssAPI.blueprintGet = async (requestParameters, options) => { + const cacheKey = `${requestParameters.typeRef}${requestParameters.context}` + const cachedValue = window.sessionStorage.getItem(cacheKey) + if (!cachedValue) { + return dmssAPIOriginal + .blueprintGet(requestParameters) + .then((response) => { + if (props.enableBlueprintCache) + window.sessionStorage.setItem( + cacheKey, + JSON.stringify(response.data) + ) + return response + }) + } + return { + data: JSON.parse(cachedValue), + status: 200, + statusText: 'ok', + } + } + + function getUiPlugin( + pluginName: string + ): (props: IUIPlugin) => React.ReactElement { + const plugin = props.plugins[pluginName]?.component + if (!plugin) + return () => ( + Did not find the plugin: {pluginName} + ) + return plugin + } + + useEffect(() => { + let newRoles = props.application.roles || [] + if (tokenData) { + newRoles = newRoles.filter((role) => + tokenData.roles.includes(role.authServerRoleName) + ) + } + if (props.application.roles && !props.application.roles.length) { + // No roles is configured in application. Generate from roles in token + newRoles = generateFallbackRoles(tokenData?.roles) + } + setRoles(newRoles) + setRole(findFirstCommonRole(newRoles, tokenData?.roles ?? [])) // Default select the first role that exists in tokenData + }, [tokenData]) + + useEffect(() => { + setLoading(true) + tree + .initFromDataSources(props.application.dataSources) + .finally(() => setLoading(false)) + }, []) + + return ( + + {props.children} + + + ) +} +export const useApplication = () => { + const context = useContext(ApplicationContext) + if (context === undefined) { + throw new Error('useApplication must be used within a ApplicationProvider') + } + return context +} diff --git a/packages/dm-core/src/components/AccessControl/AccessControlListComponent.tsx b/packages/dm-core/src/components/AccessControl/AccessControlListComponent.tsx index d68220c8d..0c8a32d47 100644 --- a/packages/dm-core/src/components/AccessControl/AccessControlListComponent.tsx +++ b/packages/dm-core/src/components/AccessControl/AccessControlListComponent.tsx @@ -4,7 +4,7 @@ import React, { useEffect, useState } from 'react' import { AxiosError, AxiosResponse } from 'axios' import { toast } from 'react-toastify' -import { useDMSS } from '../../context/DMSSContext' +import { useApplication } from '../../ApplicationContext' import { useLocalStorage } from '../../hooks/useLocalStorage' import { AccessControlList, AccessLevel } from '../../services' import { TUserIdMapping } from '../../types' @@ -29,7 +29,7 @@ export const AccessControlListComponent = (props: { const [loadingACLDocument, setLoadingACLDocument] = useState(false) const [tokenWithReadAccess, setTokenWithReadAccess] = useState('') const [refreshToken] = useLocalStorage('ROCP_refreshToken', '') - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const [documentACL, setDocumentACL] = useState({ owner: '', diff --git a/packages/dm-core/src/components/CopyLinkDialog.tsx b/packages/dm-core/src/components/CopyLinkDialog.tsx index 6486a5f99..fe9e3d1ab 100644 --- a/packages/dm-core/src/components/CopyLinkDialog.tsx +++ b/packages/dm-core/src/components/CopyLinkDialog.tsx @@ -21,7 +21,7 @@ import { TLinkReference, TValidEntity, truncatePathString, - useDMSS, + useApplication, useDocument, } from '../index' import { setMetaInDocument } from '../utils/setMetaInDocument' @@ -85,12 +85,9 @@ export const CopyLinkDialog = (props: TProps) => { useState(false) const [showLinkTargetDialog, setShowLinkTargetDialog] = useState(false) - const { document, isLoading: documentIsLoading } = useDocument( - idReference, - 9 - ) + const { document } = useDocument(idReference, 9) const [isLoading, setIsLoading] = useState(false) - const dmss = useDMSS() + const { dmssAPI } = useApplication() const copyEntityToDestination = async (): Promise< Promise | Promise @@ -114,14 +111,14 @@ export const CopyLinkDialog = (props: TProps) => { if (wrapper) { const wrapperEntity: TValidEntity = // TODO: Handle relative/unresolved addresses? Perhaps in blueprint upload? - (await dmss.instantiateEntity({ entity: { type: wrapper } })) + (await dmssAPI.instantiateEntity({ entity: { type: wrapper } })) .data as TValidEntity wrapperEntity[wrapperAttribute] = newDocument wrapperEntity._meta_ = newDocument._meta_ newDocument = wrapperEntity } - return dmss + return dmssAPI .documentAdd({ address: selectedDestination.address, document: JSON.stringify(newDocument), @@ -152,7 +149,7 @@ export const CopyLinkDialog = (props: TProps) => { referenceType: 'link', type: EBlueprint.REFERENCE, } - dmss + dmssAPI .documentAdd({ address: selectedLinkTarget.address, document: JSON.stringify(linkReference), diff --git a/packages/dm-core/src/components/NewEntityButton.tsx b/packages/dm-core/src/components/NewEntityButton.tsx index f95c11769..f15b967c4 100644 --- a/packages/dm-core/src/components/NewEntityButton.tsx +++ b/packages/dm-core/src/components/NewEntityButton.tsx @@ -2,7 +2,7 @@ import { Button, Input, Label, Progress } from '@equinor/eds-core-react' import { AxiosError, AxiosResponse } from 'axios' import React, { useEffect, useState } from 'react' import { toast } from 'react-toastify' -import { useDMSS } from '../context/DMSSContext' +import { useApplication } from '../ApplicationContext' import { TGenericObject, TReference } from '../types' import { INPUT_FIELD_WIDTH } from '../utils/variables' import { Dialog } from './Dialog' @@ -33,7 +33,7 @@ export function NewEntityButton(props: { >(undefined) const [typeToCreate, setTypeToCreate] = useState(type || '') const [loading, setLoading] = useState(false) - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() useEffect(() => setTypeToCreate(type || ''), [type]) useEffect(() => { diff --git a/packages/dm-core/src/components/Pickers/BlueprintPicker.tsx b/packages/dm-core/src/components/Pickers/BlueprintPicker.tsx index f5f3f2070..de0121335 100644 --- a/packages/dm-core/src/components/Pickers/BlueprintPicker.tsx +++ b/packages/dm-core/src/components/Pickers/BlueprintPicker.tsx @@ -1,4 +1,4 @@ -import { useContext, useState } from 'react' +import { useState } from 'react' import { toast } from 'react-toastify' import { EBlueprint } from '../../Enums' import { @@ -17,7 +17,7 @@ import { } from '@equinor/eds-core-react' import { Variants } from '@equinor/eds-core-react/dist/types/components/types' import { add } from '@equinor/eds-icons' -import { FSTreeContext } from '../../context/FileSystemTreeContext' +import { useApplication } from '../../ApplicationContext' import { TreeNode } from '../../domain/Tree' import { truncatePathString } from '../../utils/truncatePathString' import { Dialog } from '../Dialog' @@ -71,7 +71,7 @@ export const BlueprintPicker = (props: TBlueprintPickerProps) => { ...props, } const [showModal, setShowModal] = useState(false) - const { treeNodes, loading } = useContext(FSTreeContext) + const { treeNodes, loading } = useApplication() return (
diff --git a/packages/dm-core/src/components/Pickers/DestinationPicker.tsx b/packages/dm-core/src/components/Pickers/DestinationPicker.tsx index 870119c5a..55d567ec9 100644 --- a/packages/dm-core/src/components/Pickers/DestinationPicker.tsx +++ b/packages/dm-core/src/components/Pickers/DestinationPicker.tsx @@ -1,4 +1,4 @@ -import { useContext, useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { EBlueprint } from '../../Enums' import { PATH_INPUT_FIELD_WIDTH, @@ -15,7 +15,7 @@ import { Tooltip, } from '@equinor/eds-core-react' import styled from 'styled-components' -import { ApplicationContext } from '../../context/ApplicationContext' +import { useApplication } from '../../ApplicationContext' import { Tree, TreeNode } from '../../domain/Tree' import { truncatePathString } from '../../utils/truncatePathString' import { Dialog } from '../Dialog' @@ -30,12 +30,12 @@ type TDestinationPickerProps = { export const DestinationPicker = (props: TDestinationPickerProps) => { const { onChange, formData, disabled, scope, label } = props - const appConfig = useContext(ApplicationContext) + const { visibleDataSources, dmssAPI } = useApplication() const [showModal, setShowModal] = useState(false) const [loading, setLoading] = useState(true) const [treeNodes, setTreeNodes] = useState([]) - const tree: Tree = new Tree((t: Tree) => setTreeNodes([...t])) + const tree: Tree = new Tree(dmssAPI, (t: Tree) => setTreeNodes([...t])) useEffect(() => { setLoading(true) @@ -43,7 +43,7 @@ export const DestinationPicker = (props: TDestinationPickerProps) => { tree.initFromPath(scope).finally(() => setLoading(false)) } else { tree - .initFromDataSources(appConfig.visibleDataSources) + .initFromDataSources(visibleDataSources) .finally(() => setLoading(false)) } }, [scope]) diff --git a/packages/dm-core/src/components/Pickers/EntityPickerDialog.tsx b/packages/dm-core/src/components/Pickers/EntityPickerDialog.tsx index 91b0c063e..0495250d9 100644 --- a/packages/dm-core/src/components/Pickers/EntityPickerDialog.tsx +++ b/packages/dm-core/src/components/Pickers/EntityPickerDialog.tsx @@ -1,4 +1,4 @@ -import { ChangeEvent, useContext, useEffect, useState } from 'react' +import { ChangeEvent, useEffect, useState } from 'react' import { Button, @@ -8,8 +8,8 @@ import { } from '@equinor/eds-core-react' import { toast } from 'react-toastify' import styled from 'styled-components' +import { useApplication } from '../../ApplicationContext' import { EBlueprint } from '../../Enums' -import { ApplicationContext } from '../../context/ApplicationContext' import { Tree, TreeNode } from '../../domain/Tree' import { TValidEntity } from '../../types' import { truncatePathString } from '../../utils/truncatePathString' @@ -136,20 +136,21 @@ export const EntityPickerDialog = ( multiple = false, hideInvalidTypes = false, } = props - const appConfig = useContext(ApplicationContext) + const { dmssAPI, visibleDataSources } = useApplication() const [loading, setLoading] = useState(true) const [treeNodes, setTreeNodes] = useState([]) - const tree: Tree = new Tree((t: Tree) => setTreeNodes([...t])) + const tree: Tree = new Tree(dmssAPI, (t: Tree) => setTreeNodes([...t])) const [selectedNodes, setSelectedNodes] = useState([]) useEffect(() => { setLoading(true) if (scope) { + 2 tree.initFromPath(scope).finally(() => setLoading(false)) } else { tree - .initFromDataSources(appConfig.visibleDataSources) + .initFromDataSources(visibleDataSources) .finally(() => setLoading(false)) } }, [scope]) diff --git a/packages/dm-core/src/components/Pickers/JobHandlerPicker.tsx b/packages/dm-core/src/components/Pickers/JobHandlerPicker.tsx index 7a2a2be12..ba2bccefc 100644 --- a/packages/dm-core/src/components/Pickers/JobHandlerPicker.tsx +++ b/packages/dm-core/src/components/Pickers/JobHandlerPicker.tsx @@ -1,6 +1,6 @@ import { ChangeEvent } from 'react' -import { useDMSS } from '../../context/DMSSContext' -import { useSearch } from '../../hooks/useSearch' +import { useApplication } from '../../ApplicationContext' +import { useSearch } from '../../hooks' import { Select } from '../Select' export const JobHandlerPicker = (props: { @@ -9,7 +9,7 @@ export const JobHandlerPicker = (props: { }) => { const { onChange, formData } = props const blueprintName = formData.split('/').pop() - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const [searchResult] = useSearch( { type: 'dmss://system/SIMOS/Blueprint', diff --git a/packages/dm-core/src/components/UploadFileButton.tsx b/packages/dm-core/src/components/UploadFileButton.tsx index 5525b8e32..d3a31f499 100644 --- a/packages/dm-core/src/components/UploadFileButton.tsx +++ b/packages/dm-core/src/components/UploadFileButton.tsx @@ -2,7 +2,7 @@ import { Button, Progress } from '@equinor/eds-core-react' import { AxiosError, AxiosResponse } from 'axios' import { ChangeEvent, useEffect, useRef, useState } from 'react' import { toast } from 'react-toastify' -import { useDMSS } from '../context/DMSSContext' +import { useApplication } from '../ApplicationContext' import { TGenericObject, TReference, TValidEntity } from '../types' import { getKey } from '../utils/objectUtilities' @@ -21,7 +21,7 @@ export function UploadFileButton(props: { const textInput = useRef(null) const [error, setError] = useState() const [loading, setLoading] = useState(false) - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() useEffect(() => setError(undefined), [formData]) diff --git a/packages/dm-core/src/components/ViewCreator/InlineRecipeView.tsx b/packages/dm-core/src/components/ViewCreator/InlineRecipeView.tsx index 5466c76d4..47697b7cc 100644 --- a/packages/dm-core/src/components/ViewCreator/InlineRecipeView.tsx +++ b/packages/dm-core/src/components/ViewCreator/InlineRecipeView.tsx @@ -1,4 +1,4 @@ -import { ErrorBoundary, IUIPlugin, useUiPlugins } from '../../index' +import { ErrorBoundary, IUIPlugin, useApplication } from '../../index' import { TInlineRecipeViewConfig } from '../../types' type TInlineRecipeViewProps = IUIPlugin & { @@ -7,7 +7,7 @@ type TInlineRecipeViewProps = IUIPlugin & { export const InlineRecipeView = (props: TInlineRecipeViewProps) => { const { idReference, type, viewConfig, onOpen, onSubmit, onChange } = props - const { getUiPlugin } = useUiPlugins() + const { getUiPlugin } = useApplication() const UiPlugin = getUiPlugin(viewConfig.recipe.plugin) return ( diff --git a/packages/dm-core/src/components/ViewCreator/ViewCreator.tsx b/packages/dm-core/src/components/ViewCreator/ViewCreator.tsx index 68ce59418..92ebab793 100644 --- a/packages/dm-core/src/components/ViewCreator/ViewCreator.tsx +++ b/packages/dm-core/src/components/ViewCreator/ViewCreator.tsx @@ -1,7 +1,7 @@ import { Typography } from '@equinor/eds-core-react' import { AxiosResponse } from 'axios' import React, { useEffect, useState } from 'react' -import { EntityView, Loading, TAttribute, useDMSS } from '../../index' +import { EntityView, Loading, TAttribute, useApplication } from '../../index' import { IUIPlugin, TInlineRecipeViewConfig, @@ -39,7 +39,7 @@ type TViewCreator = Omit & { */ export const ViewCreator = (props: TViewCreator): React.ReactElement => { const { idReference, viewConfig, onOpen, onSubmit, onChange } = props - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState() const [attribute, setAttribute] = useState() diff --git a/packages/dm-core/src/context/ApplicationContext.tsx b/packages/dm-core/src/context/ApplicationContext.tsx deleted file mode 100644 index f1b26f973..000000000 --- a/packages/dm-core/src/context/ApplicationContext.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import React, { useContext } from 'react' -export const ApplicationContext = React.createContext({}) - -export const useApplication = () => { - const context = useContext(ApplicationContext) - if (context === undefined) { - throw new Error('useApplication must be used within a ApplicationProvider') - } - return context -} diff --git a/packages/dm-core/src/context/DMJobContext.tsx b/packages/dm-core/src/context/DMJobContext.tsx deleted file mode 100644 index a1e4ad434..000000000 --- a/packages/dm-core/src/context/DMJobContext.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { ReactNode, createContext, useContext } from 'react' -import { AuthContext } from 'react-oauth2-code-pkce' -import { DmJobAPI } from '../services' - -const DMJobContext = createContext(undefined) - -export const DMJobProvider = (props: { - children: ReactNode - dmJobPath?: string -}) => { - const { token } = useContext(AuthContext) - if (!props.dmJobPath) - throw new Error('DMJobProvider is missing a job api url') - const dmJobApi = new DmJobAPI(token, props.dmJobPath) - return ( - - {props.children} - - ) -} - -export const useDmJob = () => { - const context = useContext(DMJobContext) - if (!context) throw new Error('useDmJob must be used within a DMJobProvider') - return context -} diff --git a/packages/dm-core/src/context/DMSSContext.tsx b/packages/dm-core/src/context/DMSSContext.tsx deleted file mode 100644 index a37b828b4..000000000 --- a/packages/dm-core/src/context/DMSSContext.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import { ReactNode, createContext, useContext } from 'react' -import { AuthContext } from 'react-oauth2-code-pkce' -import DmssAPI from '../services/api/DmssAPI' - -const DMSSContext = createContext(undefined) - -export const DMSSProvider = (props: { - children: ReactNode - dmssBasePath?: string - enableBlueprintCache?: boolean -}) => { - const { token } = useContext(AuthContext) - const dmssAPIOriginal = new DmssAPI(token, props.dmssBasePath) - const dmssAPI = new DmssAPI(token, props.dmssBasePath) - - // @ts-ignore - dmssAPI.blueprintGet = async (requestParameters, options) => { - const cacheKey = `${requestParameters.typeRef}${requestParameters.context}` - const cachedValue = window.sessionStorage.getItem(cacheKey) - if (!cachedValue) { - return dmssAPIOriginal - .blueprintGet(requestParameters) - .then((response) => { - if (props.enableBlueprintCache) - window.sessionStorage.setItem( - cacheKey, - JSON.stringify(response.data) - ) - return response - }) - } - return { - data: JSON.parse(cachedValue), - status: 200, - statusText: 'ok', - } - } - - return ( - - {props.children} - - ) -} - -export const useDMSS = () => { - const context = useContext(DMSSContext) - if (context === undefined) { - throw new Error('useDMSS must be used within a DMSSProvider') - } - return context -} diff --git a/packages/dm-core/src/context/FileSystemTreeContext.tsx b/packages/dm-core/src/context/FileSystemTreeContext.tsx deleted file mode 100644 index c55718127..000000000 --- a/packages/dm-core/src/context/FileSystemTreeContext.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { ReactNode, createContext, useEffect, useState } from 'react' -import { Tree, TreeNode } from '../domain/Tree' - -export const FSTreeContext = createContext<{ - tree: null | Tree - treeNodes: TreeNode[] - loading: boolean -}>({ - tree: null, - treeNodes: [], - loading: false, -}) - -export const FSTreeProvider = (props: { - children: ReactNode - visibleDataSources: string[] -}) => { - const { children, visibleDataSources } = props - const [loading, setLoading] = useState(true) - const [treeNodes, setTreeNodes] = useState([]) - const tree: Tree = new Tree((t: Tree) => setTreeNodes([...t])) - - useEffect(() => { - setLoading(true) - tree - .initFromDataSources(visibleDataSources) - .finally(() => setLoading(false)) - }, []) - - return ( - - {children} - - ) -} diff --git a/packages/dm-core/src/context/RoleContext.tsx b/packages/dm-core/src/context/RoleContext.tsx deleted file mode 100644 index 5ec8cb02a..000000000 --- a/packages/dm-core/src/context/RoleContext.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import { - Dispatch, - ReactNode, - SetStateAction, - createContext, - useContext, - useEffect, - useState, -} from 'react' -import { AuthContext } from 'react-oauth2-code-pkce' -import { TRole } from '../types' - -const DEFAULT_ROLE: TRole = { - name: 'anonymous', - authServerRoleName: 'anonymous', - label: 'Anonymous', -} -export const RoleContext = createContext<{ - role: TRole - setRole: Dispatch> - roles: TRole[] -}>({ - role: DEFAULT_ROLE, - // @ts-ignore - setRole: () => { - throw new Error('RoleContex was accessed before the provider registered.') - }, - roles: [], -}) - -function capitalizeFirstLetter(v: string): string { - return v.charAt(0).toUpperCase() + v.slice(1) -} - -function findFirstCommonRole(appRoles: TRole[], actualRoles: string[]): TRole { - const matchingRole = appRoles.find((r: TRole) => - actualRoles.includes(r.authServerRoleName) - ) - return matchingRole ?? appRoles[0] -} - -function generateFallbackRoles(roles: string[] | undefined): TRole[] { - if (!roles) { - console.warn('No roles has been configured for the application') - return [DEFAULT_ROLE] - } - return roles.map((r) => ({ - name: r, - label: capitalizeFirstLetter(r), - authServerRoleName: r, - })) -} - -export const RoleProvider = (props: { - children: ReactNode - roles: TRole[] -}) => { - const { children, roles: passedRoles } = props - const [role, setRole] = useState(DEFAULT_ROLE) - const [roles, setRoles] = useState(passedRoles) - const { tokenData } = useContext(AuthContext) - - useEffect(() => { - let newRoles = passedRoles - if (tokenData) { - newRoles = newRoles.filter((role) => - tokenData.roles.includes(role.authServerRoleName) - ) - } - if (passedRoles && !passedRoles.length) { - // No roles is configured in application. Generate from roles in token - newRoles = generateFallbackRoles(tokenData?.roles) - } - setRoles(newRoles) - setRole(findFirstCommonRole(newRoles, tokenData?.roles ?? [])) // Default select the first role that exists in tokenData - }, [tokenData]) - - return ( - - {children} - - ) -} diff --git a/packages/dm-core/src/context/UiPluginContext.tsx b/packages/dm-core/src/context/UiPluginContext.tsx deleted file mode 100644 index d2a50c513..000000000 --- a/packages/dm-core/src/context/UiPluginContext.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import React, { createContext, useContext } from 'react' -import { IUIPlugin, TUiPluginMap } from '../types' -import { ErrorGroup } from '../utils/ErrorBoundary' - -type TUiPluginContext = { - plugins: TUiPluginMap - getUiPlugin: (pluginName: string) => (props: IUIPlugin) => React.ReactElement -} - -const UiPluginContext = createContext(undefined) - -export const useUiPlugins = () => { - const context = useContext(UiPluginContext) - if (context === undefined) { - throw new Error('useUiPlugins must be used within a UiPluginProvider') - } - return context -} - -export const UiPluginProvider = ({ - pluginsToLoad: plugins, - children, -}: { - pluginsToLoad: TUiPluginMap - children: any -}) => { - function getUiPlugin( - pluginName: string - ): (props: IUIPlugin) => React.ReactElement { - const plugin = plugins[pluginName]?.component - if (!plugin) - return () => ( - Did not find the plugin: {pluginName} - ) - return plugin - } - - return ( - - {children} - - ) -} diff --git a/packages/dm-core/src/domain/Tree.ts b/packages/dm-core/src/domain/Tree.ts index f04ac0b47..37c48d7cd 100644 --- a/packages/dm-core/src/domain/Tree.ts +++ b/packages/dm-core/src/domain/Tree.ts @@ -1,6 +1,5 @@ import { AxiosResponse } from 'axios' import { EBlueprint } from '../Enums' -import { useDMSS } from '../context/DMSSContext' import { DmssAPI } from '../services' import { TAttribute, TBlueprint, TPackage } from '../types' import { splitAddress } from '../utils/addressUtilities' @@ -275,8 +274,8 @@ export class Tree { dmssApi: DmssAPI updateCallback: (t: Tree) => void - constructor(updateCallback: (t: Tree) => void) { - this.dmssApi = useDMSS() + constructor(dmssAPI: DmssAPI, updateCallback: (t: Tree) => void) { + this.dmssApi = dmssAPI this.updateCallback = updateCallback } @@ -293,6 +292,7 @@ export class Tree { } async initFromDataSources(dataSources?: string[]) { + if (!dataSources) return // Add the dataSources as the top-level nodes const allDataSources = await this.dmssApi .dataSourceGetAll() diff --git a/packages/dm-core/src/hooks/useBlueprint.tsx b/packages/dm-core/src/hooks/useBlueprint.tsx index 992c4f253..540b7c4cd 100644 --- a/packages/dm-core/src/hooks/useBlueprint.tsx +++ b/packages/dm-core/src/hooks/useBlueprint.tsx @@ -1,8 +1,7 @@ import { AxiosError } from 'axios' -import { useContext, useEffect, useState } from 'react' +import { useEffect, useState } from 'react' import { TBlueprint, TStorageRecipe, TUiRecipe } from 'src/types' -import { ApplicationContext } from '../context/ApplicationContext' -import { useDMSS } from '../context/DMSSContext' +import { useApplication } from '../ApplicationContext' import { ErrorResponse } from '../services' interface IUseBlueprint { @@ -43,8 +42,7 @@ export const useBlueprint = (typeRef: string): IUseBlueprint => { const [initialUiRecipe, setInitialUiRecipe] = useState() const [isLoading, setLoading] = useState(true) const [error, setError] = useState(null) - const { name } = useContext(ApplicationContext) - const dmssAPI = useDMSS() + const { dmssAPI, name } = useApplication() useEffect(() => { dmssAPI diff --git a/packages/dm-core/src/hooks/useDocument.test.tsx b/packages/dm-core/src/hooks/useDocument.test.tsx index 3504bdc1a..afe4d2415 100644 --- a/packages/dm-core/src/hooks/useDocument.test.tsx +++ b/packages/dm-core/src/hooks/useDocument.test.tsx @@ -1,8 +1,8 @@ import { renderHook, waitFor } from '@testing-library/react' import { useDocument } from './useDocument' import React from 'react' -import { DMSSProvider } from '../context/DMSSContext' import { mockGetDocument } from '../utils/test-utils-dm-core' +import { DMApplicationProvider } from "../ApplicationContext"; const mockDocument = [ { @@ -12,8 +12,15 @@ const mockDocument = [ ] const wrapper = (props: { children: React.ReactNode }) => ( - {props.children} -) + + {props.children} + + ) describe('useDocumentHook', () => { afterEach(() => { diff --git a/packages/dm-core/src/hooks/useDocument.tsx b/packages/dm-core/src/hooks/useDocument.tsx index 3fdc74e7f..b3d985de6 100644 --- a/packages/dm-core/src/hooks/useDocument.tsx +++ b/packages/dm-core/src/hooks/useDocument.tsx @@ -1,9 +1,9 @@ import { AxiosError } from 'axios' import { useEffect, useState } from 'react' -import { useDMSS } from '../context/DMSSContext' import { ErrorResponse } from '../services' import { toast } from 'react-toastify' +import { useApplication } from '../ApplicationContext' interface IUseDocumentReturnType { document: T | null @@ -56,7 +56,7 @@ export function useDocument( const [document, setDocument] = useState(null) const [isLoading, setLoading] = useState(true) const [error, setError] = useState(null) - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() useEffect(() => { setLoading(true) diff --git a/packages/dm-core/src/hooks/useJob.tsx b/packages/dm-core/src/hooks/useJob.tsx index b53cf6b61..25c28b03b 100644 --- a/packages/dm-core/src/hooks/useJob.tsx +++ b/packages/dm-core/src/hooks/useJob.tsx @@ -1,7 +1,6 @@ import { AxiosError, AxiosResponse } from 'axios' import { useEffect, useState } from 'react' -import { useDmJob } from '../context/DMJobContext' -import { useDMSS } from '../context/DMSSContext' +import { useApplication } from '../ApplicationContext' import { DeleteJobResponse, ErrorResponse, @@ -82,8 +81,8 @@ export function useJob(entityId?: string, jobId?: string): IUseJob { const [status, setStatus] = useState(JobStatus.NotStarted) const [isLoading, setIsLoading] = useState(true) const [error, setError] = useState() - const dmJobApi = useDmJob() - const dmssAPI = useDMSS() + const { dmJobApi } = useApplication() + const { dmssAPI } = useApplication() let statusIntervalId: NodeJS.Timeout diff --git a/packages/dm-core/src/hooks/useList/useList.test.tsx b/packages/dm-core/src/hooks/useList/useList.test.tsx index 21440dfdd..935aebdaa 100644 --- a/packages/dm-core/src/hooks/useList/useList.test.tsx +++ b/packages/dm-core/src/hooks/useList/useList.test.tsx @@ -1,7 +1,6 @@ import { cleanup, renderHook, waitFor } from '@testing-library/react' import { useList } from './useList' import React from 'react' -import { DMSSProvider } from '../../context/DMSSContext' import { mockAttributeGet, mockDocumentAdd, @@ -10,6 +9,7 @@ import { mockInstantiateEntity, mockUpdateDocument, } from '../../utils/test-utils-dm-core' +import { DMApplicationProvider } from "../../ApplicationContext"; const setupContained = () => { const attribute = { @@ -40,8 +40,16 @@ const setupContained = () => { } const wrapper = (props: { children: React.ReactNode }) => ( - {props.children} -) + + {props.children} + + ) afterEach(() => { cleanup() diff --git a/packages/dm-core/src/hooks/useList/useList.tsx b/packages/dm-core/src/hooks/useList/useList.tsx index e4a26930b..f987f1c9b 100644 --- a/packages/dm-core/src/hooks/useList/useList.tsx +++ b/packages/dm-core/src/hooks/useList/useList.tsx @@ -1,7 +1,7 @@ import { AxiosError, AxiosResponse, isAxiosError } from 'axios' import { useEffect, useState } from 'react' +import { useApplication } from '../../ApplicationContext' import { EBlueprint } from '../../Enums' -import { useDMSS } from '../../context/DMSSContext' import { ErrorResponse } from '../../services' import { TAttribute, TGenericObject, TLinkReference } from '../../types' import { resolveRelativeAddressSimplified } from '../../utils/addressUtilities' @@ -20,7 +20,7 @@ export function useList( const [error, setError] = useState(null) const [dirtyState, setDirtyState] = useState(false) const [refresh, reloadData] = useState() - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() useEffect(() => { dmssAPI diff --git a/packages/dm-core/src/hooks/useRecipe.tsx b/packages/dm-core/src/hooks/useRecipe.tsx index 71c2af5d0..8680e8ed2 100644 --- a/packages/dm-core/src/hooks/useRecipe.tsx +++ b/packages/dm-core/src/hooks/useRecipe.tsx @@ -1,13 +1,6 @@ import { AxiosError } from 'axios' -import { useContext, useEffect, useState } from 'react' -import { - ApplicationContext, - ErrorResponse, - IUIPlugin, - TUiRecipe, - useDMSS, - useUiPlugins, -} from '../index' +import { useEffect, useState } from 'react' +import { ErrorResponse, IUIPlugin, TUiRecipe, useApplication } from '../index' export const findRecipe = ( recipes: TUiRecipe[], @@ -92,7 +85,7 @@ export const useRecipe = ( recipeName?: string, dimensions: string = '' ): IUseRecipe => { - const { getUiPlugin } = useUiPlugins() + const { getUiPlugin } = useApplication() const [foundRecipe, setFoundRecipe] = useState() const [findRecipeError, setFindRecipeError] = useState( null @@ -100,8 +93,7 @@ export const useRecipe = ( const [isLoading, setLoading] = useState(true) const [error, setError] = useState(null) - const dmssAPI = useDMSS() - const { name } = useContext(ApplicationContext) + const { dmssAPI, name } = useApplication() useEffect(() => { setLoading(true) diff --git a/packages/dm-core/src/hooks/useSearch.tsx b/packages/dm-core/src/hooks/useSearch.tsx index 703747d04..b6eb53e57 100644 --- a/packages/dm-core/src/hooks/useSearch.tsx +++ b/packages/dm-core/src/hooks/useSearch.tsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react' -import { useDMSS } from '../context/DMSSContext' +import { useApplication } from '../ApplicationContext' export function useSearch( body: any, @@ -9,7 +9,7 @@ export function useSearch( const [searchResult, setSearchResult] = useState([]) const [isLoading, setIsLoading] = useState(true) const [hasError, setHasError] = useState(false) - const dmssAPI = useDMSS() + const { dmssAPI } = useApplication() useEffect(() => { setIsLoading(true) diff --git a/packages/dm-core/src/index.tsx b/packages/dm-core/src/index.tsx index dd8b130f4..a59996b31 100644 --- a/packages/dm-core/src/index.tsx +++ b/packages/dm-core/src/index.tsx @@ -6,12 +6,7 @@ export { AuthContext, AuthProvider } from 'react-oauth2-code-pkce' export * from './Enums' export * from './components' export { colors } from './colors' -export * from './context/ApplicationContext' -export * from './context/DMSSContext' -export * from './context/DMJobContext' -export * from './context/FileSystemTreeContext' -export * from './context/UiPluginContext' -export * from './context/RoleContext' +export * from './ApplicationContext' export * from './domain/Tree' export * from './services' export * from './services/api/configs/gen-job/models'