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 157c868 Clone for beans, REST and dependencies (#263) 157c868 is described below commit 157c868f53cb0d4ca7e826dc4f302ab898481833 Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Wed Mar 30 20:17:07 2022 -0400 Clone for beans, REST and dependencies (#263) * Clone bean and dependency * Rest clone --- karavan-core/src/core/api/CamelDefinitionApiExt.ts | 54 ++++++++++++++++++---- karavan-designer/src/App.tsx | 2 +- .../src/designer/beans/BeanProperties.tsx | 20 +++++++- .../src/designer/beans/BeansDesigner.tsx | 3 +- .../designer/dependencies/DependenciesDesigner.tsx | 4 +- .../designer/dependencies/DependencyProperties.tsx | 24 +++++++++- .../src/designer/rest/RestDesigner.tsx | 25 +++++++++- .../src/designer/route/DslProperties.tsx | 33 ++++++++++++- .../src/designer/route/RouteDesigner.tsx | 17 +++---- 9 files changed, 158 insertions(+), 24 deletions(-) diff --git a/karavan-core/src/core/api/CamelDefinitionApiExt.ts b/karavan-core/src/core/api/CamelDefinitionApiExt.ts index 1c3e309..4936d27 100644 --- a/karavan-core/src/core/api/CamelDefinitionApiExt.ts +++ b/karavan-core/src/core/api/CamelDefinitionApiExt.ts @@ -201,7 +201,7 @@ export class CamelDefinitionApiExt { flows.push(...integration.spec.flows?.filter(flow => flow.dslName !== 'Beans') || []); integration.spec.flows?.filter(flow => flow.dslName === 'Beans').forEach(flow => { const beans: NamedBeanDefinition[] = []; - if ((flow as Beans).beans.filter(b => b.uuid === bean.uuid).length === 0){ + if ((flow as Beans).beans.filter(b => b.uuid === bean.uuid).length === 0) { beans.push(...(flow as Beans).beans.filter(b => b.uuid !== bean.uuid)); beans.push(bean); } else { @@ -261,13 +261,25 @@ export class CamelDefinitionApiExt { if (rest.uuid !== restUuid) { flows.push(rest); } else { - switch (method.dslName){ - case 'GetDefinition': rest.get = this.addRestMethodToRestMethods(rest.get, method); break; - case 'PostDefinition': rest.post = this.addRestMethodToRestMethods(rest.post, method); break; - case 'PutDefinition': rest.put = this.addRestMethodToRestMethods(rest.put, method); break; - case 'PatchDefinition': rest.patch = this.addRestMethodToRestMethods(rest.patch, method); break; - case 'DeleteDefinition': rest.delete = this.addRestMethodToRestMethods(rest.delete, method); break; - case 'HeadDefinition': rest.head = this.addRestMethodToRestMethods(rest.head, method); break; + switch (method.dslName) { + case 'GetDefinition': + rest.get = this.addRestMethodToRestMethods(rest.get, method); + break; + case 'PostDefinition': + rest.post = this.addRestMethodToRestMethods(rest.post, method); + break; + case 'PutDefinition': + rest.put = this.addRestMethodToRestMethods(rest.put, method); + break; + case 'PatchDefinition': + rest.patch = this.addRestMethodToRestMethods(rest.patch, method); + break; + case 'DeleteDefinition': + rest.delete = this.addRestMethodToRestMethods(rest.delete, method); + break; + case 'HeadDefinition': + rest.head = this.addRestMethodToRestMethods(rest.head, method); + break; } flows.push(rest); } @@ -286,6 +298,32 @@ export class CamelDefinitionApiExt { return elements; } + static findRestMethodParent = (integration: Integration, method: CamelElement): string | undefined => { + const rests: RestDefinition[] = integration.spec.flows?.filter(flow => flow.dslName === 'RestDefinition') || []; + for (let rest of rests) { + switch (method.dslName) { + case 'GetDefinition': + if (rest.get?.find(m => m.uuid === method.uuid)) return rest.uuid; + else break; + case 'PostDefinition': + if (rest.post?.find(m => m.uuid === method.uuid)) return rest.uuid; + else break; + case 'PutDefinition': + if (rest.put?.find(m => m.uuid === method.uuid)) return rest.uuid; + else break; + case 'PatchDefinition': + if (rest.patch?.find(m => m.uuid === method.uuid)) return rest.uuid; + else break; + case 'DeleteDefinition': + if (rest.delete?.find(m => m.uuid === method.uuid)) return rest.uuid; + else break; + case 'HeadDefinition': + if (rest.head?.find(m => m.uuid === method.uuid)) return rest.uuid; + else break; + } + } + } + static deleteRestConfigurationFromIntegration = (integration: Integration): Integration => { const flows: any[] = []; integration.spec.flows?.filter(flow => flow.dslName !== 'RestConfigurationDefinition').forEach(x => flows.push(x)); diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx index 403b964..35f5a3a 100644 --- a/karavan-designer/src/App.tsx +++ b/karavan-designer/src/App.tsx @@ -98,7 +98,7 @@ class App extends React.Component<Props, State> { save(filename: string, yaml: string, propertyOnly: boolean) { // console.log(filename); - // console.log(yaml); + console.log(yaml); // console.log(propertyOnly); } diff --git a/karavan-designer/src/designer/beans/BeanProperties.tsx b/karavan-designer/src/designer/beans/BeanProperties.tsx index a25320f..d0a9138 100644 --- a/karavan-designer/src/designer/beans/BeanProperties.tsx +++ b/karavan-designer/src/designer/beans/BeanProperties.tsx @@ -18,7 +18,7 @@ import React from 'react'; import { Form, FormGroup, - TextInput, Button, + TextInput, Button, Title, Tooltip, Text, TextVariants, } from '@patternfly/react-core'; import '../karavan.css'; import "@patternfly/patternfly/patternfly.css"; @@ -31,12 +31,14 @@ import {v4 as uuidv4} from "uuid"; import DeleteIcon from "@patternfly/react-icons/dist/js/icons/times-icon"; import AddIcon from "@patternfly/react-icons/dist/js/icons/plus-circle-icon"; import {IntegrationHeader} from "../utils/KaravanComponents"; +import CloneIcon from '@patternfly/react-icons/dist/esm/icons/clone-icon' interface Props { integration: Integration bean?: NamedBeanDefinition dark: boolean onChange: (bean: NamedBeanDefinition) => void + onClone: (bean: NamedBeanDefinition) => void } interface State { @@ -103,10 +105,26 @@ export class BeanProperties extends React.Component<Props, State> { }) } + cloneBean = () => { + if (this.state.bean) { + const bean = CamelUtil.cloneBean(this.state.bean); + bean.uuid = uuidv4(); + this.props.onClone?.call(this, bean); + } + } + getBeanForm() { const bean = this.state.bean; return ( <> + <div className="headers"> + <div className="top"> + <Title headingLevel="h1" size="md">Bean</Title> + <Tooltip content="Clone bean" position="bottom"> + <Button variant="link" onClick={() => this.cloneBean()} icon={<CloneIcon/>}/> + </Tooltip> + </div> + </div> <FormGroup label="Name" fieldId="name" isRequired> <TextInput className="text-field" isRequired type="text" id="name" name="name" value={bean?.name} onChange={e => this.beanChanged("name", e)}/> diff --git a/karavan-designer/src/designer/beans/BeansDesigner.tsx b/karavan-designer/src/designer/beans/BeansDesigner.tsx index 30991b7..b61e8f6 100644 --- a/karavan-designer/src/designer/beans/BeansDesigner.tsx +++ b/karavan-designer/src/designer/beans/BeansDesigner.tsx @@ -123,7 +123,8 @@ export class BeansDesigner extends React.Component<Props, State> { <BeanProperties integration={this.props.integration} bean={this.state.selectedBean} dark={this.props.dark} - onChange={this.changeBean}/> + onChange={this.changeBean} + onClone={this.changeBean}/> </DrawerPanelContent> ) } diff --git a/karavan-designer/src/designer/dependencies/DependenciesDesigner.tsx b/karavan-designer/src/designer/dependencies/DependenciesDesigner.tsx index ef1e16a..fc40f30 100644 --- a/karavan-designer/src/designer/dependencies/DependenciesDesigner.tsx +++ b/karavan-designer/src/designer/dependencies/DependenciesDesigner.tsx @@ -26,6 +26,7 @@ import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {Integration, Dependency} from "karavan-core/lib/model/IntegrationDefinition"; import {CamelDefinitionApiExt} from "karavan-core/lib/api/CamelDefinitionApiExt"; import {DependencyCard} from "./DependencyCard"; +import {NamedBeanDefinition} from "../../../../karavan-core/lib/model/CamelDefinition"; interface Props { onSave?: (integration: Integration, propertyOnly: boolean) => void @@ -126,7 +127,8 @@ export class DependenciesDesigner extends React.Component<Props, State> { <DependencyProperties integration={this.props.integration} dependency={this.state.selectedDep} dark={this.props.dark} - onChange={this.changeDep}/> + onChange={this.changeDep} + onClone={this.changeDep}/> </DrawerPanelContent> ) } diff --git a/karavan-designer/src/designer/dependencies/DependencyProperties.tsx b/karavan-designer/src/designer/dependencies/DependencyProperties.tsx index 5a31ea4..c629a55 100644 --- a/karavan-designer/src/designer/dependencies/DependencyProperties.tsx +++ b/karavan-designer/src/designer/dependencies/DependencyProperties.tsx @@ -16,21 +16,26 @@ */ import React from 'react'; import { + Button, Form, FormGroup, - TextInput, + TextInput, Title, Tooltip, } from '@patternfly/react-core'; import '../karavan.css'; import "@patternfly/patternfly/patternfly.css"; import {Integration, Dependency} from "karavan-core/lib/model/IntegrationDefinition"; import {CamelUtil} from "karavan-core/lib/api/CamelUtil"; import {IntegrationHeader} from "../utils/KaravanComponents"; +import {NamedBeanDefinition} from "../../../../karavan-core/lib/model/CamelDefinition"; +import {v4 as uuidv4} from "uuid"; +import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon"; interface Props { integration: Integration dependency?: Dependency dark: boolean onChange: (dependency: Dependency) => void + onClone: (dependency: Dependency) => void } interface State { @@ -58,10 +63,27 @@ export class DependencyProperties extends React.Component<Props, State> { } } + + cloneDependency = () => { + if (this.state.dependency) { + const dependency = CamelUtil.cloneDependency(this.state.dependency); + dependency.uuid = uuidv4(); + this.props.onClone?.call(this, dependency); + } + } + getDependencyForm() { const dependency = this.state.dependency; return ( <> + <div className="headers"> + <div className="top"> + <Title headingLevel="h1" size="md">Dependency</Title> + <Tooltip content="Clone dependency" position="bottom"> + <Button variant="link" onClick={() => this.cloneDependency()} icon={<CloneIcon/>}/> + </Tooltip> + </div> + </div> <FormGroup label="Group" fieldId="group" isRequired> <TextInput className="text-field" isRequired type="text" id="group" name="group" value={dependency?.group} onChange={e => this.dependencyChanged("group", e)}/> diff --git a/karavan-designer/src/designer/rest/RestDesigner.tsx b/karavan-designer/src/designer/rest/RestDesigner.tsx index d7630a6..083717c 100644 --- a/karavan-designer/src/designer/rest/RestDesigner.tsx +++ b/karavan-designer/src/designer/rest/RestDesigner.tsx @@ -32,6 +32,7 @@ import {RestMethodSelector} from "./RestMethodSelector"; import {DslMetaModel} from "../utils/DslMetaModel"; import {CamelDefinitionApi} from "karavan-core/lib/api/CamelDefinitionApi"; import {RestConfigurationCard} from "./RestConfigurationCard"; +import {v4 as uuidv4} from "uuid"; interface Props { onSave?: (integration: Integration, propertyOnly: boolean) => void @@ -167,6 +168,27 @@ export class RestDesigner extends React.Component<Props, State> { } } + cloneRest = (rest: CamelElement) => { + if (rest.dslName === 'RestDefinition'){ + const cloneRest = CamelUtil.cloneStep(rest); + cloneRest.uuid = uuidv4(); + const cloneIntegration = CamelUtil.cloneIntegration(this.state.integration); + const i = CamelDefinitionApiExt.addRestToIntegration(cloneIntegration, cloneRest); + this.setState({integration: i, propertyOnly: false, key: Math.random().toString(), selectedStep: cloneRest}); + } else if (rest.dslName === 'RestConfigurationDefinition') { + // could be only one RestConfigurationDefinition + } else if (this.state.selectedStep) { + const parentId = CamelDefinitionApiExt.findRestMethodParent(this.state.integration, rest); + if (parentId){ + const cloneRest = CamelUtil.cloneStep(rest); + cloneRest.uuid = uuidv4(); + const cloneIntegration = CamelUtil.cloneIntegration(this.state.integration); + const i = CamelDefinitionApiExt.addRestMethodToIntegration(cloneIntegration, cloneRest, parentId); + this.setState({integration: i, key: Math.random().toString(), selectedStep: cloneRest, showSelector: false}); + } + } + } + selectMethod = (element: CamelElement) => { this.setState({selectedStep: element, showSelector: true}) } @@ -219,7 +241,8 @@ export class RestDesigner extends React.Component<Props, State> { onIntegrationUpdate={this.onIntegrationUpdate} onPropertyUpdate={this.onPropertyUpdate} clipboardStep={undefined} - onSaveClipboardStep={element => {}} + isRouteDesigner={false} + onClone={this.cloneRest} /> </DrawerPanelContent> ) diff --git a/karavan-designer/src/designer/route/DslProperties.tsx b/karavan-designer/src/designer/route/DslProperties.tsx index 23ad9e9..d7540b5 100644 --- a/karavan-designer/src/designer/route/DslProperties.tsx +++ b/karavan-designer/src/designer/route/DslProperties.tsx @@ -38,6 +38,7 @@ import {CamelMetadataApi, PropertyMeta} from "karavan-core/lib/model/CamelMetada import {IntegrationHeader} from "../utils/KaravanComponents"; import CopyIcon from '@patternfly/react-icons/dist/esm/icons/copy-icon' import PasteIcon from '@patternfly/react-icons/dist/esm/icons/paste-icon' +import CloneIcon from "@patternfly/react-icons/dist/esm/icons/clone-icon"; interface Props { integration: Integration, @@ -45,7 +46,9 @@ interface Props { onIntegrationUpdate?: any, onPropertyUpdate?: (element: CamelElement, updatedUuid: string, newRoute?: RouteToCreate) => void clipboardStep?: CamelElement - onSaveClipboardStep: (element?: CamelElement) => void + onSaveClipboardStep?: (element?: CamelElement) => void + onClone?: (element: CamelElement) => void + isRouteDesigner: boolean } interface State { @@ -111,6 +114,12 @@ export class DslProperties extends React.Component<Props, State> { } } + cloneElement = () => { + if (this.state.step) { + this.props.onClone?.call(this, this.state.step); + } + } + componentDidUpdate = (prevProps: Readonly<Props>, prevState: Readonly<State>, snapshot?: any) => { if (prevProps.step !== this.props.step) { this.setStep(this.props.step); @@ -124,7 +133,7 @@ export class DslProperties extends React.Component<Props, State> { }); } - getComponentHeader = (): JSX.Element => { + getRouteHeader= (): JSX.Element => { const title = this.state.step && CamelUi.getTitle(this.state.step) const kamelet = this.state.step && CamelUi.getKamelet(this.state.step) const description = this.state.step && kamelet @@ -142,11 +151,31 @@ export class DslProperties extends React.Component<Props, State> { </Tooltip> </div> <Text component={TextVariants.p}>{description}</Text> + </div> + ) + } + getClonableElementHeader = (): JSX.Element => { + const title = this.state.step && CamelUi.getTitle(this.state.step) + const description = this.state.step?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(this.state.step?.dslName)?.description : title; + return ( + <div className="headers"> + <div className="top"> + <Title headingLevel="h1" size="md">{title}</Title> + <Tooltip content="Clone element" position="bottom"> + <Button variant="link" onClick={() => this.cloneElement()} icon={<CloneIcon/>}/> + </Tooltip> + </div> + <Text component={TextVariants.p}>{description}</Text> </div> ) } + getComponentHeader = (): JSX.Element => { + if (this.props.isRouteDesigner) return this.getRouteHeader() + else return this.getClonableElementHeader(); + } + getProperties = (): PropertyMeta[] => { const dslName = this.state.step?.dslName; return CamelDefinitionApiExt.getElementProperties(dslName) diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx index 18e4f90..d493ba1 100644 --- a/karavan-designer/src/designer/route/RouteDesigner.tsx +++ b/karavan-designer/src/designer/route/RouteDesigner.tsx @@ -313,14 +313,15 @@ export class RouteDesigner extends React.Component<Props, State> { getPropertiesPanel() { return ( <DrawerPanelContent isResizable hasNoBorder defaultSize={'400px'} maxSize={'800px'} minSize={'300px'}> - <DslProperties ref={this.state.ref} - integration={this.state.integration} - step={this.state.selectedStep} - onIntegrationUpdate={this.onIntegrationUpdate} - onPropertyUpdate={this.onPropertyUpdate} - clipboardStep={this.state.clipboardStep} - onSaveClipboardStep={this.saveToClipboard} - /> + <DslProperties ref={this.state.ref} + integration={this.state.integration} + step={this.state.selectedStep} + onIntegrationUpdate={this.onIntegrationUpdate} + onPropertyUpdate={this.onPropertyUpdate} + clipboardStep={this.state.clipboardStep} + isRouteDesigner={true} + onSaveClipboardStep={this.saveToClipboard} + /> </DrawerPanelContent> ) }