From 29a822cd3fa02608f28f5b008f248f0fc0fc291a Mon Sep 17 00:00:00 2001
From: Tomohisa Igarashi
Date: Wed, 1 Nov 2023 20:55:17 -0400
Subject: [PATCH] feat: Support configuring expression in Canvas form
Fixes: #270
---
.../PipeErrorHandlerEditor.test.tsx | 16 +-
.../Visualization/Canvas/CanvasForm.tsx | 11 +-
.../Canvas/ExpressionEditor.test.tsx | 74 ++++
.../Visualization/Canvas/ExpressionEditor.tsx | 126 +++++++
.../ExpressionEditor.test.tsx.snap | 340 ++++++++++++++++++
.../Canvas/expression.service.test.ts | 130 +++++++
.../Canvas/expression.service.ts | 146 ++++++++
packages/ui/src/models/camel-catalog-index.ts | 11 +-
.../src/models/camel-dataformats-catalog.ts | 25 ++
.../ui/src/models/camel-languages-catalog.ts | 29 ++
packages/ui/src/models/catalog-kind.ts | 2 +
packages/ui/src/models/index.ts | 2 +
.../flows/camel-catalog.service.ts | 11 +
.../support/camel-component-schema.service.ts | 6 +-
.../ui/src/providers/catalog.provider.tsx | 12 +-
packages/ui/src/stubs/PipeErrorHandler.ts | 111 ------
16 files changed, 926 insertions(+), 126 deletions(-)
create mode 100644 packages/ui/src/components/Visualization/Canvas/ExpressionEditor.test.tsx
create mode 100644 packages/ui/src/components/Visualization/Canvas/ExpressionEditor.tsx
create mode 100644 packages/ui/src/components/Visualization/Canvas/__snapshots__/ExpressionEditor.test.tsx.snap
create mode 100644 packages/ui/src/components/Visualization/Canvas/expression.service.test.ts
create mode 100644 packages/ui/src/components/Visualization/Canvas/expression.service.ts
create mode 100644 packages/ui/src/models/camel-dataformats-catalog.ts
create mode 100644 packages/ui/src/models/camel-languages-catalog.ts
delete mode 100644 packages/ui/src/stubs/PipeErrorHandler.ts
diff --git a/packages/ui/src/components/MetadataEditor/PipeErrorHandlerEditor.test.tsx b/packages/ui/src/components/MetadataEditor/PipeErrorHandlerEditor.test.tsx
index 6a91e6b69..d4b274c58 100644
--- a/packages/ui/src/components/MetadataEditor/PipeErrorHandlerEditor.test.tsx
+++ b/packages/ui/src/components/MetadataEditor/PipeErrorHandlerEditor.test.tsx
@@ -1,7 +1,7 @@
import { PipeErrorHandlerEditor } from './PipeErrorHandlerEditor';
import { within } from '@testing-library/dom';
import { fireEvent, render, screen } from '@testing-library/react';
-import { pipeErrorHandlerJson } from '../../stubs/PipeErrorHandler';
+import * as pipeErrorHandlerSchema from '@kaoto-next/camel-catalog/PipeErrorHandler.json';
describe('PipeErrorHandlerEditor', () => {
it('should render', () => {
@@ -13,8 +13,8 @@ describe('PipeErrorHandlerEditor', () => {
},
},
};
- render( {}} schema={pipeErrorHandlerJson} />);
- const element = screen.getByTestId('metadata-editor-form-Log ErrorHandler');
+ render( {}} schema={pipeErrorHandlerSchema} />);
+ const element = screen.getByTestId('metadata-editor-form-Log Pipe ErrorHandler');
expect(element).toBeTruthy();
const inputs = screen.getAllByTestId('num-field');
expect(inputs.length).toBe(2);
@@ -23,8 +23,8 @@ describe('PipeErrorHandlerEditor', () => {
});
it('should not render a form if model is empty', () => {
- render( {}} schema={pipeErrorHandlerJson} />);
- const element = screen.queryByTestId('metadata-editor-form-Log ErrorHandler');
+ render( {}} schema={pipeErrorHandlerSchema} />);
+ const element = screen.queryByTestId('metadata-editor-form-Log Pipe ErrorHandler');
expect(element).toBeFalsy();
});
@@ -36,19 +36,19 @@ describe('PipeErrorHandlerEditor', () => {
onChangeModel={(m) => {
model = m;
}}
- schema={pipeErrorHandlerJson}
+ schema={pipeErrorHandlerSchema}
/>,
);
const button = screen.getByRole('button');
fireEvent(button!, new MouseEvent('click', { bubbles: true }));
const options = screen.getAllByTestId(/pipe-error-handler-select-option.*/);
options.forEach((option) => {
- if (option.innerHTML.includes('Log ErrorHandler')) {
+ if (option.innerHTML.includes('Log Pipe ErrorHandler')) {
const button = within(option).getByRole('option');
fireEvent(button, new MouseEvent('click', { bubbles: true }));
}
});
- const element = screen.getByTestId('metadata-editor-form-Log ErrorHandler');
+ const element = screen.getByTestId('metadata-editor-form-Log Pipe ErrorHandler');
expect(element).toBeTruthy();
expect(model.log).toBeTruthy();
});
diff --git a/packages/ui/src/components/Visualization/Canvas/CanvasForm.tsx b/packages/ui/src/components/Visualization/Canvas/CanvasForm.tsx
index 9034b616b..bd02e4e0a 100644
--- a/packages/ui/src/components/Visualization/Canvas/CanvasForm.tsx
+++ b/packages/ui/src/components/Visualization/Canvas/CanvasForm.tsx
@@ -1,11 +1,12 @@
import { AutoFields, AutoForm, ErrorsField } from '@kaoto-next/uniforms-patternfly';
import { Title } from '@patternfly/react-core';
-import { FunctionComponent, useCallback, useContext, useEffect, useRef, useState } from 'react';
+import { FunctionComponent, useCallback, useContext, useEffect, useMemo, useRef, useState } from 'react';
import { ErrorBoundary } from '../../ErrorBoundary';
import { SchemaService } from '../../Form';
import { CustomAutoField } from '../../Form/CustomAutoField';
import { CanvasNode } from './canvas.models';
import { EntitiesContext } from '../../../providers/entities.provider';
+import { ExpressionEditor } from './ExpressionEditor';
interface CanvasFormProps {
selectedNode: CanvasNode;
@@ -38,12 +39,16 @@ export const CanvasForm: FunctionComponent = (props) => {
[entitiesContext, props.selectedNode.data?.vizNode],
);
+ const isExpressionAwareStep = useMemo(() => {
+ return schema?.schema?.properties?.expression !== undefined;
+ }, [schema]);
+
return schema?.schema === undefined ? null : (
This node cannot be configured yet
}>
{componentName}
-
+ {isExpressionAwareStep && }
-
+
diff --git a/packages/ui/src/components/Visualization/Canvas/ExpressionEditor.test.tsx b/packages/ui/src/components/Visualization/Canvas/ExpressionEditor.test.tsx
new file mode 100644
index 000000000..1d5631a5c
--- /dev/null
+++ b/packages/ui/src/components/Visualization/Canvas/ExpressionEditor.test.tsx
@@ -0,0 +1,74 @@
+import * as yamlDslSchema from '@kaoto-next/camel-catalog/camelYamlDsl.json';
+import * as languageCatalog from '@kaoto-next/camel-catalog/camel-catalog-aggregate-languages.json';
+import { fireEvent, render, screen } from '@testing-library/react';
+import { ExpressionEditor } from './ExpressionEditor';
+import { CamelCatalogService } from '../../../models/visualization/flows';
+import { CatalogKind, ICamelLanguageDefinition } from '../../../models';
+import { CanvasNode } from './canvas.models';
+import { JSONSchemaType } from 'ajv';
+import { IVisualizationNode, VisualComponentSchema } from '../../../models/visualization/base-visual-entity';
+import { useSchemasStore } from '../../../store';
+import { act } from 'react-dom/test-utils';
+
+describe('ExpressionEditor', () => {
+ let mockNode: CanvasNode;
+ beforeAll(() => {
+ /* eslint-disable @typescript-eslint/no-explicit-any */
+ delete (yamlDslSchema as any).default;
+ delete (languageCatalog as any).default;
+ CamelCatalogService.setCatalogKey(
+ CatalogKind.Language,
+ languageCatalog as unknown as Record,
+ );
+
+ act(() => {
+ useSchemasStore.setState({
+ schemas: {
+ camelYamlDsl: {
+ name: 'camelYamlDsl',
+ tags: ['camel'],
+ version: '1.0.0',
+ uri: '',
+ schema: yamlDslSchema as unknown as Record,
+ },
+ },
+ });
+ });
+
+ const visualComponentSchema: VisualComponentSchema = {
+ title: 'My Node',
+ schema: {
+ type: 'object',
+ properties: {
+ name: {
+ type: 'string',
+ },
+ },
+ } as unknown as JSONSchemaType,
+ definition: {
+ name: 'my node',
+ },
+ };
+
+ mockNode = {
+ id: '1',
+ type: 'node',
+ data: {
+ vizNode: {
+ getComponentSchema: () => visualComponentSchema,
+ updateModel: (_value: unknown) => {},
+ } as IVisualizationNode,
+ },
+ };
+ });
+
+ it('should render', () => {
+ render();
+ const buttons = screen.getAllByRole('button');
+ fireEvent.click(buttons[1]);
+ const jsonpath = screen.getByTestId('expression-dropdownitem-jsonpath');
+ fireEvent.click(jsonpath.getElementsByTagName('button')[0]);
+ const form = screen.getByTestId('metadata-editor-form-expression');
+ expect(form.innerHTML).toContain('Suppress Exceptions');
+ });
+});
diff --git a/packages/ui/src/components/Visualization/Canvas/ExpressionEditor.tsx b/packages/ui/src/components/Visualization/Canvas/ExpressionEditor.tsx
new file mode 100644
index 000000000..8dad105fd
--- /dev/null
+++ b/packages/ui/src/components/Visualization/Canvas/ExpressionEditor.tsx
@@ -0,0 +1,126 @@
+import { FunctionComponent, Ref, useCallback, useContext, useMemo, useState } from 'react';
+import {
+ Card,
+ CardBody,
+ CardExpandableContent,
+ CardHeader,
+ CardTitle,
+ Dropdown,
+ DropdownItem,
+ DropdownList,
+ MenuToggle,
+ MenuToggleElement,
+} from '@patternfly/react-core';
+import { MetadataEditor } from '../../MetadataEditor';
+import { EntitiesContext } from '../../../providers';
+import { CanvasNode } from './canvas.models';
+import { ExpressionService } from './expression.service';
+import { useSchemasStore } from '../../../store';
+
+interface ExpressionEditorProps {
+ selectedNode: CanvasNode;
+}
+
+export const ExpressionEditor: FunctionComponent = (props) => {
+ const entitiesContext = useContext(EntitiesContext);
+ const [isOpen, setIsOpen] = useState(false);
+ const [isExpanded, setIsExpanded] = useState(true);
+ const camelYamlDslSchema = useSchemasStore((state) => state.schemas['camelYamlDsl'].schema) as Record<
+ string,
+ unknown
+ >;
+ const languageCatalogMap = useMemo(() => {
+ return ExpressionService.getLanguageMap(camelYamlDslSchema);
+ }, [camelYamlDslSchema]);
+
+ const visualComponentSchema = props.selectedNode.data?.vizNode?.getComponentSchema();
+ if (visualComponentSchema) {
+ if (!visualComponentSchema.definition) {
+ visualComponentSchema.definition = {};
+ }
+ }
+ const model = visualComponentSchema?.definition;
+ const { language, model: expressionModel } = ExpressionService.parseExpressionModel(languageCatalogMap, model);
+ const languageSchema = useMemo(() => {
+ return ExpressionService.getLanguageSchema(language!);
+ }, [language]);
+
+ const onToggleClick = useCallback(() => {
+ setIsOpen(!isOpen);
+ }, [isOpen]);
+
+ const handleOnChange = useCallback(
+ (selectedLanguage: string, newExpressionModel: Record) => {
+ if (!model) return;
+ ExpressionService.setExpressionModel(languageCatalogMap, model, selectedLanguage, newExpressionModel);
+ props.selectedNode.data?.vizNode?.updateModel(model);
+ entitiesContext?.updateCodeFromEntities();
+ },
+ [entitiesContext, languageCatalogMap, model, props.selectedNode.data?.vizNode],
+ );
+
+ const onSelect = useCallback(
+ (_event: React.MouseEvent | undefined, value: string | number | undefined) => {
+ setIsOpen(false);
+ if (value === language!.language.modelName) return;
+ handleOnChange(value as string, {});
+ },
+ [handleOnChange, language],
+ );
+
+ const toggle = useCallback(
+ (toggleRef: Ref) => (
+
+ {language!.language.title}
+
+ ),
+ [language, isOpen, onToggleClick],
+ );
+
+ return (
+ languageCatalogMap &&
+ model &&
+ language && (
+
+ setIsExpanded(!isExpanded)}>
+ Expression
+
+
+
+
+
+ {Object.values(languageCatalogMap).map((language) => {
+ return (
+
+ {language.language.title}
+
+ );
+ })}
+
+
+ handleOnChange(language.language.modelName, model)}
+ />
+
+
+
+ )
+ );
+};
diff --git a/packages/ui/src/components/Visualization/Canvas/__snapshots__/ExpressionEditor.test.tsx.snap b/packages/ui/src/components/Visualization/Canvas/__snapshots__/ExpressionEditor.test.tsx.snap
new file mode 100644
index 000000000..10bdfd619
--- /dev/null
+++ b/packages/ui/src/components/Visualization/Canvas/__snapshots__/ExpressionEditor.test.tsx.snap
@@ -0,0 +1,340 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`ExpressionEditor should render correctly 1`] = `
+
+`;
diff --git a/packages/ui/src/components/Visualization/Canvas/expression.service.test.ts b/packages/ui/src/components/Visualization/Canvas/expression.service.test.ts
new file mode 100644
index 000000000..f2babda3e
--- /dev/null
+++ b/packages/ui/src/components/Visualization/Canvas/expression.service.test.ts
@@ -0,0 +1,130 @@
+import { ExpressionService } from './expression.service';
+import * as yamlDslSchema from '@kaoto-next/camel-catalog/camelYamlDsl.json';
+import * as languageCatalog from '@kaoto-next/camel-catalog/camel-catalog-aggregate-languages.json';
+import { CatalogKind, ICamelLanguageDefinition } from '../../../models';
+import { CamelCatalogService } from '../../../models/visualization/flows';
+
+describe('ExpressionService', () => {
+ beforeAll(() => {
+ /* eslint-disable @typescript-eslint/no-explicit-any */
+ delete (yamlDslSchema as any).default;
+ delete (languageCatalog as any).default;
+ CamelCatalogService.setCatalogKey(
+ CatalogKind.Language,
+ languageCatalog as unknown as Record,
+ );
+ });
+
+ describe('getLanguageMap', () => {
+ it('should return language map', () => {
+ const languageMap = ExpressionService.getLanguageMap(yamlDslSchema);
+ expect(languageMap.simple.language.title).toEqual('Simple');
+ expect(languageMap.jq.language.name).toEqual('jq');
+ expect(languageMap.file).toBeUndefined();
+ expect(languageMap.language.language.description).toContain('custom');
+ expect(languageMap.language.properties.language.title).toEqual('Language');
+ });
+
+ it('should return language map without custom if schema is empty', () => {
+ const languageMap = ExpressionService.getLanguageMap({});
+ expect(languageMap.simple.language.title).toEqual('Simple');
+ expect(languageMap.jq.language.name).toEqual('jq');
+ expect(languageMap.language).toBeUndefined();
+ });
+ });
+
+ describe('getLanguageSchema', () => {
+ it('should return language schema', () => {
+ const languageMap = ExpressionService.getLanguageMap(yamlDslSchema);
+ const jsonpathSchema = ExpressionService.getLanguageSchema(languageMap.jsonpath);
+ expect(jsonpathSchema.properties.suppressExceptions.type).toBe('boolean');
+ const customSchema = ExpressionService.getLanguageSchema(languageMap.language);
+ expect(customSchema.properties.language.type).toBe('string');
+ });
+ });
+
+ describe('parseExpressionModel', () => {
+ let languageMap: Record;
+ beforeAll(() => {
+ languageMap = ExpressionService.getLanguageMap(yamlDslSchema);
+ });
+
+ it('should parse #1', () => {
+ const { language, model } = ExpressionService.parseExpressionModel(languageMap, { simple: '${body}' });
+ expect(language).toEqual(languageMap.simple);
+ expect(model).toEqual({ expression: '${body}' });
+ });
+
+ it('should parse #2', () => {
+ const { language, model } = ExpressionService.parseExpressionModel(languageMap, {
+ simple: { expression: '${body}' },
+ });
+ expect(language).toEqual(languageMap.simple);
+ expect(model).toEqual({ expression: '${body}' });
+ });
+
+ it('should parse #3', () => {
+ const { language, model } = ExpressionService.parseExpressionModel(languageMap, {
+ expression: {
+ simple: '${body}',
+ },
+ });
+ expect(language).toEqual(languageMap.simple);
+ expect(model).toEqual({ expression: '${body}' });
+ });
+
+ it('should parse #4', () => {
+ const { language, model } = ExpressionService.parseExpressionModel(languageMap, {
+ expression: {
+ simple: { expression: '${body}' },
+ },
+ });
+ expect(language).toEqual(languageMap.simple);
+ expect(model).toEqual({ expression: '${body}' });
+ });
+
+ it('should return simple and empty model if model is empty', () => {
+ const { language, model } = ExpressionService.parseExpressionModel(languageMap, {});
+ expect(language).toEqual(languageMap.simple);
+ expect(model).toEqual({});
+ });
+
+ it('should return simple and empty model if language map and model is empty', () => {
+ const { language, model } = ExpressionService.parseExpressionModel({}, {});
+ expect(language).toBeUndefined();
+ expect(model).toEqual({});
+ });
+ });
+
+ describe('setExpressionModel', () => {
+ let languageMap: Record;
+ beforeAll(() => {
+ languageMap = ExpressionService.getLanguageMap(yamlDslSchema);
+ });
+
+ it('should write expression', () => {
+ /* eslint-disable @typescript-eslint/no-explicit-any */
+ const parentModel: any = {};
+ ExpressionService.setExpressionModel(languageMap, parentModel, 'simple', { expression: '${body}' });
+ expect(parentModel.expression.simple.expression).toEqual('${body}');
+ });
+
+ it('should write expression and remove existing', () => {
+ /* eslint-disable @typescript-eslint/no-explicit-any */
+ const parentModel: any = { constant: 'foo' };
+ ExpressionService.setExpressionModel(languageMap, parentModel, 'simple', {
+ expression: '${body}',
+ resultType: 'string',
+ });
+ expect(parentModel.constant).toBeUndefined();
+ expect(parentModel.expression.simple.expression).toEqual('${body}');
+ expect(parentModel.expression.simple.resultType).toEqual('string');
+ });
+
+ it('should not write if empty', () => {
+ const parentModel: any = {};
+ ExpressionService.setExpressionModel(languageMap, parentModel, '', {});
+ expect(parentModel.expression.simple).toBeUndefined();
+ });
+ });
+});
diff --git a/packages/ui/src/components/Visualization/Canvas/expression.service.ts b/packages/ui/src/components/Visualization/Canvas/expression.service.ts
new file mode 100644
index 000000000..baa045de8
--- /dev/null
+++ b/packages/ui/src/components/Visualization/Canvas/expression.service.ts
@@ -0,0 +1,146 @@
+import { CamelCatalogService } from '../../../models/visualization/flows';
+import { CatalogKind, ICamelLanguageDefinition, ICamelLanguageModel, ICamelLanguageProperty } from '../../../models';
+import { CamelComponentSchemaService } from '../../../models/visualization/flows/support/camel-component-schema.service';
+
+export class ExpressionService {
+ /**
+ * Get the language catalog map from the Camel catalog. Since "language" language is not in the languages Camel
+ * catalog, it is extracted from the YAML DSL schema.
+ * @param yamlDslSchema
+ */
+ static getLanguageMap(yamlDslSchema: Record): Record {
+ const catalog = { ...CamelCatalogService.getLanguageMap() };
+
+ // "language" for custom language is not in the Camel catalog. Create from YAML DSL schema and Add here.
+ const customDefinition = ExpressionService.createCustomLanguageDefinition(yamlDslSchema);
+ if (customDefinition) {
+ catalog['language'] = customDefinition;
+ }
+
+ // "file" language is merged with "simple" language and it doesn't exist in YAML DSL schema. Remove it here.
+ delete catalog['file'];
+
+ return catalog;
+ }
+
+ private static createCustomLanguageDefinition(yamlDslSchema: Record) {
+ const definitions = (yamlDslSchema.items as Record)?.definitions as Record;
+ const customLanguageSchema =
+ definitions && (definitions['org.apache.camel.model.language.LanguageExpression'] as Record);
+ if (!customLanguageSchema) return;
+
+ const properties = Object.entries(customLanguageSchema.properties as Record).reduce(
+ (acc, [key, value]) => {
+ acc[key] = {
+ index: Object.keys(acc).length,
+ required: (customLanguageSchema.required as string[]).includes(key),
+ ...(value as Record),
+ } as ICamelLanguageProperty;
+ return acc;
+ },
+ {} as Record,
+ );
+ return {
+ language: {
+ kind: CatalogKind.Language,
+ name: 'language',
+ title: customLanguageSchema.title,
+ description: customLanguageSchema.description,
+ deprecated: false,
+ modelName: 'language',
+ } as ICamelLanguageModel,
+ properties: properties,
+ } as ICamelLanguageDefinition;
+ }
+
+ /**
+ * Get the language schema from the language catalog. ATM it delegates to {@link CamelComponentSchemaService.getSchemaFromCamelCommonProperties}.
+ * @TODO The language `xtokenize` has `namespace` property with the type `array`, which causes an error in uniforms. Fix the schema here, or in {@link CamelComponentSchemaService.getSchemaFromCamelCommonProperties} if it could be common.
+ * @param languageCatalog
+ */
+ static getLanguageSchema(languageCatalog: ICamelLanguageDefinition) {
+ return CamelComponentSchemaService.getSchemaFromCamelCommonProperties(languageCatalog.properties);
+ }
+
+ /**
+ * Parse the expression model from the parent step model object. Since expression has several dialects,
+ * this method tries to read all possibility and merge into a single representation.
+ * @param languageCatalogMap
+ * @param parentModel
+ */
+ static parseExpressionModel(
+ languageCatalogMap: Record,
+ parentModel: Record,
+ ): {
+ language: ICamelLanguageDefinition | undefined;
+ model: Record;
+ } {
+ let languageModelName = 'simple';
+ let model = undefined;
+ if (parentModel?.expression && Object.keys(parentModel.expression).length > 0) {
+ languageModelName = Object.keys(parentModel.expression)[0];
+ model = ExpressionService.parseLanguageModel(
+ parentModel.expression as Record,
+ languageModelName,
+ );
+ } else {
+ for (const language of Object.values(languageCatalogMap)) {
+ if (parentModel[language.language.modelName]) {
+ languageModelName = language.language.modelName;
+ model = ExpressionService.parseLanguageModel(parentModel, language.language.modelName);
+ break;
+ }
+ }
+ if (!model) {
+ parentModel.expression = {};
+ model = {};
+ (parentModel.expression as Record)[languageModelName] = model;
+ }
+ }
+ const language = this.getDefinitionFromModelName(languageCatalogMap, languageModelName);
+ return { language, model };
+ }
+
+ private static parseLanguageModel(model: Record, langName: string) {
+ const lang = model[langName];
+ if (typeof lang === 'string') {
+ return { expression: lang };
+ } else {
+ return lang as Record;
+ }
+ }
+
+ private static getDefinitionFromModelName(
+ languageCatalogMap: Record,
+ modelName: string,
+ ): ICamelLanguageDefinition | undefined {
+ return Object.values(languageCatalogMap).find((model) => model.language.modelName === modelName);
+ }
+
+ /**
+ * Set the expression model to the parent step model object. This method uses the most verbose dialect, i.e.
+ * ```yaml
+ * - setBody:
+ * expression:
+ * simple:
+ * expression: ${body}
+ * trim: true
+ * ```
+ * @param languageCatalogMap
+ * @param parentModel
+ * @param languageModelName
+ * @param newExpressionModel
+ */
+ static setExpressionModel(
+ languageCatalogMap: Record,
+ parentModel: Record,
+ languageModelName: string,
+ newExpressionModel: Record,
+ ): void {
+ Object.values(languageCatalogMap).forEach((language) => {
+ delete parentModel[language.language.modelName];
+ });
+ parentModel.expression = {};
+ (parentModel.expression as Record)[languageModelName] = newExpressionModel;
+ }
+}
diff --git a/packages/ui/src/models/camel-catalog-index.ts b/packages/ui/src/models/camel-catalog-index.ts
index 994aaeb77..494f7f7d4 100644
--- a/packages/ui/src/models/camel-catalog-index.ts
+++ b/packages/ui/src/models/camel-catalog-index.ts
@@ -1,5 +1,7 @@
import { ICamelComponentDefinition } from './camel-components-catalog';
import { ICamelProcessorDefinition } from './camel-processors-catalog';
+import { ICamelLanguageDefinition } from './camel-languages-catalog';
+import { ICamelDataformatDefinition } from './camel-dataformats-catalog';
import { CatalogKind } from './catalog-kind';
import { IKameletDefinition } from './kamelets-catalog';
@@ -26,7 +28,12 @@ export interface SchemaEntry extends CatalogEntry {
description: string;
}
-export type ComponentsCatalogTypes = ICamelComponentDefinition | ICamelProcessorDefinition | IKameletDefinition;
+export type ComponentsCatalogTypes =
+ | ICamelComponentDefinition
+ | ICamelProcessorDefinition
+ | ICamelLanguageDefinition
+ | ICamelDataformatDefinition
+ | IKameletDefinition;
export type DefinedComponent = {
name: string;
type: CatalogKind;
@@ -37,5 +44,7 @@ export type DefinedComponent = {
export interface ComponentsCatalog {
[CatalogKind.Component]?: Record;
[CatalogKind.Processor]?: Record;
+ [CatalogKind.Language]?: Record;
+ [CatalogKind.Dataformat]?: Record;
[CatalogKind.Kamelet]?: Record;
}
diff --git a/packages/ui/src/models/camel-dataformats-catalog.ts b/packages/ui/src/models/camel-dataformats-catalog.ts
new file mode 100644
index 000000000..3ef3d566b
--- /dev/null
+++ b/packages/ui/src/models/camel-dataformats-catalog.ts
@@ -0,0 +1,25 @@
+import { CamelPropertyCommon } from './camel-properties-common';
+import { CatalogKind } from './catalog-kind';
+
+export interface ICamelDataformatDefinition {
+ model: ICamelDataformatModel;
+ properties: Record;
+}
+
+export interface ICamelDataformatModel {
+ kind: CatalogKind.Dataformat;
+ name: string;
+ title: string;
+ description?: string;
+ deprecated: boolean;
+ label: string;
+ javaType?: string;
+ abstract?: boolean;
+ input?: boolean;
+ output?: boolean;
+}
+
+export interface ICamelDataformatProperty extends CamelPropertyCommon {
+ oneOf?: string[];
+ type: string;
+}
diff --git a/packages/ui/src/models/camel-languages-catalog.ts b/packages/ui/src/models/camel-languages-catalog.ts
new file mode 100644
index 000000000..aee5a6c00
--- /dev/null
+++ b/packages/ui/src/models/camel-languages-catalog.ts
@@ -0,0 +1,29 @@
+import { CamelPropertyCommon } from './camel-properties-common';
+import { CatalogKind } from './catalog-kind';
+
+export interface ICamelLanguageDefinition {
+ language: ICamelLanguageModel;
+ properties: Record;
+}
+
+export interface ICamelLanguageModel {
+ kind: CatalogKind.Language;
+ name: string;
+ title: string;
+ description?: string;
+ deprecated: boolean;
+ firstVersion: string;
+ label: string;
+ javaType?: string;
+ supportLelvel: string;
+ groupId: string;
+ artifactId: string;
+ version: string;
+ modelName: string;
+ modelJavaType: string;
+}
+
+export interface ICamelLanguageProperty extends CamelPropertyCommon {
+ type: string;
+ title: string;
+}
diff --git a/packages/ui/src/models/catalog-kind.ts b/packages/ui/src/models/catalog-kind.ts
index 798d3fc52..65cba6ac0 100644
--- a/packages/ui/src/models/catalog-kind.ts
+++ b/packages/ui/src/models/catalog-kind.ts
@@ -1,5 +1,7 @@
export const enum CatalogKind {
Component = 'component',
Processor = 'model',
+ Language = 'language',
+ Dataformat = 'dataformat',
Kamelet = 'kamelet',
}
diff --git a/packages/ui/src/models/index.ts b/packages/ui/src/models/index.ts
index b34f02a93..b03fccdd0 100644
--- a/packages/ui/src/models/index.ts
+++ b/packages/ui/src/models/index.ts
@@ -1,6 +1,8 @@
export * from './camel-catalog-index';
export * from './camel-components-catalog';
export * from './camel-processors-catalog';
+export * from './camel-languages-catalog';
+export * from './camel-dataformats-catalog';
export * from './camel-properties-common';
export * from './catalog-kind';
export * from './kamelets-catalog';
diff --git a/packages/ui/src/models/visualization/flows/camel-catalog.service.ts b/packages/ui/src/models/visualization/flows/camel-catalog.service.ts
index 190784745..f42a408e1 100644
--- a/packages/ui/src/models/visualization/flows/camel-catalog.service.ts
+++ b/packages/ui/src/models/visualization/flows/camel-catalog.service.ts
@@ -3,6 +3,8 @@ import { ICamelComponentDefinition } from '../../camel-components-catalog';
import { ICamelProcessorDefinition } from '../../camel-processors-catalog';
import { CatalogKind } from '../../catalog-kind';
import { IKameletDefinition } from '../../kamelets-catalog';
+import { ICamelDataformatDefinition } from '../../camel-dataformats-catalog';
+import { ICamelLanguageDefinition } from '../../camel-languages-catalog';
export class CamelCatalogService {
private static catalogs: ComponentsCatalog = {};
@@ -20,6 +22,11 @@ export class CamelCatalogService {
static getComponent(catalogKey: CatalogKind.Component, componentName?: string): ICamelComponentDefinition | undefined;
static getComponent(catalogKey: CatalogKind.Processor, componentName?: string): ICamelProcessorDefinition | undefined;
+ static getComponent(catalogKey: CatalogKind.Language, languageName?: string): ICamelLanguageDefinition | undefined;
+ static getComponent(
+ catalogKey: CatalogKind.Dataformat,
+ dataformatName?: string,
+ ): ICamelDataformatDefinition | undefined;
static getComponent(catalogKey: CatalogKind.Kamelet, componentName?: string): IKameletDefinition | undefined;
static getComponent(catalogKey: CatalogKind, componentName?: string): ComponentsCatalogTypes | undefined;
static getComponent(catalogKey: CatalogKind, componentName?: string): ComponentsCatalogTypes | undefined {
@@ -28,6 +35,10 @@ export class CamelCatalogService {
return this.catalogs[catalogKey]?.[componentName];
}
+ static getLanguageMap(): Record {
+ return this.catalogs[CatalogKind.Language] || {};
+ }
+
/**
* Public only as a convenience method for test
* not meant to be used in production code
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 5a028974a..df855d80f 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
@@ -7,6 +7,7 @@ import { CatalogKind } from '../../../catalog-kind';
import { VisualComponentSchema } from '../../base-visual-entity';
import { CamelCatalogService } from '../camel-catalog.service';
import { CamelProcessorStepsProperties, ICamelElementLookupResult } from './camel-component-types';
+import { ICamelLanguageProperty } from '../../../camel-languages-catalog';
export class CamelComponentSchemaService {
static DISABLED_SIBLING_STEPS = ['from', 'when', 'otherwise', 'doCatch', 'doFinally'];
@@ -225,9 +226,10 @@ export class CamelComponentSchemaService {
/**
* Transform Camel Common properties into a JSON Schema
+ * @TODO Now this is also used by {@link ExpressionService} and will be used for dataformats for next, possibly move it to a common place
*/
- private static getSchemaFromCamelCommonProperties(
- properties: Record,
+ static getSchemaFromCamelCommonProperties(
+ properties: Record,
): JSONSchemaType {
const required: string[] = [];
const schema = {
diff --git a/packages/ui/src/providers/catalog.provider.tsx b/packages/ui/src/providers/catalog.provider.tsx
index a64c42708..f92d74997 100644
--- a/packages/ui/src/providers/catalog.provider.tsx
+++ b/packages/ui/src/providers/catalog.provider.tsx
@@ -23,18 +23,28 @@ export const CatalogLoaderProvider: FunctionComponent = (prop
const camelProcessorsFiles = CatalogSchemaLoader.fetchFile(
catalogIndex.catalogs.models.file,
);
+ const camelLanguagesFiles = CatalogSchemaLoader.fetchFile(
+ catalogIndex.catalogs.languages.file,
+ );
+ const camelDataformatsFiles = CatalogSchemaLoader.fetchFile(
+ catalogIndex.catalogs.dataformats.file,
+ );
const kameletsFiles = CatalogSchemaLoader.fetchFile(
catalogIndex.catalogs.kamelets.file,
);
- const [camelComponents, camelProcessors, kamelets] = await Promise.all([
+ const [camelComponents, camelProcessors, camelLanguages, camelDataformats, kamelets] = await Promise.all([
camelComponentsFiles,
camelProcessorsFiles,
+ camelLanguagesFiles,
+ camelDataformatsFiles,
kameletsFiles,
]);
CamelCatalogService.setCatalogKey(CatalogKind.Component, camelComponents.body);
CamelCatalogService.setCatalogKey(CatalogKind.Processor, camelProcessors.body);
+ CamelCatalogService.setCatalogKey(CatalogKind.Language, camelLanguages.body);
+ CamelCatalogService.setCatalogKey(CatalogKind.Dataformat, camelDataformats.body);
CamelCatalogService.setCatalogKey(CatalogKind.Kamelet, kamelets.body);
})
.then(() => {
diff --git a/packages/ui/src/stubs/PipeErrorHandler.ts b/packages/ui/src/stubs/PipeErrorHandler.ts
deleted file mode 100644
index 777e4ecaa..000000000
--- a/packages/ui/src/stubs/PipeErrorHandler.ts
+++ /dev/null
@@ -1,111 +0,0 @@
-export const pipeErrorHandlerJson = {
- $schema: 'http://json-schema.org/draft-07/schema#',
- type: 'object',
- additionalProperties: false,
- description:
- 'Camel K Pipe ErrorHandler. See https://camel.apache.org/camel-k/latest/pipe-step.html#_error_handler for more details.',
- oneOf: [
- {
- title: 'No ErrorHandler',
- type: 'object',
- properties: {
- none: {
- type: 'object',
- },
- },
- required: ['none'],
- },
- {
- title: 'Log ErrorHandler',
- type: 'object',
- properties: {
- log: {
- type: 'object',
- additionalProperties: false,
- properties: {
- parameters: {
- type: 'object',
- properties: {
- maximumRedeliveries: {
- type: 'number',
- },
- redeliveryDelay: {
- type: 'number',
- },
- },
- additionalProperties: {
- type: 'string',
- },
- },
- },
- },
- },
- required: ['log'],
- },
- {
- title: 'Sink ErrorHandler',
- type: 'object',
- properties: {
- sink: {
- type: 'object',
- additionalProperties: false,
- properties: {
- endpoint: {
- type: 'object',
- additionalProperties: false,
- properties: {
- ref: {
- type: 'object',
- additionalProperties: false,
- properties: {
- kind: {
- type: 'string',
- },
- apiVersion: {
- type: 'string',
- },
- name: {
- type: 'string',
- },
- },
- required: ['kind', 'apiVersion', 'name'],
- },
- properties: {
- type: 'object',
- properties: {
- message: {
- type: 'string',
- },
- additionalProperties: {
- type: 'string',
- },
- },
- },
- },
- },
- parameters: {
- type: 'object',
- properties: {
- maximumRedeliveries: {
- type: 'number',
- },
- redeliveryDelay: {
- type: 'number',
- },
- },
- additionalProperties: {
- type: 'string',
- },
- },
- },
- },
- },
- required: ['sink'],
- },
- ],
- properties: {
- none: {},
- log: {},
- sink: {},
- },
-};