Major adjustments
This commit is contained in:
parent
a291e1d2e3
commit
55e33d7f3d
@ -16,48 +16,76 @@
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import {
|
||||
HttpErrorResponse,
|
||||
HttpEvent,
|
||||
HttpHandler,
|
||||
HttpInterceptor,
|
||||
HttpRequest,
|
||||
HttpErrorResponse,
|
||||
HttpStatusCode
|
||||
} from '@angular/common/http';
|
||||
import { Observable, throwError, of } from 'rxjs';
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
import { catchError, switchMap } from 'rxjs/operators';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { EntityConflictDialogComponent } from '@shared/components/dialog/entity-conflict-dialog/entity-conflict-dialog.component';
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
|
||||
interface ConflictedEntity { version: number; id: EntityId }
|
||||
import {
|
||||
EntityConflictDialogComponent
|
||||
} from '@shared/components/dialog/entity-conflict-dialog/entity-conflict-dialog.component';
|
||||
import { InterceptorConfigService } from '@core/services/interceptor-config.service';
|
||||
import { HasId } from '@shared/models/base-data';
|
||||
import { HasVersion } from '@shared/models/entity.models';
|
||||
|
||||
@Injectable()
|
||||
export class EntityConflictInterceptor implements HttpInterceptor {
|
||||
constructor(private dialog: MatDialog) {}
|
||||
|
||||
intercept(request: HttpRequest<unknown & ConflictedEntity>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
constructor(
|
||||
private dialog: MatDialog,
|
||||
private interceptorConfigService: InterceptorConfigService
|
||||
) {}
|
||||
|
||||
intercept(request: HttpRequest<unknown & HasId & HasVersion>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||
if (!request.url.startsWith('/api/')) {
|
||||
return next.handle(request);
|
||||
}
|
||||
|
||||
return next.handle(request).pipe(
|
||||
catchError((error: HttpErrorResponse) => {
|
||||
if (error.status === HttpStatusCode.Conflict) {
|
||||
return this.resolveConflictRequest(request, error.error.message)
|
||||
.pipe(switchMap(httpRequest => next.handle(httpRequest)));
|
||||
} else {
|
||||
if (error.status !== HttpStatusCode.Conflict) {
|
||||
return throwError(() => error);
|
||||
}
|
||||
|
||||
return this.handleConflictError(request, next, error);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private resolveConflictRequest(request: HttpRequest<unknown & ConflictedEntity>, message: string): Observable<HttpRequest<unknown>> {
|
||||
const dialogRef = this.dialog.open(EntityConflictDialogComponent, {data: {message, entityId: request.body.id}});
|
||||
private handleConflictError(
|
||||
request: HttpRequest<unknown & HasId & HasVersion>,
|
||||
next: HttpHandler,
|
||||
error: HttpErrorResponse
|
||||
): Observable<HttpEvent<unknown>> {
|
||||
if (this.interceptorConfigService.getInterceptorConfig(request).ignoreVersionConflict) {
|
||||
return next.handle(this.updateRequestVersion(request));
|
||||
}
|
||||
|
||||
return dialogRef.afterClosed().pipe(
|
||||
return this.openConflictDialog(request, error.error.message).pipe(
|
||||
switchMap(result => {
|
||||
if (result) {
|
||||
request.body.version = null;
|
||||
return next.handle(this.updateRequestVersion(request));
|
||||
}
|
||||
return of(request);
|
||||
return of(null);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private updateRequestVersion(request: HttpRequest<unknown & HasId & HasVersion>): HttpRequest<unknown & HasId & HasVersion> {
|
||||
const body = { ...request.body, version: null };
|
||||
return request.clone({ body });
|
||||
}
|
||||
|
||||
private openConflictDialog(request: HttpRequest<unknown & HasId & HasVersion>, message: string): Observable<boolean> {
|
||||
const dialogRef = this.dialog.open(EntityConflictDialogComponent, {
|
||||
data: { message, entity: request.body }
|
||||
});
|
||||
|
||||
return dialogRef.afterClosed();
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,10 +16,9 @@
|
||||
|
||||
import { HttpErrorResponse, HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '@angular/common/http';
|
||||
import { Observable } from 'rxjs/internal/Observable';
|
||||
import { Inject, Injectable } from '@angular/core';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { AuthService } from '@core/auth/auth.service';
|
||||
import { Constants } from '@shared/models/constants';
|
||||
import { InterceptorHttpParams } from './interceptor-http-params';
|
||||
import { catchError, delay, finalize, mergeMap, switchMap } from 'rxjs/operators';
|
||||
import { of, throwError } from 'rxjs';
|
||||
import { InterceptorConfig } from './interceptor-config';
|
||||
@ -30,6 +29,7 @@ import { ActionNotificationShow } from '@app/core/notification/notification.acti
|
||||
import { DialogService } from '@core/services/dialog.service';
|
||||
import { TranslateService } from '@ngx-translate/core';
|
||||
import { parseHttpErrorMessage } from '@core/utils';
|
||||
import { InterceptorConfigService } from '@core/services/interceptor-config.service';
|
||||
|
||||
const tmpHeaders = {};
|
||||
|
||||
@ -39,22 +39,19 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
|
||||
private AUTH_SCHEME = 'Bearer ';
|
||||
private AUTH_HEADER_NAME = 'X-Authorization';
|
||||
|
||||
private internalUrlPrefixes = [
|
||||
'/api/auth/token',
|
||||
'/api/rpc'
|
||||
];
|
||||
|
||||
private activeRequests = 0;
|
||||
|
||||
constructor(@Inject(Store) private store: Store<AppState>,
|
||||
@Inject(DialogService) private dialogService: DialogService,
|
||||
@Inject(TranslateService) private translate: TranslateService,
|
||||
@Inject(AuthService) private authService: AuthService) {
|
||||
}
|
||||
constructor(
|
||||
private store: Store<AppState>,
|
||||
private dialogService: DialogService,
|
||||
private translate: TranslateService,
|
||||
private authService: AuthService,
|
||||
private interceptorConfigService: InterceptorConfigService
|
||||
) {}
|
||||
|
||||
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||
if (req.url.startsWith('/api/')) {
|
||||
const config = this.getInterceptorConfig(req);
|
||||
const config = this.interceptorConfigService.getInterceptorConfig(req);
|
||||
this.updateLoadingState(config, true);
|
||||
let observable$: Observable<HttpEvent<any>>;
|
||||
if (this.isTokenBasedAuthEntryPoint(req.url)) {
|
||||
@ -98,7 +95,7 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
|
||||
}
|
||||
|
||||
private handleResponseError(req: HttpRequest<any>, next: HttpHandler, errorResponse: HttpErrorResponse): Observable<HttpEvent<any>> {
|
||||
const config = this.getInterceptorConfig(req);
|
||||
const config = this.interceptorConfigService.getInterceptorConfig(req);
|
||||
let unhandled = false;
|
||||
const ignoreErrors = config.ignoreErrors;
|
||||
const resendRequest = config.resendRequest;
|
||||
@ -171,15 +168,6 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
private isInternalUrlPrefix(url: string): boolean {
|
||||
for (const index in this.internalUrlPrefixes) {
|
||||
if (url.startsWith(this.internalUrlPrefixes[index])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private isTokenBasedAuthEntryPoint(url: string): boolean {
|
||||
return url.startsWith('/api/') &&
|
||||
!url.startsWith(Constants.entryPoints.login) &&
|
||||
@ -202,19 +190,6 @@ export class GlobalHttpInterceptor implements HttpInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
private getInterceptorConfig(req: HttpRequest<any>): InterceptorConfig {
|
||||
let config: InterceptorConfig;
|
||||
if (req.params && req.params instanceof InterceptorHttpParams) {
|
||||
config = (req.params as InterceptorHttpParams).interceptorConfig;
|
||||
} else {
|
||||
config = new InterceptorConfig(false, false);
|
||||
}
|
||||
if (this.isInternalUrlPrefix(req.url)) {
|
||||
config.ignoreLoading = true;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private showError(error: string, timeout: number = 0) {
|
||||
setTimeout(() => {
|
||||
this.store.dispatch(new ActionNotificationShow({message: error, type: 'error'}));
|
||||
|
||||
@ -17,5 +17,6 @@
|
||||
export class InterceptorConfig {
|
||||
constructor(public ignoreLoading: boolean = false,
|
||||
public ignoreErrors: boolean = false,
|
||||
public ignoreVersionConflict: boolean = false,
|
||||
public resendRequest: boolean = false) {}
|
||||
}
|
||||
|
||||
53
ui-ngx/src/app/core/services/interceptor-config.service.ts
Normal file
53
ui-ngx/src/app/core/services/interceptor-config.service.ts
Normal file
@ -0,0 +1,53 @@
|
||||
///
|
||||
/// Copyright © 2016-2024 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.
|
||||
///
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpRequest } from '@angular/common/http';
|
||||
import { InterceptorConfig } from '@core/interceptors/interceptor-config';
|
||||
import { InterceptorHttpParams } from '@core/interceptors/interceptor-http-params';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class InterceptorConfigService {
|
||||
|
||||
private readonly internalUrlPrefixes = [
|
||||
'/api/auth/token',
|
||||
'/api/rpc'
|
||||
];
|
||||
|
||||
getInterceptorConfig(req: HttpRequest<unknown>): InterceptorConfig {
|
||||
let config: InterceptorConfig;
|
||||
if (req.params && req.params instanceof InterceptorHttpParams) {
|
||||
config = (req.params as InterceptorHttpParams).interceptorConfig;
|
||||
} else {
|
||||
config = new InterceptorConfig();
|
||||
}
|
||||
if (this.isInternalUrlPrefix(req.url)) {
|
||||
config.ignoreLoading = true;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
private isInternalUrlPrefix(url: string): boolean {
|
||||
for (const prefix of this.internalUrlPrefixes) {
|
||||
if (url.startsWith(prefix)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -27,9 +27,9 @@
|
||||
<div mat-dialog-content>
|
||||
<div class="message-container">
|
||||
<span>{{ data.message }}.</span>
|
||||
<span *ngIf="ExportableEntityTypes.includes(data.entityId.entityType)">
|
||||
<span>
|
||||
{{ 'entity.version-conflict.link' | translate:
|
||||
{ entityType: (entityTypeTranslations.get(data.entityId.entityType).type | translate) }
|
||||
{ entityType: (entityTypeTranslations.get(data.entity.id.entityType).type | translate) }
|
||||
}}
|
||||
<a class="cursor-pointer" (click)="onLinkClick($event)">{{ 'entity.link' | translate }}</a>.
|
||||
</span>
|
||||
|
||||
@ -18,14 +18,13 @@ import { Component, Inject } from '@angular/core';
|
||||
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
|
||||
import { SharedModule } from '@shared/shared.module';
|
||||
import { ImportExportService } from '@shared/import-export/import-export.service';
|
||||
import { ExportableEntityTypes } from '@shared/import-export/import-export.models';
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { EntityType, entityTypeTranslations } from '@shared/models/entity-type.models';
|
||||
import { entityTypeTranslations } from '@shared/models/entity-type.models';
|
||||
import { EntityInfoData } from '@shared/models/entity.models';
|
||||
|
||||
interface EntityConflictDialogData {
|
||||
message: string;
|
||||
entityId: EntityId & {entityType: EntityType};
|
||||
entity: EntityInfoData;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -39,7 +38,6 @@ interface EntityConflictDialogData {
|
||||
],
|
||||
})
|
||||
export class EntityConflictDialogComponent {
|
||||
readonly ExportableEntityTypes = ExportableEntityTypes;
|
||||
readonly entityTypeTranslations = entityTypeTranslations;
|
||||
|
||||
constructor(
|
||||
@ -58,6 +56,6 @@ export class EntityConflictDialogComponent {
|
||||
|
||||
onLinkClick(event: MouseEvent): void {
|
||||
event.preventDefault();
|
||||
this.importExportService.exportEntity(this.data.entityId);
|
||||
this.importExportService.exportEntity(this.data.entity);
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,16 +17,6 @@
|
||||
import { Widget, WidgetTypeDetails } from '@app/shared/models/widget.models';
|
||||
import { DashboardLayoutId } from '@shared/models/dashboard.models';
|
||||
import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
|
||||
import { EntityType } from '@shared/models/entity-type.models';
|
||||
|
||||
export const ExportableEntityTypes = [
|
||||
EntityType.DEVICE_PROFILE,
|
||||
EntityType.ASSET_PROFILE,
|
||||
EntityType.RULE_CHAIN,
|
||||
EntityType.DASHBOARD,
|
||||
EntityType.WIDGET_TYPE,
|
||||
EntityType.WIDGETS_BUNDLE
|
||||
];
|
||||
|
||||
export interface ImportWidgetResult {
|
||||
widget: Widget;
|
||||
|
||||
@ -55,7 +55,7 @@ import { EntityType } from '@shared/models/entity-type.models';
|
||||
import { UtilsService } from '@core/services/utils.service';
|
||||
import { WidgetService } from '@core/http/widget.service';
|
||||
import { WidgetsBundle } from '@shared/models/widgets-bundle.model';
|
||||
import { ImportEntitiesResultInfo, ImportEntityData } from '@shared/models/entity.models';
|
||||
import { EntityInfoData, ImportEntitiesResultInfo, ImportEntityData } from '@shared/models/entity.models';
|
||||
import { RequestConfig } from '@core/http/http-utils';
|
||||
import { RuleChain, RuleChainImport, RuleChainMetaData, RuleChainType } from '@shared/models/rule-chain.models';
|
||||
import { RuleChainService } from '@core/http/rule-chain.service';
|
||||
@ -79,7 +79,7 @@ import { ImageService } from '@core/http/image.service';
|
||||
import { ImageExportData, ImageResourceInfo, ImageResourceType } from '@shared/models/resource.models';
|
||||
import { selectUserSettingsProperty } from '@core/auth/auth.selectors';
|
||||
import { ActionPreferencesPutUserSettings } from '@core/auth/auth.actions';
|
||||
import { ExportableEntity } from '@shared/models/base-data';
|
||||
import { ExportableEntity, HasId } from '@shared/models/base-data';
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
|
||||
export type editMissingAliasesFunction = (widgets: Array<Widget>, isSingleWidget: boolean,
|
||||
@ -380,29 +380,33 @@ export class ImportExportService {
|
||||
});
|
||||
}
|
||||
|
||||
public exportEntity(entityId: EntityId): void {
|
||||
switch (entityId.entityType) {
|
||||
public exportEntity(entityData: EntityInfoData): void {
|
||||
let preparedData;
|
||||
switch (entityData.id.entityType) {
|
||||
case EntityType.DEVICE_PROFILE:
|
||||
this.exportDeviceProfile(entityId.id);
|
||||
break;
|
||||
case EntityType.ASSET_PROFILE:
|
||||
this.exportAssetProfile(entityId.id);
|
||||
preparedData = this.prepareProfileExport(entityData as DeviceProfile | AssetProfile);
|
||||
break;
|
||||
case EntityType.RULE_CHAIN:
|
||||
this.exportRuleChain(entityId.id);
|
||||
break;
|
||||
this.ruleChainService.getRuleChainMetadata(entityData.id.id)
|
||||
.pipe(
|
||||
take(1),
|
||||
map((ruleChainMetaData) => {
|
||||
const ruleChainExport: RuleChainImport = {
|
||||
ruleChain: this.prepareRuleChain(entityData as RuleChain),
|
||||
metadata: this.prepareRuleChainMetaData(ruleChainMetaData)
|
||||
};
|
||||
return ruleChainExport;
|
||||
}))
|
||||
.subscribe(ruleChainData => this.exportToPc(ruleChainData, entityData.name));
|
||||
return;
|
||||
case EntityType.DASHBOARD:
|
||||
this.exportDashboard(entityId.id);
|
||||
break;
|
||||
case EntityType.WIDGET_TYPE:
|
||||
this.exportWidgetType(entityId.id);
|
||||
break;
|
||||
case EntityType.WIDGETS_BUNDLE:
|
||||
this.exportWidgetsBundle(entityId.id);
|
||||
preparedData = this.prepareDashboardExport(entityData as Dashboard);
|
||||
break;
|
||||
default:
|
||||
throwError(() => 'Not supported Entity Type');
|
||||
preparedData = this.prepareExport(entityData);
|
||||
}
|
||||
this.exportToPc(preparedData, entityData.name);
|
||||
}
|
||||
|
||||
private exportWidgetsBundleWithWidgetTypes(widgetsBundle: WidgetsBundle) {
|
||||
@ -1133,6 +1137,9 @@ export class ImportExportService {
|
||||
if (isDefined(exportedData.externalId)) {
|
||||
delete exportedData.externalId;
|
||||
}
|
||||
if (isDefined(exportedData.version)) {
|
||||
delete exportedData.version;
|
||||
}
|
||||
return exportedData;
|
||||
}
|
||||
|
||||
|
||||
@ -22,9 +22,9 @@ import { EntitySearchQuery } from '@shared/models/relation.models';
|
||||
import { AssetProfileId } from '@shared/models/id/asset-profile-id';
|
||||
import { RuleChainId } from '@shared/models/id/rule-chain-id';
|
||||
import { DashboardId } from '@shared/models/id/dashboard-id';
|
||||
import { EntityInfoData, HasTenantId } from '@shared/models/entity.models';
|
||||
import { EntityInfoData, HasTenantId, HasVersion } from '@shared/models/entity.models';
|
||||
|
||||
export interface AssetProfile extends BaseData<AssetProfileId>, HasTenantId, ExportableEntity<AssetProfileId> {
|
||||
export interface AssetProfile extends BaseData<AssetProfileId>, HasTenantId, HasVersion, ExportableEntity<AssetProfileId> {
|
||||
tenantId?: TenantId;
|
||||
name: string;
|
||||
description?: string;
|
||||
@ -42,7 +42,7 @@ export interface AssetProfileInfo extends EntityInfoData {
|
||||
defaultDashboardId?: DashboardId;
|
||||
}
|
||||
|
||||
export interface Asset extends BaseData<AssetId>, HasTenantId, ExportableEntity<AssetId> {
|
||||
export interface Asset extends BaseData<AssetId>, HasTenantId, HasVersion, ExportableEntity<AssetId> {
|
||||
tenantId?: TenantId;
|
||||
customerId?: CustomerId;
|
||||
name: string;
|
||||
|
||||
@ -18,9 +18,9 @@ import { CustomerId } from '@shared/models/id/customer-id';
|
||||
import { ContactBased } from '@shared/models/contact-based.model';
|
||||
import { TenantId } from './id/tenant-id';
|
||||
import { ExportableEntity } from '@shared/models/base-data';
|
||||
import { HasTenantId } from '@shared/models/entity.models';
|
||||
import { HasTenantId, HasVersion } from '@shared/models/entity.models';
|
||||
|
||||
export interface Customer extends ContactBased<CustomerId>, HasTenantId, ExportableEntity<CustomerId> {
|
||||
export interface Customer extends ContactBased<CustomerId>, HasTenantId, HasVersion, ExportableEntity<CustomerId> {
|
||||
tenantId: TenantId;
|
||||
title: string;
|
||||
additionalInfo?: any;
|
||||
|
||||
@ -23,9 +23,9 @@ import { Timewindow } from '@shared/models/time/time.models';
|
||||
import { EntityAliases } from './alias.models';
|
||||
import { Filters } from '@shared/models/query/query.models';
|
||||
import { MatDialogRef } from '@angular/material/dialog';
|
||||
import { HasTenantId } from '@shared/models/entity.models';
|
||||
import { HasTenantId, HasVersion } from '@shared/models/entity.models';
|
||||
|
||||
export interface DashboardInfo extends BaseData<DashboardId>, HasTenantId, ExportableEntity<DashboardId> {
|
||||
export interface DashboardInfo extends BaseData<DashboardId>, HasTenantId, HasVersion, ExportableEntity<DashboardId> {
|
||||
tenantId?: TenantId;
|
||||
title?: string;
|
||||
image?: string;
|
||||
|
||||
@ -22,7 +22,7 @@ import { DeviceCredentialsId } from '@shared/models/id/device-credentials-id';
|
||||
import { EntitySearchQuery } from '@shared/models/relation.models';
|
||||
import { DeviceProfileId } from '@shared/models/id/device-profile-id';
|
||||
import { RuleChainId } from '@shared/models/id/rule-chain-id';
|
||||
import { EntityInfoData, HasTenantId } from '@shared/models/entity.models';
|
||||
import { EntityInfoData, HasTenantId, HasVersion } from '@shared/models/entity.models';
|
||||
import { FilterPredicateValue, KeyFilter } from '@shared/models/query/query.models';
|
||||
import { TimeUnit } from '@shared/models/time/time.models';
|
||||
import * as _moment from 'moment';
|
||||
@ -584,7 +584,7 @@ export interface DeviceProfileData {
|
||||
provisionConfiguration?: DeviceProvisionConfiguration;
|
||||
}
|
||||
|
||||
export interface DeviceProfile extends BaseData<DeviceProfileId>, HasTenantId, ExportableEntity<DeviceProfileId> {
|
||||
export interface DeviceProfile extends BaseData<DeviceProfileId>, HasTenantId, HasVersion, ExportableEntity<DeviceProfileId> {
|
||||
tenantId?: TenantId;
|
||||
name: string;
|
||||
description?: string;
|
||||
@ -711,7 +711,7 @@ export interface DeviceData {
|
||||
transportConfiguration: DeviceTransportConfiguration;
|
||||
}
|
||||
|
||||
export interface Device extends BaseData<DeviceId>, HasTenantId, ExportableEntity<DeviceId> {
|
||||
export interface Device extends BaseData<DeviceId>, HasTenantId, HasVersion, ExportableEntity<DeviceId> {
|
||||
tenantId?: TenantId;
|
||||
customerId?: CustomerId;
|
||||
name: string;
|
||||
@ -801,7 +801,7 @@ export const credentialTypesByTransportType = new Map<DeviceTransportType, Devic
|
||||
]
|
||||
);
|
||||
|
||||
export interface DeviceCredentials extends BaseData<DeviceCredentialsId> {
|
||||
export interface DeviceCredentials extends BaseData<DeviceCredentialsId>, HasTenantId {
|
||||
deviceId: DeviceId;
|
||||
credentialsType: DeviceCredentialsType;
|
||||
credentialsId: string;
|
||||
|
||||
@ -22,9 +22,9 @@ import { EntitySearchQuery } from '@shared/models/relation.models';
|
||||
import { RuleChainId } from '@shared/models/id/rule-chain-id';
|
||||
import { BaseEventBody } from '@shared/models/event.models';
|
||||
import { EventId } from '@shared/models/id/event-id';
|
||||
import { HasTenantId } from '@shared/models/entity.models';
|
||||
import { HasTenantId, HasVersion } from '@shared/models/entity.models';
|
||||
|
||||
export interface Edge extends BaseData<EdgeId>, HasTenantId {
|
||||
export interface Edge extends BaseData<EdgeId>, HasTenantId, HasVersion {
|
||||
tenantId?: TenantId;
|
||||
customerId?: CustomerId;
|
||||
name: string;
|
||||
|
||||
@ -20,7 +20,7 @@ import { CustomerId } from '@shared/models/id/customer-id';
|
||||
import { EntityViewId } from '@shared/models/id/entity-view-id';
|
||||
import { EntityId } from '@shared/models/id/entity-id';
|
||||
import { EntitySearchQuery } from '@shared/models/relation.models';
|
||||
import { HasTenantId } from '@shared/models/entity.models';
|
||||
import { HasTenantId, HasVersion } from '@shared/models/entity.models';
|
||||
|
||||
export interface AttributesEntityView {
|
||||
cs: Array<string>;
|
||||
@ -33,7 +33,7 @@ export interface TelemetryEntityView {
|
||||
attributes: AttributesEntityView;
|
||||
}
|
||||
|
||||
export interface EntityView extends BaseData<EntityViewId>, HasTenantId, ExportableEntity<EntityViewId> {
|
||||
export interface EntityView extends BaseData<EntityViewId>, HasTenantId, HasVersion, ExportableEntity<EntityViewId> {
|
||||
tenantId: TenantId;
|
||||
customerId: CustomerId;
|
||||
entityId: EntityId;
|
||||
|
||||
@ -187,3 +187,7 @@ export const entityFields: {[fieldName: string]: EntityField} = {
|
||||
export interface HasTenantId {
|
||||
tenantId?: TenantId;
|
||||
}
|
||||
|
||||
export interface HasVersion {
|
||||
version?: number;
|
||||
}
|
||||
|
||||
@ -20,9 +20,9 @@ import { RuleChainId } from '@shared/models/id/rule-chain-id';
|
||||
import { RuleNodeId } from '@shared/models/id/rule-node-id';
|
||||
import { RuleNode, RuleNodeComponentDescriptor, RuleNodeType } from '@shared/models/rule-node.models';
|
||||
import { ComponentClusteringMode, ComponentType } from '@shared/models/component-descriptor.models';
|
||||
import { HasTenantId } from '@shared/models/entity.models';
|
||||
import { HasTenantId, HasVersion } from '@shared/models/entity.models';
|
||||
|
||||
export interface RuleChain extends BaseData<RuleChainId>, HasTenantId, ExportableEntity<RuleChainId> {
|
||||
export interface RuleChain extends BaseData<RuleChainId>, HasTenantId, HasVersion, ExportableEntity<RuleChainId> {
|
||||
tenantId: TenantId;
|
||||
name: string;
|
||||
firstRuleNodeId: RuleNodeId;
|
||||
|
||||
@ -17,9 +17,9 @@
|
||||
import { BaseData, ExportableEntity } from '@shared/models/base-data';
|
||||
import { TenantId } from '@shared/models/id/tenant-id';
|
||||
import { WidgetsBundleId } from '@shared/models/id/widgets-bundle-id';
|
||||
import { HasTenantId } from '@shared/models/entity.models';
|
||||
import { HasTenantId, HasVersion } from '@shared/models/entity.models';
|
||||
|
||||
export interface WidgetsBundle extends BaseData<WidgetsBundleId>, HasTenantId, ExportableEntity<WidgetsBundleId> {
|
||||
export interface WidgetsBundle extends BaseData<WidgetsBundleId>, HasTenantId, HasVersion, ExportableEntity<WidgetsBundleId> {
|
||||
tenantId: TenantId;
|
||||
alias: string;
|
||||
title: string;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user