karavan-core
Version:
Apache Camel Karavan Core
393 lines (367 loc) • 18 kB
text/typescript
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
DeleteDefinition,
FromDefinition,
GetDefinition,
HeadDefinition,
PatchDefinition,
PostDefinition,
PutDefinition,
RestDefinition, RouteConfigurationDefinition, RouteDefinition, SagaDefinition,
} from '../model/CamelDefinition';
import {
CamelElement,
Integration,
} from '../model/IntegrationDefinition';
import {
TopologyIncomingNode,
TopologyOutgoingNode,
TopologyRestNode, TopologyRouteConfigurationNode,
TopologyRouteNode,
} from '../model/TopologyDefinition';
import { ComponentApi, INTERNAL_COMPONENTS } from './ComponentApi';
import { CamelDefinitionApiExt } from './CamelDefinitionApiExt';
import { CamelDisplayUtil } from './CamelDisplayUtil';
import { CamelUtil } from './CamelUtil';
const outgoingDefinitions: string[] = ['ToDefinition', 'KameletDefinition', 'ToDynamicDefinition', 'PollEnrichDefinition', 'EnrichDefinition', 'WireTapDefinition', 'SagaDefinition', 'PollDefinition'];
export class ChildElement {
constructor(public name: string = '', public className: string = '', public multiple: boolean = false) {
}
}
export class TopologyUtils {
private constructor() {
}
static getOutgoingDefinitions = (): string[] => {
return outgoingDefinitions;
};
static isElementInternalComponent = (element: CamelElement): boolean => {
const uri = (element as any).uri;
const component = ComponentApi.findByName(uri);
if (INTERNAL_COMPONENTS.includes(uri?.split(':')?.[0])) return true;
return component !== undefined && component.component.remote !== true;
};
static getConnectorType = (element: CamelElement): 'component' | 'kamelet' => {
return CamelUtil.isKameletComponent(element) ? 'kamelet' : 'component';
};
static cutKameletUriSuffix = (uri: string): string => {
if (uri.endsWith('-sink')) {
return uri.substring(0, uri.length - 5);
} else if (uri.endsWith('-source')) {
return uri.substring(0, uri.length - 7);
} else if (uri.endsWith('-action')) {
return uri.substring(0, uri.length - 7);
} else {
return uri;
}
};
static getUniqueUri = (element: CamelElement): string => {
const uri: string = (element as any).uri || '';
let result = uri.startsWith('kamelet') ? TopologyUtils.cutKameletUriSuffix(uri).concat(':') : uri.concat(':');
const className = element.dslName;
if (className === 'FromDefinition' || className === 'ToDefinition') {
if (!CamelUtil.isKameletComponent(element)) {
const requiredProperties = CamelUtil.getComponentProperties(element).filter(p => p.required);
for (const property of requiredProperties) {
const value = CamelDefinitionApiExt.getParametersValue(element, property.name, property.kind === 'path');
if (value !== undefined && property.type === 'string' && value.trim().length > 0) {
result = result + property.name + '=' + value + '&';
}
}
} else {
const requiredProperties = CamelUtil.getKameletProperties(element, true);
for (const property of requiredProperties) {
const value = CamelDefinitionApiExt.getParametersValue(element, property.id);
if (value !== undefined && property.type === 'string' && value.toString().trim().length > 0) {
result = result + property.id + '=' + value + '&';
}
}
}
}
return result.endsWith('&') ? result.substring(0, result.length - 1) : result;
};
static hasDirectUri = (element: CamelElement): boolean => {
return this.hasUriStartWith(element, 'direct');
};
static hasSedaUri = (element: CamelElement): boolean => {
return this.hasUriStartWith(element, 'seda');
};
static hasUriStartWith = (element: CamelElement, text: string): boolean => {
if ((element as any).uri && typeof (element as any).uri === 'string') {
return (element as any).uri.startsWith(text);
} else if (element.dslName === 'SagaDefinition') {
const completion = (element as SagaDefinition).completion || '';
const compensation = (element as SagaDefinition).compensation || '';
return completion.startsWith(text) || compensation.startsWith(text);
} else {
return false;
}
};
static findTopologyRestNodes = (integration: Integration[]): TopologyRestNode[] => {
const result: TopologyRestNode[] = [];
integration.forEach(i => {
try {
const filename = i.metadata.name;
const routes = i.spec.flows?.filter(flow => flow.dslName === 'RestDefinition');
routes?.forEach((rest: RestDefinition) => {
const uris: string[] = [];
rest?.get?.forEach((d: GetDefinition) => {
if (d.to) uris.push(d.to);
});
rest?.post?.forEach((d: PostDefinition) => {
if (d.to) uris.push(d.to);
});
rest?.put?.forEach((d: PutDefinition) => {
if (d.to) uris.push(d.to);
});
rest?.delete?.forEach((d: DeleteDefinition) => {
if (d.to) uris.push(d.to);
});
rest?.patch?.forEach((d: PatchDefinition) => {
if (d.to) uris.push(d.to);
});
rest?.head?.forEach((d: HeadDefinition) => {
if (d.to) uris.push(d.to);
});
const title = '' + (rest.description ? rest.description : rest.id);
result.push(new TopologyRestNode(rest.path || '', '' + rest.id, uris, title, filename, rest));
});
} catch (e) {
console.error(e);
}
});
return result;
};
static findTopologyIncomingNodes = (integration: Integration[]): TopologyIncomingNode[] => {
const result: TopologyIncomingNode[] = [];
integration.forEach(i => {
try {
const filename = i.metadata.name;
const routes = i.spec.flows?.filter(flow => flow.dslName === 'RouteDefinition');
const routeElements = routes?.map(r => {
const id = 'incoming-' + r.id;
const title = CamelDisplayUtil.getStepDescription(r.from);
const type = TopologyUtils.isElementInternalComponent(r.from) ? 'internal' : 'external';
const connectorType = TopologyUtils.getConnectorType(r.from);
const uniqueUri = TopologyUtils.getUniqueUri(r.from);
return new TopologyIncomingNode(id, type, connectorType, r.id, title, filename, r.from, uniqueUri);
}) || [];
result.push(...routeElements);
} catch (e) {
console.error(e);
}
});
return result;
};
static findTopologyRouteNodes = (integration: Integration[]): TopologyRouteNode[] => {
const result: TopologyRouteNode[] = [];
integration.forEach(i => {
try {
const filename = i.metadata.name;
const routes = i.spec.flows?.filter(flow => flow.dslName === 'RouteDefinition');
const routeElements = routes?.map(r => {
const id = 'route-' + r.id;
const title = '' + (r.description ? r.description : r.id);
return new TopologyRouteNode(id, r.id, title, filename, r.from, r);
}) || [];
result.push(...routeElements);
} catch (e) {
console.error(e);
}
});
return result;
};
static findTopologyRouteConfigurationNodes = (integration: Integration[]): TopologyRouteConfigurationNode[] => {
const result: TopologyRouteConfigurationNode[] = [];
integration.forEach(i => {
try {
const filename = i.metadata.name;
const routes = i.spec.flows?.filter(flow => flow.dslName === 'RouteConfigurationDefinition');
const routeElements = routes?.map(r => {
const id = 'route-' + r.id;
const title = '' + (r.description ? r.description : r.id);
return new TopologyRouteConfigurationNode(id, r.id, title, filename, r);
}) || [];
result.push(...routeElements);
} catch (e) {
console.error(e);
}
});
return result;
};
static findTopologyRouteOutgoingNodes = (integrations: Integration[]): TopologyOutgoingNode[] => {
const result: TopologyOutgoingNode[] = [];
integrations.forEach(i => {
try {
const filename = i.metadata.name;
const routes = i.spec.flows?.filter(flow => flow.dslName === 'RouteDefinition');
routes?.forEach(route => {
const from: FromDefinition = route.from;
const elements = TopologyUtils.findOutgoingInStep(from, []);
elements.forEach((e: any) => {
const id = 'outgoing-' + route.id + '-' + e.id;
const title = CamelDisplayUtil.getStepDescription(e);
const type = TopologyUtils.isElementInternalComponent(e) ? 'internal' : 'external';
const connectorType = TopologyUtils.getConnectorType(e);
const uniqueUri = TopologyUtils.getUniqueUri(e);
if (
connectorType !== 'kamelet' ||
CamelUtil.getKamelet(e)?.metadata.labels['camel.apache.org/kamelet.type'] !== 'action'
) {
result.push(new TopologyOutgoingNode(id, type, connectorType, route.id, title, filename, e, uniqueUri));
}
});
result.push(...TopologyUtils.findDeadLetterChannelNodes(route, filename));
});
} catch (e) {
console.error(e);
}
});
return result;
};
static findDeadLetterChannelNodes(route: RouteDefinition, filename: string): TopologyOutgoingNode[] {
const result: TopologyOutgoingNode[] = [];
try {
const deadLetterChannel = route.errorHandler?.deadLetterChannel;
const deadLetterUri = deadLetterChannel?.deadLetterUri;
if (deadLetterChannel !== undefined && deadLetterUri !== undefined) {
const parts = deadLetterUri.split(':');
if (parts.length > 1 && INTERNAL_COMPONENTS.includes(parts[0])) {
const id = 'outgoing-' + route.id + '-' + deadLetterChannel?.id;
const title = CamelDisplayUtil.getStepDescription(deadLetterChannel);
const type = 'internal';
const connectorType = 'component';
result.push(new TopologyOutgoingNode(id, type, connectorType, route.id || '', title, filename, deadLetterChannel, deadLetterUri));
}
}
} catch (e) {
console.error(e);
}
return result;
}
static findTopologyRouteConfigurationOutgoingNodes = (integrations: Integration[]): TopologyOutgoingNode[] => {
const result: TopologyOutgoingNode[] = [];
integrations.forEach(i => {
try {
const filename = i.metadata.name;
const rcs = i.spec.flows?.filter(flow => flow.dslName === 'RouteConfigurationDefinition');
rcs?.forEach((rc: RouteConfigurationDefinition) => {
const children: CamelElement[] = [];
children.push(...rc.intercept || []);
children.push(...rc.interceptFrom || []);
children.push(...rc.interceptSendToEndpoint || []);
children.push(...rc.onCompletion || []);
children.push(...rc.onException || []);
children.forEach(child => {
const elements = TopologyUtils.findOutgoingInStep(child, []);
elements.forEach((e: any) => {
const id = 'outgoing-' + rc.id + '-' + e.id;
const title = CamelDisplayUtil.getStepDescription(e);
const type = TopologyUtils.isElementInternalComponent(e) ? 'internal' : 'external';
const connectorType = TopologyUtils.getConnectorType(e);
const uniqueUri = TopologyUtils.getUniqueUri(e);
result.push(new TopologyOutgoingNode(id, type, connectorType, rc.id || 'undefined', title, filename, e, uniqueUri));
});
});
if (rc.errorHandler?.deadLetterChannel) {
const e = rc.errorHandler?.deadLetterChannel;
const id = 'outgoing-' + rc.id + '-' + e.id;
const title = CamelDisplayUtil.getStepDescription(e);
const comp = e?.deadLetterUri?.split(':')?.[0];
const type = INTERNAL_COMPONENTS.includes(comp) ? 'internal' : 'external';
const connectorType = 'component';
const uniqueUri = e?.deadLetterUri;
result.push(new TopologyOutgoingNode(id, type, connectorType, rc.id || 'undefined', title, filename, e, uniqueUri));
}
});
} catch (e) {
console.error(e);
}
});
return result;
};
static findOutgoingInStep = (step: CamelElement, result: CamelElement[]): CamelElement[] => {
if (step !== undefined) {
const el = (step as any);
try {
if (outgoingDefinitions.includes(el.dslName)) {
result.push(step);
} else {
const childElements = CamelDefinitionApiExt.getElementChildrenDefinition(el.dslName);
childElements.forEach(child => {
if (child.multiple) {
const sub = (el[child.name] as CamelElement[]);
TopologyUtils.findOutgoingInSteps(sub, result);
} else {
const sub = (el[child.name] as CamelElement);
TopologyUtils.findOutgoingInStep(sub, result);
}
});
}
} catch (e) {
console.error(e);
}
}
return result;
};
static findOutgoingInSteps = (steps: CamelElement[], result: CamelElement[]): CamelElement[] => {
if (steps !== undefined && steps.length > 0) {
steps.forEach(step => TopologyUtils.findOutgoingInStep(step, result));
}
return result;
};
static getNodeIdByUriAndName(tins: TopologyIncomingNode[], uri: string, name: string): string | undefined {
if (uri && name) {
const node = tins
.filter(r => r.from.uri === uri
&& (r?.from?.parameters?.name === name || r?.from?.parameters?.address === name),
).at(0);
if (node) {
return node.id;
}
}
}
static getNodeIdByUri(tins: TopologyIncomingNode[], uri: string): string | undefined {
const parts = uri.split(':');
if (parts.length > 1) {
return TopologyUtils.getNodeIdByUriAndName(tins, parts[0], parts[1]);
}
}
static getRouteIdByUriAndName(tins: TopologyIncomingNode[], uri: string, name: string): string | undefined {
if (uri && name) {
const node = tins
.filter(r => r.from.uri === uri
&& (r?.from?.parameters?.name === name || r?.from?.parameters?.address === name),
).at(0);
if (node) {
return 'route-' + node.routeId;
}
}
}
static getNodeIdByUniqueUri(tins: TopologyIncomingNode[], uniqueUri: string): string [] {
const result: string[] = [];
tins.filter(r => r.uniqueUri === uniqueUri)
?.forEach(node => result.push(node.id));
return result;
}
static getRouteIdByUri(tins: TopologyIncomingNode[], uri: string): string | undefined {
const parts = uri.split(':');
if (parts.length > 1) {
return TopologyUtils.getRouteIdByUriAndName(tins, parts[0], parts[1]);
}
}
}