thingsboard/ui-ngx/src/app/core/http/rule-chain.service.ts

286 lines
12 KiB
TypeScript
Raw Normal View History

///
/// Copyright © 2016-2019 The Thingsboard Authors
///
/// Licensed 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.
///
2019-12-23 14:36:44 +02:00
import { ComponentFactory, Injectable } from '@angular/core';
2019-11-22 17:58:13 +02:00
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { forkJoin, Observable, of } from 'rxjs/index';
import { HttpClient } from '@angular/common/http';
import { PageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data';
import {
ResolvedRuleChainMetaData,
RuleChain, RuleChainConnectionInfo,
RuleChainMetaData,
ruleChainNodeComponent,
ruleNodeTypeComponentTypes, unknownNodeComponent
} from '@shared/models/rule-chain.models';
import { ComponentDescriptorService } from './component-descriptor.service';
2019-12-23 14:36:44 +02:00
import {
IRuleNodeConfigurationComponent,
LinkLabel,
2019-12-27 16:35:11 +02:00
RuleNodeComponentDescriptor, TestScriptInputParams, TestScriptResult
2019-12-23 14:36:44 +02:00
} from '@app/shared/models/rule-node.models';
2019-11-22 17:58:13 +02:00
import { ResourcesService } from '../services/resources.service';
import { catchError, map, mergeMap } from 'rxjs/operators';
import { TranslateService } from '@ngx-translate/core';
import { EntityType } from '@shared/models/entity-type.models';
2019-12-23 14:36:44 +02:00
import { deepClone, snakeCase } from '@core/utils';
2019-12-24 13:54:00 +02:00
import { DebugRuleNodeEventBody } from '@app/shared/models/event.models';
@Injectable({
providedIn: 'root'
})
export class RuleChainService {
2019-11-22 17:58:13 +02:00
private ruleNodeComponents: Array<RuleNodeComponentDescriptor>;
2019-12-23 14:36:44 +02:00
private ruleNodeConfigFactories: {[directive: string]: ComponentFactory<IRuleNodeConfigurationComponent>} = {};
2019-11-22 17:58:13 +02:00
constructor(
2019-11-22 17:58:13 +02:00
private http: HttpClient,
private componentDescriptorService: ComponentDescriptorService,
private resourcesService: ResourcesService,
private translate: TranslateService
) { }
public getRuleChains(pageLink: PageLink, config?: RequestConfig): Observable<PageData<RuleChain>> {
return this.http.get<PageData<RuleChain>>(`/api/ruleChains${pageLink.toQuery()}`,
defaultHttpOptionsFromConfig(config));
}
public getRuleChain(ruleChainId: string, config?: RequestConfig): Observable<RuleChain> {
return this.http.get<RuleChain>(`/api/ruleChain/${ruleChainId}`, defaultHttpOptionsFromConfig(config));
}
public saveRuleChain(ruleChain: RuleChain, config?: RequestConfig): Observable<RuleChain> {
return this.http.post<RuleChain>('/api/ruleChain', ruleChain, defaultHttpOptionsFromConfig(config));
}
public deleteRuleChain(ruleChainId: string, config?: RequestConfig) {
return this.http.delete(`/api/ruleChain/${ruleChainId}`, defaultHttpOptionsFromConfig(config));
}
public setRootRuleChain(ruleChainId: string, config?: RequestConfig): Observable<RuleChain> {
return this.http.post<RuleChain>(`/api/ruleChain/${ruleChainId}/root`, null, defaultHttpOptionsFromConfig(config));
}
2019-11-22 17:58:13 +02:00
public getRuleChainMetadata(ruleChainId: string, config?: RequestConfig): Observable<RuleChainMetaData> {
return this.http.get<RuleChainMetaData>(`/api/ruleChain/${ruleChainId}/metadata`, defaultHttpOptionsFromConfig(config));
}
public getResolvedRuleChainMetadata(ruleChainId: string, config?: RequestConfig): Observable<ResolvedRuleChainMetaData> {
return this.getRuleChainMetadata(ruleChainId, config).pipe(
mergeMap((ruleChainMetaData) => this.resolveRuleChainMetadata(ruleChainMetaData))
2019-11-22 17:58:13 +02:00
);
}
public saveRuleChainMetadata(ruleChainMetaData: RuleChainMetaData, config?: RequestConfig): Observable<RuleChainMetaData> {
return this.http.post<RuleChainMetaData>('/api/ruleChain/metadata', ruleChainMetaData, defaultHttpOptionsFromConfig(config));
}
public saveAndGetResolvedRuleChainMetadata(ruleChainMetaData: RuleChainMetaData,
config?: RequestConfig): Observable<ResolvedRuleChainMetaData> {
return this.saveRuleChainMetadata(ruleChainMetaData, config).pipe(
mergeMap((savedRuleChainMetaData) => this.resolveRuleChainMetadata(savedRuleChainMetaData))
);
}
public resolveRuleChainMetadata(ruleChainMetaData: RuleChainMetaData): Observable<ResolvedRuleChainMetaData> {
return this.resolveTargetRuleChains(ruleChainMetaData.ruleChainConnections).pipe(
map((targetRuleChainsMap) => {
const resolvedRuleChainMetadata: ResolvedRuleChainMetaData = {...ruleChainMetaData, targetRuleChainsMap};
return resolvedRuleChainMetadata;
2019-11-22 17:58:13 +02:00
})
);
}
2019-12-23 14:36:44 +02:00
public getRuleNodeComponents(ruleNodeConfigResourcesModulesMap: {[key: string]: any}, config?: RequestConfig):
Observable<Array<RuleNodeComponentDescriptor>> {
2019-11-22 17:58:13 +02:00
if (this.ruleNodeComponents) {
return of(this.ruleNodeComponents);
} else {
return this.loadRuleNodeComponents(config).pipe(
mergeMap((components) => {
2019-12-23 14:36:44 +02:00
return this.resolveRuleNodeComponentsUiResources(components, ruleNodeConfigResourcesModulesMap).pipe(
2019-11-22 17:58:13 +02:00
map((ruleNodeComponents) => {
this.ruleNodeComponents = ruleNodeComponents;
this.ruleNodeComponents.push(ruleChainNodeComponent);
this.ruleNodeComponents.sort(
(comp1, comp2) => {
let result = comp1.type.toString().localeCompare(comp2.type.toString());
if (result === 0) {
result = comp1.name.localeCompare(comp2.name);
}
return result;
}
);
return this.ruleNodeComponents;
})
);
})
);
}
}
2019-12-23 14:36:44 +02:00
public getRuleNodeConfigFactory(directive: string): ComponentFactory<IRuleNodeConfigurationComponent> {
return this.ruleNodeConfigFactories[directive];
}
2019-11-22 17:58:13 +02:00
public getRuleNodeComponentByClazz(clazz: string): RuleNodeComponentDescriptor {
const found = this.ruleNodeComponents.filter((component) => component.clazz === clazz);
if (found && found.length) {
return found[0];
} else {
const unknownComponent = deepClone(unknownNodeComponent);
unknownComponent.clazz = clazz;
unknownComponent.configurationDescriptor.nodeDefinition.details = 'Unknown Rule Node class: ' + clazz;
return unknownComponent;
}
}
2019-12-17 20:16:40 +02:00
public getRuleNodeSupportedLinks(component: RuleNodeComponentDescriptor): {[label: string]: LinkLabel} {
const relationTypes = component.configurationDescriptor.nodeDefinition.relationTypes;
const linkLabels: {[label: string]: LinkLabel} = {};
relationTypes.forEach((label) => {
linkLabels[label] = {
name: label,
value: label
};
});
return linkLabels;
}
public ruleNodeAllowCustomLinks(component: RuleNodeComponentDescriptor): boolean {
return component.configurationDescriptor.nodeDefinition.customRelations;
}
2019-12-24 13:54:00 +02:00
public getLatestRuleNodeDebugInput(ruleNodeId: string, config?: RequestConfig): Observable<DebugRuleNodeEventBody> {
return this.http.get<DebugRuleNodeEventBody>(`/api/ruleNode/${ruleNodeId}/debugIn`, defaultHttpOptionsFromConfig(config));
}
2019-12-27 16:35:11 +02:00
public testScript(inputParams: TestScriptInputParams, config?: RequestConfig): Observable<TestScriptResult> {
return this.http.post<TestScriptResult>('/api/ruleChain/testScript', inputParams, defaultHttpOptionsFromConfig(config));
}
2019-11-22 17:58:13 +02:00
private resolveTargetRuleChains(ruleChainConnections: Array<RuleChainConnectionInfo>): Observable<{[ruleChainId: string]: RuleChain}> {
if (ruleChainConnections && ruleChainConnections.length) {
const tasks: Observable<RuleChain>[] = [];
ruleChainConnections.forEach((connection) => {
tasks.push(this.resolveRuleChain(connection.targetRuleChainId.id));
});
return forkJoin(tasks).pipe(
map((ruleChains) => {
const ruleChainsMap: {[ruleChainId: string]: RuleChain} = {};
ruleChains.forEach((ruleChain) => {
ruleChainsMap[ruleChain.id.id] = ruleChain;
});
return ruleChainsMap;
})
);
} else {
return of({} as {[ruleChainId: string]: RuleChain});
}
}
private loadRuleNodeComponents(config?: RequestConfig): Observable<Array<RuleNodeComponentDescriptor>> {
return this.componentDescriptorService.getComponentDescriptorsByTypes(ruleNodeTypeComponentTypes, config).pipe(
map((components) => {
const ruleNodeComponents: RuleNodeComponentDescriptor[] = [];
components.forEach((component) => {
ruleNodeComponents.push(component as RuleNodeComponentDescriptor);
});
return ruleNodeComponents;
})
);
}
2019-12-23 14:36:44 +02:00
private resolveRuleNodeComponentsUiResources(components: Array<RuleNodeComponentDescriptor>,
ruleNodeConfigResourcesModulesMap: {[key: string]: any}):
2019-11-22 17:58:13 +02:00
Observable<Array<RuleNodeComponentDescriptor>> {
const tasks: Observable<RuleNodeComponentDescriptor>[] = [];
components.forEach((component) => {
2019-12-23 14:36:44 +02:00
tasks.push(this.resolveRuleNodeComponentUiResources(component, ruleNodeConfigResourcesModulesMap));
2019-11-22 17:58:13 +02:00
});
return forkJoin(tasks).pipe(
catchError((err) => {
return of(components);
})
);
}
2019-12-23 14:36:44 +02:00
private resolveRuleNodeComponentUiResources(component: RuleNodeComponentDescriptor,
ruleNodeConfigResourcesModulesMap: {[key: string]: any}):
Observable<RuleNodeComponentDescriptor> {
const nodeDefinition = component.configurationDescriptor.nodeDefinition;
const uiResources = nodeDefinition.uiResources;
2019-11-22 17:58:13 +02:00
if (uiResources && uiResources.length) {
2019-12-23 14:36:44 +02:00
const commonResources = uiResources.filter((resource) => !resource.endsWith('.js'));
const moduleResource = uiResources.find((resource) => resource.endsWith('.js'));
2019-11-22 17:58:13 +02:00
const tasks: Observable<any>[] = [];
2019-12-23 14:36:44 +02:00
if (commonResources && commonResources.length) {
commonResources.forEach((resource) => {
tasks.push(this.resourcesService.loadResource(resource));
});
}
if (moduleResource) {
tasks.push(this.resourcesService.loadModule(moduleResource, ruleNodeConfigResourcesModulesMap).pipe(
map((res) => {
if (nodeDefinition.configDirective && nodeDefinition.configDirective.length) {
const selector = snakeCase(nodeDefinition.configDirective, '-');
const componentFactory = res.componentFactories.find((factory) =>
factory.selector === selector);
if (componentFactory) {
this.ruleNodeConfigFactories[nodeDefinition.configDirective] = componentFactory;
} else {
component.configurationDescriptor.nodeDefinition.uiResourceLoadError =
this.translate.instant('rulenode.directive-is-not-loaded',
{directiveName: nodeDefinition.configDirective});
}
}
return of(component);
})
));
}
2019-11-22 17:58:13 +02:00
return forkJoin(tasks).pipe(
map((res) => {
return component;
}),
catchError(() => {
component.configurationDescriptor.nodeDefinition.uiResourceLoadError = this.translate.instant('rulenode.ui-resources-load-error');
return of(component);
})
);
} else {
return of(component);
}
}
private resolveRuleChain(ruleChainId: string): Observable<RuleChain> {
return this.getRuleChain(ruleChainId, {ignoreErrors: true}).pipe(
map(ruleChain => ruleChain),
catchError((err) => {
const ruleChain = {
2019-12-23 14:36:44 +02:00
id: {
2019-11-22 17:58:13 +02:00
entityType: EntityType.RULE_CHAIN,
id: ruleChainId
}
} as RuleChain;
return of(ruleChain);
})
);
}
}