diff --git a/packages/ui/src/models/camel-properties-common.ts b/packages/ui/src/models/camel-properties-common.ts index 0b63b09bd..e022b118b 100644 --- a/packages/ui/src/models/camel-properties-common.ts +++ b/packages/ui/src/models/camel-properties-common.ts @@ -5,6 +5,8 @@ export interface CamelPropertyCommon { displayName: string; label?: string; required: boolean; + multiValue?: boolean; + prefix?: string; javaType: string; enum?: string[]; autowired: boolean; diff --git a/packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.ts b/packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.ts index 26d2849a5..d06e512c7 100644 --- a/packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.ts +++ b/packages/ui/src/models/visualization/flows/abstract-camel-visual-entity.ts @@ -72,7 +72,8 @@ export abstract class AbstractCamelVisualEntity implements Bas updateModel(path: string | undefined, value: unknown): void { if (!path) return; - const updatedValue = CamelComponentSchemaService.getUriSerializedDefinition(path, value); + let updatedValue = CamelComponentSchemaService.getUriSerializedDefinition(path, value); + updatedValue = CamelComponentSchemaService.getMultiValueSerializedDefinition(path, updatedValue); setValue(this.route, path, updatedValue); } diff --git a/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.test.ts b/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.test.ts index b25a1d66c..fa51400ae 100644 --- a/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.test.ts +++ b/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.test.ts @@ -559,6 +559,43 @@ describe('CamelComponentSchemaService', () => { }); }); + describe('getMultiValueSerializedDefinition', () => { + it('should return the same parameters if the definition is not a component', () => { + const definition = { log: { message: 'Hello World' } }; + const result = CamelComponentSchemaService.getMultiValueSerializedDefinition('from', definition); + + expect(result).toEqual(definition); + }); + + it('should return the same parameters if the component is not found', () => { + const definition = { + uri: 'unknown-component', + parameters: { jobParameters: { test: 'test' }, triggerParameters: { test: 'test' } }, + }; + const result = CamelComponentSchemaService.getMultiValueSerializedDefinition('from', definition); + + expect(result).toEqual(definition); + }); + + it('should query the catalog service', () => { + const definition = { uri: 'log', parameters: { message: 'Hello World' } }; + const catalogServiceSpy = jest.spyOn(CamelCatalogService, 'getCatalogLookup'); + + CamelComponentSchemaService.getMultiValueSerializedDefinition('from', definition); + expect(catalogServiceSpy).toHaveBeenCalledWith('log'); + }); + + it('should return the serialized definition', () => { + const definition = { + uri: 'quartz', + parameters: { jobParameters: { test: 'test' }, triggerParameters: { test: 'test' } }, + }; + const result = CamelComponentSchemaService.getMultiValueSerializedDefinition('from', definition); + + expect(result).toEqual({ uri: 'quartz', parameters: { 'job.test': 'test', 'trigger.test': 'test' } }); + }); + }); + describe('getUriSerializedDefinition', () => { it('should return the same parameters if the definition is not a component', () => { const definition = { log: { message: 'Hello World' } }; diff --git a/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.ts b/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.ts index d3983a7d1..5e30ae79f 100644 --- a/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.ts +++ b/packages/ui/src/models/visualization/flows/support/camel-component-schema.service.ts @@ -257,6 +257,40 @@ export class CamelComponentSchemaService { return definition; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + static getMultiValueSerializedDefinition(path: string, definition: any): ParsedParameters | undefined { + const camelElementLookup = this.getCamelComponentLookup(path, definition); + if (camelElementLookup.componentName === undefined) { + return definition; + } + + const catalogLookup = CamelCatalogService.getCatalogLookup(camelElementLookup.componentName); + if (catalogLookup.catalogKind === CatalogKind.Component) { + const multiValueParameters: Map = new Map(); + if (catalogLookup.definition?.properties !== undefined) { + Object.entries(catalogLookup.definition.properties).forEach(([key, value]) => { + if (value.multiValue) multiValueParameters.set(key, value.prefix!); + }); + } + const defaultMultiValues: ParsedParameters = {}; + const filteredParameters = definition.parameters; + + Object.keys(definition.parameters).forEach((key) => { + if (multiValueParameters.has(key)) { + if (definition.parameters[key] === undefined) { + return; + } + Object.keys(definition.parameters[key]).forEach((subKey) => { + defaultMultiValues[multiValueParameters.get(key) + subKey] = definition.parameters[key][subKey]; + }); + delete filteredParameters[key]; + } + }); + return Object.assign({}, definition, { parameters: { ...filteredParameters, ...defaultMultiValues } }); + } + return definition; + } + /** * If the processor is a `from` or `to` processor, we need to extract the component name from the uri property * and return both the processor name and the underlying component name to build the combined schema @@ -385,11 +419,42 @@ export class CamelComponentSchemaService { if (camelElementLookup.componentName !== undefined) { updatedDefinition.parameters = updatedDefinition.parameters ?? {}; this.applyParametersFromSyntax(camelElementLookup.componentName, updatedDefinition); + this.readMultiValue(camelElementLookup.componentName, updatedDefinition); } return updatedDefinition; } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + private static readMultiValue(componentName: string, definition: any) { + const catalogLookup = CamelCatalogService.getCatalogLookup(componentName); + + const multiValueParameters: Map = new Map(); + if (catalogLookup !== undefined && catalogLookup.definition?.properties !== undefined) { + Object.entries(catalogLookup.definition.properties).forEach(([key, value]) => { + if (value.multiValue) multiValueParameters.set(key, value.prefix!); + }); + } + if (multiValueParameters.size > 0) { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const parameters: any = {}; + const filteredParameters = definition.parameters; + + for (const [key, value] of multiValueParameters) { + const nestParameters: ParsedParameters = {}; + + Object.entries(definition.parameters).forEach(([paramKey, paramValue]) => { + if (paramKey.startsWith(value)) { + nestParameters[paramKey.replace(value, '')] = paramValue as string; + delete filteredParameters[paramKey]; + } + parameters[key] = { ...nestParameters }; + }); + } + Object.assign(definition, { parameters: { ...filteredParameters, ...parameters } }); + } + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any private static applyParametersFromSyntax(componentName: string, definition: any) { const catalogLookup = CamelCatalogService.getCatalogLookup(componentName);