Merge pull request #11522 from maxunbearable/feature/rule-chain-versioning-ui
Added UI part of RuleChain Editor Version Conflict
This commit is contained in:
commit
3e675b632e
@ -77,8 +77,8 @@ import {
|
|||||||
} from '@shared/models/rule-node.models';
|
} from '@shared/models/rule-node.models';
|
||||||
import { FcRuleNodeModel, FcRuleNodeTypeModel, RuleChainMenuContextInfo } from './rulechain-page.models';
|
import { FcRuleNodeModel, FcRuleNodeTypeModel, RuleChainMenuContextInfo } from './rulechain-page.models';
|
||||||
import { RuleChainService } from '@core/http/rule-chain.service';
|
import { RuleChainService } from '@core/http/rule-chain.service';
|
||||||
import { NEVER, Observable, of, ReplaySubject, skip, startWith, Subject } from 'rxjs';
|
import { NEVER, Observable, of, ReplaySubject, skip, startWith, Subject, throwError } from 'rxjs';
|
||||||
import { debounceTime, distinctUntilChanged, mergeMap, takeUntil, tap } from 'rxjs/operators';
|
import { catchError, debounceTime, distinctUntilChanged, mergeMap, takeUntil, tap } from 'rxjs/operators';
|
||||||
import { ISearchableComponent } from '../../models/searchable-component.models';
|
import { ISearchableComponent } from '../../models/searchable-component.models';
|
||||||
import { deepClone, isDefinedAndNotNull } from '@core/utils';
|
import { deepClone, isDefinedAndNotNull } from '@core/utils';
|
||||||
import { RuleNodeDetailsComponent } from '@home/pages/rulechain/rule-node-details.component';
|
import { RuleNodeDetailsComponent } from '@home/pages/rulechain/rule-node-details.component';
|
||||||
@ -93,6 +93,7 @@ import { TbPopoverService } from '@shared/components/popover.service';
|
|||||||
import { VersionControlComponent } from '@home/components/vc/version-control.component';
|
import { VersionControlComponent } from '@home/components/vc/version-control.component';
|
||||||
import { ComponentClusteringMode } from '@shared/models/component-descriptor.models';
|
import { ComponentClusteringMode } from '@shared/models/component-descriptor.models';
|
||||||
import { MatDrawer } from '@angular/material/sidenav';
|
import { MatDrawer } from '@angular/material/sidenav';
|
||||||
|
import { HttpStatusCode } from '@angular/common/http';
|
||||||
import Timeout = NodeJS.Timeout;
|
import Timeout = NodeJS.Timeout;
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -1282,7 +1283,9 @@ export class RuleChainPageComponent extends PageComponent
|
|||||||
onDebugEventSelected(debugEventBody: DebugRuleNodeEventBody) {
|
onDebugEventSelected(debugEventBody: DebugRuleNodeEventBody) {
|
||||||
const ruleNodeConfigComponent = this.ruleNodeComponent.ruleNodeConfigComponent;
|
const ruleNodeConfigComponent = this.ruleNodeComponent.ruleNodeConfigComponent;
|
||||||
const ruleNodeConfigDefinedComponent = ruleNodeConfigComponent.definedConfigComponent;
|
const ruleNodeConfigDefinedComponent = ruleNodeConfigComponent.definedConfigComponent;
|
||||||
if (ruleNodeConfigComponent.useDefinedDirective() && ruleNodeConfigDefinedComponent.hasScript && ruleNodeConfigDefinedComponent.testScript) {
|
if (ruleNodeConfigComponent.useDefinedDirective()
|
||||||
|
&& ruleNodeConfigDefinedComponent.hasScript
|
||||||
|
&& ruleNodeConfigDefinedComponent.testScript) {
|
||||||
ruleNodeConfigDefinedComponent.testScript(debugEventBody);
|
ruleNodeConfigDefinedComponent.testScript(debugEventBody);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1456,7 +1459,8 @@ export class RuleChainPageComponent extends PageComponent
|
|||||||
const ruleChainMetaData: RuleChainMetaData = {
|
const ruleChainMetaData: RuleChainMetaData = {
|
||||||
ruleChainId: this.ruleChain.id,
|
ruleChainId: this.ruleChain.id,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
connections: []
|
connections: [],
|
||||||
|
version: ruleChain.version
|
||||||
};
|
};
|
||||||
const nodes: FcRuleNode[] = [];
|
const nodes: FcRuleNode[] = [];
|
||||||
this.ruleChainModel.nodes.forEach((node) => {
|
this.ruleChainModel.nodes.forEach((node) => {
|
||||||
@ -1465,7 +1469,9 @@ export class RuleChainPageComponent extends PageComponent
|
|||||||
id: node.ruleNodeId,
|
id: node.ruleNodeId,
|
||||||
type: node.component.clazz,
|
type: node.component.clazz,
|
||||||
name: node.name,
|
name: node.name,
|
||||||
configurationVersion: isDefinedAndNotNull(node.configurationVersion) ? node.configurationVersion : node.component.configurationVersion,
|
configurationVersion: isDefinedAndNotNull(node.configurationVersion)
|
||||||
|
? node.configurationVersion
|
||||||
|
: node.component.configurationVersion,
|
||||||
configuration: node.configuration,
|
configuration: node.configuration,
|
||||||
additionalInfo: node.additionalInfo ? node.additionalInfo : {},
|
additionalInfo: node.additionalInfo ? node.additionalInfo : {},
|
||||||
debugMode: node.debugMode,
|
debugMode: node.debugMode,
|
||||||
@ -1500,7 +1506,17 @@ export class RuleChainPageComponent extends PageComponent
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
this.ruleChainService.saveRuleChainMetadata(ruleChainMetaData).subscribe((savedRuleChainMetaData) => {
|
this.ruleChainService.saveRuleChainMetadata(ruleChainMetaData)
|
||||||
|
.pipe(
|
||||||
|
catchError(err => {
|
||||||
|
if (err.status === HttpStatusCode.Conflict) {
|
||||||
|
return this.ruleChainService.getRuleChainMetadata(ruleChainMetaData.ruleChainId.id);
|
||||||
|
}
|
||||||
|
return throwError(() => err);
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.subscribe((savedRuleChainMetaData) => {
|
||||||
|
this.ruleChain.version = savedRuleChainMetaData.version;
|
||||||
this.ruleChainMetaData = savedRuleChainMetaData;
|
this.ruleChainMetaData = savedRuleChainMetaData;
|
||||||
if (this.isImport) {
|
if (this.isImport) {
|
||||||
this.isDirtyValue = false;
|
this.isDirtyValue = false;
|
||||||
|
|||||||
@ -30,7 +30,7 @@
|
|||||||
<div class="message-container">
|
<div class="message-container">
|
||||||
<span>
|
<span>
|
||||||
{{ 'entity.version-conflict.link' | translate:
|
{{ 'entity.version-conflict.link' | translate:
|
||||||
{ entityType: (entityTypeTranslations.get(data.entity.id.entityType).type | translate) }
|
{ entityType: (entityTypeTranslations.get(entityId.entityType).type | translate) }
|
||||||
}}
|
}}
|
||||||
<a class="cursor-pointer" (click)="onLinkClick($event)">{{ 'entity.link' | translate }}</a>.
|
<a class="cursor-pointer" (click)="onLinkClick($event)">{{ 'entity.link' | translate }}</a>.
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@ -21,10 +21,12 @@ import { ImportExportService } from '@shared/import-export/import-export.service
|
|||||||
import { CommonModule } from '@angular/common';
|
import { CommonModule } from '@angular/common';
|
||||||
import { entityTypeTranslations } from '@shared/models/entity-type.models';
|
import { entityTypeTranslations } from '@shared/models/entity-type.models';
|
||||||
import { EntityInfoData } from '@shared/models/entity.models';
|
import { EntityInfoData } from '@shared/models/entity.models';
|
||||||
|
import { EntityId } from '@shared/models/id/entity-id';
|
||||||
|
import { RuleChainMetaData } from '@shared/models/rule-chain.models';
|
||||||
|
|
||||||
interface EntityConflictDialogData {
|
interface EntityConflictDialogData {
|
||||||
message: string;
|
message: string;
|
||||||
entity: EntityInfoData;
|
entity: EntityInfoData | RuleChainMetaData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
@ -38,13 +40,18 @@ interface EntityConflictDialogData {
|
|||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class EntityConflictDialogComponent {
|
export class EntityConflictDialogComponent {
|
||||||
|
|
||||||
|
entityId: EntityId;
|
||||||
|
|
||||||
readonly entityTypeTranslations = entityTypeTranslations;
|
readonly entityTypeTranslations = entityTypeTranslations;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
@Inject(MAT_DIALOG_DATA) public data: EntityConflictDialogData,
|
@Inject(MAT_DIALOG_DATA) public data: EntityConflictDialogData,
|
||||||
private dialogRef: MatDialogRef<EntityConflictDialogComponent>,
|
private dialogRef: MatDialogRef<EntityConflictDialogComponent>,
|
||||||
private importExportService: ImportExportService,
|
private importExportService: ImportExportService,
|
||||||
) {}
|
) {
|
||||||
|
this.entityId = (data.entity as EntityInfoData).id ?? (data.entity as RuleChainMetaData).ruleChainId;
|
||||||
|
}
|
||||||
|
|
||||||
onCancel(): void {
|
onCancel(): void {
|
||||||
this.dialogRef.close();
|
this.dialogRef.close();
|
||||||
|
|||||||
@ -55,7 +55,11 @@ import { EntityType } from '@shared/models/entity-type.models';
|
|||||||
import { UtilsService } from '@core/services/utils.service';
|
import { UtilsService } from '@core/services/utils.service';
|
||||||
import { WidgetService } from '@core/http/widget.service';
|
import { WidgetService } from '@core/http/widget.service';
|
||||||
import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
|
import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
|
||||||
import { EntityInfoData, ImportEntitiesResultInfo, ImportEntityData } from '@shared/models/entity.models';
|
import {
|
||||||
|
EntityInfoData,
|
||||||
|
ImportEntitiesResultInfo,
|
||||||
|
ImportEntityData
|
||||||
|
} from '@shared/models/entity.models';
|
||||||
import { RequestConfig } from '@core/http/http-utils';
|
import { RequestConfig } from '@core/http/http-utils';
|
||||||
import { RuleChain, RuleChainImport, RuleChainMetaData, RuleChainType } from '@shared/models/rule-chain.models';
|
import { RuleChain, RuleChainImport, RuleChainMetaData, RuleChainType } from '@shared/models/rule-chain.models';
|
||||||
import { RuleChainService } from '@core/http/rule-chain.service';
|
import { RuleChainService } from '@core/http/rule-chain.service';
|
||||||
@ -360,25 +364,26 @@ export class ImportExportService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public exportEntity(entityData: EntityInfoData): void {
|
public exportEntity(entityData: EntityInfoData | RuleChainMetaData): void {
|
||||||
|
const id = (entityData as EntityInfoData).id ?? (entityData as RuleChainMetaData).ruleChainId;
|
||||||
let preparedData;
|
let preparedData;
|
||||||
switch (entityData.id.entityType) {
|
switch (id.entityType) {
|
||||||
case EntityType.DEVICE_PROFILE:
|
case EntityType.DEVICE_PROFILE:
|
||||||
case EntityType.ASSET_PROFILE:
|
case EntityType.ASSET_PROFILE:
|
||||||
preparedData = this.prepareProfileExport(entityData as DeviceProfile | AssetProfile);
|
preparedData = this.prepareProfileExport(entityData as DeviceProfile | AssetProfile);
|
||||||
break;
|
break;
|
||||||
case EntityType.RULE_CHAIN:
|
case EntityType.RULE_CHAIN:
|
||||||
this.ruleChainService.getRuleChainMetadata(entityData.id.id)
|
forkJoin([this.ruleChainService.getRuleChainMetadata(id.id), this.ruleChainService.getRuleChain(id.id)])
|
||||||
.pipe(
|
.pipe(
|
||||||
take(1),
|
take(1),
|
||||||
map((ruleChainMetaData) => {
|
map(([ruleChainMetaData, ruleChain]) => {
|
||||||
const ruleChainExport: RuleChainImport = {
|
const ruleChainExport: RuleChainImport = {
|
||||||
ruleChain: this.prepareRuleChain(entityData as RuleChain),
|
ruleChain: this.prepareRuleChain(ruleChain),
|
||||||
metadata: this.prepareRuleChainMetaData(ruleChainMetaData)
|
metadata: this.prepareRuleChainMetaData(ruleChainMetaData)
|
||||||
};
|
};
|
||||||
return ruleChainExport;
|
return ruleChainExport;
|
||||||
}))
|
}))
|
||||||
.subscribe(ruleChainData => this.exportToPc(ruleChainData, entityData.name));
|
.subscribe(this.onRuleChainExported());
|
||||||
return;
|
return;
|
||||||
case EntityType.WIDGETS_BUNDLE:
|
case EntityType.WIDGETS_BUNDLE:
|
||||||
this.exportSelectedWidgetsBundle(entityData as WidgetsBundle);
|
this.exportSelectedWidgetsBundle(entityData as WidgetsBundle);
|
||||||
@ -389,7 +394,7 @@ export class ImportExportService {
|
|||||||
default:
|
default:
|
||||||
preparedData = this.prepareExport(entityData);
|
preparedData = this.prepareExport(entityData);
|
||||||
}
|
}
|
||||||
this.exportToPc(preparedData, entityData.name);
|
this.exportToPc(preparedData, (entityData as EntityInfoData).name);
|
||||||
}
|
}
|
||||||
|
|
||||||
private exportSelectedWidgetsBundle(widgetsBundle: WidgetsBundle): void {
|
private exportSelectedWidgetsBundle(widgetsBundle: WidgetsBundle): void {
|
||||||
@ -584,8 +589,12 @@ export class ImportExportService {
|
|||||||
return ruleChainExport;
|
return ruleChainExport;
|
||||||
})
|
})
|
||||||
))
|
))
|
||||||
).subscribe({
|
).subscribe(this.onRuleChainExported());
|
||||||
next: (ruleChainExport) => {
|
}
|
||||||
|
|
||||||
|
private onRuleChainExported() {
|
||||||
|
return {
|
||||||
|
next: (ruleChainExport: RuleChainImport) => {
|
||||||
let name = ruleChainExport.ruleChain.name;
|
let name = ruleChainExport.ruleChain.name;
|
||||||
name = name.toLowerCase().replace(/\W/g, '_');
|
name = name.toLowerCase().replace(/\W/g, '_');
|
||||||
this.exportToPc(ruleChainExport, name);
|
this.exportToPc(ruleChainExport, name);
|
||||||
@ -593,7 +602,7 @@ export class ImportExportService {
|
|||||||
error: (e) => {
|
error: (e) => {
|
||||||
this.handleExportError(e, 'rulechain.export-failed-error');
|
this.handleExportError(e, 'rulechain.export-failed-error');
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public importRuleChain(expectedRuleChainType: RuleChainType): Observable<RuleChainImport> {
|
public importRuleChain(expectedRuleChainType: RuleChainType): Observable<RuleChainImport> {
|
||||||
|
|||||||
@ -34,7 +34,7 @@ export interface RuleChain extends BaseData<RuleChainId>, HasTenantId, HasVersio
|
|||||||
isDefault?: boolean;
|
isDefault?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface RuleChainMetaData {
|
export interface RuleChainMetaData extends HasVersion {
|
||||||
ruleChainId: RuleChainId;
|
ruleChainId: RuleChainId;
|
||||||
firstNodeIndex?: number;
|
firstNodeIndex?: number;
|
||||||
nodes: Array<RuleNode>;
|
nodes: Array<RuleNode>;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user