Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(canvas): Support root OnException #819

Merged
merged 1 commit into from
Feb 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/ui/src/components/Form/schema.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export class SchemaService {
'dataFormatType',
'outputs',
'steps',
'onWhen',
'when',
'otherwise',
'doCatch',
Expand Down
15 changes: 13 additions & 2 deletions packages/ui/src/models/camel/camel-route-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@ import { createCamelPropertiesSorter, isDefined } from '../../utils';
import { AddStepMode } from '../visualization/base-visual-entity';
import { CamelRouteVisualEntity, isCamelFrom, isCamelRoute } from '../visualization/flows';
import { FlowTemplateService } from '../visualization/flows/flow-templates-service';
import { NonVisualEntity } from '../visualization/flows/non-visual-entity';
import { CamelOnExceptionVisualEntity } from '../visualization/flows/camel-on-exception-visual-entity';
import { CamelComponentFilterService } from '../visualization/flows/support/camel-component-filter.service';
import { CamelRouteVisualEntityData } from '../visualization/flows/support/camel-component-types';
import { BeansEntity, isBeans } from '../visualization/metadata';
import { BeansAwareResource, CamelResource } from './camel-resource';
import { BaseCamelEntity } from './entities';
import { SourceSchemaType } from './source-schema-type';
import { NonVisualEntity } from '../visualization/flows/non-visual-entity';

export class CamelRouteResource implements CamelResource, BeansAwareResource {
static readonly SUPPORTED_ENTITIES = [CamelOnExceptionVisualEntity];
static readonly PARAMETERS_ORDER = ['id', 'description', 'uri', 'parameters', 'steps'];
readonly sortFn = createCamelPropertiesSorter(CamelRouteResource.PARAMETERS_ORDER) as (
a: unknown,
Expand Down Expand Up @@ -50,7 +52,9 @@ export class CamelRouteResource implements CamelResource, BeansAwareResource {
}

getVisualEntities(): CamelRouteVisualEntity[] {
return this.entities.filter((entity) => entity instanceof CamelRouteVisualEntity) as CamelRouteVisualEntity[];
return this.entities.filter(
(entity) => entity instanceof CamelRouteVisualEntity || entity instanceof CamelOnExceptionVisualEntity,
) as CamelRouteVisualEntity[];
}

getEntities(): BaseCamelEntity[] {
Expand Down Expand Up @@ -110,6 +114,13 @@ export class CamelRouteResource implements CamelResource, BeansAwareResource {
} else if (isBeans(rawItem)) {
return new BeansEntity(rawItem);
}

for (const Entity of CamelRouteResource.SUPPORTED_ENTITIES) {
if (Entity.isApplicable(rawItem)) {
return new Entity(rawItem);
}
}

return new NonVisualEntity(rawItem as string);
}
}
1 change: 1 addition & 0 deletions packages/ui/src/models/camel/entities/base-entity.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/** This is the enum with the registered Camel entities supported by Kaoto */
export const enum EntityType {
Route = 'route',
OnException = 'onException',
Integration = 'integration',
Kamelet = 'kamelet',
KameletBinding = 'kameletBinding',
Expand Down
3 changes: 0 additions & 3 deletions packages/ui/src/models/visualization/base-visual-entity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,6 @@ export interface BaseVisualCamelEntity extends BaseCamelEntity {
/** Given a path, update the model */
updateModel(path: string | undefined, value: unknown): void;

/** Retrieve the steps from the underlying Camel entity */
getSteps: () => unknown[];

/** Add a step to the underlying Camel entity */
addStep: (options: {
definedComponent: DefinedComponent;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable no-case-declarations */
import { DoCatch, ProcessorDefinition, RouteDefinition, When1 } from '@kaoto-next/camel-catalog/types';
import { getArrayProperty, getValue, isDefined, setValue } from '../../../utils';
import { ProcessorDefinition, RouteDefinition } from '@kaoto-next/camel-catalog/types';
import { ROOT_PATH, getArrayProperty, getValue, isDefined, setValue } from '../../../utils';
import { NodeIconResolver } from '../../../utils/node-icon-resolver';
import { DefinedComponent } from '../../camel-catalog-index';
import { EntityType } from '../../camel/entities';
Expand All @@ -15,11 +15,8 @@ import {
import { createVisualizationNode } from '../visualization-node';
import { CamelComponentDefaultService } from './support/camel-component-default.service';
import { CamelComponentSchemaService } from './support/camel-component-schema.service';
import {
CamelProcessorStepsProperties,
CamelRouteVisualEntityData,
ICamelElementLookupResult,
} from './support/camel-component-types';
import { CamelProcessorStepsProperties, CamelRouteVisualEntityData } from './support/camel-component-types';
import { CamelStepsService } from './support/camel-steps.service';
import { ModelValidationService } from './support/validators/model-validation.service';

export abstract class AbstractCamelVisualEntity implements BaseVisualCamelEntity {
Expand Down Expand Up @@ -76,10 +73,6 @@ export abstract class AbstractCamelVisualEntity implements BaseVisualCamelEntity
setValue(this.route, path, value);
}

getSteps(): ProcessorDefinition[] {
return this.route.from?.steps ?? [];
}

/**
* Add a step to the route
*
Expand Down Expand Up @@ -225,99 +218,28 @@ export abstract class AbstractCamelVisualEntity implements BaseVisualCamelEntity
}

toVizNode(): IVisualizationNode {
const fromNode = this.getVizNodeFromProcessor('from', {
processorName: 'from' as keyof ProcessorDefinition,
componentName: CamelComponentSchemaService.getComponentNameFromUri(this.getRootUri()!),
});

if (!this.getRootUri()) {
fromNode.data.icon = NodeIconResolver.getPlaceholderIcon();
}

const routeNode = createVisualizationNode(this.id, {
path: '#',
const routeGroupNode = createVisualizationNode(this.id, {
path: ROOT_PATH,
entity: this,
isGroup: true,
icon: NodeIconResolver.getIcon('route'),
icon: NodeIconResolver.getIcon(this.type),
});
routeNode.addChild(fromNode);

return routeNode;
}

private getVizNodeFromProcessor(path: string, componentLookup: ICamelElementLookupResult): IVisualizationNode {
const data: CamelRouteVisualEntityData = {
path,
icon: NodeIconResolver.getIcon(CamelComponentSchemaService.getIconName(componentLookup)),
processorName: componentLookup.processorName,
componentName: componentLookup.componentName,
};

const vizNode = createVisualizationNode(componentLookup.componentName ?? componentLookup.processorName, data);

const childrenStepsProperties = CamelComponentSchemaService.getProcessorStepsProperties(
componentLookup.processorName as keyof ProcessorDefinition,
const fromNode = CamelStepsService.getVizNodeFromProcessor(
'from',
{
processorName: 'from' as keyof ProcessorDefinition,
componentName: CamelComponentSchemaService.getComponentNameFromUri(this.getRootUri()!),
},
this.route,
);

childrenStepsProperties.forEach((stepsProperty) => {
const childrenVizNodes = this.getVizNodesFromChildren(path, stepsProperty);
childrenVizNodes.forEach((childVizNode) => vizNode.addChild(childVizNode));
});

return vizNode;
}

private getVizNodesFromChildren(path: string, stepsProperty: CamelProcessorStepsProperties): IVisualizationNode[] {
let singlePath: string;

switch (stepsProperty.type) {
case 'branch':
singlePath = `${path}.${stepsProperty.name}`;
const stepsList = getValue(this.route, singlePath, []) as ProcessorDefinition[];

return stepsList.reduce((accStepsNodes, step, index) => {
const singlePropertyName = Object.keys(step)[0];
const childPath = `${singlePath}.${index}.${singlePropertyName}`;
const childComponentLookup = CamelComponentSchemaService.getCamelComponentLookup(
childPath,
getValue(step, singlePropertyName),
);

const vizNode = this.getVizNodeFromProcessor(childPath, childComponentLookup);

const previousVizNode = accStepsNodes[accStepsNodes.length - 1];
if (previousVizNode !== undefined) {
previousVizNode.setNextNode(vizNode);
vizNode.setPreviousNode(previousVizNode);
}

accStepsNodes.push(vizNode);
return accStepsNodes;
}, [] as IVisualizationNode[]);

case 'single-clause':
const childPath = `${path}.${stepsProperty.name}`;
const childComponentLookup = CamelComponentSchemaService.getCamelComponentLookup(childPath, this.route);

/** If the single-clause property is not defined, we don't create a IVisualizationNode for it */
if (getValue(this.route, childPath) === undefined) return [];

return [this.getVizNodeFromProcessor(childPath, childComponentLookup)];

case 'clause-list':
singlePath = `${path}.${stepsProperty.name}`;
const expressionList = getValue(this.route, singlePath, []) as When1[] | DoCatch[];

return expressionList.map((_step, index) => {
const childPath = `${singlePath}.${index}`;
const childComponentLookup = { processorName: stepsProperty.name as keyof ProcessorDefinition }; // when, doCatch

return this.getVizNodeFromProcessor(childPath, childComponentLookup);
});

default:
return [];
if (!this.getRootUri()) {
fromNode.data.icon = NodeIconResolver.getPlaceholderIcon();
}
routeGroupNode.addChild(fromNode);

return routeGroupNode;
}

private insertChildStep(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { CamelOnExceptionVisualEntity } from './camel-on-exception-visual-entity';
import { OnException } from '@kaoto-next/camel-catalog/types';

describe('CamelOnExceptionVisualEntity', () => {
const ONEXCEPTION_ID_REGEXP = /^onException-[a-zA-Z0-9]{4}$/;

describe('isApplicable', () => {
it.each([
[true, { onException: { id: 'onExceptionId' } }],
[false, { from: { id: 'from-1234', steps: [] } }],
[false, { onException: { id: 'onExceptionId' }, anotherProperty: true }],
])('should return %s for %s', (result, definition) => {
expect(CamelOnExceptionVisualEntity.isApplicable(definition)).toEqual(result);
});
});

describe('constructor', () => {
it('should set id to onExceptionDef.onException.id if it is defined', () => {
const onExceptionDef: { onException: OnException } = { onException: { id: 'onExceptionId' } };
const entity = new CamelOnExceptionVisualEntity(onExceptionDef);

expect(entity.id).toEqual('onExceptionId');
expect(onExceptionDef.onException.id).toEqual('onExceptionId');
});

it('should set id to generated id if onExceptionDef.onException.id is not defined', () => {
const onExceptionDef = { onException: {} as OnException };
const entity = new CamelOnExceptionVisualEntity(onExceptionDef);

expect(entity.id).toMatch(ONEXCEPTION_ID_REGEXP);
expect(onExceptionDef.onException.id).toEqual(entity.id);
});
});
});
Loading
Loading