Skip to content

Commit

Permalink
feat: Configure KameletBinding & Pipe properties
Browse files Browse the repository at this point in the history
Fixes: #149
  • Loading branch information
igarashitm committed Oct 3, 2023
1 parent 82b7546 commit 1a7c734
Show file tree
Hide file tree
Showing 6 changed files with 355 additions and 32 deletions.
153 changes: 145 additions & 8 deletions packages/ui/src/models/visualization/flows/kamelet-binding.test.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,160 @@
import { KameletBinding as KameletBindingModel } from '@kaoto-next/camel-catalog/types';
import { JSONSchemaType } from 'ajv';
import { kameletBindingJson } from '../../../stubs/kamelet-binding';
import { EntityType } from '../../camel-entities/base-entity';
import { KameletBinding } from './kamelet-binding';
import { KameletSchemaService } from './kamelet-schema.service.ts';

describe('Kamelet Binding', () => {
let kameletBinding: KameletBinding;

beforeEach(() => {
kameletBinding = new KameletBinding();
kameletBinding = new KameletBinding(JSON.parse(JSON.stringify(kameletBindingJson)));
});

it('should have an uuid', () => {
expect(kameletBinding.id).toBeDefined();
expect(typeof kameletBinding.id).toBe('string');
describe('id', () => {
it('should have an uuid', () => {
expect(kameletBinding.id).toBeDefined();
expect(typeof kameletBinding.id).toBe('string');
});

it('should have a type', () => {
expect(kameletBinding.type).toEqual(EntityType.KameletBinding);
});

it('should return the id', () => {
expect(kameletBinding.getId()).toEqual(expect.any(String));
});
});

describe('getComponentSchema', () => {
it('should return undefined if no path is provided', () => {
expect(kameletBinding.getComponentSchema()).toBeUndefined();
});

it('should return undefined if no component model is found', () => {
const result = kameletBinding.getComponentSchema('test');

expect(result).toBeUndefined();
});

it('should return the component schema', () => {
const spy = jest.spyOn(KameletSchemaService, 'getVisualComponentSchema');
spy.mockReturnValueOnce({
title: 'test',
schema: {} as JSONSchemaType<unknown>,
definition: {},
});

kameletBinding.getComponentSchema('source');
expect(spy).toBeCalledTimes(1);
});
});

it('should return the json', () => {
expect(kameletBinding.toJSON()).toEqual(kameletBindingJson);
});

describe('updateModel', () => {
it('should not update the model if no path is provided', () => {
const originalObject = JSON.parse(JSON.stringify(kameletBindingJson));

kameletBinding.updateModel(undefined, undefined);

expect(originalObject).toEqual(kameletBindingJson);
});

it('should update the model', () => {
const name = 'timer-source';

kameletBinding.updateModel('source.ref.name', name);

expect(kameletBinding.route.spec?.source?.ref?.name).toEqual(name);
});
});

it('should have a type', () => {
expect(kameletBinding.type).toEqual(EntityType.KameletBinding);
describe('getSteps', () => {
it('should return an empty array if there is no route', () => {
const route = new KameletBinding();

expect(route.getSteps()).toEqual([]);
});

it('should return an empty array if there is no steps', () => {
const route = new KameletBinding({ spec: {} } as KameletBindingModel);

expect(route.getSteps()).toEqual([]);
});

it('should return the steps', () => {
expect(kameletBinding.getSteps()).toEqual([
{
ref: {
apiVersion: 'camel.apache.org/v1',
kind: 'Kamelet',
name: 'log-sink',
properties: {
showHeaders: 'true',
},
},
},
{
ref: {
apiVersion: 'camel.apache.org/v1',
kind: 'Kamelet',
name: 'kafka-sink',
properties: {
bootstrapServers: '192.168.0.1',
password: 'test',
topic: 'myTopic',
user: 'test2',
},
},
},
]);
});
});

it('should return the steps', () => {
expect(kameletBinding.getSteps()).toEqual([]);
describe('toVizNode', () => {
it('should return the viz node and set the initial path to `source`', () => {
const vizNode = kameletBinding.toVizNode();

expect(vizNode).toBeDefined();
expect(vizNode.path).toEqual('source');
});

it('should use the uri as the node label', () => {
const vizNode = kameletBinding.toVizNode();

expect(vizNode.label).toEqual('timer-source');
});

it('should set an empty label if the uri is not available', () => {
kameletBinding = new KameletBinding({ spec: {} } as KameletBindingModel);
const vizNode = kameletBinding.toVizNode();

expect(vizNode.label).toBeUndefined();
});

it('should populate the viz node chain with the steps', () => {
const vizNode = kameletBinding.toVizNode();

expect(vizNode.path).toEqual('source');
expect(vizNode.label).toEqual('timer-source');
expect(vizNode.getPreviousNode()).toBeUndefined();
expect(vizNode.getNextNode()).toBeDefined();

const steps0 = vizNode.getNextNode()!;
expect(steps0.path).toEqual('steps.0');
expect(steps0.label).toEqual('log-sink');
expect(steps0.getPreviousNode()).toBe(vizNode);
expect(steps0.getNextNode()).toBeDefined();

const sink = steps0.getNextNode()!;
expect(sink.path).toEqual('sink');
expect(sink.label).toEqual('kafka-sink');
expect(sink.getPreviousNode()).toBe(steps0);
expect(sink.getNextNode()).toBeUndefined();
});
});
});
66 changes: 42 additions & 24 deletions packages/ui/src/models/visualization/flows/kamelet-binding.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import get from 'lodash.get';
import set from 'lodash.set';
import { KameletBinding as KameletBindingModel } from '@kaoto-next/camel-catalog/types';
import { v4 as uuidv4 } from 'uuid';
import { EntityType } from '../../camel-entities/base-entity';
import {
KameletBindingSink,
KameletBindingSource,
KameletBindingStep,
KameletBindingSteps,
} from '../../camel-entities/kamelet-binding-overrides';
import { EntityType } from '../../camel-entities';
import { KameletBindingStep, KameletBindingSteps } from '../../camel-entities/kamelet-binding-overrides';
import { BaseVisualCamelEntity, VisualComponentSchema } from '../base-visual-entity';
import { VisualizationNode } from '../visualization-node';
import { KameletSchemaService } from './kamelet-schema.service';

export class KameletBinding implements BaseVisualCamelEntity {
readonly id = uuidv4();
Expand All @@ -21,16 +19,21 @@ export class KameletBinding implements BaseVisualCamelEntity {
return '';
}

getComponentSchema(): VisualComponentSchema | undefined {
return undefined;
getComponentSchema(path?: string): VisualComponentSchema | undefined {
if (!path) return undefined;
const stepModel = get(this.route.spec, path) as KameletBindingStep;
return stepModel ? KameletSchemaService.getVisualComponentSchema(stepModel) : undefined;
}

toJSON() {
return { route: this.route };
return this.route;
}

updateModel(): void {
return;
updateModel(path: string | undefined, value: unknown): void {
if (!path) return;

const stepModel = get(this.route.spec, path) as KameletBindingStep;
if (stepModel) set(stepModel, 'ref.properties', value);
}

getSteps() {
Expand All @@ -46,15 +49,27 @@ export class KameletBinding implements BaseVisualCamelEntity {
}

toVizNode(): VisualizationNode {
const source: KameletBindingSource = this.route.spec?.source;
const rootNode = new VisualizationNode(source?.ref?.name ?? '');
const vizNodes = this.getVizNodesFromSteps(this.getSteps());
const rootNode = this.getVizNodeFromStep(this.route.spec?.source, 'source');
const stepNodes = this.route.spec?.steps ? this.getVizNodesFromSteps(this.route.spec?.steps) : undefined;
const sinkNode = this.getVizNodeFromStep(this.route.spec?.sink, 'sink');

if (vizNodes !== undefined) {
const firstVizNode = vizNodes[0];
if (firstVizNode !== undefined) {
rootNode.setNextNode(firstVizNode);
firstVizNode.setPreviousNode(rootNode);
if (stepNodes !== undefined) {
const firstStepNode = stepNodes[0];
if (firstStepNode !== undefined) {
rootNode.setNextNode(firstStepNode);
firstStepNode.setPreviousNode(rootNode);
}
}
if (sinkNode !== undefined) {
if (stepNodes !== undefined) {
const lastStepNode = stepNodes[stepNodes.length - 1];
if (lastStepNode !== undefined) {
lastStepNode.setNextNode(sinkNode);
sinkNode.setPreviousNode(lastStepNode);
}
} else {
rootNode.setNextNode(sinkNode);
sinkNode.setPreviousNode(rootNode);
}
}
return rootNode;
Expand All @@ -63,7 +78,7 @@ export class KameletBinding implements BaseVisualCamelEntity {
private getVizNodesFromSteps(steps: Array<KameletBindingStep>): VisualizationNode[] {
return steps?.reduce((acc, camelRouteStep) => {
const previousVizNode = acc[acc.length - 1];
const vizNode = this.getVizNodeFromStep(camelRouteStep);
const vizNode = this.getVizNodeFromStep(camelRouteStep, 'steps.' + acc.length);

if (previousVizNode !== undefined) {
previousVizNode.setNextNode(vizNode);
Expand All @@ -74,9 +89,12 @@ export class KameletBinding implements BaseVisualCamelEntity {
}, [] as VisualizationNode[]);
}

private getVizNodeFromStep(step: KameletBindingSink): VisualizationNode {
private getVizNodeFromStep(step: KameletBindingStep, path: string): VisualizationNode {
const stepName = step?.ref?.name;
const parentStep = new VisualizationNode(stepName!);
return parentStep;
const answer = new VisualizationNode(stepName!, this);
answer.path = path;
const kameletDefinition = KameletSchemaService.getKameletDefinition(step);
answer.iconData = kameletDefinition?.metadata.annotations['camel.apache.org/kamelet.icon'];
return answer;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { IKameletDefinition } from '../../kamelets-catalog';
import { VisualComponentSchema } from '../base-visual-entity';
import { KameletBindingStep } from '../../camel-entities/kamelet-binding-overrides';
import { useCatalogStore } from '../../../store';
import { CatalogKind } from '../../catalog-kind';
import { JSONSchemaType } from 'ajv';

export class KameletSchemaService {
static getVisualComponentSchema(stepModel: KameletBindingStep): VisualComponentSchema | undefined {
if (stepModel === undefined) {
return undefined;
}
const definition = KameletSchemaService.getKameletDefinition(stepModel);
return {
title: definition?.metadata.name || '',
schema: KameletSchemaService.getSchemaFromKameletDefinition(definition),
definition: stepModel?.ref?.properties || {},
};
}

static getSchemaFromKameletDefinition(definition: IKameletDefinition | undefined): JSONSchemaType<unknown> {
const required: string[] = [];
const schema = {
type: 'object',
properties: {},
required,
} as unknown as JSONSchemaType<unknown>;
const properties = definition?.spec.definition.properties;
if (!properties) {
return schema;
}

Object.keys(properties).forEach((propertyName) => {
const property = properties[propertyName];
const propertySchema = {
type: property.type,
title: property.title,
description: property.description,
} as unknown as JSONSchemaType<unknown>;

schema.properties[propertyName] = propertySchema;
});

if (definition.spec.definition.required) {
required.push(...definition.spec.definition.required);
}

return schema;
}

static getKameletDefinition(step: KameletBindingStep): IKameletDefinition | undefined {
if (step?.ref?.kind === 'Kamelet') {
const stepName = step?.ref?.name;
const kameletCatalog = useCatalogStore.getState().catalogs[CatalogKind.Kamelet];
return kameletCatalog && kameletCatalog[stepName!];
}
return undefined;
}
}
29 changes: 29 additions & 0 deletions packages/ui/src/models/visualization/flows/pipe.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { pipeJson } from '../../../stubs/pipe-route';
import { Pipe } from '.';

describe('Pipe', () => {
let pipe: Pipe;

beforeEach(() => {
pipe = new Pipe(JSON.parse(JSON.stringify(pipeJson)));
});

it('should return the steps', () => {
expect(pipe.getSteps()).toEqual([
{
ref: {
apiVersion: 'camel.apache.org/v1',
kind: 'Kamelet',
name: 'delay-action',
},
},
{
ref: {
apiVersion: 'camel.apache.org/v1alpha1',
kind: 'Kamelet',
name: 'log-sink',
},
},
]);
});
});
1 change: 1 addition & 0 deletions packages/ui/src/models/visualization/visualization-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export class VisualizationNode implements IVisualizationNode {
private nextNode: IVisualizationNode | undefined = undefined;
private children: IVisualizationNode[] | undefined;
path: string | undefined;
iconData: string | undefined;

constructor(
public label: string,
Expand Down
Loading

0 comments on commit 1a7c734

Please sign in to comment.