This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
The following commit(s) were added to refs/heads/main by this push: new e2fe861 Update karavan-space e2fe861 is described below commit e2fe8619dc8f0eec22c6a3dedb412e53508bf178 Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Tue Jan 10 22:46:08 2023 -0500 Update karavan-space --- karavan-space/package-lock.json | 37 +++-- karavan-space/package.json | 3 +- karavan-space/src/designer/KaravanDesigner.tsx | 5 - karavan-space/src/designer/beans/BeansDesigner.tsx | 2 +- karavan-space/src/designer/error/ErrorDesigner.tsx | 74 ---------- .../src/designer/error/ErrorHandlerCard.tsx | 48 ------ .../src/designer/error/ErrorHandlerDesigner.tsx | 164 --------------------- .../src/designer/exception/ExceptionDesigner.tsx | 74 ---------- karavan-space/src/designer/karavan.css | 1 + karavan-space/src/designer/rest/RestDesigner.tsx | 18 +-- karavan-space/src/designer/route/DslElement.tsx | 22 ++- karavan-space/src/designer/route/RouteDesigner.tsx | 54 +++++-- .../designer/route/property/DslPropertyField.tsx | 3 + karavan-space/src/designer/utils/CamelUi.tsx | 25 +++- karavan-space/src/designer/utils/KaravanIcons.tsx | 114 +++++++++++++- 15 files changed, 219 insertions(+), 425 deletions(-) diff --git a/karavan-space/package-lock.json b/karavan-space/package-lock.json index f473e4e..0474528 100644 --- a/karavan-space/package-lock.json +++ b/karavan-space/package-lock.json @@ -14,6 +14,7 @@ "@patternfly/react-core": "4.239.0", "@patternfly/react-table": "^4.108.0", "@types/js-yaml": "^4.0.5", + "@types/node": "^18.11.18", "@types/uuid": "8.3.4", "axios": "^0.25.0", "dagre": "^0.8.5", @@ -26,7 +27,7 @@ "react-router-dom": "^6.2.1", "react-scripts": "5.0.0", "rxjs": "^7.5.2", - "typescript": "^4.5.5", + "typescript": "^4.9.4", "uuid": "8.3.2" }, "devDependencies": { @@ -44,15 +45,14 @@ "dependencies": { "@types/js-yaml": "^4.0.5", "@types/uuid": "^8.3.4", - "typescript": "^4.5.5", + "typescript": "^4.9.4", "uuid": "8.3.2" }, "devDependencies": { "@types/chai": "^4.3.0", "@types/dagre": "^0.7.47", - "@types/localforage": "0.0.34", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.23", + "@types/node": "^18.11.18", "chai": "^4.3.4", "cross-env": "^7.0.3", "fs": "^0.0.1-security", @@ -4087,9 +4087,9 @@ "dev": true }, "node_modules/@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" }, "node_modules/@types/parse-json": { "version": "4.0.0", @@ -16102,9 +16102,9 @@ } }, "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -20036,9 +20036,9 @@ "dev": true }, "@types/node": { - "version": "18.11.9", - "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.9.tgz", - "integrity": "sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg==" + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" }, "@types/parse-json": { "version": "4.0.0", @@ -25571,16 +25571,15 @@ "@types/chai": "^4.3.0", "@types/dagre": "^0.7.47", "@types/js-yaml": "^4.0.5", - "@types/localforage": "0.0.34", "@types/mocha": "^9.1.0", - "@types/node": "^17.0.23", + "@types/node": "^18.11.18", "@types/uuid": "^8.3.4", "chai": "^4.3.4", "cross-env": "^7.0.3", "fs": "^0.0.1-security", "mocha": "^9.2.0", "ts-node": "^10.4.0", - "typescript": "^4.5.5", + "typescript": "^4.9.4", "uuid": "8.3.2" } }, @@ -28666,9 +28665,9 @@ } }, "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==" + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==" }, "unbox-primitive": { "version": "1.0.2", diff --git a/karavan-space/package.json b/karavan-space/package.json index b150cf5..2a70872 100644 --- a/karavan-space/package.json +++ b/karavan-space/package.json @@ -38,6 +38,7 @@ "@patternfly/react-core": "4.239.0", "@patternfly/react-table": "^4.108.0", "@types/js-yaml": "^4.0.5", + "@types/node": "^18.11.18", "@types/uuid": "8.3.4", "axios": "^0.25.0", "dagre": "^0.8.5", @@ -50,7 +51,7 @@ "react-router-dom": "^6.2.1", "react-scripts": "5.0.0", "rxjs": "^7.5.2", - "typescript": "^4.5.5", + "typescript": "^4.9.4", "uuid": "8.3.2" }, "devDependencies": { diff --git a/karavan-space/src/designer/KaravanDesigner.tsx b/karavan-space/src/designer/KaravanDesigner.tsx index b215192..e7f975b 100644 --- a/karavan-space/src/designer/KaravanDesigner.tsx +++ b/karavan-space/src/designer/KaravanDesigner.tsx @@ -27,7 +27,6 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {CamelUi} from "./utils/CamelUi"; import {BeansDesigner} from "./beans/BeansDesigner"; import {RestDesigner} from "./rest/RestDesigner"; -import {ErrorHandlerDesigner} from "./error/ErrorHandlerDesigner"; import {getDesignerIcon} from "./utils/KaravanIcons"; interface Props { @@ -132,7 +131,6 @@ export class KaravanDesigner extends React.Component<Props, State> { <Tab eventKey='routes' title={this.getTab("Routes", "Integration flows", "routes")}></Tab> <Tab eventKey='rest' title={this.getTab("REST", "REST services", "rest")}></Tab> <Tab eventKey='beans' title={this.getTab("Beans", "Beans Configuration", "beans")}></Tab> - <Tab eventKey='error' title={this.getTab("Error Handler", "Global Error Handler", "error")}></Tab> </Tabs> {tab === 'routes' && <RouteDesigner integration={this.state.integration} onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)} @@ -144,9 +142,6 @@ export class KaravanDesigner extends React.Component<Props, State> { {tab === 'beans' && <BeansDesigner integration={this.state.integration} onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)} dark={this.props.dark}/>} - {tab === 'error' && <ErrorHandlerDesigner integration={this.state.integration} - onSave={(integration, propertyOnly) => this.save(integration, propertyOnly)} - dark={this.props.dark}/>} </PageSection> ) } diff --git a/karavan-space/src/designer/beans/BeansDesigner.tsx b/karavan-space/src/designer/beans/BeansDesigner.tsx index d48ea68..037df83 100644 --- a/karavan-space/src/designer/beans/BeansDesigner.tsx +++ b/karavan-space/src/designer/beans/BeansDesigner.tsx @@ -148,7 +148,7 @@ export class BeansDesigner extends React.Component<Props, State> { variant={beans?.length === 0 ? "primary" : "secondary"} data-click="ADD_REST" icon={<PlusIcon/>} - onClick={e => this.createBean()}>Create new bean + onClick={e => this.createBean()}>Create bean </Button> </div> </div> diff --git a/karavan-space/src/designer/error/ErrorDesigner.tsx b/karavan-space/src/designer/error/ErrorDesigner.tsx deleted file mode 100644 index ef11836..0000000 --- a/karavan-space/src/designer/error/ErrorDesigner.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 { - EmptyState, EmptyStateBody, EmptyStateIcon, - PageSection, Title -} from '@patternfly/react-core'; -import '../karavan.css'; -import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition"; -import CubesIcon from '@patternfly/react-icons/dist/esm/icons/cubes-icon'; - -interface Props { - onSave?: (integration: Integration, propertyOnly: boolean) => void - integration: Integration - dark: boolean -} - -interface State { - integration: Integration - selectedStep?: CamelElement - key: string - propertyOnly: boolean -} - -export class ErrorDesigner extends React.Component<Props, State> { - - public state: State = { - integration: this.props.integration, - key: "", - propertyOnly: false - }; - - componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => { - if (prevState.key !== this.state.key) { - this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly); - } - } - - onIntegrationUpdate = (i: Integration) => { - this.setState({integration: i, key: Math.random().toString()}); - } - - render() { - return ( - <PageSection className="error-page" isFilled padding={{default: 'noPadding'}}> - <div className="error-page-columns"> - <EmptyState> - <EmptyStateIcon icon={CubesIcon} /> - <Title headingLevel="h4" size="lg"> - Error handler - </Title> - <EmptyStateBody> - Error handler not implemented yet - </EmptyStateBody> - </EmptyState> - </div> - </PageSection> - ); - } -} diff --git a/karavan-space/src/designer/error/ErrorHandlerCard.tsx b/karavan-space/src/designer/error/ErrorHandlerCard.tsx deleted file mode 100644 index f0d534c..0000000 --- a/karavan-space/src/designer/error/ErrorHandlerCard.tsx +++ /dev/null @@ -1,48 +0,0 @@ -/* - * 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 { - Button -} from '@patternfly/react-core'; -import '../karavan.css'; -import {ErrorHandlerDefinition} from "karavan-core/lib/model/CamelDefinition"; -import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-circle-icon"; - -interface Props { - errorHandler: ErrorHandlerDefinition - deleteElement: (element: ErrorHandlerDefinition) => void -} - -export class ErrorHandlerCard extends React.Component<Props, any> { - - delete = (evt: React.MouseEvent) => { - evt.stopPropagation(); - this.props.deleteElement.call(this, this.props.errorHandler); - } - - render() { - return ( - <div className="rest-card rest-card-selected"> - <div className="header"> - <div className="title">Error Handler</div> - <div className="description">Global error handler for the RouteBuilder</div> - <Button variant="link" className="delete-button" onClick={e => this.delete(e)}><DeleteIcon/></Button> - </div> - </div> - ); - } -} diff --git a/karavan-space/src/designer/error/ErrorHandlerDesigner.tsx b/karavan-space/src/designer/error/ErrorHandlerDesigner.tsx deleted file mode 100644 index ba35ccc..0000000 --- a/karavan-space/src/designer/error/ErrorHandlerDesigner.tsx +++ /dev/null @@ -1,164 +0,0 @@ -/* - * 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 { - Button, Drawer, DrawerContent, DrawerContentBody, DrawerPanelContent, Modal, PageSection -} from '@patternfly/react-core'; -import '../karavan.css'; -import {ErrorHandlerDefinition} from "karavan-core/lib/model/CamelDefinition"; -import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition"; -import {CamelUi} from "../utils/CamelUi"; -import PlusIcon from "@patternfly/react-icons/dist/esm/icons/plus-icon"; -import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"; -import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; -import {ErrorHandlerCard} from "./ErrorHandlerCard"; -import {DslProperties} from "../route/DslProperties"; - -interface Props { - onSave?: (integration: Integration, propertyOnly: boolean) => void - integration: Integration - dark: boolean -} - -interface State { - integration: Integration - showDeleteConfirmation: boolean - errorHandler?: ErrorHandlerDefinition - key: string - propertyOnly: boolean -} - -export class ErrorHandlerDesigner extends React.Component<Props, State> { - - public state: State = { - integration: this.props.integration, - showDeleteConfirmation: false, - key: "", - propertyOnly: false - } - - componentDidMount() { - this.setState({key: Math.random().toString()}) - } - - componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => { - if (prevState.key !== this.state.key) { - this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly); - } - } - - showDeleteConfirmation = (errorHandler: ErrorHandlerDefinition) => { - this.setState({errorHandler: errorHandler, showDeleteConfirmation: true}); - } - - onIntegrationUpdate = (i: Integration) => { - this.setState({integration: i, propertyOnly: false, showDeleteConfirmation: false, key: Math.random().toString()}); - } - - deleteErrorHandler = () => { - const i = CamelDefinitionApiExt.deleteErrorHandlerFromIntegration(this.state.integration); - this.setState({ - integration: i, - showDeleteConfirmation: false, - key: Math.random().toString(), - errorHandler: undefined, - propertyOnly: false - }); - } - - changeErrorHandler = (errorHandler: ErrorHandlerDefinition) => { - const clone = CamelUtil.cloneIntegration(this.state.integration); - const i = CamelDefinitionApiExt.addErrorHandlerToIntegration(clone, errorHandler); - this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), errorHandler: errorHandler}); - } - - getDeleteConfirmation() { - return (<Modal - className="modal-delete" - title="Confirmation" - isOpen={this.state.showDeleteConfirmation} - onClose={() => this.setState({showDeleteConfirmation: false})} - actions={[ - <Button key="confirm" variant="primary" onClick={e => this.deleteErrorHandler()}>Delete</Button>, - <Button key="cancel" variant="link" - onClick={e => this.setState({showDeleteConfirmation: false})}>Cancel</Button> - ]} - onEscapePress={e => this.setState({showDeleteConfirmation: false})}> - <div> - Delete Global Error Handler from integration? - </div> - </Modal>) - } - - createErrorHandlerErrorHandle = () => { - this.changeErrorHandler(new ErrorHandlerDefinition()); - } - - onPropertyUpdate = (element: CamelElement) => { - const clone = CamelUtil.cloneIntegration(this.state.integration); - const i = CamelDefinitionApiExt.addErrorHandlerToIntegration(clone, element); - this.setState({integration: i, propertyOnly: true, key: Math.random().toString()}); - } - - getPropertiesPanel(errorHandler?: ErrorHandlerDefinition) { - return ( - <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}> - <DslProperties - integration={this.props.integration} - step={errorHandler} - onIntegrationUpdate={this.onIntegrationUpdate} - onPropertyUpdate={this.onPropertyUpdate} - clipboardStep={undefined} - isRouteDesigner={false} - onClone={element => {}} - dark={this.props.dark}/> - </DrawerPanelContent> - ) - } - - render() { - const errorHandler = CamelUi.getErrorHandler(this.state.integration); - return ( - <PageSection className="rest-page" isFilled padding={{default: 'noPadding'}}> - <div className="rest-page-columns"> - <Drawer isExpanded isInline> - <DrawerContent panelContent={this.getPropertiesPanel(errorHandler)}> - <DrawerContentBody> - <div className="graph" data-click="REST"> - <div className="flows"> - {errorHandler && <ErrorHandlerCard key={errorHandler.uuid + this.state.key} - errorHandler={errorHandler} - deleteElement={this.showDeleteConfirmation}/>} - <div className="add-rest"> - {errorHandler === undefined && <Button - variant="primary" - data-click="ADD_REST" - icon={<PlusIcon/>} - onClick={e => this.createErrorHandlerErrorHandle()}>Create new error handler - </Button>} - </div> - </div> - </div> - </DrawerContentBody> - </DrawerContent> - </Drawer> - </div> - {this.getDeleteConfirmation()} - </PageSection> - ) - } -} diff --git a/karavan-space/src/designer/exception/ExceptionDesigner.tsx b/karavan-space/src/designer/exception/ExceptionDesigner.tsx deleted file mode 100644 index d3b39d8..0000000 --- a/karavan-space/src/designer/exception/ExceptionDesigner.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* - * 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 { - EmptyState, EmptyStateBody, EmptyStateIcon, - PageSection, Title -} from '@patternfly/react-core'; -import '../karavan.css'; -import {Integration, CamelElement} from "karavan-core/lib/model/IntegrationDefinition"; -import CubesIcon from '@patternfly/react-icons/dist/esm/icons/cubes-icon'; - -interface Props { - onSave?: (integration: Integration, propertyOnly: boolean) => void - integration: Integration - dark: boolean -} - -interface State { - integration: Integration - selectedStep?: CamelElement - key: string - propertyOnly: boolean -} - -export class ExceptionDesigner extends React.Component<Props, State> { - - public state: State = { - integration: this.props.integration, - key: "", - propertyOnly: false - }; - - componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => { - if (prevState.key !== this.state.key) { - this.props.onSave?.call(this, this.state.integration, this.state.propertyOnly); - } - } - - onIntegrationUpdate = (i: Integration) => { - this.setState({integration: i, key: Math.random().toString()}); - } - - render() { - return ( - <PageSection className="exception-page" isFilled padding={{default: 'noPadding'}}> - <div className="exception-page-columns"> - <EmptyState> - <EmptyStateIcon icon={CubesIcon} /> - <Title headingLevel="h4" size="lg"> - Exception Clauses - </Title> - <EmptyStateBody> - Exception Clauses not implemented yet - </EmptyStateBody> - </EmptyState> - </div> - </PageSection> - ); - } -} diff --git a/karavan-space/src/designer/karavan.css b/karavan-space/src/designer/karavan.css index 2ae90aa..3598b1a 100644 --- a/karavan-space/src/designer/karavan.css +++ b/karavan-space/src/designer/karavan.css @@ -525,6 +525,7 @@ margin-top: 16px; display: flex; justify-content: center; + gap: 6px; } /*connections*/ diff --git a/karavan-space/src/designer/rest/RestDesigner.tsx b/karavan-space/src/designer/rest/RestDesigner.tsx index bc32cc9..f91c512 100644 --- a/karavan-space/src/designer/rest/RestDesigner.tsx +++ b/karavan-space/src/designer/rest/RestDesigner.tsx @@ -99,7 +99,7 @@ export class RestDesigner extends React.Component<Props, State> { evt.stopPropagation() this.setState({selectedStep: undefined,}) } - }; + } addRest = (rest: RestDefinition) => { const clone = CamelUtil.cloneIntegration(this.state.integration); @@ -263,20 +263,20 @@ export class RestDesigner extends React.Component<Props, State> { {config && this.getRestConfigurationCard(config)} {data && this.getRestCards(data)} <div className="add-rest"> + <Button + variant={data?.length === 0 ? "primary" : "secondary"} + data-click="ADD_REST" + icon={<PlusIcon/>} + onClick={e => this.createRest()}>Create service + </Button> {config === undefined && <Button - variant="primary" + variant="secondary" data-click="ADD_REST_REST_CONFIG" icon={<PlusIcon/>} - onClick={e => this.createRestConfiguration()}>Create REST Configuration + onClick={e => this.createRestConfiguration()}>Create configuration </Button> } - <Button - variant={data?.length === 0 ? "primary" : "secondary"} - data-click="ADD_REST" - icon={<PlusIcon/>} - onClick={e => this.createRest()}>Create REST Service - </Button> </div> </div> </div> diff --git a/karavan-space/src/designer/route/DslElement.tsx b/karavan-space/src/designer/route/DslElement.tsx index 31ee222..868f3e2 100644 --- a/karavan-space/src/designer/route/DslElement.tsx +++ b/karavan-space/src/designer/route/DslElement.tsx @@ -131,15 +131,19 @@ export class DslElement extends React.Component<Props, State> { hasBorder = (): boolean => { return (this.props.step?.hasSteps() && !['FromDefinition'].includes(this.props.step.dslName)) - || ['RouteDefinition', 'TryDefinition', 'ChoiceDefinition', 'SwitchDefinition'].includes(this.props.step.dslName); + || ['RouteConfigurationDefinition', + 'RouteDefinition', + 'TryDefinition', + 'ChoiceDefinition', + 'SwitchDefinition'].includes(this.props.step.dslName); } isNotDraggable = (): boolean => { - return ['FromDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(this.props.step.dslName); + return ['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(this.props.step.dslName); } isWide = (): boolean => { - return ['RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition'] + return ['RouteConfigurationDefinition', 'RouteDefinition', 'ChoiceDefinition', 'SwitchDefinition', 'MulticastDefinition', 'TryDefinition', 'CircuitBreakerDefinition'] .includes(this.props.step.dslName); } @@ -153,7 +157,7 @@ export class DslElement extends React.Component<Props, State> { } isRoot = (): boolean => { - return this.props.step?.dslName?.startsWith("RouteDefinition"); + return ['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step?.dslName); } isInStepWithChildren = () => { @@ -224,14 +228,18 @@ export class DslElement extends React.Component<Props, State> { getHeader = () => { const step: CamelElement = this.props.step; + const parent = this.props.parent; + const inRouteConfiguration = parent !== undefined && parent.dslName === 'RouteConfigurationDefinition'; const availableModels = CamelUi.getSelectorModelsForParent(step.dslName, false); const showAddButton = !['CatchDefinition', 'RouteDefinition'].includes(step.dslName) && availableModels.length > 0; - const showInsertButton = !['FromDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName); - const headerClass = step.dslName === 'RouteDefinition' ? "header-route" : "header" + const showInsertButton = + !['FromDefinition', 'RouteConfigurationDefinition', 'RouteDefinition', 'CatchDefinition', 'FinallyDefinition', 'WhenDefinition', 'OtherwiseDefinition'].includes(step.dslName) + && !inRouteConfiguration; + const headerClass = ['RouteConfigurationDefinition', 'RouteDefinition'].includes(step.dslName) ? "header-route" : "header" const headerClasses = this.isSelected() ? headerClass + " selected" : headerClass; return ( <div className={headerClasses} style={this.getHeaderStyle()}> - {this.props.step.dslName !== 'RouteDefinition' && + {!['RouteConfigurationDefinition', 'RouteDefinition'].includes(this.props.step.dslName) && <div ref={el => this.sendPosition(el, this.isSelected())} className={"header-icon"} style={this.isWide() ? {width: ""} : {}}> diff --git a/karavan-space/src/designer/route/RouteDesigner.tsx b/karavan-space/src/designer/route/RouteDesigner.tsx index 6c976a9..949295e 100644 --- a/karavan-space/src/designer/route/RouteDesigner.tsx +++ b/karavan-space/src/designer/route/RouteDesigner.tsx @@ -28,7 +28,7 @@ import {DslSelector} from "./DslSelector"; import {DslMetaModel} from "../utils/DslMetaModel"; import {DslProperties} from "./DslProperties"; import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; -import {FromDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition"; +import {FromDefinition, RouteConfigurationDefinition, RouteDefinition} from "karavan-core/lib/model/CamelDefinition"; import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition"; import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"; import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi"; @@ -40,7 +40,6 @@ import {CamelUi, RouteToCreate} from "../utils/CamelUi"; import {findDOMNode} from "react-dom"; import {CamelDisplayUtil} from "karavan-core/lib/api/CamelDisplayUtil"; import {toPng} from 'html-to-image'; -import {KaravanDesigner} from "../KaravanDesigner"; interface Props { onSave?: (integration: Integration, propertyOnly: boolean) => void @@ -149,6 +148,7 @@ export class RouteDesigner extends React.Component<Props, State> { showDeleteConfirmation = (id: string) => { let message: string; let ce: CamelElement; + let isRouteConfiguration: boolean = false; ce = CamelDefinitionApiExt.findElementInIntegration(this.state.integration, id)!; if (ce.dslName === 'FromDefinition') { // Get the RouteDefinition for this. Use its uuid. let flows = this.state.integration.spec.flows!; @@ -164,6 +164,9 @@ export class RouteDesigner extends React.Component<Props, State> { message = 'Deleting the first element will delete the entire route!'; } else if (ce.dslName === 'RouteDefinition') { message = 'Delete route?'; + } else if (ce.dslName === 'RouteConfigurationDefinition') { + message = 'Delete route configuration?'; + isRouteConfiguration = true; } else { message = 'Delete element from route?'; } @@ -241,6 +244,19 @@ export class RouteDesigner extends React.Component<Props, State> { } } + createRouteConfiguration = () => { + const clone = CamelUtil.cloneIntegration(this.state.integration); + const routeConfiguration = new RouteConfigurationDefinition(); + const i = CamelDefinitionApiExt.addRouteConfigurationToIntegration(clone, routeConfiguration); + this.setState({ + integration: i, + propertyOnly: false, + key: Math.random().toString(), + selectedStep: routeConfiguration, + selectedUuid: routeConfiguration.uuid, + }); + } + addStep = (step: CamelElement, parentId: string, position?: number | undefined) => { const i = CamelDefinitionApiExt.addStepToIntegration(this.state.integration, step, parentId, position); const clone = CamelUtil.cloneIntegration(i); @@ -356,21 +372,35 @@ export class RouteDesigner extends React.Component<Props, State> { } getGraph() { - const routes = CamelUi.getRoutes(this.state.integration); + const {selectedUuid, integration, key, width, height, top, left} = this.state; + const routes = CamelUi.getRoutes(integration); + const routeConfigurations = CamelUi.getRouteConfigurations(integration); return ( <div ref={this.state.printerRef} className="graph"> - <DslConnections height={this.state.height} width={this.state.width} top={this.state.top} - left={this.state.left} integration={this.state.integration}/> + <DslConnections height={height} width={width} top={top} left={left} integration={integration}/> <div className="flows" data-click="FLOWS" onClick={event => this.unselectElement(event)} ref={el => this.onResizePage(el)}> + {routeConfigurations?.map((routeConfiguration , index: number) => ( + <DslElement key={routeConfiguration.uuid + key} + integration={integration} + openSelector={this.openSelector} + deleteElement={this.showDeleteConfirmation} + selectElement={this.selectElement} + moveElement={this.moveElement} + selectedUuid={selectedUuid} + inSteps={false} + position={index} + step={routeConfiguration} + parent={undefined}/> + ))} {routes?.map((route: any, index: number) => ( - <DslElement key={route.uuid + this.state.key} - integration={this.state.integration} + <DslElement key={route.uuid + key} + integration={integration} openSelector={this.openSelector} deleteElement={this.showDeleteConfirmation} selectElement={this.selectElement} moveElement={this.moveElement} - selectedUuid={this.state.selectedUuid} + selectedUuid={selectedUuid} inSteps={false} position={index} step={route} @@ -379,9 +409,13 @@ export class RouteDesigner extends React.Component<Props, State> { <div className="add-flow"> <Button variant={routes.length === 0 ? "primary" : "secondary"} - data-click="ADD_ROUTE" icon={<PlusIcon/>} - onClick={e => this.openSelector(undefined, undefined)}>Create new route + onClick={e => this.openSelector(undefined, undefined)}>Create route + </Button> + <Button + variant="secondary" + icon={<PlusIcon/>} + onClick={e => this.createRouteConfiguration()}>Create configuration </Button> </div> </div> diff --git a/karavan-space/src/designer/route/property/DslPropertyField.tsx b/karavan-space/src/designer/route/property/DslPropertyField.tsx index f553cfe..a05575f 100644 --- a/karavan-space/src/designer/route/property/DslPropertyField.tsx +++ b/karavan-space/src/designer/route/property/DslPropertyField.tsx @@ -116,6 +116,9 @@ export class DslPropertyField extends React.Component<Props, State> { } propertyChanged = (fieldId: string, value: string | number | boolean | any, newRoute?: RouteToCreate) => { + if (fieldId === 'id' && CamelDefinitionApiExt.hasElementWithId(this.props.integration, value)) { + value = this.props.value; + } this.props.onChange?.call(this, fieldId, value, newRoute); this.setState({selectStatus: new Map<string, boolean>([[fieldId, false]])}); } diff --git a/karavan-space/src/designer/utils/CamelUi.tsx b/karavan-space/src/designer/utils/CamelUi.tsx index 51f8517..ab015ea 100644 --- a/karavan-space/src/designer/utils/CamelUi.tsx +++ b/karavan-space/src/designer/utils/CamelUi.tsx @@ -21,9 +21,16 @@ import {ComponentApi} from "karavan-core/lib/api/ComponentApi"; import {CamelMetadataApi} from "karavan-core/lib/model/CamelMetadata"; import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"; -import {ErrorHandlerDefinition, NamedBeanDefinition, RouteDefinition, SagaDefinition, ToDefinition} from "karavan-core/lib/model/CamelDefinition"; +import { + InterceptSendToEndpointDefinition, + NamedBeanDefinition, + RouteConfigurationDefinition, + RouteDefinition, + SagaDefinition, + ToDefinition +} from "karavan-core/lib/model/CamelDefinition"; import {CamelElement, Integration} from "karavan-core/lib/model/IntegrationDefinition"; -import {AggregateIcon, ChoiceIcon, FilterIcon, SagaIcon, SortIcon, SplitIcon} from "./KaravanIcons"; +import {AggregateIcon, ChoiceIcon, FilterIcon, Intercept, InterceptFrom, InterceptSendToEndpoint, OnCompletion, SagaIcon, SortIcon, SplitIcon} from "./KaravanIcons"; import React from "react"; const StepElements: string[] = [ @@ -489,6 +496,10 @@ export class CamelUi { case 'SagaDefinition' :return <SagaIcon/>; case 'FilterDefinition' :return <FilterIcon/>; case 'SortDefinition' :return <SortIcon/>; + case 'OnCompletionDefinition' :return <OnCompletion/>; + case 'InterceptDefinition' :return <Intercept/>; + case 'InterceptFromDefinition' :return <InterceptFrom/>; + case 'InterceptSendToEndpointDefinition' :return <InterceptSendToEndpoint/>; default: return this.getIconFromSource(CamelUi.getIconSrcForName(dslName)) } } @@ -512,7 +523,7 @@ export class CamelUi { const result = new Map<string, number>(); result.set('routes', i.spec.flows?.filter((e: any) => e.dslName === 'RouteDefinition').length || 0); result.set('rest', i.spec.flows?.filter((e: any) => e.dslName === 'RestDefinition').length || 0); - result.set('error', i.spec.flows?.filter((e: any) => e.dslName === 'ErrorHandlerDefinition').length || 0); + result.set('routeConfiguration', i.spec.flows?.filter((e: any) => e.dslName === 'RouteConfigurationDefinition').length || 0); const beans = i.spec.flows?.filter((e: any) => e.dslName === 'Beans'); if (beans && beans.length > 0 && beans[0].beans && beans[0].beans.length > 0){ result.set('beans', Array.from(beans[0].beans).length); @@ -536,8 +547,10 @@ export class CamelUi { return result; } - static getErrorHandler = (integration: Integration): ErrorHandlerDefinition | undefined => { - const errorHandler = integration.spec.flows?.filter((e: any) => e.dslName === 'ErrorHandlerDefinition').at(0); - return errorHandler; + static getRouteConfigurations = (integration: Integration): RouteConfigurationDefinition[] | undefined => { + const result: CamelElement[] = []; + integration.spec.flows?.filter((e: any) => e.dslName === 'RouteConfigurationDefinition') + .forEach((f: any) => result.push(f)); + return result; } } \ No newline at end of file diff --git a/karavan-space/src/designer/utils/KaravanIcons.tsx b/karavan-space/src/designer/utils/KaravanIcons.tsx index 37a3182..63bb26d 100644 --- a/karavan-space/src/designer/utils/KaravanIcons.tsx +++ b/karavan-space/src/designer/utils/KaravanIcons.tsx @@ -254,16 +254,17 @@ export function getDesignerIcon(icon: string) { <path d="M16,4A12,12,0,1,1,4,16,12.0136,12.0136,0,0,1,16,4m0-2A14,14,0,1,0,30,16,14,14,0,0,0,16,2Z" transform="translate(0)"/> <rect id="_Transparent_Rectangle_" data-name="<Transparent Rectangle>" className="cls-1" width="32" height="32"/> </svg>) - if (icon === 'template') return ( - <svg className="top-icon" width="32px" height="32px" viewBox="0 0 32 32" id="icon" xmlns="http://www.w3.org/2000/svg"> + if (icon === 'routeConfiguration') return ( + <svg className="top-icon" width="32" height="32" viewBox="0 0 32 32"> <defs> <style>{".cls-1{fill:none;}"}</style> </defs> - <title>code</title> - <polygon points="31 16 24 23 22.59 21.59 28.17 16 22.59 10.41 24 9 31 16"/> - <polygon points="1 16 8 9 9.41 10.41 3.83 16 9.41 21.59 8 23 1 16"/> - <rect x="5.91" y="15" width="20.17" height="2" transform="translate(-3.6 27.31) rotate(-75)"/> - <rect id="_Transparent_Rectangle_" data-name="<Transparent Rectangle>" className="cls-1" width="32" height="32" transform="translate(0 32) rotate(-90)"/> + <path d="M28.83 21.17L25 17.37l.67-.67a1 1 0 000-1.41l-6-6a1 1 0 00-1.41 0l-.79.79-6.76-6.79a1 1 0 00-1.41 0l-4 4-.12.15-4 6a1 1 0 00.12 1.26l3 3a1 1 0 001.42 0L10 13.41l2.09 2.09-4.8 4.79a1 1 0 000 1.41l2 2a1 1 0 00.71.3 1 1 0 00.52-.15l4.33-2.6 2.44 2.45a1 1 0 001.41 0l.67-.7 3.79 3.83a4 4 0 005.66-5.66zM10 10.58l-5 5-1.71-1.71 3.49-5.24L10 5.41l6.09 6.09-2.59 2.58zm8 11l-2.84-2.84-5 3-.74-.74L19 11.41 23.59 16zm9.42 3.83a2 2 0 01-2.83 0l-3.8-3.79 2.83-2.83 3.8 3.79a2 2 0 0 [...] + <path + d="M0 0H32V32H0z" + className="cls-1" + data-name="<Transparent Rectangle>" + ></path> </svg>) if (icon === 'yaml') return ( <svg className="top-icon" x="0px" y="0px" width="32px" height="32px" @@ -449,6 +450,105 @@ export function SortIcon() { ); } +export function OnCompletion() { + return ( + <svg + className="icon" width="32px" height="32px" + xmlns="http://www.w3.org/2000/svg" + id="icon" + fill="#000" + viewBox="0 0 32 32" + > + <defs> + <style>{".cls-1 { fill: none; }"}</style> + </defs> + <path d="M22 26.59L19.41 24 18 25.41 22 29.41 30 21.41 28.59 20 22 26.59z"></path> + <circle cx="16" cy="16" r="2"></circle> + <path d="M16 22a6 6 0 116-6 6.007 6.007 0 01-6 6zm0-10a4 4 0 104 4 4.005 4.005 0 00-4-4z"></path> + <path d="M28 16a12 12 0 10-12 12v-2a10 10 0 1110-10z"></path> + <path + id="_Transparent_Rectangle_" + d="M0 0H32V32H0z" + className="cls-1" + data-name="<Transparent Rectangle>" + ></path> + </svg> + ); +} + +export function Intercept() { + return ( + <svg + className="icon" width="32px" height="32px" + xmlns="http://www.w3.org/2000/svg" + id="icon" + fill="#000" + viewBox="0 0 32 32" + > + <defs> + <style>{".cls-1 { fill: none; }"}</style> + </defs> + <path d="M15 4H17V28H15z"></path> + <path d="M10 7v18H4V7h6m0-2H4a2 2 0 00-2 2v18a2 2 0 002 2h6a2 2 0 002-2V7a2 2 0 00-2-2zM28 7v18h-6V7h6m0-2h-6a2 2 0 00-2 2v18a2 2 0 002 2h6a2 2 0 002-2V7a2 2 0 00-2-2z"></path> + <path + id="_Transparent_Rectangle_" + d="M0 0H32V32H0z" + className="cls-1" + data-name="<Transparent Rectangle>" + ></path> + </svg> + ); +} + +export function InterceptFrom() { + return ( + <svg + className="icon" width="32px" height="32px" + xmlns="http://www.w3.org/2000/svg" + id="icon" + fill="#000" + viewBox="0 0 32 32" + > + <defs> + <style>{".cls-1 { fill: none; }"}</style> + </defs> + <path d="M26 30H14a2 2 0 01-2-2v-3h2v3h12V4H14v3h-2V4a2 2 0 012-2h12a2 2 0 012 2v24a2 2 0 01-2 2z"></path> + <path d="M14.59 20.59L18.17 17 4 17 4 15 18.17 15 14.59 11.41 16 10 22 16 16 22 14.59 20.59z"></path> + <path + id="_Transparent_Rectangle_" + d="M0 0H32V32H0z" + className="cls-1" + data-name="<Transparent Rectangle>" + ></path> + </svg> + ); +} + +export function InterceptSendToEndpoint() { + return ( + <svg + className="icon" width="32px" height="32px" + xmlns="http://www.w3.org/2000/svg" + id="icon" + fill="#000" + viewBox="0 0 32 32" + > + <defs> + <style>{".cls-1 { fill: none; }"}</style> + </defs> + <path d="M6 30h12a2.002 2.002 0 002-2v-3h-2v3H6V4h12v3h2V4a2.002 2.002 0 00-2-2H6a2.002 2.002 0 00-2 2v24a2.002 2.002 0 002 2z"></path> + <path d="M20.586 20.586L24.172 17 10 17 10 15 24.172 15 20.586 11.414 22 10 28 16 22 22 20.586 20.586z"></path> + <path + id="_Transparent_Rectangle_" + d="M0 0H32V32H0z" + className="cls-1" + data-name="<Transparent Rectangle>" + ></path> + </svg> + ); +} + + export function SpringIcon() { return ( <svg