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 a9d79f19 Route Template Parameters Placeholder a9d79f19 is described below commit a9d79f19edf0c350c568f0917dc5eaa51d2b4fa4 Author: Marat Gubaidullin <ma...@talismancloud.io> AuthorDate: Wed Nov 27 10:58:52 2024 -0500 Route Template Parameters Placeholder --- .../src/main/webui/src/designer/DesignerStore.ts | 10 ++++ .../webui/src/designer/property/DslProperties.tsx | 16 +++++- .../property/PropertyPlaceholderDropdown.css | 5 ++ .../property/PropertyPlaceholderDropdown.tsx | 34 +++++++---- .../src/main/webui/src/designer/utils/CamelUi.tsx | 2 +- karavan-core/src/core/api/CamelDefinitionApiExt.ts | 17 ++++++ karavan-core/src/core/api/CamelDefinitionYaml.ts | 2 +- karavan-designer/public/example/demo.camel.yaml | 65 ++++++++-------------- karavan-designer/src/designer/DesignerStore.ts | 10 ++++ .../src/designer/property/DslProperties.tsx | 16 +++++- .../property/PropertyPlaceholderDropdown.css | 5 ++ .../property/PropertyPlaceholderDropdown.tsx | 34 +++++++---- karavan-designer/src/designer/utils/CamelUi.tsx | 2 +- karavan-space/src/designer/DesignerStore.ts | 10 ++++ .../src/designer/property/DslProperties.tsx | 16 +++++- .../property/PropertyPlaceholderDropdown.css | 5 ++ .../property/PropertyPlaceholderDropdown.tsx | 34 +++++++---- karavan-space/src/designer/utils/CamelUi.tsx | 2 +- 18 files changed, 200 insertions(+), 85 deletions(-) diff --git a/karavan-app/src/main/webui/src/designer/DesignerStore.ts b/karavan-app/src/main/webui/src/designer/DesignerStore.ts index cd610c0f..7535b93b 100644 --- a/karavan-app/src/main/webui/src/designer/DesignerStore.ts +++ b/karavan-app/src/main/webui/src/designer/DesignerStore.ts @@ -213,6 +213,7 @@ type DesignerState = { left: number, moveElements: [string | undefined, string | undefined], propertyPlaceholders: string[], + parameterPlaceholders: [string, string][], // route template parameters beans: BeanFactoryDefinition[] } @@ -232,6 +233,7 @@ const designerState: DesignerState = { left: 0, moveElements: [undefined, undefined], propertyPlaceholders: [], + parameterPlaceholders: [], beans: [] }; @@ -249,6 +251,7 @@ type DesignerAction = { setNotification: (notificationBadge: boolean, notificationMessage: [string, string]) => void; setMoveElements: (moveElements: [string | undefined, string | undefined]) => void; setPropertyPlaceholders: (propertyPlaceholders: string[]) => void; + setParameterPlaceholders: (parameterPlaceholders: [string, string][]) => void; setBeans: (beans: BeanFactoryDefinition[]) => void; } @@ -309,6 +312,13 @@ export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAct return state; }) }, + setParameterPlaceholders: (parameterPlaceholders: [string, string][]) => { + set((state: DesignerState) => { + state.parameterPlaceholders.length = 0; + state.parameterPlaceholders.push(...parameterPlaceholders); + return state; + }) + }, setBeans: (beans: BeanFactoryDefinition[]) => { set((state: DesignerState) => { return {beans: [...beans]}; diff --git a/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx b/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx index dd2376ee..52b36ef7 100644 --- a/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx +++ b/karavan-app/src/main/webui/src/designer/property/DslProperties.tsx @@ -47,6 +47,7 @@ import {PropertiesHeader} from "./PropertiesHeader"; import {PropertyUtil} from "./property/PropertyUtil"; import {usePropertiesStore} from "./PropertyStore"; import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon"; +import {RouteTemplateDefinition} from "karavan-core/lib/model/CamelDefinition"; interface Props { designerType: 'routes' | 'rest' | 'beans' @@ -65,8 +66,8 @@ export function DslProperties(props: Props) { } = usePropertiesHook(props.designerType); - const [selectedStep, dark] - = useDesignerStore((s) => [s.selectedStep, s.dark], shallow) + const [selectedStep, dark, setParameterPlaceholders] + = useDesignerStore((s) => [s.selectedStep, s.dark, s.setParameterPlaceholders], shallow) const [propertyFilter, changedOnly, requiredOnly, setChangedOnly, sensitiveOnly, setSensitiveOnly, setPropertyFilter, setRequiredOnly] = usePropertiesStore((s) => [s.propertyFilter, s.changedOnly, s.requiredOnly, s.setChangedOnly, s.sensitiveOnly, s.setSensitiveOnly, s.setPropertyFilter, s.setRequiredOnly], shallow) @@ -78,8 +79,19 @@ export function DslProperties(props: Props) { setChangedOnly(false); setSensitiveOnly(false); setPropertyFilter(''); + getRouteTemplateParameters() }, [selectedStep?.uuid]) + function getRouteTemplateParameters() { + if (selectedStep) { + const root = CamelDefinitionApiExt.findTopRouteElement(integration, selectedStep.uuid); + if ('RouteTemplateDefinition' === root?.dslName) { + const paramPlaceholders: [string, string][] = (root as RouteTemplateDefinition).parameters?.map(p => [p.name, p.description ||'']) || []; + setParameterPlaceholders(paramPlaceholders); + } + } + } + function getClonableElementHeader(): React.JSX.Element { const title = selectedStep && CamelDisplayUtil.getTitle(selectedStep); const description = selectedStep?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(selectedStep?.dslName)?.description : title; diff --git a/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.css b/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.css index 43991634..3984ae02 100644 --- a/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.css +++ b/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.css @@ -28,6 +28,11 @@ display: none; } +.karavan .properties .property-placeholder-dropdown .pf-v5-c-menu__group-title { + font-weight: bold; + font-size: 13px; +} + .pf-v5-c-popover .property-placeholder-toggle-form { width: 300px; } diff --git a/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.tsx b/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.tsx index e920ce0a..ec532b55 100644 --- a/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.tsx +++ b/karavan-app/src/main/webui/src/designer/property/property/PropertyPlaceholderDropdown.tsx @@ -59,8 +59,8 @@ interface Props { export function PropertyPlaceholderDropdown(props: Props) { - const [propertyPlaceholders, setPropertyPlaceholders] = useDesignerStore((s) => - [s.propertyPlaceholders, s.setPropertyPlaceholders], shallow) + const [propertyPlaceholders, setPropertyPlaceholders, parameterPlaceholders] = useDesignerStore((s) => + [s.propertyPlaceholders, s.setPropertyPlaceholders, s.parameterPlaceholders], shallow) const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = useState<boolean>(false); const [propValue, setPropValue] = useState<string>(''); const [isVisible, setIsVisible] = React.useState(false); @@ -72,13 +72,15 @@ export function PropertyPlaceholderDropdown(props: Props) { const {property, value} = props; const valueIsPlaceholder: boolean = value && value.toString().startsWith('{{') && value.toString().endsWith('}}'); const placeholderValue = valueIsPlaceholder ? value.toString().replace('{{', '').replace('}}', '') : undefined; + const isRouteTemplateParameter = parameterPlaceholders.map(p => p[0]).includes(placeholderValue); const showAddButton = valueIsPlaceholder + && !isRouteTemplateParameter && !propertyPlaceholders.includes(placeholderValue) - && !SYNTAX_EXAMPLES.map(se=> se.value).includes(removeBrackets(placeholderValue)) - && SYNTAX_EXAMPLES.findIndex(se=> removeBrackets(placeholderValue).startsWith(se.key)) === -1; + && !SYNTAX_EXAMPLES.map(se => se.value).includes(removeBrackets(placeholderValue)) + && SYNTAX_EXAMPLES.findIndex(se => removeBrackets(placeholderValue).startsWith(se.key)) === -1; const popoverId = "popover-selector-" + property.hasOwnProperty('name') ? (property as any).name : (property as any).id; - const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0 ); + const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0); function parametersChanged(value: string | number | boolean | any) { if (property instanceof ComponentProperty) { @@ -167,13 +169,23 @@ export function PropertyPlaceholderDropdown(props: Props) { shouldFocusToggleOnSelect > <DropdownList> - {hasPlaceholders && <DropdownGroup label="Application Properties"> - {propertyPlaceholders.map((pp, index) => - <DropdownItem value={pp} key={index}>{pp}</DropdownItem> - )} - </DropdownGroup>} + {parameterPlaceholders && + <DropdownGroup label="Template Parameters" className='property-placeholder-dropdown'> + {parameterPlaceholders.map((pp, index) => + <DropdownItem value={pp[0]} key={index} description={pp[1]}>{pp[0]}</DropdownItem> + )} + </DropdownGroup> + } + {parameterPlaceholders && <Divider component="li"/>} + {hasPlaceholders && + <DropdownGroup label="Application Properties" className='property-placeholder-dropdown'> + {propertyPlaceholders.map((pp, index) => + <DropdownItem value={pp} key={index}>{pp}</DropdownItem> + )} + </DropdownGroup> + } {hasPlaceholders && <Divider component="li"/>} - <DropdownGroup label="Syntax examples"> + <DropdownGroup label="Syntax examples" className='property-placeholder-dropdown'> {SYNTAX_EXAMPLES.map(se => <DropdownItem value={se.value} key={se.key} description={se.description}> {se.value} diff --git a/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx b/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx index f89c8e85..a12e19d6 100644 --- a/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx +++ b/karavan-app/src/main/webui/src/designer/utils/CamelUi.tsx @@ -821,7 +821,7 @@ export class CamelUi { static getFlowCounts = (i: Integration): Map<string, number> => { const result = new Map<string, number>(); - result.set('routes', i.spec.flows?.filter((e: any) => e.dslName === 'RouteDefinition').length || 0); + result.set('routes', i.spec.flows?.filter((e: any) => ['RouteDefinition', 'RouteTemplateDefinition'].includes(e.dslName)).length || 0); result.set('rest', i.spec.flows?.filter((e: any) => e.dslName === 'RestDefinition').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'); diff --git a/karavan-core/src/core/api/CamelDefinitionApiExt.ts b/karavan-core/src/core/api/CamelDefinitionApiExt.ts index f542253d..36c2032b 100644 --- a/karavan-core/src/core/api/CamelDefinitionApiExt.ts +++ b/karavan-core/src/core/api/CamelDefinitionApiExt.ts @@ -174,6 +174,23 @@ export class CamelDefinitionApiExt { return result; }; + static findTopRouteElement = (integration: Integration, uuid: string): CamelElement | undefined => { + const result: string[] = []; + let meta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, uuid); + if (meta) { + while (meta.parentUuid !== undefined) { + if (meta.parentUuid) { + result.push(meta.parentUuid); + meta = CamelDefinitionApiExt.findElementMetaInIntegration(integration, meta.parentUuid); + } else { + break; + } + } + } + const last = result.at(-1); + return last ? CamelDefinitionApiExt.findElementInIntegration(integration, last): undefined + }; + static findElementInElements = (steps: CamelElement[] | undefined, uuid: string, result: CamelElementMeta = new CamelElementMeta(undefined, undefined, undefined), parentUuid?: string,): CamelElementMeta => { if (result?.step !== undefined) { diff --git a/karavan-core/src/core/api/CamelDefinitionYaml.ts b/karavan-core/src/core/api/CamelDefinitionYaml.ts index d82020fb..93ca86f3 100644 --- a/karavan-core/src/core/api/CamelDefinitionYaml.ts +++ b/karavan-core/src/core/api/CamelDefinitionYaml.ts @@ -19,7 +19,7 @@ import { Beans, CamelElement, Integration } from '../model/IntegrationDefinition import { BeanFactoryDefinition, RouteConfigurationDefinition, - RouteDefinition, RouteTemplateDefinition, + RouteDefinition, } from '../model/CamelDefinition'; import { CamelUtil } from './CamelUtil'; import { CamelDefinitionYamlStep } from './CamelDefinitionYamlStep'; diff --git a/karavan-designer/public/example/demo.camel.yaml b/karavan-designer/public/example/demo.camel.yaml index b60f1af7..6c655d36 100644 --- a/karavan-designer/public/example/demo.camel.yaml +++ b/karavan-designer/public/example/demo.camel.yaml @@ -1,24 +1,9 @@ -- routeTemplate: - id: routeFileReaderTemplate - description: File reader - route: - id: routeFileReader - description: File reader - from: - id: from-4101 - uri: sftp - steps: - - to: - id: to-1234 - uri: sql - parameters: - - name: folderName - routeTemplate: id: routeFileReaderTemplate2 - description: File reader 2 + description: File reader template route: id: routeFileReader 2 - description: File reader 2 + description: File reader route from: id: from-4101 uri: sftp @@ -26,29 +11,25 @@ - to: id: to-1234 uri: sql + parameters: + query: "{{folderName}}" + - loadBalance: + id: loadBalance-8172 + - log: + id: log-7fa8 + message: ${body} + - loop: + id: loop-6068 + expression: + groovy: + id: groovy-4e44 + steps: + - marshal: + id: marshal-930d + json: + id: json-135e parameters: - - name: folderName -- route: - id: route-dummy - description: Dummy route - autoStartup: false - from: - id: from-b6a5 - uri: kamelet:aws-kinesis-source - parameters: - secretKey: asasdasd - accessKey: asdasd - steps: - - log: - id: log-b47b - message: DUMMY -- route: - id: route-file - description: File consumer - from: - id: from-file - uri: file - steps: - - log: - id: log-file - message: DUMMY + - description: Folder with files + name: folderName + - description: Server Hostname + name: serverName diff --git a/karavan-designer/src/designer/DesignerStore.ts b/karavan-designer/src/designer/DesignerStore.ts index cd610c0f..7535b93b 100644 --- a/karavan-designer/src/designer/DesignerStore.ts +++ b/karavan-designer/src/designer/DesignerStore.ts @@ -213,6 +213,7 @@ type DesignerState = { left: number, moveElements: [string | undefined, string | undefined], propertyPlaceholders: string[], + parameterPlaceholders: [string, string][], // route template parameters beans: BeanFactoryDefinition[] } @@ -232,6 +233,7 @@ const designerState: DesignerState = { left: 0, moveElements: [undefined, undefined], propertyPlaceholders: [], + parameterPlaceholders: [], beans: [] }; @@ -249,6 +251,7 @@ type DesignerAction = { setNotification: (notificationBadge: boolean, notificationMessage: [string, string]) => void; setMoveElements: (moveElements: [string | undefined, string | undefined]) => void; setPropertyPlaceholders: (propertyPlaceholders: string[]) => void; + setParameterPlaceholders: (parameterPlaceholders: [string, string][]) => void; setBeans: (beans: BeanFactoryDefinition[]) => void; } @@ -309,6 +312,13 @@ export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAct return state; }) }, + setParameterPlaceholders: (parameterPlaceholders: [string, string][]) => { + set((state: DesignerState) => { + state.parameterPlaceholders.length = 0; + state.parameterPlaceholders.push(...parameterPlaceholders); + return state; + }) + }, setBeans: (beans: BeanFactoryDefinition[]) => { set((state: DesignerState) => { return {beans: [...beans]}; diff --git a/karavan-designer/src/designer/property/DslProperties.tsx b/karavan-designer/src/designer/property/DslProperties.tsx index dd2376ee..52b36ef7 100644 --- a/karavan-designer/src/designer/property/DslProperties.tsx +++ b/karavan-designer/src/designer/property/DslProperties.tsx @@ -47,6 +47,7 @@ import {PropertiesHeader} from "./PropertiesHeader"; import {PropertyUtil} from "./property/PropertyUtil"; import {usePropertiesStore} from "./PropertyStore"; import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon"; +import {RouteTemplateDefinition} from "karavan-core/lib/model/CamelDefinition"; interface Props { designerType: 'routes' | 'rest' | 'beans' @@ -65,8 +66,8 @@ export function DslProperties(props: Props) { } = usePropertiesHook(props.designerType); - const [selectedStep, dark] - = useDesignerStore((s) => [s.selectedStep, s.dark], shallow) + const [selectedStep, dark, setParameterPlaceholders] + = useDesignerStore((s) => [s.selectedStep, s.dark, s.setParameterPlaceholders], shallow) const [propertyFilter, changedOnly, requiredOnly, setChangedOnly, sensitiveOnly, setSensitiveOnly, setPropertyFilter, setRequiredOnly] = usePropertiesStore((s) => [s.propertyFilter, s.changedOnly, s.requiredOnly, s.setChangedOnly, s.sensitiveOnly, s.setSensitiveOnly, s.setPropertyFilter, s.setRequiredOnly], shallow) @@ -78,8 +79,19 @@ export function DslProperties(props: Props) { setChangedOnly(false); setSensitiveOnly(false); setPropertyFilter(''); + getRouteTemplateParameters() }, [selectedStep?.uuid]) + function getRouteTemplateParameters() { + if (selectedStep) { + const root = CamelDefinitionApiExt.findTopRouteElement(integration, selectedStep.uuid); + if ('RouteTemplateDefinition' === root?.dslName) { + const paramPlaceholders: [string, string][] = (root as RouteTemplateDefinition).parameters?.map(p => [p.name, p.description ||'']) || []; + setParameterPlaceholders(paramPlaceholders); + } + } + } + function getClonableElementHeader(): React.JSX.Element { const title = selectedStep && CamelDisplayUtil.getTitle(selectedStep); const description = selectedStep?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(selectedStep?.dslName)?.description : title; diff --git a/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.css b/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.css index 43991634..3984ae02 100644 --- a/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.css +++ b/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.css @@ -28,6 +28,11 @@ display: none; } +.karavan .properties .property-placeholder-dropdown .pf-v5-c-menu__group-title { + font-weight: bold; + font-size: 13px; +} + .pf-v5-c-popover .property-placeholder-toggle-form { width: 300px; } diff --git a/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.tsx b/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.tsx index e920ce0a..ec532b55 100644 --- a/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.tsx +++ b/karavan-designer/src/designer/property/property/PropertyPlaceholderDropdown.tsx @@ -59,8 +59,8 @@ interface Props { export function PropertyPlaceholderDropdown(props: Props) { - const [propertyPlaceholders, setPropertyPlaceholders] = useDesignerStore((s) => - [s.propertyPlaceholders, s.setPropertyPlaceholders], shallow) + const [propertyPlaceholders, setPropertyPlaceholders, parameterPlaceholders] = useDesignerStore((s) => + [s.propertyPlaceholders, s.setPropertyPlaceholders, s.parameterPlaceholders], shallow) const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = useState<boolean>(false); const [propValue, setPropValue] = useState<string>(''); const [isVisible, setIsVisible] = React.useState(false); @@ -72,13 +72,15 @@ export function PropertyPlaceholderDropdown(props: Props) { const {property, value} = props; const valueIsPlaceholder: boolean = value && value.toString().startsWith('{{') && value.toString().endsWith('}}'); const placeholderValue = valueIsPlaceholder ? value.toString().replace('{{', '').replace('}}', '') : undefined; + const isRouteTemplateParameter = parameterPlaceholders.map(p => p[0]).includes(placeholderValue); const showAddButton = valueIsPlaceholder + && !isRouteTemplateParameter && !propertyPlaceholders.includes(placeholderValue) - && !SYNTAX_EXAMPLES.map(se=> se.value).includes(removeBrackets(placeholderValue)) - && SYNTAX_EXAMPLES.findIndex(se=> removeBrackets(placeholderValue).startsWith(se.key)) === -1; + && !SYNTAX_EXAMPLES.map(se => se.value).includes(removeBrackets(placeholderValue)) + && SYNTAX_EXAMPLES.findIndex(se => removeBrackets(placeholderValue).startsWith(se.key)) === -1; const popoverId = "popover-selector-" + property.hasOwnProperty('name') ? (property as any).name : (property as any).id; - const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0 ); + const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0); function parametersChanged(value: string | number | boolean | any) { if (property instanceof ComponentProperty) { @@ -167,13 +169,23 @@ export function PropertyPlaceholderDropdown(props: Props) { shouldFocusToggleOnSelect > <DropdownList> - {hasPlaceholders && <DropdownGroup label="Application Properties"> - {propertyPlaceholders.map((pp, index) => - <DropdownItem value={pp} key={index}>{pp}</DropdownItem> - )} - </DropdownGroup>} + {parameterPlaceholders && + <DropdownGroup label="Template Parameters" className='property-placeholder-dropdown'> + {parameterPlaceholders.map((pp, index) => + <DropdownItem value={pp[0]} key={index} description={pp[1]}>{pp[0]}</DropdownItem> + )} + </DropdownGroup> + } + {parameterPlaceholders && <Divider component="li"/>} + {hasPlaceholders && + <DropdownGroup label="Application Properties" className='property-placeholder-dropdown'> + {propertyPlaceholders.map((pp, index) => + <DropdownItem value={pp} key={index}>{pp}</DropdownItem> + )} + </DropdownGroup> + } {hasPlaceholders && <Divider component="li"/>} - <DropdownGroup label="Syntax examples"> + <DropdownGroup label="Syntax examples" className='property-placeholder-dropdown'> {SYNTAX_EXAMPLES.map(se => <DropdownItem value={se.value} key={se.key} description={se.description}> {se.value} diff --git a/karavan-designer/src/designer/utils/CamelUi.tsx b/karavan-designer/src/designer/utils/CamelUi.tsx index f89c8e85..a12e19d6 100644 --- a/karavan-designer/src/designer/utils/CamelUi.tsx +++ b/karavan-designer/src/designer/utils/CamelUi.tsx @@ -821,7 +821,7 @@ export class CamelUi { static getFlowCounts = (i: Integration): Map<string, number> => { const result = new Map<string, number>(); - result.set('routes', i.spec.flows?.filter((e: any) => e.dslName === 'RouteDefinition').length || 0); + result.set('routes', i.spec.flows?.filter((e: any) => ['RouteDefinition', 'RouteTemplateDefinition'].includes(e.dslName)).length || 0); result.set('rest', i.spec.flows?.filter((e: any) => e.dslName === 'RestDefinition').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'); diff --git a/karavan-space/src/designer/DesignerStore.ts b/karavan-space/src/designer/DesignerStore.ts index cd610c0f..7535b93b 100644 --- a/karavan-space/src/designer/DesignerStore.ts +++ b/karavan-space/src/designer/DesignerStore.ts @@ -213,6 +213,7 @@ type DesignerState = { left: number, moveElements: [string | undefined, string | undefined], propertyPlaceholders: string[], + parameterPlaceholders: [string, string][], // route template parameters beans: BeanFactoryDefinition[] } @@ -232,6 +233,7 @@ const designerState: DesignerState = { left: 0, moveElements: [undefined, undefined], propertyPlaceholders: [], + parameterPlaceholders: [], beans: [] }; @@ -249,6 +251,7 @@ type DesignerAction = { setNotification: (notificationBadge: boolean, notificationMessage: [string, string]) => void; setMoveElements: (moveElements: [string | undefined, string | undefined]) => void; setPropertyPlaceholders: (propertyPlaceholders: string[]) => void; + setParameterPlaceholders: (parameterPlaceholders: [string, string][]) => void; setBeans: (beans: BeanFactoryDefinition[]) => void; } @@ -309,6 +312,13 @@ export const useDesignerStore = createWithEqualityFn<DesignerState & DesignerAct return state; }) }, + setParameterPlaceholders: (parameterPlaceholders: [string, string][]) => { + set((state: DesignerState) => { + state.parameterPlaceholders.length = 0; + state.parameterPlaceholders.push(...parameterPlaceholders); + return state; + }) + }, setBeans: (beans: BeanFactoryDefinition[]) => { set((state: DesignerState) => { return {beans: [...beans]}; diff --git a/karavan-space/src/designer/property/DslProperties.tsx b/karavan-space/src/designer/property/DslProperties.tsx index dd2376ee..52b36ef7 100644 --- a/karavan-space/src/designer/property/DslProperties.tsx +++ b/karavan-space/src/designer/property/DslProperties.tsx @@ -47,6 +47,7 @@ import {PropertiesHeader} from "./PropertiesHeader"; import {PropertyUtil} from "./property/PropertyUtil"; import {usePropertiesStore} from "./PropertyStore"; import TimesIcon from "@patternfly/react-icons/dist/esm/icons/times-icon"; +import {RouteTemplateDefinition} from "karavan-core/lib/model/CamelDefinition"; interface Props { designerType: 'routes' | 'rest' | 'beans' @@ -65,8 +66,8 @@ export function DslProperties(props: Props) { } = usePropertiesHook(props.designerType); - const [selectedStep, dark] - = useDesignerStore((s) => [s.selectedStep, s.dark], shallow) + const [selectedStep, dark, setParameterPlaceholders] + = useDesignerStore((s) => [s.selectedStep, s.dark, s.setParameterPlaceholders], shallow) const [propertyFilter, changedOnly, requiredOnly, setChangedOnly, sensitiveOnly, setSensitiveOnly, setPropertyFilter, setRequiredOnly] = usePropertiesStore((s) => [s.propertyFilter, s.changedOnly, s.requiredOnly, s.setChangedOnly, s.sensitiveOnly, s.setSensitiveOnly, s.setPropertyFilter, s.setRequiredOnly], shallow) @@ -78,8 +79,19 @@ export function DslProperties(props: Props) { setChangedOnly(false); setSensitiveOnly(false); setPropertyFilter(''); + getRouteTemplateParameters() }, [selectedStep?.uuid]) + function getRouteTemplateParameters() { + if (selectedStep) { + const root = CamelDefinitionApiExt.findTopRouteElement(integration, selectedStep.uuid); + if ('RouteTemplateDefinition' === root?.dslName) { + const paramPlaceholders: [string, string][] = (root as RouteTemplateDefinition).parameters?.map(p => [p.name, p.description ||'']) || []; + setParameterPlaceholders(paramPlaceholders); + } + } + } + function getClonableElementHeader(): React.JSX.Element { const title = selectedStep && CamelDisplayUtil.getTitle(selectedStep); const description = selectedStep?.dslName ? CamelMetadataApi.getCamelModelMetadataByClassName(selectedStep?.dslName)?.description : title; diff --git a/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.css b/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.css index 43991634..3984ae02 100644 --- a/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.css +++ b/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.css @@ -28,6 +28,11 @@ display: none; } +.karavan .properties .property-placeholder-dropdown .pf-v5-c-menu__group-title { + font-weight: bold; + font-size: 13px; +} + .pf-v5-c-popover .property-placeholder-toggle-form { width: 300px; } diff --git a/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.tsx b/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.tsx index e920ce0a..ec532b55 100644 --- a/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.tsx +++ b/karavan-space/src/designer/property/property/PropertyPlaceholderDropdown.tsx @@ -59,8 +59,8 @@ interface Props { export function PropertyPlaceholderDropdown(props: Props) { - const [propertyPlaceholders, setPropertyPlaceholders] = useDesignerStore((s) => - [s.propertyPlaceholders, s.setPropertyPlaceholders], shallow) + const [propertyPlaceholders, setPropertyPlaceholders, parameterPlaceholders] = useDesignerStore((s) => + [s.propertyPlaceholders, s.setPropertyPlaceholders, s.parameterPlaceholders], shallow) const [isOpenPlaceholdersDropdown, setOpenPlaceholdersDropdown] = useState<boolean>(false); const [propValue, setPropValue] = useState<string>(''); const [isVisible, setIsVisible] = React.useState(false); @@ -72,13 +72,15 @@ export function PropertyPlaceholderDropdown(props: Props) { const {property, value} = props; const valueIsPlaceholder: boolean = value && value.toString().startsWith('{{') && value.toString().endsWith('}}'); const placeholderValue = valueIsPlaceholder ? value.toString().replace('{{', '').replace('}}', '') : undefined; + const isRouteTemplateParameter = parameterPlaceholders.map(p => p[0]).includes(placeholderValue); const showAddButton = valueIsPlaceholder + && !isRouteTemplateParameter && !propertyPlaceholders.includes(placeholderValue) - && !SYNTAX_EXAMPLES.map(se=> se.value).includes(removeBrackets(placeholderValue)) - && SYNTAX_EXAMPLES.findIndex(se=> removeBrackets(placeholderValue).startsWith(se.key)) === -1; + && !SYNTAX_EXAMPLES.map(se => se.value).includes(removeBrackets(placeholderValue)) + && SYNTAX_EXAMPLES.findIndex(se => removeBrackets(placeholderValue).startsWith(se.key)) === -1; const popoverId = "popover-selector-" + property.hasOwnProperty('name') ? (property as any).name : (property as any).id; - const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0 ); + const hasPlaceholders = (propertyPlaceholders && propertyPlaceholders.length > 0); function parametersChanged(value: string | number | boolean | any) { if (property instanceof ComponentProperty) { @@ -167,13 +169,23 @@ export function PropertyPlaceholderDropdown(props: Props) { shouldFocusToggleOnSelect > <DropdownList> - {hasPlaceholders && <DropdownGroup label="Application Properties"> - {propertyPlaceholders.map((pp, index) => - <DropdownItem value={pp} key={index}>{pp}</DropdownItem> - )} - </DropdownGroup>} + {parameterPlaceholders && + <DropdownGroup label="Template Parameters" className='property-placeholder-dropdown'> + {parameterPlaceholders.map((pp, index) => + <DropdownItem value={pp[0]} key={index} description={pp[1]}>{pp[0]}</DropdownItem> + )} + </DropdownGroup> + } + {parameterPlaceholders && <Divider component="li"/>} + {hasPlaceholders && + <DropdownGroup label="Application Properties" className='property-placeholder-dropdown'> + {propertyPlaceholders.map((pp, index) => + <DropdownItem value={pp} key={index}>{pp}</DropdownItem> + )} + </DropdownGroup> + } {hasPlaceholders && <Divider component="li"/>} - <DropdownGroup label="Syntax examples"> + <DropdownGroup label="Syntax examples" className='property-placeholder-dropdown'> {SYNTAX_EXAMPLES.map(se => <DropdownItem value={se.value} key={se.key} description={se.description}> {se.value} diff --git a/karavan-space/src/designer/utils/CamelUi.tsx b/karavan-space/src/designer/utils/CamelUi.tsx index f89c8e85..a12e19d6 100644 --- a/karavan-space/src/designer/utils/CamelUi.tsx +++ b/karavan-space/src/designer/utils/CamelUi.tsx @@ -821,7 +821,7 @@ export class CamelUi { static getFlowCounts = (i: Integration): Map<string, number> => { const result = new Map<string, number>(); - result.set('routes', i.spec.flows?.filter((e: any) => e.dslName === 'RouteDefinition').length || 0); + result.set('routes', i.spec.flows?.filter((e: any) => ['RouteDefinition', 'RouteTemplateDefinition'].includes(e.dslName)).length || 0); result.set('rest', i.spec.flows?.filter((e: any) => e.dslName === 'RestDefinition').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');