This is an automated email from the ASF dual-hosted git repository. rantunes pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/incubator-kie-tools-temporary-rnd-do-not-use.git
commit 400890aa8ce6f3657d6d41cbfd49b121596aeec4 Author: Fabrizio Antonangeli <[email protected]> AuthorDate: Wed Nov 8 13:08:47 2023 +0100 KOGITO-9701: Implement Workflow Instances section (#2033) --- packages/sonataflow-deployment-webapp/README.md | 4 +- packages/sonataflow-deployment-webapp/package.json | 10 ++ packages/sonataflow-deployment-webapp/src/App.tsx | 51 +++--- .../src/AppConstants.ts | 4 + .../src/context/AppContext.tsx | 3 + .../src/context/AppContextProvider.tsx | 31 +++- .../sonataflow-deployment-webapp/src/data/index.ts | 20 +++ .../ServerlessWorkflowCombinedEditorEnvelopeApp.ts | 42 +++++ .../ServerlessWorkflowDiagramEditorEnvelopeApp.ts | 45 +++++ .../ServerlessWorkflowTextEditorEnvelopeApp.ts | 39 +++++ .../sonataflow-deployment-webapp/src/index.tsx | 1 + .../src/navigation/RoutesSwitch.tsx | 54 ++++++ .../RuntimeToolsRoutesSwitch.tsx} | 24 ++- .../src/pages/BasePage.tsx | 8 +- .../src/pages/ErrorPage.tsx | 17 +- .../src/pages/Workflows/CloudEventFormPage.tsx | 6 +- .../src/pages/Workflows/WorkflowFormPage.tsx | 74 ++++++--- .../src/pages/Workflows/Workflows.tsx | 4 +- .../src/pages/basePage/BasePageNav.tsx | 14 ++ .../src/routes/index.ts | 22 ++- .../workflows/RuntimeToolsWorkflowDetails.tsx | 106 ++++++++++++ .../workflows/RuntimeToolsWorkflowInstances.tsx | 88 ++++++++++ .../WorkflowDetails/WorkflowDetailsContext.ts} | 13 +- .../WorkflowDetailsContextProvider.tsx | 56 +++++++ .../WorkflowDetails/WorkflowDetailsGatewayApi.ts | 176 ++++++++++++++++++++ .../WorkflowDetails/WorkflowDetailsQueries.ts | 128 ++++++++++++++ .../workflows/WorkflowDetails/index.ts} | 10 +- .../WorkflowDetailsContainer.tsx | 62 +++++++ .../workflows/WorkflowList/WorkflowListContext.ts} | 13 +- .../WorkflowList/WorkflowListContextProvider.tsx | 56 +++++++ .../WorkflowList/WorkflowListGatewayApi.ts | 185 +++++++++++++++++++++ .../workflows/WorkflowList/WorkflowListQueries.ts | 90 ++++++++++ .../workflows/WorkflowList/index.ts} | 10 +- .../WorkflowListContainer.tsx | 64 +++++++ ...rverless-workflow-combined-editor-envelope.html | 45 +++++ ...erverless-workflow-diagram-editor-envelope.html | 45 +++++ .../serverless-workflow-text-editor-envelope.html | 45 +++++ .../sonataflow-deployment-webapp/webpack.config.js | 21 +++ pnpm-lock.yaml | 184 +++++++++++++++++--- 39 files changed, 1733 insertions(+), 137 deletions(-) diff --git a/packages/sonataflow-deployment-webapp/README.md b/packages/sonataflow-deployment-webapp/README.md index 574e027239..231977ccad 100644 --- a/packages/sonataflow-deployment-webapp/README.md +++ b/packages/sonataflow-deployment-webapp/README.md @@ -160,7 +160,8 @@ After the installation, you can optionally add a file at `/src/main/resources/ME ```JSON { "appName": "SonataFlow Deployment", - "showDisclaimer": true + "showDisclaimer": true, + "dataIndexUrl": "" } ``` @@ -168,3 +169,4 @@ Please replace: - `appName` with the desired name for the app - `showDisclaimer` show/hide the development disclamer +- `dataIndexUrl` optional url to your SonataFlow Data Index Service. If not set the app will try to use the embedded Data Index Service. diff --git a/packages/sonataflow-deployment-webapp/package.json b/packages/sonataflow-deployment-webapp/package.json index a114865f0f..dcd9396158 100644 --- a/packages/sonataflow-deployment-webapp/package.json +++ b/packages/sonataflow-deployment-webapp/package.json @@ -26,6 +26,8 @@ "@babel/core": "^7.16.0", "@babel/preset-env": "^7.16.0", "@babel/preset-react": "^7.16.0", + "@kie-tools-core/editor": "workspace:*", + "@kie-tools-core/keyboard-shortcuts": "workspace:*", "@kie-tools-core/patternfly-base": "workspace:*", "@kie-tools-core/react-hooks": "workspace:*", "@kie-tools-core/webpack-base": "workspace:*", @@ -34,7 +36,12 @@ "@kie-tools/runtime-tools-components": "workspace:*", "@kie-tools/runtime-tools-enveloped-components": "workspace:*", "@kie-tools/runtime-tools-gateway-api": "workspace:*", + "@kie-tools/serverless-workflow-combined-editor": "workspace:*", + "@kie-tools/serverless-workflow-diagram-editor-assets": "workspace:*", + "@kie-tools/serverless-workflow-diagram-editor-envelope": "workspace:*", + "@kie-tools/serverless-workflow-text-editor": "workspace:*", "@kie-tools/tsconfig": "workspace:*", + "@patternfly/patternfly": "^4.224.2", "@patternfly/react-core": "^4.276.6", "@patternfly/react-icons": "^4.93.6", "@patternfly/react-table": "^4.112.39", @@ -43,6 +50,9 @@ "@types/react-dom": "^17.0.5", "@types/react-router": "^5.1.14", "@types/react-router-dom": "^5.1.7", + "apollo-cache-inmemory": "1.6.6", + "apollo-client": "2.6.10", + "apollo-link-http": "1.5.17", "copy-webpack-plugin": "^11.0.0", "jest": "^26.6.3", "jest-junit": "^14.0.0", diff --git a/packages/sonataflow-deployment-webapp/src/App.tsx b/packages/sonataflow-deployment-webapp/src/App.tsx index a5a4a304c6..36830ac6c3 100644 --- a/packages/sonataflow-deployment-webapp/src/App.tsx +++ b/packages/sonataflow-deployment-webapp/src/App.tsx @@ -18,38 +18,27 @@ */ import React from "react"; -import { Route, Switch } from "react-router"; -import { HashRouter, Redirect } from "react-router-dom"; +import { HashRouter } from "react-router-dom"; import { AppContextProvider } from "./context/AppContextProvider"; import { OpenApiContextProvider } from "./context/OpenApiContextProvider"; -import { Workflows } from "./pages/Workflows/"; -import { NoMatchPage } from "./pages/NoMatchPage"; -import { routes } from "./routes"; -import { WorkflowFormPage } from "./pages/Workflows/WorkflowFormPage"; -import { CloudEventFormPage } from "./pages/Workflows/CloudEventFormPage"; +import { RoutesSwitch } from "./navigation/RoutesSwitch"; +import { WorkflowListContextProvider } from "./runtimeTools/workflows/WorkflowList"; +import { WorkflowDetailsContextProvider } from "./runtimeTools/workflows/WorkflowDetails"; -export function App() { - return ( - <AppContextProvider> - <OpenApiContextProvider> - <HashRouter> - <Switch> - <Route path={routes.workflows.form.path({ workflowId: ":workflowId" })}> - {({ match }) => <WorkflowFormPage workflowId={match!.params.workflowId!} />} - </Route> - <Route path={routes.workflows.cloudEvent.path({})}> - <CloudEventFormPage /> - </Route> - <Route path={routes.workflows.home.path({})}> - <Workflows /> - </Route> - <Route path={routes.home.path({})}> - <Redirect to={routes.workflows.home.path({})} /> - </Route> - <Route component={NoMatchPage} /> - </Switch> - </HashRouter> - </OpenApiContextProvider> - </AppContextProvider> - ); +export const App = () => ( + <HashRouter> + {nest( + [AppContextProvider, {}], + [OpenApiContextProvider, {}], + [WorkflowListContextProvider, {}], + [WorkflowDetailsContextProvider, {}], + [RoutesSwitch, {}] + )} + </HashRouter> +); + +function nest(...components: Array<[(...args: any[]) => any, object]>) { + return components.reduceRight((acc, [Component, props]) => { + return <Component {...props}>{acc}</Component>; + }, <></>); } diff --git a/packages/sonataflow-deployment-webapp/src/AppConstants.ts b/packages/sonataflow-deployment-webapp/src/AppConstants.ts index 5985bb530a..b8735e7149 100644 --- a/packages/sonataflow-deployment-webapp/src/AppConstants.ts +++ b/packages/sonataflow-deployment-webapp/src/AppConstants.ts @@ -20,9 +20,13 @@ import { AppData } from "./data"; export const SONATAFLOW_DEPLOYMENT_DOCUMENTATION_URL = "https://sonataflow.org/serverlessworkflow/latest/index.html"; +export const SONATAFLOW_DEPLOYMENT_DATAINDEX_DOCUMENTATION_URL = + "https://sonataflow.org/serverlessworkflow/latest/data-index/data-index-core-concepts.html"; export const KUBESMARTS_URL = "https://start.kubesmarts.org"; +export const APPDATA_JSON_FILENAME = "sonataflow-deployment-webapp-data.json"; export const DEFAULT_APPDATA_VALUES: AppData = { appName: "Deployment", showDisclaimer: false, + dataIndexUrl: "/graphql", }; diff --git a/packages/sonataflow-deployment-webapp/src/context/AppContext.tsx b/packages/sonataflow-deployment-webapp/src/context/AppContext.tsx index dd2828b0cf..69072f9507 100644 --- a/packages/sonataflow-deployment-webapp/src/context/AppContext.tsx +++ b/packages/sonataflow-deployment-webapp/src/context/AppContext.tsx @@ -24,6 +24,9 @@ import { AppData } from "../data"; export interface AppContextType { appDataPromise: PromiseState<AppData>; data: AppData; + dataIndexAvailable?: boolean; + isDataIndexEmbedded: boolean; + fullDataIndexUrl: string; } export const AppContext = createContext<AppContextType>({} as any); diff --git a/packages/sonataflow-deployment-webapp/src/context/AppContextProvider.tsx b/packages/sonataflow-deployment-webapp/src/context/AppContextProvider.tsx index 6c9f99d834..d9e645f007 100644 --- a/packages/sonataflow-deployment-webapp/src/context/AppContextProvider.tsx +++ b/packages/sonataflow-deployment-webapp/src/context/AppContextProvider.tsx @@ -19,31 +19,48 @@ import React, { PropsWithChildren, useEffect, useMemo, useState } from "react"; import { DEFAULT_APPDATA_VALUES } from "../AppConstants"; -import { AppData } from "../data"; +import { AppData, verifyDataIndex } from "../data"; import { useAppDataPromise } from "../hooks/useAppDataPromise"; import { AppContext } from "./AppContext"; export function AppContextProvider(props: PropsWithChildren<{}>) { const appDataPromise = useAppDataPromise(); const [data, setData] = useState<AppData>(DEFAULT_APPDATA_VALUES); + const [dataIndexAvailable, setDataIndexAvailable] = useState<boolean>(); useEffect(() => { if (!appDataPromise.data) { return; } - setData(appDataPromise.data); + const appData = { + appName: appDataPromise.data.appName || DEFAULT_APPDATA_VALUES.appName, + showDisclaimer: appDataPromise.data.showDisclaimer ?? DEFAULT_APPDATA_VALUES.showDisclaimer, + dataIndexUrl: appDataPromise.data.dataIndexUrl || DEFAULT_APPDATA_VALUES.dataIndexUrl, + }; + + setData(appData); document.title = appDataPromise.data.appName; + + verifyDataIndex(appData.dataIndexUrl).then(setDataIndexAvailable); }, [appDataPromise.data]); - const value = useMemo( - () => ({ + const value = useMemo(() => { + const isDataIndexUrlRelativePath = /^\/\w+/.test(data.dataIndexUrl); + const isDataIndexEmbedded = + data.dataIndexUrl === DEFAULT_APPDATA_VALUES.dataIndexUrl || + data.dataIndexUrl.startsWith(window.location.origin) || + isDataIndexUrlRelativePath; + + return { appDataPromise, data, - }), - [data, appDataPromise] - ); + dataIndexAvailable, + isDataIndexEmbedded, + fullDataIndexUrl: (isDataIndexEmbedded ? window.location.origin : "") + data.dataIndexUrl, + }; + }, [data, appDataPromise, dataIndexAvailable]); return <AppContext.Provider value={value}>{props.children}</AppContext.Provider>; } diff --git a/packages/sonataflow-deployment-webapp/src/data/index.ts b/packages/sonataflow-deployment-webapp/src/data/index.ts index 64a598819a..37aa288410 100644 --- a/packages/sonataflow-deployment-webapp/src/data/index.ts +++ b/packages/sonataflow-deployment-webapp/src/data/index.ts @@ -22,9 +22,29 @@ import { routes } from "../routes"; export interface AppData { appName: string; showDisclaimer: boolean; + dataIndexUrl: string; } export async function fetchAppData(): Promise<AppData> { const response = await fetch(routes.dataJson.path({})); return (await response.json()) as AppData; } + +export async function verifyDataIndex(dataIndexUrl?: string): Promise<boolean> { + if (!dataIndexUrl) { + return false; + } + + try { + const response = await fetch(dataIndexUrl, { + headers: { + "Content-Type": "application/json", + }, + method: "POST", + body: '{"query":""}', + }); + return response.status === 200; + } catch (e) { + return false; + } +} diff --git a/packages/sonataflow-deployment-webapp/src/envelope/ServerlessWorkflowCombinedEditorEnvelopeApp.ts b/packages/sonataflow-deployment-webapp/src/envelope/ServerlessWorkflowCombinedEditorEnvelopeApp.ts new file mode 100644 index 0000000000..346a4e5c03 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/envelope/ServerlessWorkflowCombinedEditorEnvelopeApp.ts @@ -0,0 +1,42 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as EditorEnvelope from "@kie-tools-core/editor/dist/envelope"; +import { NoOpKeyboardShortcutsService } from "@kie-tools-core/keyboard-shortcuts/dist/envelope"; +import { + ServerlessWorkflowCombinedEditorApi, + ServerlessWorkflowCombinedEditorChannelApi, + ServerlessWorkflowCombinedEditorEnvelopeApi, +} from "@kie-tools/serverless-workflow-combined-editor/dist/api"; +import { ServerlessWorkflowCombinedEditorFactory } from "@kie-tools/serverless-workflow-combined-editor/dist/editor"; +import { ServerlessWorkflowCombinedEditorEnvelopeApiImpl } from "@kie-tools/serverless-workflow-combined-editor/dist/envelope"; + +EditorEnvelope.initCustom< + ServerlessWorkflowCombinedEditorApi, + ServerlessWorkflowCombinedEditorEnvelopeApi, + ServerlessWorkflowCombinedEditorChannelApi +>({ + container: document.getElementById("swf-combined-editor-envelope-app")!, + bus: { postMessage: (message, targetOrigin, _) => window.parent.postMessage(message, targetOrigin!, _) }, + apiImplFactory: { + create: (args) => + new ServerlessWorkflowCombinedEditorEnvelopeApiImpl(args, new ServerlessWorkflowCombinedEditorFactory()), + }, + keyboardShortcutsService: new NoOpKeyboardShortcutsService(), +}); diff --git a/packages/sonataflow-deployment-webapp/src/envelope/ServerlessWorkflowDiagramEditorEnvelopeApp.ts b/packages/sonataflow-deployment-webapp/src/envelope/ServerlessWorkflowDiagramEditorEnvelopeApp.ts new file mode 100644 index 0000000000..94729de859 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/envelope/ServerlessWorkflowDiagramEditorEnvelopeApp.ts @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as EditorEnvelope from "@kie-tools-core/editor/dist/envelope"; +import { + ServerlessWorkflowDiagramEditorChannelApi, + ServerlessWorkflowDiagramEditorEnvelopeApi, +} from "@kie-tools/serverless-workflow-diagram-editor-envelope/dist/api"; +import { + ServerlessWorkflowDiagramEditor, + ServerlessWorkflowDiagramEditorEnvelopeApiImpl, + ServerlessWorkflowDiagramEditorFactory, +} from "@kie-tools/serverless-workflow-diagram-editor-envelope/dist/envelope"; + +EditorEnvelope.initCustom< + ServerlessWorkflowDiagramEditor, + ServerlessWorkflowDiagramEditorEnvelopeApi, + ServerlessWorkflowDiagramEditorChannelApi +>({ + container: document.getElementById("swf-diagram-editor-envelope-app")!, + bus: { postMessage: (message, targetOrigin, _) => window.parent.postMessage(message, targetOrigin!, _) }, + apiImplFactory: { + create: (args) => + new ServerlessWorkflowDiagramEditorEnvelopeApiImpl( + args, + new ServerlessWorkflowDiagramEditorFactory({ shouldLoadResourcesDynamically: true }) + ), + }, +}); diff --git a/packages/sonataflow-deployment-webapp/src/envelope/ServerlessWorkflowTextEditorEnvelopeApp.ts b/packages/sonataflow-deployment-webapp/src/envelope/ServerlessWorkflowTextEditorEnvelopeApp.ts new file mode 100644 index 0000000000..39d4415c02 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/envelope/ServerlessWorkflowTextEditorEnvelopeApp.ts @@ -0,0 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import * as EditorEnvelope from "@kie-tools-core/editor/dist/envelope"; +import { + ServerlessWorkflowTextEditorApi, + ServerlessWorkflowTextEditorChannelApi, + ServerlessWorkflowTextEditorEnvelopeApi, +} from "@kie-tools/serverless-workflow-text-editor/dist/api"; +import { ServerlessWorkflowTextEditorFactory } from "@kie-tools/serverless-workflow-text-editor/dist/editor"; +import { ServerlessWorkflowTextEditorEnvelopeApiImpl } from "@kie-tools/serverless-workflow-text-editor/dist/envelope"; + +EditorEnvelope.initCustom< + ServerlessWorkflowTextEditorApi, + ServerlessWorkflowTextEditorEnvelopeApi, + ServerlessWorkflowTextEditorChannelApi +>({ + container: document.getElementById("swf-text-editor-envelope-app")!, + bus: { postMessage: (message, targetOrigin, _) => window.parent.postMessage(message, targetOrigin!, _) }, + apiImplFactory: { + create: (args) => new ServerlessWorkflowTextEditorEnvelopeApiImpl(args, new ServerlessWorkflowTextEditorFactory()), + }, +}); diff --git a/packages/sonataflow-deployment-webapp/src/index.tsx b/packages/sonataflow-deployment-webapp/src/index.tsx index 47689759b5..5444882fc0 100644 --- a/packages/sonataflow-deployment-webapp/src/index.tsx +++ b/packages/sonataflow-deployment-webapp/src/index.tsx @@ -18,6 +18,7 @@ */ import "@patternfly/react-core/dist/styles/base.css"; +import "@patternfly/patternfly/patternfly-addons.css"; import * as React from "react"; import * as ReactDOM from "react-dom"; import { App } from "./App"; diff --git a/packages/sonataflow-deployment-webapp/src/navigation/RoutesSwitch.tsx b/packages/sonataflow-deployment-webapp/src/navigation/RoutesSwitch.tsx new file mode 100644 index 0000000000..b363566dcf --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/navigation/RoutesSwitch.tsx @@ -0,0 +1,54 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React from "react"; +import { Redirect, Route, Switch } from "react-router-dom"; +import { APPDATA_JSON_FILENAME } from "../AppConstants"; +import { ErrorKind, ErrorPage } from "../pages/ErrorPage"; +import { NoMatchPage } from "../pages/NoMatchPage"; +import { Workflows } from "../pages/Workflows/"; +import { CloudEventFormPage } from "../pages/Workflows/CloudEventFormPage"; +import { WorkflowFormPage } from "../pages/Workflows/WorkflowFormPage"; +import { routes } from "../routes"; +import { RuntimeToolsRoutesSwitch } from "./RuntimeToolsRoutesSwitch"; + +export function RoutesSwitch() { + return ( + <Switch> + <Route path={routes.workflows.form.path({ workflowId: ":workflowId" })}> + {({ match }) => <WorkflowFormPage workflowId={match!.params.workflowId!} />} + </Route> + <Route path={routes.workflows.cloudEvent.path({})}> + <CloudEventFormPage /> + </Route> + <Route path={routes.workflows.home.path({})}> + <Workflows /> + </Route> + <Route path={routes.runtimeTools.home.path({})}> + <RuntimeToolsRoutesSwitch /> + </Route> + <Route path={routes.dataJsonError.path({})}> + <ErrorPage kind={ErrorKind.APPDATA_JSON} errors={[`There was an error with the ${APPDATA_JSON_FILENAME}`]} /> + </Route> + <Route path={routes.home.path({})}> + <Redirect to={routes.workflows.home.path({})} /> + </Route> + <Route component={NoMatchPage} /> + </Switch> + ); +} diff --git a/packages/sonataflow-deployment-webapp/src/data/index.ts b/packages/sonataflow-deployment-webapp/src/navigation/RuntimeToolsRoutesSwitch.tsx similarity index 54% copy from packages/sonataflow-deployment-webapp/src/data/index.ts copy to packages/sonataflow-deployment-webapp/src/navigation/RuntimeToolsRoutesSwitch.tsx index 64a598819a..9651c00d03 100644 --- a/packages/sonataflow-deployment-webapp/src/data/index.ts +++ b/packages/sonataflow-deployment-webapp/src/navigation/RuntimeToolsRoutesSwitch.tsx @@ -16,15 +16,21 @@ * specific language governing permissions and limitations * under the License. */ - +import React from "react"; +import { Route, Switch } from "react-router-dom"; +import { RuntimeToolsWorkflowInstances } from "../runtimeTools/workflows/RuntimeToolsWorkflowInstances"; import { routes } from "../routes"; +import { RuntimeToolsWorkflowDetails } from "../runtimeTools/workflows/RuntimeToolsWorkflowDetails"; -export interface AppData { - appName: string; - showDisclaimer: boolean; -} - -export async function fetchAppData(): Promise<AppData> { - const response = await fetch(routes.dataJson.path({})); - return (await response.json()) as AppData; +export function RuntimeToolsRoutesSwitch() { + return ( + <Switch> + <Route path={routes.runtimeTools.workflowInstances.path({})}> + <RuntimeToolsWorkflowInstances /> + </Route> + <Route path={routes.runtimeTools.workflowDetails.path({ workflowId: ":workflowId" })}> + {({ match }) => <RuntimeToolsWorkflowDetails workflowId={match!.params.workflowId!} />} + </Route> + </Switch> + ); } diff --git a/packages/sonataflow-deployment-webapp/src/pages/BasePage.tsx b/packages/sonataflow-deployment-webapp/src/pages/BasePage.tsx index c0d555855d..cd722b1eb3 100644 --- a/packages/sonataflow-deployment-webapp/src/pages/BasePage.tsx +++ b/packages/sonataflow-deployment-webapp/src/pages/BasePage.tsx @@ -34,7 +34,7 @@ import { Tooltip } from "@patternfly/react-core/dist/js/components/Tooltip"; import { BarsIcon } from "@patternfly/react-icons/dist/js/icons"; import HelpIcon from "@patternfly/react-icons/dist/js/icons/help-icon"; import * as React from "react"; -import { useMemo } from "react"; +import { useMemo, useEffect } from "react"; import { useHistory } from "react-router"; import { useApp } from "../context/AppContext"; import { routes } from "../routes"; @@ -102,6 +102,12 @@ export function BasePage(props: { children?: React.ReactNode }) { [app.data.appName, history, app.appDataPromise.status, app.data.showDisclaimer] ); + useEffect(() => { + if (app.appDataPromise.status === PromiseStateStatus.REJECTED) { + history.replace(routes.dataJsonError.path({})); + } + }, [history, app.appDataPromise]); + return ( <Page sidebar={<PageSidebar nav={<BasePageNav />} theme="dark" />} header={masthead} isManagedSidebar> {props.children} diff --git a/packages/sonataflow-deployment-webapp/src/pages/ErrorPage.tsx b/packages/sonataflow-deployment-webapp/src/pages/ErrorPage.tsx index 866f110071..2286584f13 100644 --- a/packages/sonataflow-deployment-webapp/src/pages/ErrorPage.tsx +++ b/packages/sonataflow-deployment-webapp/src/pages/ErrorPage.tsx @@ -29,10 +29,17 @@ import { WorkflowDefinition } from "@kie-tools/runtime-tools-gateway-api/dist/ty import { routes } from "../routes"; import { BasePage } from "./BasePage"; +export enum ErrorKind { + APPDATA_JSON = "AppDataJson", + OPENAPI = "OpenApi", + WORKFLOW = "Workflow", +} + export type ErrorPageProps = { errors: string[] } & ( - | { kind: "OpenApi" } + | { kind: ErrorKind.APPDATA_JSON } + | { kind: ErrorKind.OPENAPI } | { - kind: "Workflow"; + kind: ErrorKind.WORKFLOW; workflowId: WorkflowDefinition["workflowName"]; } ); @@ -43,17 +50,17 @@ export function ErrorPage(props: ErrorPageProps) { const errorDetails = useMemo(() => props.errors.filter(Boolean).join("\n"), [props.errors]); const title = useMemo(() => { - if (props.kind === "Workflow") { + if (props.kind === ErrorKind.WORKFLOW) { return "Cannot open workflow"; } return "Cannot open the requested page"; }, [props.kind]); const description = useMemo(() => { - if (props.kind === "OpenApi") { + if (props.kind === ErrorKind.APPDATA_JSON || props.kind === ErrorKind.OPENAPI) { return `There was an error contacting the server.`; } - if (props.kind === "Workflow") { + if (props.kind === ErrorKind.WORKFLOW) { return `There was an error opening the workflow with name "${props.workflowId}".`; } return "There was an error opening the requested page."; diff --git a/packages/sonataflow-deployment-webapp/src/pages/Workflows/CloudEventFormPage.tsx b/packages/sonataflow-deployment-webapp/src/pages/Workflows/CloudEventFormPage.tsx index f224296b8a..a67aabdbfa 100644 --- a/packages/sonataflow-deployment-webapp/src/pages/Workflows/CloudEventFormPage.tsx +++ b/packages/sonataflow-deployment-webapp/src/pages/Workflows/CloudEventFormPage.tsx @@ -32,7 +32,7 @@ import { useOpenApi } from "../../context/OpenApiContext"; import { CloudEventFormGatewayApiImpl } from "../../impl/CloudEventFormGatewayApiImpl"; import { routes } from "../../routes"; import { BasePage } from "../BasePage"; -import { ErrorPage } from "../ErrorPage"; +import { ErrorKind, ErrorPage } from "../ErrorPage"; import { CloudEventFormDefaultValues } from "@kie-tools/runtime-tools-enveloped-components/dist/cloudEventForm"; import { CloudEventFormDriver } from "@kie-tools/runtime-tools-enveloped-components/dist/cloudEventForm/api/CloudEventFormDriver"; @@ -131,14 +131,14 @@ export function CloudEventFormPage() { ); if (openApi.openApiPromise.status === PromiseStateStatus.REJECTED) { - return <ErrorPage kind="OpenApi" errors={["OpenAPI service not available"]} />; + return <ErrorPage kind={ErrorKind.OPENAPI} errors={["OpenAPI service not available"]} />; } return ( <BasePage> <PageSection variant={"light"} title="Start New Workflow"> <TextContent> - <Text component={TextVariants.h1}>Start New Workflow</Text> + <Text component={TextVariants.h1}>Trigger Cloud Event</Text> </TextContent> {notification && ( <div> diff --git a/packages/sonataflow-deployment-webapp/src/pages/Workflows/WorkflowFormPage.tsx b/packages/sonataflow-deployment-webapp/src/pages/Workflows/WorkflowFormPage.tsx index 960326f15e..efa45de98e 100644 --- a/packages/sonataflow-deployment-webapp/src/pages/Workflows/WorkflowFormPage.tsx +++ b/packages/sonataflow-deployment-webapp/src/pages/Workflows/WorkflowFormPage.tsx @@ -18,7 +18,11 @@ */ import React from "react"; import { PromiseStateStatus } from "@kie-tools-core/react-hooks/dist/PromiseState"; -import { FormNotification, Notification } from "@kie-tools/runtime-tools-components/dist/components/FormNotification"; +import { + Action, + FormNotification, + Notification, +} from "@kie-tools/runtime-tools-components/dist/components/FormNotification"; import { WorkflowFormDriver } from "@kie-tools/runtime-tools-enveloped-components/dist/workflowForm/api/WorkflowFormDriver"; import CustomWorkflowForm from "@kie-tools/runtime-tools-enveloped-components/dist/workflowForm/envelope/components/CustomWorkflowForm/CustomWorkflowForm"; import WorkflowForm from "@kie-tools/runtime-tools-enveloped-components/dist/workflowForm/envelope/components/WorkflowForm/WorkflowForm"; @@ -36,13 +40,15 @@ import { useOpenApi } from "../../context/OpenApiContext"; import { WorkflowFormGatewayApiImpl } from "../../impl/WorkflowFormGatewayApiImpl"; import { routes } from "../../routes"; import { BasePage } from "../BasePage"; -import { ErrorPage } from "../ErrorPage"; +import { ErrorKind, ErrorPage } from "../ErrorPage"; +import { useApp } from "../../context/AppContext"; export function WorkflowFormPage(props: { workflowId: string }) { const [notification, setNotification] = useState<Notification>(); const [workflowResponse, setWorkflowResponse] = useState<WorkflowResponse>(); const openApi = useOpenApi(); const [customFormSchema, setCustomFormSchema] = useState<Record<string, any>>(); + const app = useApp(); const history = useHistory(); const gatewayApi = useMemo( () => @@ -63,34 +69,56 @@ export function WorkflowFormPage(props: { workflowId: string }) { history.push(routes.workflows.home.path({})); }, [history]); + const openWorkflowInstance = useCallback( + (id: string) => { + history.push({ + pathname: routes.runtimeTools.workflowDetails.path({ workflowId: id }), + }); + }, + [history] + ); + const showNotification = useCallback( - (notificationType: "error" | "success", submitMessage: string, notificationDetails?: string) => { + ( + notificationType: "error" | "success", + submitMessage: string, + notificationDetails?: string, + customActions?: Action[] + ) => { setNotification({ type: notificationType, message: submitMessage, details: notificationDetails, - customActions: [ - { - label: "Go to workflow list", - onClick: () => { - setNotification(undefined); - goToWorkflowList(); - }, - }, - ], + customActions, close: () => { setNotification(undefined); }, }); }, - [goToWorkflowList] + [] ); const onSubmitSuccess = useCallback( - (message: string): void => { - showNotification("success", message); + (message: string, id: string): void => { + showNotification("success", message, undefined, [ + !app.dataIndexAvailable + ? { + label: "Go to workflow list", + onClick: () => { + setNotification(undefined); + goToWorkflowList(); + }, + } + : { + label: "View details", + onClick: () => { + setNotification(undefined); + openWorkflowInstance(id); + }, + }, + ]); }, - [showNotification] + [showNotification, openWorkflowInstance, goToWorkflowList, app] ); const onSubmitError = useCallback( @@ -111,7 +139,7 @@ export function WorkflowFormPage(props: { workflowId: string }) { return gatewayApi ?.startWorkflow(endpoint, data) .then((response: WorkflowResponse) => { - onSubmitSuccess(`A workflow with id ${response.id} was triggered successfully.`); + onSubmitSuccess(`A workflow with id ${response.id} was started successfully.`, response.id); setWorkflowResponse(response); }) .catch((error) => { @@ -137,19 +165,25 @@ export function WorkflowFormPage(props: { workflowId: string }) { }, [gatewayApi, props.workflowId]); if (openApi.openApiPromise.status === PromiseStateStatus.REJECTED) { - return <ErrorPage kind="OpenApi" errors={["OpenAPI service not available"]} />; + return <ErrorPage kind={ErrorKind.OPENAPI} errors={["OpenAPI service not available"]} />; } if ( openApi.openApiPromise.status === PromiseStateStatus.RESOLVED && !openApi.openApiData?.tags?.find((t) => t.name === workflowDefinition.workflowName) ) { - return <ErrorPage kind="Workflow" workflowId={workflowDefinition.workflowName} errors={["Workflow not found"]} />; + return ( + <ErrorPage + kind={ErrorKind.WORKFLOW} + workflowId={workflowDefinition.workflowName} + errors={["Workflow not found"]} + /> + ); } return ( <BasePage> - <PageSection variant={"light"} title="Start New Workflow"> + <PageSection variant={"light"}> <TextContent> <Text component={TextVariants.h1}>Start New Workflow</Text> </TextContent> diff --git a/packages/sonataflow-deployment-webapp/src/pages/Workflows/Workflows.tsx b/packages/sonataflow-deployment-webapp/src/pages/Workflows/Workflows.tsx index 8a2aea5516..f6378bd3e7 100644 --- a/packages/sonataflow-deployment-webapp/src/pages/Workflows/Workflows.tsx +++ b/packages/sonataflow-deployment-webapp/src/pages/Workflows/Workflows.tsx @@ -35,13 +35,13 @@ import { useOpenApi } from "../../context/OpenApiContext"; import { BasePage } from "../BasePage"; import { Link } from "react-router-dom"; import { routes } from "../../routes"; -import { ErrorPage } from "../ErrorPage"; +import { ErrorKind, ErrorPage } from "../ErrorPage"; export function Workflows() { const openApi = useOpenApi(); if (openApi.openApiPromise.status === PromiseStateStatus.REJECTED) { - return <ErrorPage kind="OpenApi" errors={["OpenAPI service not available"]} />; + return <ErrorPage kind={ErrorKind.OPENAPI} errors={["OpenAPI service not available"]} />; } return ( diff --git a/packages/sonataflow-deployment-webapp/src/pages/basePage/BasePageNav.tsx b/packages/sonataflow-deployment-webapp/src/pages/basePage/BasePageNav.tsx index c7c5d82422..154eabd7b2 100644 --- a/packages/sonataflow-deployment-webapp/src/pages/basePage/BasePageNav.tsx +++ b/packages/sonataflow-deployment-webapp/src/pages/basePage/BasePageNav.tsx @@ -44,6 +44,20 @@ export function BasePageNav() { <Link to={routes.home.path({})}>Workflows</Link> </NavItem> + <NavItem + itemId={0} + key={"RuntimeToolsWorkflowInstances-nav"} + isActive={ + location.pathname === routes.runtimeTools.workflowInstances.path({}) || + matchPath(location.pathname, { + path: routes.runtimeTools.workflowDetails.path({ workflowId: ":workflowId" }), + })?.isExact + } + ouiaId="runtime-tools-workflow-instances-nav" + > + <Link to={routes.runtimeTools.workflowInstances.path({})}>Workflow Instances</Link> + </NavItem> + <NavItem itemId={3} key={"OpenApi-nav"} diff --git a/packages/sonataflow-deployment-webapp/src/routes/index.ts b/packages/sonataflow-deployment-webapp/src/routes/index.ts index 193186f8ab..efd867497c 100644 --- a/packages/sonataflow-deployment-webapp/src/routes/index.ts +++ b/packages/sonataflow-deployment-webapp/src/routes/index.ts @@ -17,9 +17,16 @@ * under the License. */ +import { APPDATA_JSON_FILENAME } from "../AppConstants"; + const IS_HASH_ROUTER = true; +const WORKFLOWS_ROUTE = "/workflows"; +const RUNTIME_TOOLS_ROUTE = "/runtime-tools"; -export enum QueryParams {} +export enum QueryParams { + FILTERS = "filters", + SORT_BY = "sortBy", +} export enum PathParams { WORKFLOW_ID = "workflowId", @@ -79,9 +86,9 @@ export interface QueryParamsImpl<Q extends string> { toString(): string; } -const WORKFLOWS_ROUTE = "/workflows"; export const routes = { home: new Route<{}>(() => "/"), + dataJsonError: new Route<{}>(() => "/data-json-error"), workflows: { home: new Route<{}>(() => WORKFLOWS_ROUTE), @@ -91,6 +98,15 @@ export const routes = { cloudEvent: new Route<{}>(() => `/triggerCloudEvent`), }, - dataJson: new Route<{}>(() => "/sonataflow-deployment-webapp-data.json"), + runtimeTools: { + home: new Route<{}>(() => RUNTIME_TOOLS_ROUTE), + workflowInstances: new Route<{}>(() => RUNTIME_TOOLS_ROUTE + `/workflow-instances`), + workflowDetails: new Route<{ + queryParams: QueryParams.FILTERS | QueryParams.SORT_BY; + pathParams: PathParams.WORKFLOW_ID; + }>(({ workflowId }) => RUNTIME_TOOLS_ROUTE + `/workflow-details/${workflowId}`), + }, + + dataJson: new Route<{}>(() => "/" + APPDATA_JSON_FILENAME), openApiJson: new Route<{}>(() => "/q/openapi.json"), }; diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/RuntimeToolsWorkflowDetails.tsx b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/RuntimeToolsWorkflowDetails.tsx new file mode 100644 index 0000000000..db43544290 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/RuntimeToolsWorkflowDetails.tsx @@ -0,0 +1,106 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useEffect, useState } from "react"; +import { PageSection } from "@patternfly/react-core/dist/js/components/Page"; +import { Text, TextContent, TextVariants } from "@patternfly/react-core/dist/js/components/Text"; +import { ouiaPageTypeAndObjectId } from "@kie-tools/runtime-tools-components/dist/ouiaTools"; +import { useWorkflowDetailsGatewayApi, WorkflowDetailsGatewayApi } from "./WorkflowDetails"; +import WorkflowDetailsContainer from "./WorkflowDetailsContainer/WorkflowDetailsContainer"; +import { WorkflowInstance } from "@kie-tools/runtime-tools-gateway-api/dist/types"; +import { ServerErrors } from "@kie-tools/runtime-tools-components/dist/components/ServerErrors"; +import { KogitoSpinner } from "@kie-tools/runtime-tools-components/dist/components/KogitoSpinner"; +import { Card } from "@patternfly/react-core/dist/js/components/Card"; +import { Bullseye } from "@patternfly/react-core/dist/js/layouts/Bullseye"; +import { BasePage } from "../../pages/BasePage"; +import { useApp } from "../../context/AppContext"; + +const PAGE_TITLE = "Workflow Details"; + +interface WorkflowListContainerProps { + workflowId: string; +} +export function RuntimeToolsWorkflowDetails(props: WorkflowListContainerProps) { + const gatewayApi: WorkflowDetailsGatewayApi = useWorkflowDetailsGatewayApi(); + const app = useApp(); + + const [workflowInstance, setWorkflowInstance] = useState<WorkflowInstance>({} as WorkflowInstance); + const [isLoading, setIsLoading] = useState<boolean>(false); + const [fetchError, setFetchError] = useState<string>(""); + + useEffect(() => { + gatewayApi + .workflowDetailsQuery(props.workflowId) + .then((response) => { + if (!response) { + return setFetchError(`There was an error opening the instance with id "${props.workflowId}".`); + } + setWorkflowInstance(response); + }) + .catch((error) => { + setFetchError(error); + }) + .finally(() => { + setIsLoading(false); + }); + }, [gatewayApi, props.workflowId]); + + useEffect(() => { + return ouiaPageTypeAndObjectId("workflow-details"); + }); + + return ( + <> + <BasePage> + <PageSection variant={"light"}> + <TextContent> + <Text component={TextVariants.h1}>{PAGE_TITLE}</Text> + <Text component={TextVariants.p}> + Explore the execution status, details, timeline and variables of a workflow instance. + <br /> + Your Data Index URL is: {app.fullDataIndexUrl} + </Text> + </TextContent> + </PageSection> + + <PageSection isFilled aria-label="workflow-details-section"> + {isLoading && ( + <Card> + <KogitoSpinner spinnerText="Loading workflow details..." /> + </Card> + )} + + {!isLoading && workflowInstance && Object.keys(workflowInstance).length > 0 && !fetchError ? ( + <WorkflowDetailsContainer workflowInstance={workflowInstance} /> + ) : ( + <> + {fetchError.length > 0 && ( + <Card className="kogito-management-console__card-size"> + <Bullseye> + <ServerErrors error={fetchError} variant="large" /> + </Bullseye> + </Card> + )} + </> + )} + </PageSection> + </BasePage> + </> + ); +} diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/RuntimeToolsWorkflowInstances.tsx b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/RuntimeToolsWorkflowInstances.tsx new file mode 100644 index 0000000000..58a44df63a --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/RuntimeToolsWorkflowInstances.tsx @@ -0,0 +1,88 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +import React, { useMemo } from "react"; +import { EmptyState, EmptyStateBody, EmptyStateIcon } from "@patternfly/react-core/dist/js/components/EmptyState"; +import { PageSection } from "@patternfly/react-core/dist/js/components/Page"; +import { Text, TextContent, TextVariants } from "@patternfly/react-core/dist/js/components/Text"; +import { Title } from "@patternfly/react-core/dist/js/components/Title"; +import { Bullseye } from "@patternfly/react-core/dist/js/layouts/Bullseye"; +import { CubesIcon } from "@patternfly/react-icons/dist/js/icons"; +import { useHistory } from "react-router"; +import { useApp } from "../../context/AppContext"; +import { WorkflowListState } from "./WorkflowList/WorkflowListGatewayApi"; +import WorkflowListContainer from "./WorkflowListContainer/WorkflowListContainer"; +import { BasePage } from "../../pages/BasePage"; +import { SONATAFLOW_DEPLOYMENT_DATAINDEX_DOCUMENTATION_URL } from "../../AppConstants"; + +const PAGE_TITLE = "Workflow Instances"; + +export function RuntimeToolsWorkflowInstances() { + const history = useHistory(); + const app = useApp(); + const initialState: WorkflowListState = history.location && (history.location.state as WorkflowListState); + + const dataIndexNotAvailable = useMemo( + () => ( + <PageSection variant="light"> + <Bullseye> + <EmptyState> + <EmptyStateIcon icon={CubesIcon} /> + <Title headingLevel="h4" size="lg"> + {`Data Index service not available`} + </Title> + <EmptyStateBody> + <TextContent> + <Text> + Read more on + <a href={SONATAFLOW_DEPLOYMENT_DATAINDEX_DOCUMENTATION_URL} target="_blank" rel="noopener noreferrer"> + SonataFlow Guides. + </a> + </Text> + </TextContent> + </EmptyStateBody> + </EmptyState> + </Bullseye> + </PageSection> + ), + [] + ); + + return ( + <BasePage> + <PageSection variant={"light"}> + <TextContent> + <Text component={TextVariants.h1}>{PAGE_TITLE}</Text> + <Text component={TextVariants.p}> + List and view workflows from the Data Index linked in your Runtime Tools settings. + <br /> + Your Data Index URL is: {app.fullDataIndexUrl} + </Text> + </TextContent> + </PageSection> + + <PageSection isFilled aria-label="workflow-instances-section"> + {app.dataIndexAvailable === false ? ( + dataIndexNotAvailable + ) : ( + <WorkflowListContainer initialState={initialState} /> + )} + </PageSection> + </BasePage> + ); +} diff --git a/packages/sonataflow-deployment-webapp/src/data/index.ts b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsContext.ts similarity index 72% copy from packages/sonataflow-deployment-webapp/src/data/index.ts copy to packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsContext.ts index 64a598819a..3d121100b6 100644 --- a/packages/sonataflow-deployment-webapp/src/data/index.ts +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsContext.ts @@ -17,14 +17,11 @@ * under the License. */ -import { routes } from "../routes"; +import React, { useContext } from "react"; +import { WorkflowDetailsGatewayApi } from "./WorkflowDetailsGatewayApi"; -export interface AppData { - appName: string; - showDisclaimer: boolean; -} +export const WorkflowDetailsContext = React.createContext<WorkflowDetailsGatewayApi>({} as any); -export async function fetchAppData(): Promise<AppData> { - const response = await fetch(routes.dataJson.path({})); - return (await response.json()) as AppData; +export function useWorkflowDetailsGatewayApi() { + return useContext(WorkflowDetailsContext); } diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsContextProvider.tsx b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsContextProvider.tsx new file mode 100644 index 0000000000..1c1da6aa64 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsContextProvider.tsx @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useMemo } from "react"; +import { ApolloClient } from "apollo-client"; +import { WorkflowDetailsContext } from "./WorkflowDetailsContext"; +import { WorkflowDetailsGatewayApiImpl } from "./WorkflowDetailsGatewayApi"; +import { GraphQLWorkflowDetailsQueries } from "./WorkflowDetailsQueries"; +import { HttpLink } from "apollo-link-http"; +import { InMemoryCache, NormalizedCacheObject } from "apollo-cache-inmemory"; +import { useApp } from "../../../context/AppContext"; + +export function WorkflowDetailsContextProvider(props: React.PropsWithChildren<{}>) { + const app = useApp(); + + const httpLink = useMemo( + () => + new HttpLink({ + uri: app.data.dataIndexUrl, + }), + [app] + ); + + const cache = useMemo(() => new InMemoryCache(), []); + + const apolloClient: ApolloClient<NormalizedCacheObject> = useMemo( + () => + new ApolloClient({ + cache, + link: httpLink, + }), + [cache, httpLink] + ); + + const gatewayApiImpl = useMemo(() => { + return new WorkflowDetailsGatewayApiImpl(new GraphQLWorkflowDetailsQueries(apolloClient)); + }, [apolloClient]); + + return <WorkflowDetailsContext.Provider value={gatewayApiImpl}>{props.children}</WorkflowDetailsContext.Provider>; +} diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsGatewayApi.ts b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsGatewayApi.ts new file mode 100644 index 0000000000..2d3dfb8905 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsGatewayApi.ts @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + WorkflowInstance, + Job, + JobCancel, + TriggerableNode, + NodeInstance, +} from "@kie-tools/runtime-tools-gateway-api/dist/types"; +import { WorkflowDetailsQueries } from "./WorkflowDetailsQueries"; + +export interface OnOpenWorkflowInstanceDetailsListener { + onOpen(id: string): void; +} + +export interface WorkflowDetailsUnSubscribeHandler { + unSubscribe: () => void; +} + +export interface WorkflowDetailsState { + id: string; +} + +export interface WorkflowDetailsGatewayApi { + workflowDetailsState: any; + handleWorkflowAbort: (workflowInstance: WorkflowInstance) => Promise<void>; + cancelJob: (job: Job) => Promise<JobCancel>; + rescheduleJob: ( + job: Job, + repeatInterval: number | string, + repeatLimit: number | string, + scheduleDate: Date + ) => Promise<{ modalTitle: string; modalContent: string }>; + getTriggerableNodes(workflowInstance: WorkflowInstance): Promise<TriggerableNode[]>; + handleNodeTrigger(workflowInstance: WorkflowInstance, node: any): Promise<void>; + handleWorkflowVariableUpdate: ( + workflowInstance: WorkflowInstance, + updateJson: Record<string, unknown> + ) => Promise<Record<string, unknown>>; + workflowDetailsQuery(id: string): Promise<WorkflowInstance>; + jobsQuery(id: string): Promise<Job[]>; + openWorkflowInstanceDetails(id: string): Promise<void>; + onOpenWorkflowInstanceDetailsListener: ( + listener: OnOpenWorkflowInstanceDetailsListener + ) => WorkflowDetailsUnSubscribeHandler; + handleWorkflowRetry: (workflowInstance: WorkflowInstance) => Promise<void>; + handleNodeInstanceCancel: (workflowInstance: WorkflowInstance, node: NodeInstance) => Promise<void>; + handleWorkflowSkip: (workflowInstance: WorkflowInstance) => Promise<void>; + handleNodeInstanceRetrigger(workflowInstance: WorkflowInstance, node: NodeInstance): Promise<void>; +} + +export class WorkflowDetailsGatewayApiImpl implements WorkflowDetailsGatewayApi { + private readonly queries: WorkflowDetailsQueries; + private _WorkflowDetailsState: WorkflowDetailsState; + private readonly listeners: OnOpenWorkflowInstanceDetailsListener[] = []; + + constructor(queries: WorkflowDetailsQueries) { + this.queries = queries; + this._WorkflowDetailsState = { id: "" }; + } + + get workflowDetailsState(): WorkflowDetailsState { + return this._WorkflowDetailsState; + } + + handleWorkflowAbort = (workflowInstance: WorkflowInstance): Promise<void> => { + return this.queries.handleWorkflowAbort(workflowInstance); + }; + + cancelJob = (job: Job): Promise<JobCancel> => { + return this.queries.jobCancel(job); + }; + + rescheduleJob = ( + job: Job, + repeatInterval: number | string, + repeatLimit: number | string, + scheduleDate: Date + ): Promise<{ modalTitle: string; modalContent: string }> => { + return this.queries.rescheduleJob(job, repeatInterval, repeatLimit, scheduleDate); + }; + + getTriggerableNodes(workflowInstance: WorkflowInstance): Promise<TriggerableNode[]> { + return this.queries.getTriggerableNodes(workflowInstance); + } + + handleNodeTrigger(workflowInstance: WorkflowInstance, node: TriggerableNode): Promise<void> { + return this.queries.handleNodeTrigger(workflowInstance, node); + } + + handleWorkflowVariableUpdate = (workflowInstance: WorkflowInstance, updatedJson: Record<string, unknown>) => { + return this.queries.handleWorkflowVariableUpdate(workflowInstance, updatedJson); + }; + + workflowDetailsQuery(id: string): Promise<WorkflowInstance> { + return new Promise<any>((resolve, reject) => { + this.queries + .getWorkflowDetails(id) + .then((value: WorkflowInstance) => { + resolve(value); + }) + .catch((reason: any) => { + reject(reason); + }); + }); + } + + jobsQuery(id: string): Promise<Job[]> { + return new Promise<any>((resolve, reject) => { + this.queries + .getJobs(id) + .then((value: Job[]) => { + resolve(value); + }) + .catch((reason: any) => { + reject(reason); + }); + }); + } + + openWorkflowInstanceDetails(id: string): Promise<void> { + this._WorkflowDetailsState = { id: id }; + this.listeners.forEach((listener) => listener.onOpen(id)); + return Promise.resolve(); + } + + onOpenWorkflowInstanceDetailsListener( + listener: OnOpenWorkflowInstanceDetailsListener + ): WorkflowDetailsUnSubscribeHandler { + this.listeners.push(listener); + + const unSubscribe = () => { + const index = this.listeners.indexOf(listener); + if (index > -1) { + this.listeners.splice(index, 1); + } + }; + + return { + unSubscribe, + }; + } + + handleWorkflowRetry(workflowInstance: WorkflowInstance): Promise<void> { + return this.queries.handleWorkflowRetry(workflowInstance); + } + + handleNodeInstanceCancel(workflowInstance: WorkflowInstance, node: NodeInstance): Promise<void> { + return this.queries.handleNodeInstanceCancel(workflowInstance, node); + } + + handleWorkflowSkip(workflowInstance: WorkflowInstance): Promise<void> { + return this.queries.handleWorkflowSkip(workflowInstance); + } + + handleNodeInstanceRetrigger(workflowInstance: WorkflowInstance, node: NodeInstance): Promise<void> { + return this.queries.handleNodeInstanceRetrigger(workflowInstance, node); + } +} diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsQueries.ts b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsQueries.ts new file mode 100644 index 0000000000..be93174c72 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/WorkflowDetailsQueries.ts @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ApolloClient } from "apollo-client"; +import { + WorkflowInstance, + Job, + JobCancel, + TriggerableNode, + NodeInstance, +} from "@kie-tools/runtime-tools-gateway-api/dist/types"; +import { + handleWorkflowAbort, + handleWorkflowSkip, + handleWorkflowRetry, + jobCancel, + handleJobReschedule, + handleNodeTrigger, + handleWorkflowVariableUpdate, + handleNodeInstanceCancel, + handleNodeInstanceRetrigger, + getWorkflowDetails, + getJobs, + getTriggerableNodes, +} from "@kie-tools/runtime-tools-gateway-api/dist/gatewayApi"; + +export interface WorkflowDetailsQueries { + getWorkflowDetails(id: string): Promise<WorkflowInstance>; + getJobs(id: string): Promise<Job[]>; + handleWorkflowSkip(workflowInstance: WorkflowInstance): Promise<void>; + handleWorkflowAbort(workflowInstance: WorkflowInstance): Promise<void>; + handleWorkflowRetry(workflowInstance: WorkflowInstance): Promise<void>; + jobCancel(job: Job): Promise<JobCancel>; + rescheduleJob: ( + job: Job, + repeatInterval: number | string, + repeatLimit: number | string, + scheduleDate: Date + ) => Promise<{ modalTitle: string; modalContent: string }>; + getTriggerableNodes(workflowInstance: WorkflowInstance): Promise<TriggerableNode[]>; + handleNodeTrigger(workflowInstance: WorkflowInstance, node: any): Promise<void>; + handleWorkflowVariableUpdate: ( + workflowInstance: WorkflowInstance, + updateJson: Record<string, unknown> + ) => Promise<Record<string, unknown>>; + handleNodeInstanceCancel: (workflowInstance: WorkflowInstance, node: NodeInstance) => Promise<void>; + handleNodeInstanceRetrigger(workflowInstance: WorkflowInstance, node: NodeInstance): Promise<void>; +} + +export class GraphQLWorkflowDetailsQueries implements WorkflowDetailsQueries { + private readonly client: ApolloClient<any>; + + constructor(client: ApolloClient<any>) { + this.client = client; + } + + async getWorkflowDetails(id: string): Promise<WorkflowInstance> { + return getWorkflowDetails(id, this.client); + } + + async getJobs(id: string): Promise<Job[]> { + return Promise.resolve(getJobs(id, this.client)); + } + + async handleWorkflowSkip(workflowInstance: WorkflowInstance): Promise<void> { + return handleWorkflowSkip(workflowInstance, this.client); + } + + async handleWorkflowAbort(workflowInstance: WorkflowInstance): Promise<void> { + return handleWorkflowAbort(workflowInstance, this.client); + } + + async handleWorkflowRetry(workflowInstance: WorkflowInstance): Promise<void> { + return handleWorkflowRetry(workflowInstance, this.client); + } + + async jobCancel(job: Job): Promise<JobCancel> { + return jobCancel(job, this.client); + } + + async rescheduleJob( + job: Job, + repeatInterval: number | string, + repeatLimit: number | string, + scheduleDate: Date + ): Promise<{ modalTitle: string; modalContent: string }> { + return handleJobReschedule(job, repeatInterval, repeatLimit, scheduleDate, this.client); + } + + async getTriggerableNodes(workflowInstance: WorkflowInstance): Promise<TriggerableNode[]> { + return Promise.resolve(getTriggerableNodes(workflowInstance, this.client)); + } + + async handleNodeTrigger(workflowInstance: WorkflowInstance, node: any): Promise<void> { + return handleNodeTrigger(workflowInstance, node, this.client); + } + + async handleNodeInstanceCancel(workflowInstance: WorkflowInstance, node: NodeInstance): Promise<void> { + return handleNodeInstanceCancel(workflowInstance, node, this.client); + } + + async handleWorkflowVariableUpdate( + workflowInstance: WorkflowInstance, + updateJson: Record<string, unknown> + ): Promise<Record<string, unknown>> { + return handleWorkflowVariableUpdate(workflowInstance, updateJson, this.client); + } + + async handleNodeInstanceRetrigger(workflowInstance: WorkflowInstance, node: NodeInstance): Promise<void> { + return handleNodeInstanceRetrigger(workflowInstance, node, this.client); + } +} diff --git a/packages/sonataflow-deployment-webapp/src/index.tsx b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/index.ts similarity index 76% copy from packages/sonataflow-deployment-webapp/src/index.tsx copy to packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/index.ts index 47689759b5..e70ebd3e78 100644 --- a/packages/sonataflow-deployment-webapp/src/index.tsx +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetails/index.ts @@ -17,10 +17,6 @@ * under the License. */ -import "@patternfly/react-core/dist/styles/base.css"; -import * as React from "react"; -import * as ReactDOM from "react-dom"; -import { App } from "./App"; -import "../static/resources/style.css"; - -ReactDOM.render(<App />, document.getElementById("app")); +export { WorkflowDetailsGatewayApi } from "./WorkflowDetailsGatewayApi"; +export * from "./WorkflowDetailsContext"; +export { WorkflowDetailsContextProvider } from "./WorkflowDetailsContextProvider"; diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetailsContainer/WorkflowDetailsContainer.tsx b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetailsContainer/WorkflowDetailsContainer.tsx new file mode 100644 index 0000000000..ba3e1c8dcc --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowDetailsContainer/WorkflowDetailsContainer.tsx @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useEffect } from "react"; +import { useHistory } from "react-router-dom"; +import { componentOuiaProps, OUIAProps } from "@kie-tools/runtime-tools-components/dist/ouiaTools"; +import { EmbeddedWorkflowDetails } from "@kie-tools/runtime-tools-enveloped-components/dist/workflowDetails/embedded"; +import { WorkflowDetailsGatewayApi, useWorkflowDetailsGatewayApi } from "../WorkflowDetails"; +import { WorkflowInstance } from "@kie-tools/runtime-tools-gateway-api/dist/types"; +import { routes } from "../../../routes"; + +interface WorkflowListContainerProps { + workflowInstance: WorkflowInstance; +} + +const WorkflowDetailsContainer: React.FC<WorkflowListContainerProps & OUIAProps> = ({ + workflowInstance, + ouiaId, + ouiaSafe, +}) => { + const history = useHistory(); + const gatewayApi: WorkflowDetailsGatewayApi = useWorkflowDetailsGatewayApi(); + + useEffect(() => { + const unSubscribeHandler = gatewayApi.onOpenWorkflowInstanceDetailsListener({ + onOpen(id: string) { + history.push(`/`); + history.push(routes.runtimeTools.workflowDetails.path({ workflowId: id })); + }, + }); + return () => { + unSubscribeHandler.unSubscribe(); + }; + }, []); + + return ( + <EmbeddedWorkflowDetails + {...componentOuiaProps(ouiaId, "workflow-list-container", ouiaSafe)} + driver={gatewayApi} + targetOrigin={window.location.origin} + workflowInstance={workflowInstance} + /> + ); +}; + +export default WorkflowDetailsContainer; diff --git a/packages/sonataflow-deployment-webapp/src/data/index.ts b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListContext.ts similarity index 73% copy from packages/sonataflow-deployment-webapp/src/data/index.ts copy to packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListContext.ts index 64a598819a..03ddf6f790 100644 --- a/packages/sonataflow-deployment-webapp/src/data/index.ts +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListContext.ts @@ -17,14 +17,11 @@ * under the License. */ -import { routes } from "../routes"; +import React, { useContext } from "react"; +import { WorkflowListGatewayApi } from "./WorkflowListGatewayApi"; -export interface AppData { - appName: string; - showDisclaimer: boolean; -} +export const WorkflowListContext = React.createContext<WorkflowListGatewayApi>({} as any); -export async function fetchAppData(): Promise<AppData> { - const response = await fetch(routes.dataJson.path({})); - return (await response.json()) as AppData; +export function useWorkflowListGatewayApi() { + return useContext(WorkflowListContext); } diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListContextProvider.tsx b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListContextProvider.tsx new file mode 100644 index 0000000000..69c23a4d9f --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListContextProvider.tsx @@ -0,0 +1,56 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useMemo } from "react"; +import { ApolloClient } from "apollo-client"; +import { WorkflowListContext } from "./WorkflowListContext"; +import { WorkflowListGatewayApiImpl } from "./WorkflowListGatewayApi"; +import { GraphQLWorkflowListQueries } from "./WorkflowListQueries"; +import { HttpLink } from "apollo-link-http"; +import { InMemoryCache, NormalizedCacheObject } from "apollo-cache-inmemory"; +import { useApp } from "../../../context/AppContext"; + +export function WorkflowListContextProvider(props: React.PropsWithChildren<{}>) { + const app = useApp(); + + const httpLink = useMemo( + () => + new HttpLink({ + uri: app.data.dataIndexUrl, + }), + [app] + ); + + const cache = useMemo(() => new InMemoryCache(), []); + + const apolloClient: ApolloClient<NormalizedCacheObject> = useMemo( + () => + new ApolloClient({ + cache, + link: httpLink, + }), + [cache, httpLink] + ); + + const gatewayApiImpl = useMemo(() => { + return new WorkflowListGatewayApiImpl(new GraphQLWorkflowListQueries(apolloClient)); + }, [apolloClient]); + + return <WorkflowListContext.Provider value={gatewayApiImpl}>{props.children}</WorkflowListContext.Provider>; +} diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListGatewayApi.ts b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListGatewayApi.ts new file mode 100644 index 0000000000..9d4b1f2d63 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListGatewayApi.ts @@ -0,0 +1,185 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { + BulkWorkflowInstanceActionResponse, + WorkflowInstance, + WorkflowInstanceFilter, + WorkflowListSortBy, + OperationType, +} from "@kie-tools/runtime-tools-gateway-api/dist/types"; +import { WorkflowListQueries } from "./WorkflowListQueries"; + +export interface WorkflowListGatewayApi { + workflowListState: WorkflowListState; + initialLoad: (filter: WorkflowInstanceFilter, sortBy: WorkflowListSortBy) => Promise<void>; + openWorkflow: (workflow: WorkflowInstance) => Promise<void>; + applyFilter: (filter: WorkflowInstanceFilter) => Promise<void>; + applySorting: (SortBy: WorkflowListSortBy) => Promise<void>; + handleWorkflowSkip: (workflowInstance: WorkflowInstance) => Promise<void>; + handleWorkflowRetry: (workflowInstance: WorkflowInstance) => Promise<void>; + handleWorkflowAbort: (workflowInstance: WorkflowInstance) => Promise<void>; + handleWorkflowMultipleAction: ( + workflowInstances: WorkflowInstance[], + operationType: OperationType + ) => Promise<BulkWorkflowInstanceActionResponse>; + query(offset: number, limit: number): Promise<WorkflowInstance[]>; + getChildWorkflowsQuery(rootWorkflowInstanceId: string): Promise<WorkflowInstance[]>; + openTriggerCloudEvent: (workflowInstance?: WorkflowInstance) => void; + onOpenWorkflowListen: (listener: OnOpenWorkflowListener) => UnSubscribeHandler; + onOpenTriggerCloudEventListen: (listener: OnOpenTriggerCloudEventListener) => UnSubscribeHandler; +} + +export interface WorkflowListState { + filters: WorkflowInstanceFilter; + sortBy: WorkflowListSortBy; +} + +export interface OnOpenWorkflowListener { + onOpen: (workflow: WorkflowInstance) => void; +} + +export interface OnOpenTriggerCloudEventListener { + onOpen: (workflow?: WorkflowInstance) => void; +} + +export interface UnSubscribeHandler { + unSubscribe: () => void; +} +export class WorkflowListGatewayApiImpl implements WorkflowListGatewayApi { + private readonly onOpenWorkflowListeners: OnOpenWorkflowListener[] = []; + private readonly onOpenTriggerCloudEventListeners: OnOpenTriggerCloudEventListener[] = []; + + private readonly queries: WorkflowListQueries; + private _WorkflowListState: WorkflowListState; + + constructor(queries: WorkflowListQueries) { + this.queries = queries; + this._WorkflowListState = { + filters: { + status: [], + businessKey: [], + }, + sortBy: {}, + }; + } + + get workflowListState(): WorkflowListState { + return this._WorkflowListState; + } + + openWorkflow = (workflow: WorkflowInstance): Promise<void> => { + this.onOpenWorkflowListeners.forEach((listener) => listener.onOpen(workflow)); + return Promise.resolve(); + }; + + initialLoad = (filter: WorkflowInstanceFilter, sortBy: WorkflowListSortBy): Promise<void> => { + this._WorkflowListState.filters = filter; + this._WorkflowListState.sortBy = sortBy; + return Promise.resolve(); + }; + + applyFilter = (filter: WorkflowInstanceFilter): Promise<void> => { + this.workflowListState.filters = filter; + return Promise.resolve(); + }; + + applySorting = (sortBy: WorkflowListSortBy) => { + this._WorkflowListState.sortBy = sortBy; + return Promise.resolve(); + }; + + handleWorkflowSkip = async (workflowInstance: WorkflowInstance): Promise<void> => { + return this.queries.handleWorkflowSkip(workflowInstance); + }; + + handleWorkflowRetry = async (workflowInstance: WorkflowInstance): Promise<void> => { + return this.queries.handleWorkflowRetry(workflowInstance); + }; + + handleWorkflowAbort = async (workflowInstance: WorkflowInstance): Promise<void> => { + return this.queries.handleWorkflowAbort(workflowInstance); + }; + + handleWorkflowMultipleAction = async ( + workflowInstances: WorkflowInstance[], + operationType: OperationType + ): Promise<BulkWorkflowInstanceActionResponse> => { + return this.queries.handleWorkflowMultipleAction(workflowInstances, operationType); + }; + query(offset: number, limit: number): Promise<WorkflowInstance[]> { + return new Promise<WorkflowInstance[]>((resolve, reject) => { + this.queries + .getWorkflowInstances(offset, limit, this._WorkflowListState.filters, this._WorkflowListState.sortBy) + .then((value) => { + resolve(value); + }) + .catch((reason) => { + reject(reason); + }); + }); + } + + getChildWorkflowsQuery(rootWorkflowInstanceId: string): Promise<WorkflowInstance[]> { + return new Promise<WorkflowInstance[]>((resolve, reject) => { + this.queries + .getChildWorkflowInstances(rootWorkflowInstanceId) + .then((value) => { + resolve(value); + }) + .catch((reason) => { + reject(reason); + }); + }); + } + + onOpenWorkflowListen(listener: OnOpenWorkflowListener): UnSubscribeHandler { + this.onOpenWorkflowListeners.push(listener); + + const unSubscribe = () => { + const index = this.onOpenWorkflowListeners.indexOf(listener); + if (index > -1) { + this.onOpenWorkflowListeners.splice(index, 1); + } + }; + + return { + unSubscribe, + }; + } + + onOpenTriggerCloudEventListen(listener: OnOpenTriggerCloudEventListener): UnSubscribeHandler { + this.onOpenTriggerCloudEventListeners.push(listener); + + const unSubscribe = () => { + const index = this.onOpenTriggerCloudEventListeners.indexOf(listener); + if (index > -1) { + this.onOpenTriggerCloudEventListeners.splice(index, 1); + } + }; + + return { + unSubscribe, + }; + } + + openTriggerCloudEvent(workflowInstance?: WorkflowInstance): void { + this.onOpenTriggerCloudEventListeners.forEach((listener) => listener.onOpen(workflowInstance)); + } +} diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListQueries.ts b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListQueries.ts new file mode 100644 index 0000000000..d8dd1240c4 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/WorkflowListQueries.ts @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import { ApolloClient } from "apollo-client"; +import { + BulkWorkflowInstanceActionResponse, + WorkflowInstance, + WorkflowListSortBy, + WorkflowInstanceFilter, + OperationType, +} from "@kie-tools/runtime-tools-gateway-api/dist/types"; +import { + handleWorkflowAbort, + handleWorkflowMultipleAction, + handleWorkflowSkip, + handleWorkflowRetry, + getWorkflowInstances, + getChildWorkflowInstances, +} from "@kie-tools/runtime-tools-gateway-api/dist/gatewayApi"; + +export interface WorkflowListQueries { + getWorkflowInstances( + start: number, + end: number, + filters: WorkflowInstanceFilter, + sortBy: WorkflowListSortBy + ): Promise<WorkflowInstance[]>; + getChildWorkflowInstances(rootWorkflowInstanceId: string): Promise<WorkflowInstance[]>; + handleWorkflowSkip(workflowInstance: WorkflowInstance): Promise<void>; + handleWorkflowAbort(workflowInstance: WorkflowInstance): Promise<void>; + handleWorkflowRetry(workflowInstance: WorkflowInstance): Promise<void>; + + handleWorkflowMultipleAction( + workflowInstances: WorkflowInstance[], + operationType: OperationType + ): Promise<BulkWorkflowInstanceActionResponse>; +} + +export class GraphQLWorkflowListQueries implements WorkflowListQueries { + private readonly client: ApolloClient<any>; + + constructor(client: ApolloClient<any>) { + this.client = client; + } + + getWorkflowInstances( + offset: number, + limit: number, + filters: WorkflowInstanceFilter, + sortBy: WorkflowListSortBy + ): Promise<WorkflowInstance[]> { + return getWorkflowInstances(offset, limit, filters, sortBy, this.client); + } + + getChildWorkflowInstances(rootWorkflowInstanceId: string): Promise<WorkflowInstance[]> { + return getChildWorkflowInstances(rootWorkflowInstanceId, this.client); + } + + async handleWorkflowSkip(workflowInstance: WorkflowInstance): Promise<void> { + return handleWorkflowSkip(workflowInstance, this.client); + } + + async handleWorkflowAbort(workflowInstance: WorkflowInstance): Promise<void> { + return handleWorkflowAbort(workflowInstance, this.client); + } + + async handleWorkflowRetry(workflowInstance: WorkflowInstance): Promise<void> { + return handleWorkflowRetry(workflowInstance, this.client); + } + + async handleWorkflowMultipleAction(workflowInstances: WorkflowInstance[], operationType: OperationType) { + return handleWorkflowMultipleAction(workflowInstances, operationType, this.client); + } +} diff --git a/packages/sonataflow-deployment-webapp/src/index.tsx b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/index.ts similarity index 76% copy from packages/sonataflow-deployment-webapp/src/index.tsx copy to packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/index.ts index 47689759b5..7298d8e07f 100644 --- a/packages/sonataflow-deployment-webapp/src/index.tsx +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowList/index.ts @@ -17,10 +17,6 @@ * under the License. */ -import "@patternfly/react-core/dist/styles/base.css"; -import * as React from "react"; -import * as ReactDOM from "react-dom"; -import { App } from "./App"; -import "../static/resources/style.css"; - -ReactDOM.render(<App />, document.getElementById("app")); +export { WorkflowListGatewayApi } from "./WorkflowListGatewayApi"; +export * from "./WorkflowListContext"; +export { WorkflowListContextProvider } from "./WorkflowListContextProvider"; diff --git a/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowListContainer/WorkflowListContainer.tsx b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowListContainer/WorkflowListContainer.tsx new file mode 100644 index 0000000000..2582367f06 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/src/runtimeTools/workflows/WorkflowListContainer/WorkflowListContainer.tsx @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React, { useEffect } from "react"; +import { useHistory } from "react-router-dom"; +import { componentOuiaProps, OUIAProps } from "@kie-tools/runtime-tools-components/dist/ouiaTools"; +import { EmbeddedWorkflowList } from "@kie-tools/runtime-tools-enveloped-components/dist/workflowList/embedded"; +import { WorkflowListGatewayApi, useWorkflowListGatewayApi } from "../WorkflowList"; +import { WorkflowInstance, WorkflowListState } from "@kie-tools/runtime-tools-gateway-api/dist/types"; +import { routes } from "../../../routes"; + +interface WorkflowListContainerProps { + initialState: WorkflowListState; +} + +const WorkflowListContainer: React.FC<WorkflowListContainerProps & OUIAProps> = ({ + initialState, + ouiaId, + ouiaSafe, +}) => { + const history = useHistory(); + const gatewayApi: WorkflowListGatewayApi = useWorkflowListGatewayApi(); + + useEffect(() => { + const onOpenInstanceUnsubscriber = gatewayApi.onOpenWorkflowListen({ + onOpen(workflow: WorkflowInstance) { + history.push({ + pathname: routes.runtimeTools.workflowDetails.path({ workflowId: workflow.id }), + state: gatewayApi.workflowListState, + }); + }, + }); + return () => { + onOpenInstanceUnsubscriber.unSubscribe(); + }; + }, []); + + return ( + <EmbeddedWorkflowList + {...componentOuiaProps(ouiaId, "workflow-list-container", ouiaSafe)} + driver={gatewayApi} + targetOrigin={window.location.origin} + initialState={initialState} + /> + ); +}; + +export default WorkflowListContainer; diff --git a/packages/sonataflow-deployment-webapp/static/envelope/serverless-workflow-combined-editor-envelope.html b/packages/sonataflow-deployment-webapp/static/envelope/serverless-workflow-combined-editor-envelope.html new file mode 100644 index 0000000000..e6bdb4fcf3 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/static/envelope/serverless-workflow-combined-editor-envelope.html @@ -0,0 +1,45 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. +--> + +<!DOCTYPE html> +<html lang="en"> + <head> + <style> + html, + body, + div#envelope-app { + margin: 0; + border: 0; + padding: 0; + overflow: hidden; + height: 100%; + width: 100%; + } + </style> + + <title></title> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + </head> + <body> + <div id="swf-combined-editor-envelope-app"></div> + <script src="serverless-workflow-combined-editor-envelope.js"></script> + </body> +</html> diff --git a/packages/sonataflow-deployment-webapp/static/envelope/serverless-workflow-diagram-editor-envelope.html b/packages/sonataflow-deployment-webapp/static/envelope/serverless-workflow-diagram-editor-envelope.html new file mode 100644 index 0000000000..0a9d86581f --- /dev/null +++ b/packages/sonataflow-deployment-webapp/static/envelope/serverless-workflow-diagram-editor-envelope.html @@ -0,0 +1,45 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. +--> + +<!DOCTYPE html> +<html lang="en"> + <head> + <style> + html, + body, + div#envelope-app { + margin: 0; + border: 0; + padding: 0; + overflow: hidden; + height: 100%; + width: 100%; + } + </style> + + <title></title> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + </head> + <body> + <div id="swf-diagram-editor-envelope-app"></div> + <script src="serverless-workflow-diagram-editor-envelope.js"></script> + </body> +</html> diff --git a/packages/sonataflow-deployment-webapp/static/envelope/serverless-workflow-text-editor-envelope.html b/packages/sonataflow-deployment-webapp/static/envelope/serverless-workflow-text-editor-envelope.html new file mode 100644 index 0000000000..7c3a1a95b7 --- /dev/null +++ b/packages/sonataflow-deployment-webapp/static/envelope/serverless-workflow-text-editor-envelope.html @@ -0,0 +1,45 @@ +<!-- + ~ Licensed to the Apache Software Foundation (ASF) under one + ~ or more contributor license agreements. See the NOTICE file + ~ distributed with this work for additional information + ~ regarding copyright ownership. The ASF licenses this file + ~ to you under the Apache License, Version 2.0 (the + ~ "License"); you may not use this file except in compliance + ~ with the License. You may obtain a copy of the License at + ~ + ~ http://www.apache.org/licenses/LICENSE-2.0 + ~ + ~ Unless required by applicable law or agreed to in writing, + ~ software distributed under the License is distributed on an + ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + ~ KIND, either express or implied. See the License for the + ~ specific language governing permissions and limitations + ~ under the License. +--> + +<!DOCTYPE html> +<html lang="en"> + <head> + <style> + html, + body, + div#envelope-app { + margin: 0; + border: 0; + padding: 0; + overflow: hidden; + height: 100%; + width: 100%; + } + </style> + + <title></title> + <meta charset="UTF-8" /> + <meta name="viewport" content="width=device-width, initial-scale=1.0" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + </head> + <body> + <div id="swf-text-editor-envelope-app"></div> + <script src="serverless-workflow-text-editor-envelope.js"></script> + </body> +</html> diff --git a/packages/sonataflow-deployment-webapp/webpack.config.js b/packages/sonataflow-deployment-webapp/webpack.config.js index 22285e56a2..3658661402 100644 --- a/packages/sonataflow-deployment-webapp/webpack.config.js +++ b/packages/sonataflow-deployment-webapp/webpack.config.js @@ -25,6 +25,7 @@ const patternflyBase = require("@kie-tools-core/patternfly-base"); const { merge } = require("webpack-merge"); const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin"); const common = require("@kie-tools-core/webpack-base/webpack.common.config"); +const swEditor = require("@kie-tools/serverless-workflow-diagram-editor-assets"); const { env } = require("./env"); const buildEnv = env; @@ -32,6 +33,9 @@ module.exports = async (env) => merge(common(env), { entry: { index: "./src/index.tsx", + "serverless-workflow-combined-editor-envelope": "./src/envelope/ServerlessWorkflowCombinedEditorEnvelopeApp.ts", + "serverless-workflow-diagram-editor-envelope": "./src/envelope/ServerlessWorkflowDiagramEditorEnvelopeApp.ts", + "serverless-workflow-text-editor-envelope": "./src/envelope/ServerlessWorkflowTextEditorEnvelopeApp.ts", }, optimization: { minimizer: [ @@ -52,6 +56,23 @@ module.exports = async (env) => { from: "./static/resources", to: "./resources" }, { from: "./static/favicon.svg", to: "./favicon.svg" }, { from: "./static/sonataflow-deployment-webapp-data.json", to: "./sonataflow-deployment-webapp-data.json" }, + { + from: "./static/envelope/serverless-workflow-combined-editor-envelope.html", + to: "./serverless-workflow-combined-editor-envelope.html", + }, + { + from: "./static/envelope/serverless-workflow-diagram-editor-envelope.html", + to: "./serverless-workflow-diagram-editor-envelope.html", + }, + { + from: swEditor.swEditorPath(), + to: "./diagram", + globOptions: { ignore: ["**/WEB-INF/**/*", "**/*.html"] }, + }, + { + from: "./static/envelope/serverless-workflow-text-editor-envelope.html", + to: "./serverless-workflow-text-editor-envelope.html", + }, ], }), new MonacoWebpackPlugin({ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 05100e5508..aec758786e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5409,7 +5409,7 @@ importers: version: 4.8.4 url-loader: specifier: ^4.1.1 - version: 4.1.1([email protected])([email protected]) + version: 4.1.1([email protected]) webpack: specifier: ^5.88.2 version: 5.88.2([email protected]) @@ -5822,7 +5822,7 @@ importers: version: 4.1.1([email protected])([email protected]) webpack: specifier: ^5.88.2 - version: 5.88.2([email protected]) + version: 5.88.2 packages/playwright-base: devDependencies: @@ -8134,6 +8134,12 @@ importers: "@babel/preset-react": specifier: ^7.16.0 version: 7.16.0(@babel/[email protected]) + "@kie-tools-core/editor": + specifier: workspace:* + version: link:../editor + "@kie-tools-core/keyboard-shortcuts": + specifier: workspace:* + version: link:../keyboard-shortcuts "@kie-tools-core/patternfly-base": specifier: workspace:* version: link:../patternfly-base @@ -8158,9 +8164,24 @@ importers: "@kie-tools/runtime-tools-gateway-api": specifier: workspace:* version: link:../runtime-tools-gateway-api + "@kie-tools/serverless-workflow-combined-editor": + specifier: workspace:* + version: link:../serverless-workflow-combined-editor + "@kie-tools/serverless-workflow-diagram-editor-assets": + specifier: workspace:* + version: link:../serverless-workflow-diagram-editor-assets + "@kie-tools/serverless-workflow-diagram-editor-envelope": + specifier: workspace:* + version: link:../serverless-workflow-diagram-editor-envelope + "@kie-tools/serverless-workflow-text-editor": + specifier: workspace:* + version: link:../serverless-workflow-text-editor "@kie-tools/tsconfig": specifier: workspace:* version: link:../tsconfig + "@patternfly/patternfly": + specifier: ^4.224.2 + version: 4.224.2 "@patternfly/react-core": specifier: ^4.276.6 version: 4.276.6([email protected])([email protected]) @@ -8185,6 +8206,15 @@ importers: "@types/react-router-dom": specifier: ^5.1.7 version: 5.1.7 + apollo-cache-inmemory: + specifier: 1.6.6 + version: 1.6.6([email protected]) + apollo-client: + specifier: 2.6.10 + version: 2.6.10([email protected]) + apollo-link-http: + specifier: 1.5.17 + version: 1.5.17([email protected]) copy-webpack-plugin: specifier: ^11.0.0 version: 11.0.0([email protected]) @@ -8607,10 +8637,10 @@ importers: version: 4.8.4 webpack: specifier: ^5.88.2 - version: 5.88.2([email protected]) + version: 5.88.2 webpack-dev-server: specifier: ^4.15.1 - version: 4.15.1([email protected])([email protected]) + version: 4.15.1([email protected]) webpack-merge: specifier: ^5.9.0 version: 5.9.0 @@ -9493,7 +9523,7 @@ importers: version: 9.4.2([email protected])([email protected]) webpack: specifier: ^5.88.2 - version: 5.88.2([email protected]) + version: 5.88.2 packages/workspace: dependencies: @@ -18419,7 +18449,6 @@ packages: /@patternfly/[email protected]: resolution: { integrity: sha512-HGNV26uyHSIECuhjPg/WGn0mXbAotcs6ODfhAOkfYjIgGylddgiwElxUe1rpEHV5mQJJ2rMn4OdeJIIpzRX61g== } - dev: false /@patternfly/[email protected](@patternfly/[email protected])([email protected])([email protected])([email protected]): resolution: @@ -21917,14 +21946,14 @@ packages: { integrity: sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g== } dependencies: "@types/connect": 3.4.34 - "@types/node": 18.13.0 + "@types/node": 18.17.18 dev: true /@types/[email protected]: resolution: { integrity: sha512-p7ienRMiS41Nu2/igbJxxLDWrSZ0WxM8UQgCeO9KhoVF7cOVFkrKsiDr1EsJIla8vV3oEEjGcz11jc5yimhzZw== } dependencies: - "@types/node": 18.13.0 + "@types/node": 18.17.18 dev: true /@types/[email protected]: @@ -21967,7 +21996,7 @@ packages: { integrity: sha512-h8QJa8xSb1WD4fpKBDcATDNGXghFj6/3GRWG6dhmRcu0RX1Ubasur2Uvx5aeEwlf0MwblEC2bMzzMQntxnw/Cw== } dependencies: "@types/express-serve-static-core": 4.17.35 - "@types/node": 18.13.0 + "@types/node": 18.17.18 dev: true /@types/[email protected]: @@ -22155,7 +22184,7 @@ packages: resolution: { integrity: sha512-wALWQwrgiB2AWTT91CB62b6Yt0sNHpznUXeZEcnPU3DRdlDIz74x8Qg1UUYKSVFi+va5vKOLYRBI1bRKiLLKIg== } dependencies: - "@types/node": 18.13.0 + "@types/node": 18.17.18 "@types/qs": 6.9.7 "@types/range-parser": 1.2.4 "@types/send": 0.17.1 @@ -22664,7 +22693,7 @@ packages: { integrity: sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ== } dependencies: "@types/mime": 1.3.2 - "@types/node": 18.13.0 + "@types/node": 18.17.18 dev: true /@types/[email protected]: @@ -22702,7 +22731,7 @@ packages: resolution: { integrity: sha512-f0KEEe05NvUnat+boPTZ0dgaLZ4SfSouXUgv5noUiefG2ajgKjmETo9ZJyuqsl7dfl2aHlLJUiki6B4ZYldiiw== } dependencies: - "@types/node": 18.13.0 + "@types/node": 18.17.18 dev: true /@types/[email protected]: @@ -22788,14 +22817,14 @@ packages: resolution: { integrity: sha512-zdQDHKUgcX/zBc4GrwsE/7dVdAD8JR4EuiAXiiUhhfyIJXXb2+PrGshFyeXWQPMmmZ2XxgaqclgpIC7eTXc1mg== } dependencies: - "@types/node": 18.13.0 + "@types/node": 18.17.18 dev: true /@types/[email protected]: resolution: { integrity: sha512-lwhs8hktwxSjf9UaZ9tG5M03PGogvFaH8gUgLNbN9HKIg0dvv6q+gkSuJ8HN4/VbyxkuLzCjlN7GquQ0gUJfIg== } dependencies: - "@types/node": 18.13.0 + "@types/node": 18.17.18 dev: true /@types/[email protected]: @@ -23408,7 +23437,6 @@ packages: dependencies: "@types/node": 18.17.18 tslib: 1.14.1 - dev: false /@wry/[email protected]: resolution: @@ -24182,7 +24210,6 @@ packages: optimism: 0.10.3 ts-invariant: 0.4.4 tslib: 1.14.1 - dev: false /[email protected]([email protected]): resolution: @@ -24231,7 +24258,6 @@ packages: graphql: 14.3.1 ts-invariant: 0.4.4 tslib: 1.14.1 - dev: false /[email protected]([email protected]): resolution: @@ -24243,7 +24269,6 @@ packages: apollo-link-http-common: 0.2.16([email protected]) graphql: 14.3.1 tslib: 1.14.1 - dev: false /[email protected]([email protected]): resolution: @@ -30006,7 +30031,7 @@ packages: is-glob: 4.0.3 normalize-path: 3.0.0 schema-utils: 4.0.0 - webpack: 5.88.2([email protected]) + webpack: 5.88.2 dev: true /[email protected]: @@ -33949,7 +33974,7 @@ packages: dependencies: glob: 7.2.0 minimatch: 3.0.5 - webpack: 5.88.2([email protected]) + webpack: 5.88.2 webpack-merge: 4.2.2 dev: true @@ -36307,7 +36332,6 @@ packages: { integrity: sha512-9A5pqGoQk49H6Vhjb9kPgAeeECfUDF6aIICbMDL23kDLStBn1MWk3YvcZ4xWF9CsSf6XEgvRLkXy4xof/56vVw== } dependencies: "@wry/context": 0.4.4 - dev: false /[email protected]: resolution: @@ -39745,7 +39769,7 @@ packages: klona: 2.0.5 neo-async: 2.6.2 sass: 1.49.9 - webpack: 5.88.2([email protected]) + webpack: 5.88.2 dev: true /[email protected]([email protected])([email protected]): @@ -40437,7 +40461,7 @@ packages: abab: 2.0.5 iconv-lite: 0.6.3 source-map-js: 0.6.2 - webpack: 5.88.2([email protected]) + webpack: 5.88.2 dev: true /[email protected]([email protected]): @@ -42393,6 +42417,23 @@ packages: optional: true dependencies: file-loader: 6.2.0([email protected]) + loader-utils: 2.0.2 + mime-types: 2.1.34 + schema-utils: 3.1.1 + webpack: 5.88.2 + dev: true + + /[email protected]([email protected]): + resolution: + { integrity: sha512-3BTV812+AVHHOJQO8O5MkWgZ5aosP7GnROJwvzLS9hWDj00lZ6Z0wNak423Lp9PBZN05N+Jk/N5Si8jRAlGyWA== } + engines: { node: ">= 10.13.0" } + peerDependencies: + file-loader: "*" + webpack: ^4.0.0 || ^5.0.0 + peerDependenciesMeta: + file-loader: + optional: true + dependencies: loader-utils: 2.0.2 mime-types: 2.1.34 schema-utils: 3.1.1 @@ -43399,6 +43440,58 @@ packages: - utf-8-validate dev: true + /[email protected]([email protected]): + resolution: + { integrity: sha512-5hbAst3h3C3L8w6W4P96L5vaV0PxSmJhxZvWKYIdgxOQm8pNZ5dEOmmSLBVpP85ReeyRt6AS1QJNyo/oFFPeVA== } + engines: { node: ">= 12.13.0" } + hasBin: true + peerDependencies: + webpack: ^4.37.0 || ^5.0.0 + webpack-cli: "*" + peerDependenciesMeta: + webpack: + optional: true + webpack-cli: + optional: true + dependencies: + "@types/bonjour": 3.5.10 + "@types/connect-history-api-fallback": 1.3.5 + "@types/express": 4.17.17 + "@types/serve-index": 1.9.1 + "@types/serve-static": 1.13.10 + "@types/sockjs": 0.3.33 + "@types/ws": 8.5.5 + ansi-html-community: 0.0.8 + bonjour-service: 1.1.1 + chokidar: 3.5.3 + colorette: 2.0.16 + compression: 1.7.4 + connect-history-api-fallback: 2.0.0 + default-gateway: 6.0.3 + express: 4.18.2 + graceful-fs: 4.2.11 + html-entities: 2.3.2 + http-proxy-middleware: 2.0.6(@types/[email protected]) + ipaddr.js: 2.0.1 + launch-editor: 2.6.0 + open: 8.4.0 + p-retry: 4.6.1 + rimraf: 3.0.2 + schema-utils: 4.0.0 + selfsigned: 2.1.1 + serve-index: 1.9.1 + sockjs: 0.3.24 + spdy: 4.0.2 + webpack: 5.88.2 + webpack-dev-middleware: 5.3.3([email protected]) + ws: 8.13.0 + transitivePeerDependencies: + - bufferutil + - debug + - supports-color + - utf-8-validate + dev: true + /[email protected]: resolution: { integrity: sha512-IRmTspuHM06aZh98OhBJtqLpeWFM8FXJS5UYpKYxCJzyFoyWj1w6VGFfomZU7OPA55dMLrQK0pRT1eQ3PACr4w== } @@ -43506,6 +43599,47 @@ packages: - uglify-js dev: true + /[email protected]: + resolution: + { integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== } + engines: { node: ">=10.13.0" } + hasBin: true + peerDependencies: + webpack-cli: "*" + peerDependenciesMeta: + webpack-cli: + optional: true + dependencies: + "@types/eslint-scope": 3.7.3 + "@types/estree": 1.0.1 + "@webassemblyjs/ast": 1.11.6 + "@webassemblyjs/wasm-edit": 1.11.6 + "@webassemblyjs/wasm-parser": 1.11.6 + acorn: 8.10.0 + acorn-import-assertions: 1.9.0([email protected]) + browserslist: 4.21.5 + chrome-trace-event: 1.0.2 + enhanced-resolve: 5.15.0 + es-module-lexer: 1.3.0 + eslint-scope: 5.1.1 + events: 3.3.0 + glob-to-regexp: 0.4.1 + graceful-fs: 4.2.11 + json-parse-even-better-errors: 2.3.1 + loader-runner: 4.2.0 + mime-types: 2.1.34 + neo-async: 2.6.2 + schema-utils: 3.3.0 + tapable: 2.2.0 + terser-webpack-plugin: 5.3.9([email protected]) + watchpack: 2.4.0 + webpack-sources: 3.2.3 + transitivePeerDependencies: + - "@swc/core" + - esbuild + - uglify-js + dev: true + /[email protected](@swc/[email protected])([email protected])([email protected]): resolution: { integrity: sha512-JmcgNZ1iKj+aiR0OvTYtWQqJwq37Pf683dY9bVORwVbUrDhLhdn/PlO2sHsFHPkj7sHNQF3JwaAkp49V+Sq1tQ== } @@ -43522,8 +43656,8 @@ packages: "@webassemblyjs/ast": 1.11.6 "@webassemblyjs/wasm-edit": 1.11.6 "@webassemblyjs/wasm-parser": 1.11.6 - acorn: 8.8.2 - acorn-import-assertions: 1.9.0([email protected]) + acorn: 8.10.0 + acorn-import-assertions: 1.9.0([email protected]) browserslist: 4.21.5 chrome-trace-event: 1.0.2 enhanced-resolve: 5.15.0 --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
