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
commit 8c72c517ffc3651fa290aa9a0155ec379946ced2 Author: Marat Gubaidullin <[email protected]> AuthorDate: Thu Feb 22 09:30:30 2024 -0500 Fix #1141 --- karavan-core/src/core/api/TopologyUtils.ts | 4 +- karavan-core/src/core/api/VariableUtil.ts | 99 ++++++++++++++++++++++ karavan-core/test/routes.spec.ts | 1 - karavan-core/test/variable1.camel.yaml | 42 +++++++++ karavan-core/test/variable2.camel.yaml | 42 +++++++++ .../test/{routes.spec.ts => variables.spec.ts} | 25 ++---- karavan-designer/public/example/demo.camel.yaml | 42 +++++++++ karavan-designer/src/App.tsx | 2 +- karavan-designer/src/DesignerPage.tsx | 1 - karavan-designer/src/designer/DesignerStore.ts | 27 +++--- karavan-designer/src/designer/KaravanDesigner.tsx | 12 +-- .../property/property/DslPropertyField.tsx | 14 ++- .../property/property/VariablesDropdown.tsx | 9 +- .../src/designer/route/RouteDesigner.tsx | 2 +- karavan-space/src/designer/DesignerStore.ts | 27 +++--- karavan-space/src/designer/KaravanDesigner.tsx | 12 +-- .../property/property/DslPropertyField.tsx | 14 ++- .../property/property/VariablesDropdown.tsx | 9 +- karavan-space/src/designer/route/RouteDesigner.tsx | 2 +- karavan-space/src/space/SpacePage.tsx | 1 - karavan-vscode/webview/App.tsx | 1 - .../src/main/webui/src/designer/DesignerStore.ts | 27 +++--- .../main/webui/src/designer/KaravanDesigner.tsx | 12 +-- .../property/property/DslPropertyField.tsx | 14 ++- .../property/property/VariablesDropdown.tsx | 9 +- .../webui/src/designer/route/RouteDesigner.tsx | 2 +- .../src/main/webui/src/project/FileEditor.tsx | 1 - 27 files changed, 339 insertions(+), 114 deletions(-) diff --git a/karavan-core/src/core/api/TopologyUtils.ts b/karavan-core/src/core/api/TopologyUtils.ts index 38932df2..73821493 100644 --- a/karavan-core/src/core/api/TopologyUtils.ts +++ b/karavan-core/src/core/api/TopologyUtils.ts @@ -211,9 +211,9 @@ export class TopologyUtils { return result; } - static findTopologyOutgoingNodes = (integration: Integration[]): TopologyOutgoingNode[] => { + static findTopologyOutgoingNodes = (integrations: Integration[]): TopologyOutgoingNode[] => { const result:TopologyOutgoingNode[] = []; - integration.forEach(i => { + integrations.forEach(i => { const filename = i.metadata.name; const routes = i.spec.flows?.filter(flow => flow.dslName === 'RouteDefinition'); routes?.forEach(route => { diff --git a/karavan-core/src/core/api/VariableUtil.ts b/karavan-core/src/core/api/VariableUtil.ts new file mode 100644 index 00000000..2e417ea3 --- /dev/null +++ b/karavan-core/src/core/api/VariableUtil.ts @@ -0,0 +1,99 @@ +/* + * 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 { + CamelElement, + IntegrationFile, +} from '../model/IntegrationDefinition'; +import { CamelDefinitionYaml } from './CamelDefinitionYaml'; +import { FromDefinition} from '../model/CamelDefinition'; +import { CamelDefinitionApiExt } from './CamelDefinitionApiExt'; + +const sendReceiveDSL: string[] = + ['ToDefinition', 'FromDefinition', 'ToDynamicDefinition', 'PollEnrichDefinition', + 'EnrichDefinition', 'WireTapDefinition', 'UnmarshalDefinition', 'MarshalDefinition']; + +export const GLOBAL = 'global:'; +export const ROUTE = 'route:'; + + +export class VariableUtil { + private constructor() { + } + + static findVariables = (files: IntegrationFile[]): string[] => { + const result: string[] = [] + const integrations = files.map(file => CamelDefinitionYaml.yamlToIntegration(file.name, file.code)); + integrations.forEach(i => { + const filename = i.metadata.name; + const routes = i.spec.flows?.filter(flow => flow.dslName === 'RouteDefinition'); + routes?.forEach(route => { + const from: FromDefinition = route.from; + VariableUtil.findVariablesInStep(from, result); + }) + + }) + return VariableUtil.sortVariables(result); + }; + + static sortVariables = (variables: string[]): string [] => { + const global = [...new Set(variables.filter(v => v && v.startsWith(GLOBAL)))].sort(); + const route = [...new Set(variables.filter(v => v && v.startsWith(ROUTE)))].sort(); + const exchange = [...new Set(variables.filter(v => v && !v.startsWith(ROUTE) && !v.startsWith(GLOBAL)))].sort(); + return global.concat(route, exchange); + } + + static findVariablesInStep = (step: CamelElement, result: string[]) => { + if (step !== undefined) { + const el = (step as any); + if (sendReceiveDSL.includes(el.dslName)) { + VariableUtil.findVariablesInProps(el, 'variableSend', result); + VariableUtil.findVariablesInProps(el, 'variableReceive', result); + } else if (el.dslName === 'ConvertVariableDefinition') { + VariableUtil.findVariablesInProps(el, 'name', result); + VariableUtil.findVariablesInProps(el, 'toName', result); + } else if (el.dslName === 'SetVariableDefinition') { + VariableUtil.findVariablesInProps(el, 'name', result); + } else if (el.dslName === 'RemoveVariableDefinition') { + VariableUtil.findVariablesInProps(el, 'name', result); + } + // check children elements + const childElements = CamelDefinitionApiExt.getElementChildrenDefinition(el.dslName); + childElements.forEach(child => { + if (child.multiple) { + const sub = (el[child.name] as CamelElement[]); + VariableUtil.findVariablesInSteps(sub, result); + } else { + const sub = (el[child.name] as CamelElement); + VariableUtil.findVariablesInStep(sub, result); + } + }) + } + } + + static findVariablesInSteps = (steps: CamelElement[], result: string[]) => { + if (steps !== undefined && steps.length > 0) { + steps.forEach(step => VariableUtil.findVariablesInStep(step, result)) + } + } + + static findVariablesInProps = (step: CamelElement, propertyName: string, result: string[]) => { + const el = (step as any); + if (el.hasOwnProperty(propertyName)) { + result.push(el[propertyName]) + } + } +} diff --git a/karavan-core/test/routes.spec.ts b/karavan-core/test/routes.spec.ts index a1fb9a9a..b593cd1c 100644 --- a/karavan-core/test/routes.spec.ts +++ b/karavan-core/test/routes.spec.ts @@ -19,7 +19,6 @@ import * as fs from 'fs'; import 'mocha'; import {CamelDefinitionYaml} from "../src/core/api/CamelDefinitionYaml"; - describe('Plain YAML with route to integration', () => { it('YAML <-> Object', () => { diff --git a/karavan-core/test/variable1.camel.yaml b/karavan-core/test/variable1.camel.yaml new file mode 100644 index 00000000..65fde7da --- /dev/null +++ b/karavan-core/test/variable1.camel.yaml @@ -0,0 +1,42 @@ +- route: + id: route-18e5 + nodePrefixId: route-656 + from: + id: from-9468 + uri: amqp + variableReceive: global:variable1 + steps: + - doTry: + id: doTry-8bb0 + doCatch: + - id: doCatch-19a9 + steps: + - convertVariableTo: + id: convertVariableTo-b0e5 + name: xxx + toName: yyy + steps: + - multicast: + id: multicast-ad44 + steps: + - to: + id: to-0c20 + variableSend: send1 + variableReceive: receive1 + uri: activemq + - to: + id: to-e79a + variableSend: route:aran1 + variableReceive: route:aran1 + uri: arangodb + - setVariable: + id: setVariable-957d + name: varrr + expression: + simple: + id: simple-cd4b + - to: + id: to-60f8 + variableSend: hello + variableReceive: world + uri: amqp diff --git a/karavan-core/test/variable2.camel.yaml b/karavan-core/test/variable2.camel.yaml new file mode 100644 index 00000000..a79f5cf9 --- /dev/null +++ b/karavan-core/test/variable2.camel.yaml @@ -0,0 +1,42 @@ +- route: + id: route-18e5 + nodePrefixId: route-656 + from: + id: from-9468 + uri: amqp + variableReceive: variable2 + steps: + - doTry: + id: doTry-8bb0 + doCatch: + - id: doCatch-19a9 + steps: + - convertVariableTo: + id: convertVariableTo-b0e5 + name: xxx2 + toName: yyy2 + steps: + - multicast: + id: multicast-ad44 + steps: + - to: + id: to-0c20 + variableSend: asend2 + variableReceive: breceive2 + uri: activemq + - to: + id: to-e79a + variableSend: route:aran2 + variableReceive: global:aran2 + uri: arangodb + - setVariable: + id: setVariable-957d + name: varrr2 + expression: + simple: + id: simple-cd4b + - to: + id: to-60f8 + variableSend: hello2 + variableReceive: world2 + uri: amqp diff --git a/karavan-core/test/routes.spec.ts b/karavan-core/test/variables.spec.ts similarity index 50% copy from karavan-core/test/routes.spec.ts copy to karavan-core/test/variables.spec.ts index a1fb9a9a..0e28a961 100644 --- a/karavan-core/test/routes.spec.ts +++ b/karavan-core/test/variables.spec.ts @@ -17,22 +17,15 @@ import {expect} from 'chai'; import * as fs from 'fs'; import 'mocha'; -import {CamelDefinitionYaml} from "../src/core/api/CamelDefinitionYaml"; +import { VariableUtil } from '../src/core/api/VariableUtil'; +import { IntegrationFile } from '../src/core/model/IntegrationDefinition'; +describe('Variables', () => { -describe('Plain YAML with route to integration', () => { - - it('YAML <-> Object', () => { - const yaml = fs.readFileSync('test/routes1.yaml',{encoding:'utf8', flag:'r'}); - const i = CamelDefinitionYaml.yamlToIntegration("test1.yaml", yaml); - expect(i.metadata.name).to.equal('test1.yaml'); - expect(i.kind).to.equal('Integration'); - expect(i.spec.flows?.length).to.equal(1); - expect(i.type).to.equal('plain'); - if (i.spec.flows) expect(i.spec.flows[0].from.uri).to.equal('timer'); - if (i.spec.flows) expect(i.spec.flows[0].from.parameters.name).to.equal('info'); - const yaml2 = CamelDefinitionYaml.integrationToYaml(i); - expect(yaml.replaceAll("\r\n", "\n")).to.equal(yaml2); // replace for Windows compatibility + it('Find Variables', () => { + const yaml1 = fs.readFileSync('test/variable1.camel.yaml',{encoding:'utf8', flag:'r'}); + const yaml2 = fs.readFileSync('test/variable2.camel.yaml',{encoding:'utf8', flag:'r'}); + const variables = VariableUtil.findVariables([new IntegrationFile('1', yaml1), new IntegrationFile('2', yaml2)]); + expect(variables.length).to.equal(19); }); - -}); +}); \ No newline at end of file diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml index b740a018..458ab33c 100644 --- a/karavan-designer/public/example/demo.camel.yaml +++ b/karavan-designer/public/example/demo.camel.yaml @@ -84,6 +84,48 @@ - to: id: to-4711 uri: metrics +- route: + id: route-18e5 + nodePrefixId: route-656 + from: + id: from-9468 + uri: amqp + variableReceive: global:variable1 + steps: + - doTry: + id: doTry-8bb0 + doCatch: + - id: doCatch-19a9 + steps: + - convertVariableTo: + id: convertVariableTo-b0e5 + name: xxx + toName: yyy + steps: + - multicast: + id: multicast-ad44 + steps: + - to: + id: to-0c20 + variableSend: send1 + variableReceive: receive1 + uri: activemq + - to: + id: to-e79a + variableSend: route:aran1 + variableReceive: route:aran1 + uri: arangodb + - setVariable: + id: setVariable-957d + name: varrr + expression: + simple: + id: simple-cd4b + - to: + id: to-60f8 + variableSend: hello + variableReceive: world + uri: amqp # steps: diff --git a/karavan-designer/src/App.tsx b/karavan-designer/src/App.tsx index 03046460..331aa343 100644 --- a/karavan-designer/src/App.tsx +++ b/karavan-designer/src/App.tsx @@ -114,7 +114,7 @@ export function App() { }); function save(filename: string, yaml: string, propertyOnly: boolean) { - console.log(yaml); + // console.log(yaml); } function getSpinner() { diff --git a/karavan-designer/src/DesignerPage.tsx b/karavan-designer/src/DesignerPage.tsx index 7fd313b0..36a2dbef 100644 --- a/karavan-designer/src/DesignerPage.tsx +++ b/karavan-designer/src/DesignerPage.tsx @@ -76,7 +76,6 @@ export const DesignerPage = (props: Props) => { // "timer.delay", // "sql.query" ]} - variables={['var1', 'global:data1', 'route:example1']} onSavePropertyPlaceholder={(key, value) => console.log("onSavePropertyPlaceholder", key, value)} beans={[]} onInternalConsumerClick={(uri, name, routeId) => { diff --git a/karavan-designer/src/designer/DesignerStore.ts b/karavan-designer/src/designer/DesignerStore.ts index faa3fc11..48976eb5 100644 --- a/karavan-designer/src/designer/DesignerStore.ts +++ b/karavan-designer/src/designer/DesignerStore.ts @@ -21,6 +21,7 @@ import {createWithEqualityFn} from "zustand/traditional"; import {shallow} from "zustand/shallow"; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition"; +import {VariableUtil} from "karavan-core/lib/api/VariableUtil"; interface IntegrationState { integration: Integration; @@ -31,6 +32,9 @@ interface IntegrationState { files: IntegrationFile [] setFiles: (files: IntegrationFile []) => void resetFiles: (files: IntegrationFile []) => void + variables: string[], + setVariables: (variables: string[]) => void; + addVariable: (variable: string) => void; } export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) => ({ @@ -62,6 +66,19 @@ export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) return {files: [...files]}; }); }, + variables: [], + setVariables: (variables: string[]) => { + set((state: IntegrationState) => { + return {variables: [...variables]}; + }) + }, + addVariable: (variable: string) => { + set((state: IntegrationState) => { + const vars = VariableUtil.findVariables(state.files); + if (!vars.includes(variable)) vars.push(variable); + return {variables: VariableUtil.sortVariables(vars)}; + }); + }, }), shallow) @@ -186,7 +203,6 @@ type DesignerState = { left: number, moveElements: [string | undefined, string | undefined], propertyPlaceholders: string[], - variables: string[], beans: RegistryBeanDefinition[] } @@ -207,7 +223,6 @@ const designerState: DesignerState = { left: 0, moveElements: [undefined, undefined], propertyPlaceholders: [], - variables: [], beans: [] }; @@ -226,7 +241,6 @@ type DesignerAction = { setNotification: (notificationBadge: boolean, notificationMessage: [string, string]) => void; setMoveElements: (moveElements: [string | undefined, string | undefined]) => void; setPropertyPlaceholders: (propertyPlaceholders: string[]) => void; - setVariables: (variables: string[]) => void; setBeans: (beans: RegistryBeanDefinition[]) => void; } @@ -290,13 +304,6 @@ export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAct return state; }) }, - setVariables: (variables: string[]) => { - set((state: DesignerState) => { - state.variables.length = 0; - state.variables.push(...variables); - return state; - }) - }, setBeans: (beans: RegistryBeanDefinition[]) => { set((state: DesignerState) => { state.beans.length = 0; diff --git a/karavan-designer/src/designer/KaravanDesigner.tsx b/karavan-designer/src/designer/KaravanDesigner.tsx index a9beb01a..8e49e054 100644 --- a/karavan-designer/src/designer/KaravanDesigner.tsx +++ b/karavan-designer/src/designer/KaravanDesigner.tsx @@ -42,6 +42,7 @@ import {CodeEditor} from "./editor/CodeEditor"; import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon'; import {KameletDesigner} from "./kamelet/KameletDesigner"; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; +import {VariableUtil} from "karavan-core/lib/api/VariableUtil"; interface Props { onSave: (filename: string, yaml: string, propertyOnly: boolean) => void @@ -56,7 +57,6 @@ interface Props { showCodeTab: boolean tab?: "routes" | "rest" | "beans" propertyPlaceholders: string[] - variables: string[] beans: RegistryBeanDefinition[] files: IntegrationFile[] } @@ -64,11 +64,11 @@ interface Props { export function KaravanDesigner(props: Props) { const [tab, setTab] = useState<string>('routes'); - const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders, setBeans, setVariables] = + const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders, setBeans] = useDesignerStore((s) => - [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders, s.setBeans, s.setVariables], shallow) - const [integration, setIntegration, resetFiles] = useIntegrationStore((s) => - [s.integration, s.setIntegration, s.resetFiles], shallow) + [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders, s.setBeans], shallow) + const [integration, setIntegration, resetFiles, setVariables] = useIntegrationStore((s) => + [s.integration, s.setIntegration, s.resetFiles, s.setVariables], shallow) useEffect(() => { const sub = EventBus.onIntegrationUpdate()?.subscribe((update: IntegrationUpdate) => @@ -93,7 +93,7 @@ export function KaravanDesigner(props: Props) { reset(); setDark(props.dark); setPropertyPlaceholders(props.propertyPlaceholders) - setVariables(props.variables) + setVariables(VariableUtil.findVariables(props.files)) setBeans(props.beans) resetFiles(props.files) setHideLogDSL(props.hideLogDSL === true); diff --git a/karavan-designer/src/designer/property/property/DslPropertyField.tsx b/karavan-designer/src/designer/property/property/DslPropertyField.tsx index 7bc57ce9..43986796 100644 --- a/karavan-designer/src/designer/property/property/DslPropertyField.tsx +++ b/karavan-designer/src/designer/property/property/DslPropertyField.tsx @@ -76,10 +76,7 @@ import {KubernetesIcon} from "../../icons/ComponentIcons"; import {BeanProperties} from "./BeanProperties"; import {PropertyPlaceholderDropdown} from "./PropertyPlaceholderDropdown"; import {VariablesDropdown} from "./VariablesDropdown"; -import {CamelModelMetadata} from "karavan-core/lib/model/CamelMetadata"; - -const GLOBAL = 'global:'; -const ROUTE = 'route:'; +import {ROUTE, GLOBAL} from "karavan-core/lib/api/VariableUtil"; interface Props { property: PropertyMeta, @@ -95,7 +92,7 @@ interface Props { export function DslPropertyField(props: Props) { - const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow) + const [integration, setIntegration, addVariable] = useIntegrationStore((s) => [s.integration, s.setIntegration, s.addVariable], shallow) const [dark, setSelectedStep] = useDesignerStore((s) => [s.dark, s.setSelectedStep], shallow) const [isShowAdvanced, setIsShowAdvanced] = useState<string[]>([]); @@ -165,6 +162,9 @@ export function DslPropertyField(props: Props) { } props.onPropertyChange?.(fieldId, value, newRoute); clearSelection(fieldId); + if (isVariable) { + addVariable(value); + } } function arrayChanged(fieldId: string, value: string) { @@ -272,10 +272,6 @@ export function DslPropertyField(props: Props) { function getVariableInput(property: PropertyMeta) { return <InputGroup> - {/*<InputGroupItem>*/} - {/* <Button variant={'control'}>{GLOBAL}</Button>*/} - {/* <Button variant={'control'}>{ROUTE}</Button>*/} - {/*</InputGroupItem>*/} <InputGroupItem> <ToggleGroup aria-label="Variable type"> <ToggleGroupItem text="global:" key='global' buttonId={"global-variable-"+ property.name} diff --git a/karavan-designer/src/designer/property/property/VariablesDropdown.tsx b/karavan-designer/src/designer/property/property/VariablesDropdown.tsx index e9f9baa5..154ccc05 100644 --- a/karavan-designer/src/designer/property/property/VariablesDropdown.tsx +++ b/karavan-designer/src/designer/property/property/VariablesDropdown.tsx @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import { Dropdown, MenuToggleElement, @@ -26,7 +26,7 @@ import { import '../../karavan.css'; import './VariablesDropdown.css'; import "@patternfly/patternfly/patternfly.css"; -import {useDesignerStore} from "../../DesignerStore"; +import {useIntegrationStore} from "../../DesignerStore"; import {shallow} from "zustand/shallow"; import EllipsisVIcon from "@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon"; @@ -36,10 +36,11 @@ interface Props { export function VariablesDropdown(props: Props) { - const [variables, setVariables] = useDesignerStore((s) => - [s.variables, s.setVariables], shallow) + const [variables] = useIntegrationStore((s) => [s.variables], shallow) const [isOpenVariablesDropdown, setOpenVariablesDropdown] = useState<boolean>(false); + useEffect(() => console.log(variables), [variables]) + const hasVariables = (variables && variables.length > 0 ); function parametersChanged(name: string ) { diff --git a/karavan-designer/src/designer/route/RouteDesigner.tsx b/karavan-designer/src/designer/route/RouteDesigner.tsx index 7a36f814..77678b3a 100644 --- a/karavan-designer/src/designer/route/RouteDesigner.tsx +++ b/karavan-designer/src/designer/route/RouteDesigner.tsx @@ -74,7 +74,7 @@ export function RouteDesigner() { useEffect(()=> { const interval = setInterval(() => { changeGraphSize(); - }, 500); + }, 300); const commandSub = EventBus.onCommand()?.subscribe((command: Command) => onCommand(command, printerRef)); if (flowRef.current === null) { clearSteps(); diff --git a/karavan-space/src/designer/DesignerStore.ts b/karavan-space/src/designer/DesignerStore.ts index faa3fc11..48976eb5 100644 --- a/karavan-space/src/designer/DesignerStore.ts +++ b/karavan-space/src/designer/DesignerStore.ts @@ -21,6 +21,7 @@ import {createWithEqualityFn} from "zustand/traditional"; import {shallow} from "zustand/shallow"; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition"; +import {VariableUtil} from "karavan-core/lib/api/VariableUtil"; interface IntegrationState { integration: Integration; @@ -31,6 +32,9 @@ interface IntegrationState { files: IntegrationFile [] setFiles: (files: IntegrationFile []) => void resetFiles: (files: IntegrationFile []) => void + variables: string[], + setVariables: (variables: string[]) => void; + addVariable: (variable: string) => void; } export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) => ({ @@ -62,6 +66,19 @@ export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) return {files: [...files]}; }); }, + variables: [], + setVariables: (variables: string[]) => { + set((state: IntegrationState) => { + return {variables: [...variables]}; + }) + }, + addVariable: (variable: string) => { + set((state: IntegrationState) => { + const vars = VariableUtil.findVariables(state.files); + if (!vars.includes(variable)) vars.push(variable); + return {variables: VariableUtil.sortVariables(vars)}; + }); + }, }), shallow) @@ -186,7 +203,6 @@ type DesignerState = { left: number, moveElements: [string | undefined, string | undefined], propertyPlaceholders: string[], - variables: string[], beans: RegistryBeanDefinition[] } @@ -207,7 +223,6 @@ const designerState: DesignerState = { left: 0, moveElements: [undefined, undefined], propertyPlaceholders: [], - variables: [], beans: [] }; @@ -226,7 +241,6 @@ type DesignerAction = { setNotification: (notificationBadge: boolean, notificationMessage: [string, string]) => void; setMoveElements: (moveElements: [string | undefined, string | undefined]) => void; setPropertyPlaceholders: (propertyPlaceholders: string[]) => void; - setVariables: (variables: string[]) => void; setBeans: (beans: RegistryBeanDefinition[]) => void; } @@ -290,13 +304,6 @@ export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAct return state; }) }, - setVariables: (variables: string[]) => { - set((state: DesignerState) => { - state.variables.length = 0; - state.variables.push(...variables); - return state; - }) - }, setBeans: (beans: RegistryBeanDefinition[]) => { set((state: DesignerState) => { state.beans.length = 0; diff --git a/karavan-space/src/designer/KaravanDesigner.tsx b/karavan-space/src/designer/KaravanDesigner.tsx index a9beb01a..8e49e054 100644 --- a/karavan-space/src/designer/KaravanDesigner.tsx +++ b/karavan-space/src/designer/KaravanDesigner.tsx @@ -42,6 +42,7 @@ import {CodeEditor} from "./editor/CodeEditor"; import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon'; import {KameletDesigner} from "./kamelet/KameletDesigner"; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; +import {VariableUtil} from "karavan-core/lib/api/VariableUtil"; interface Props { onSave: (filename: string, yaml: string, propertyOnly: boolean) => void @@ -56,7 +57,6 @@ interface Props { showCodeTab: boolean tab?: "routes" | "rest" | "beans" propertyPlaceholders: string[] - variables: string[] beans: RegistryBeanDefinition[] files: IntegrationFile[] } @@ -64,11 +64,11 @@ interface Props { export function KaravanDesigner(props: Props) { const [tab, setTab] = useState<string>('routes'); - const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders, setBeans, setVariables] = + const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders, setBeans] = useDesignerStore((s) => - [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders, s.setBeans, s.setVariables], shallow) - const [integration, setIntegration, resetFiles] = useIntegrationStore((s) => - [s.integration, s.setIntegration, s.resetFiles], shallow) + [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders, s.setBeans], shallow) + const [integration, setIntegration, resetFiles, setVariables] = useIntegrationStore((s) => + [s.integration, s.setIntegration, s.resetFiles, s.setVariables], shallow) useEffect(() => { const sub = EventBus.onIntegrationUpdate()?.subscribe((update: IntegrationUpdate) => @@ -93,7 +93,7 @@ export function KaravanDesigner(props: Props) { reset(); setDark(props.dark); setPropertyPlaceholders(props.propertyPlaceholders) - setVariables(props.variables) + setVariables(VariableUtil.findVariables(props.files)) setBeans(props.beans) resetFiles(props.files) setHideLogDSL(props.hideLogDSL === true); diff --git a/karavan-space/src/designer/property/property/DslPropertyField.tsx b/karavan-space/src/designer/property/property/DslPropertyField.tsx index 7bc57ce9..43986796 100644 --- a/karavan-space/src/designer/property/property/DslPropertyField.tsx +++ b/karavan-space/src/designer/property/property/DslPropertyField.tsx @@ -76,10 +76,7 @@ import {KubernetesIcon} from "../../icons/ComponentIcons"; import {BeanProperties} from "./BeanProperties"; import {PropertyPlaceholderDropdown} from "./PropertyPlaceholderDropdown"; import {VariablesDropdown} from "./VariablesDropdown"; -import {CamelModelMetadata} from "karavan-core/lib/model/CamelMetadata"; - -const GLOBAL = 'global:'; -const ROUTE = 'route:'; +import {ROUTE, GLOBAL} from "karavan-core/lib/api/VariableUtil"; interface Props { property: PropertyMeta, @@ -95,7 +92,7 @@ interface Props { export function DslPropertyField(props: Props) { - const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow) + const [integration, setIntegration, addVariable] = useIntegrationStore((s) => [s.integration, s.setIntegration, s.addVariable], shallow) const [dark, setSelectedStep] = useDesignerStore((s) => [s.dark, s.setSelectedStep], shallow) const [isShowAdvanced, setIsShowAdvanced] = useState<string[]>([]); @@ -165,6 +162,9 @@ export function DslPropertyField(props: Props) { } props.onPropertyChange?.(fieldId, value, newRoute); clearSelection(fieldId); + if (isVariable) { + addVariable(value); + } } function arrayChanged(fieldId: string, value: string) { @@ -272,10 +272,6 @@ export function DslPropertyField(props: Props) { function getVariableInput(property: PropertyMeta) { return <InputGroup> - {/*<InputGroupItem>*/} - {/* <Button variant={'control'}>{GLOBAL}</Button>*/} - {/* <Button variant={'control'}>{ROUTE}</Button>*/} - {/*</InputGroupItem>*/} <InputGroupItem> <ToggleGroup aria-label="Variable type"> <ToggleGroupItem text="global:" key='global' buttonId={"global-variable-"+ property.name} diff --git a/karavan-space/src/designer/property/property/VariablesDropdown.tsx b/karavan-space/src/designer/property/property/VariablesDropdown.tsx index e9f9baa5..154ccc05 100644 --- a/karavan-space/src/designer/property/property/VariablesDropdown.tsx +++ b/karavan-space/src/designer/property/property/VariablesDropdown.tsx @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import { Dropdown, MenuToggleElement, @@ -26,7 +26,7 @@ import { import '../../karavan.css'; import './VariablesDropdown.css'; import "@patternfly/patternfly/patternfly.css"; -import {useDesignerStore} from "../../DesignerStore"; +import {useIntegrationStore} from "../../DesignerStore"; import {shallow} from "zustand/shallow"; import EllipsisVIcon from "@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon"; @@ -36,10 +36,11 @@ interface Props { export function VariablesDropdown(props: Props) { - const [variables, setVariables] = useDesignerStore((s) => - [s.variables, s.setVariables], shallow) + const [variables] = useIntegrationStore((s) => [s.variables], shallow) const [isOpenVariablesDropdown, setOpenVariablesDropdown] = useState<boolean>(false); + useEffect(() => console.log(variables), [variables]) + const hasVariables = (variables && variables.length > 0 ); function parametersChanged(name: string ) { diff --git a/karavan-space/src/designer/route/RouteDesigner.tsx b/karavan-space/src/designer/route/RouteDesigner.tsx index 7a36f814..77678b3a 100644 --- a/karavan-space/src/designer/route/RouteDesigner.tsx +++ b/karavan-space/src/designer/route/RouteDesigner.tsx @@ -74,7 +74,7 @@ export function RouteDesigner() { useEffect(()=> { const interval = setInterval(() => { changeGraphSize(); - }, 500); + }, 300); const commandSub = EventBus.onCommand()?.subscribe((command: Command) => onCommand(command, printerRef)); if (flowRef.current === null) { clearSteps(); diff --git a/karavan-space/src/space/SpacePage.tsx b/karavan-space/src/space/SpacePage.tsx index 0ce7ddfc..548e5025 100644 --- a/karavan-space/src/space/SpacePage.tsx +++ b/karavan-space/src/space/SpacePage.tsx @@ -114,7 +114,6 @@ export class SpacePage extends React.Component<Props, State> { onInternalConsumerClick={(uri?: string, name?: string, routeId?: string) => { console.log("onInternalConsumerClick", uri, name, routeId) }} - variables={[]} files={[new IntegrationFile("example.camel.yaml", yaml)]} /> ) diff --git a/karavan-vscode/webview/App.tsx b/karavan-vscode/webview/App.tsx index 82148d01..6062553b 100644 --- a/karavan-vscode/webview/App.tsx +++ b/karavan-vscode/webview/App.tsx @@ -260,7 +260,6 @@ class App extends React.Component<Props, State> { onInternalConsumerClick={(uri, name, routeId) => { vscode.postMessage({ command: 'internalConsumerClick', uri: uri, name: name, routeId: routeId }); }} - variables={[]} files={this.state.files.map(f => new IntegrationFile(f.name, f.code))} /> } diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts b/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts index faa3fc11..48976eb5 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts +++ b/karavan-web/karavan-app/src/main/webui/src/designer/DesignerStore.ts @@ -21,6 +21,7 @@ import {createWithEqualityFn} from "zustand/traditional"; import {shallow} from "zustand/shallow"; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; import {IntegrationFile} from "karavan-core/lib/model/IntegrationDefinition"; +import {VariableUtil} from "karavan-core/lib/api/VariableUtil"; interface IntegrationState { integration: Integration; @@ -31,6 +32,9 @@ interface IntegrationState { files: IntegrationFile [] setFiles: (files: IntegrationFile []) => void resetFiles: (files: IntegrationFile []) => void + variables: string[], + setVariables: (variables: string[]) => void; + addVariable: (variable: string) => void; } export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) => ({ @@ -62,6 +66,19 @@ export const useIntegrationStore = createWithEqualityFn<IntegrationState>((set) return {files: [...files]}; }); }, + variables: [], + setVariables: (variables: string[]) => { + set((state: IntegrationState) => { + return {variables: [...variables]}; + }) + }, + addVariable: (variable: string) => { + set((state: IntegrationState) => { + const vars = VariableUtil.findVariables(state.files); + if (!vars.includes(variable)) vars.push(variable); + return {variables: VariableUtil.sortVariables(vars)}; + }); + }, }), shallow) @@ -186,7 +203,6 @@ type DesignerState = { left: number, moveElements: [string | undefined, string | undefined], propertyPlaceholders: string[], - variables: string[], beans: RegistryBeanDefinition[] } @@ -207,7 +223,6 @@ const designerState: DesignerState = { left: 0, moveElements: [undefined, undefined], propertyPlaceholders: [], - variables: [], beans: [] }; @@ -226,7 +241,6 @@ type DesignerAction = { setNotification: (notificationBadge: boolean, notificationMessage: [string, string]) => void; setMoveElements: (moveElements: [string | undefined, string | undefined]) => void; setPropertyPlaceholders: (propertyPlaceholders: string[]) => void; - setVariables: (variables: string[]) => void; setBeans: (beans: RegistryBeanDefinition[]) => void; } @@ -290,13 +304,6 @@ export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAct return state; }) }, - setVariables: (variables: string[]) => { - set((state: DesignerState) => { - state.variables.length = 0; - state.variables.push(...variables); - return state; - }) - }, setBeans: (beans: RegistryBeanDefinition[]) => { set((state: DesignerState) => { state.beans.length = 0; diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx index a9beb01a..8e49e054 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/KaravanDesigner.tsx @@ -42,6 +42,7 @@ import {CodeEditor} from "./editor/CodeEditor"; import BellIcon from '@patternfly/react-icons/dist/esm/icons/bell-icon'; import {KameletDesigner} from "./kamelet/KameletDesigner"; import {RegistryBeanDefinition} from "karavan-core/lib/model/CamelDefinition"; +import {VariableUtil} from "karavan-core/lib/api/VariableUtil"; interface Props { onSave: (filename: string, yaml: string, propertyOnly: boolean) => void @@ -56,7 +57,6 @@ interface Props { showCodeTab: boolean tab?: "routes" | "rest" | "beans" propertyPlaceholders: string[] - variables: string[] beans: RegistryBeanDefinition[] files: IntegrationFile[] } @@ -64,11 +64,11 @@ interface Props { export function KaravanDesigner(props: Props) { const [tab, setTab] = useState<string>('routes'); - const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders, setBeans, setVariables] = + const [setDark, hideLogDSL, setHideLogDSL, setSelectedStep, reset, badge, message, setPropertyPlaceholders, setBeans] = useDesignerStore((s) => - [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders, s.setBeans, s.setVariables], shallow) - const [integration, setIntegration, resetFiles] = useIntegrationStore((s) => - [s.integration, s.setIntegration, s.resetFiles], shallow) + [s.setDark, s.hideLogDSL, s.setHideLogDSL, s.setSelectedStep, s.reset, s.notificationBadge, s.notificationMessage, s.setPropertyPlaceholders, s.setBeans], shallow) + const [integration, setIntegration, resetFiles, setVariables] = useIntegrationStore((s) => + [s.integration, s.setIntegration, s.resetFiles, s.setVariables], shallow) useEffect(() => { const sub = EventBus.onIntegrationUpdate()?.subscribe((update: IntegrationUpdate) => @@ -93,7 +93,7 @@ export function KaravanDesigner(props: Props) { reset(); setDark(props.dark); setPropertyPlaceholders(props.propertyPlaceholders) - setVariables(props.variables) + setVariables(VariableUtil.findVariables(props.files)) setBeans(props.beans) resetFiles(props.files) setHideLogDSL(props.hideLogDSL === true); diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx index 7bc57ce9..43986796 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/DslPropertyField.tsx @@ -76,10 +76,7 @@ import {KubernetesIcon} from "../../icons/ComponentIcons"; import {BeanProperties} from "./BeanProperties"; import {PropertyPlaceholderDropdown} from "./PropertyPlaceholderDropdown"; import {VariablesDropdown} from "./VariablesDropdown"; -import {CamelModelMetadata} from "karavan-core/lib/model/CamelMetadata"; - -const GLOBAL = 'global:'; -const ROUTE = 'route:'; +import {ROUTE, GLOBAL} from "karavan-core/lib/api/VariableUtil"; interface Props { property: PropertyMeta, @@ -95,7 +92,7 @@ interface Props { export function DslPropertyField(props: Props) { - const [integration, setIntegration] = useIntegrationStore((s) => [s.integration, s.setIntegration], shallow) + const [integration, setIntegration, addVariable] = useIntegrationStore((s) => [s.integration, s.setIntegration, s.addVariable], shallow) const [dark, setSelectedStep] = useDesignerStore((s) => [s.dark, s.setSelectedStep], shallow) const [isShowAdvanced, setIsShowAdvanced] = useState<string[]>([]); @@ -165,6 +162,9 @@ export function DslPropertyField(props: Props) { } props.onPropertyChange?.(fieldId, value, newRoute); clearSelection(fieldId); + if (isVariable) { + addVariable(value); + } } function arrayChanged(fieldId: string, value: string) { @@ -272,10 +272,6 @@ export function DslPropertyField(props: Props) { function getVariableInput(property: PropertyMeta) { return <InputGroup> - {/*<InputGroupItem>*/} - {/* <Button variant={'control'}>{GLOBAL}</Button>*/} - {/* <Button variant={'control'}>{ROUTE}</Button>*/} - {/*</InputGroupItem>*/} <InputGroupItem> <ToggleGroup aria-label="Variable type"> <ToggleGroupItem text="global:" key='global' buttonId={"global-variable-"+ property.name} diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/VariablesDropdown.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/VariablesDropdown.tsx index e9f9baa5..154ccc05 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/property/property/VariablesDropdown.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/property/property/VariablesDropdown.tsx @@ -14,7 +14,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, {useState} from 'react'; +import React, {useEffect, useState} from 'react'; import { Dropdown, MenuToggleElement, @@ -26,7 +26,7 @@ import { import '../../karavan.css'; import './VariablesDropdown.css'; import "@patternfly/patternfly/patternfly.css"; -import {useDesignerStore} from "../../DesignerStore"; +import {useIntegrationStore} from "../../DesignerStore"; import {shallow} from "zustand/shallow"; import EllipsisVIcon from "@patternfly/react-icons/dist/esm/icons/ellipsis-v-icon"; @@ -36,10 +36,11 @@ interface Props { export function VariablesDropdown(props: Props) { - const [variables, setVariables] = useDesignerStore((s) => - [s.variables, s.setVariables], shallow) + const [variables] = useIntegrationStore((s) => [s.variables], shallow) const [isOpenVariablesDropdown, setOpenVariablesDropdown] = useState<boolean>(false); + useEffect(() => console.log(variables), [variables]) + const hasVariables = (variables && variables.length > 0 ); function parametersChanged(name: string ) { diff --git a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx index 7a36f814..77678b3a 100644 --- a/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/designer/route/RouteDesigner.tsx @@ -74,7 +74,7 @@ export function RouteDesigner() { useEffect(()=> { const interval = setInterval(() => { changeGraphSize(); - }, 500); + }, 300); const commandSub = EventBus.onCommand()?.subscribe((command: Command) => onCommand(command, printerRef)); if (flowRef.current === null) { clearSteps(); diff --git a/karavan-web/karavan-app/src/main/webui/src/project/FileEditor.tsx b/karavan-web/karavan-app/src/main/webui/src/project/FileEditor.tsx index e1553c52..dcea5772 100644 --- a/karavan-web/karavan-app/src/main/webui/src/project/FileEditor.tsx +++ b/karavan-web/karavan-app/src/main/webui/src/project/FileEditor.tsx @@ -127,7 +127,6 @@ export function FileEditor(props: Props) { onSavePropertyPlaceholder={onSavePropertyPlaceholder} beans={beans} onInternalConsumerClick={internalConsumerClick} - variables={[]} files={files.map(f => new IntegrationFile(f.name, f.code))} /> )
