UI: Add support name conflict strategy

This commit is contained in:
Vladyslav_Prykhodko 2025-10-10 18:35:41 +03:00
parent f1da967a7d
commit e8d888e22b
8 changed files with 81 additions and 22 deletions

View File

@ -15,7 +15,7 @@
/// ///
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { createDefaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
@ -23,6 +23,7 @@ import { PageData } from '@shared/models/page/page-data';
import { EntitySubtype } from '@shared/models/entity-type.models'; import { EntitySubtype } from '@shared/models/entity-type.models';
import { Asset, AssetInfo, AssetSearchQuery } from '@shared/models/asset.models'; import { Asset, AssetInfo, AssetSearchQuery } from '@shared/models/asset.models';
import { BulkImportRequest, BulkImportResult } from '@shared/import-export/import-export.models'; import { BulkImportRequest, BulkImportResult } from '@shared/import-export/import-export.models';
import { SaveEntityParams } from '@shared/models/entity.models';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -69,8 +70,10 @@ export class AssetService {
return this.http.get<AssetInfo>(`/api/asset/info/${assetId}`, defaultHttpOptionsFromConfig(config)); return this.http.get<AssetInfo>(`/api/asset/info/${assetId}`, defaultHttpOptionsFromConfig(config));
} }
public saveAsset(asset: Asset, config?: RequestConfig): Observable<Asset> { public saveAsset(asset: Asset, config?: RequestConfig): Observable<Asset>;
return this.http.post<Asset>('/api/asset', asset, defaultHttpOptionsFromConfig(config)); public saveAsset(asset: Asset, saveParams: SaveEntityParams, config?: RequestConfig): Observable<Asset>;
public saveAsset(asset: Asset, saveParamsOrConfig?: SaveEntityParams | RequestConfig, config?: RequestConfig): Observable<Asset> {
return this.http.post<Asset>('/api/asset', asset, createDefaultHttpOptions(saveParamsOrConfig, config));
} }
public deleteAsset(assetId: string, config?: RequestConfig) { public deleteAsset(assetId: string, config?: RequestConfig) {

View File

@ -15,12 +15,13 @@
/// ///
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { createDefaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { Customer } from '@shared/models/customer.model'; import { Customer } from '@shared/models/customer.model';
import { SaveEntityParams } from '@shared/models/entity.models';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -40,8 +41,10 @@ export class CustomerService {
return this.http.get<Customer>(`/api/customer/${customerId}`, defaultHttpOptionsFromConfig(config)); return this.http.get<Customer>(`/api/customer/${customerId}`, defaultHttpOptionsFromConfig(config));
} }
public saveCustomer(customer: Customer, config?: RequestConfig): Observable<Customer> { public saveCustomer(customer: Customer, config?: RequestConfig): Observable<Customer>;
return this.http.post<Customer>('/api/customer', customer, defaultHttpOptionsFromConfig(config)); public saveCustomer(customer: Customer, saveParams: SaveEntityParams, config?: RequestConfig): Observable<Customer>;
public saveCustomer(customer: Customer, saveParamsOrConfig?: SaveEntityParams | RequestConfig, config?: RequestConfig): Observable<Customer> {
return this.http.post<Customer>('/api/customer', customer, createDefaultHttpOptions(saveParamsOrConfig, config));
} }
public deleteCustomer(customerId: string, config?: RequestConfig) { public deleteCustomer(customerId: string, config?: RequestConfig) {

View File

@ -15,7 +15,7 @@
/// ///
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { createDefaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { Observable, ReplaySubject } from 'rxjs'; import { Observable, ReplaySubject } from 'rxjs';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
@ -28,13 +28,15 @@ import {
DeviceInfo, DeviceInfo,
DeviceInfoQuery, DeviceInfoQuery,
DeviceSearchQuery, DeviceSearchQuery,
PublishTelemetryCommand PublishTelemetryCommand,
SaveDeviceParams
} from '@shared/models/device.models'; } from '@shared/models/device.models';
import { EntitySubtype } from '@shared/models/entity-type.models'; import { EntitySubtype } from '@shared/models/entity-type.models';
import { AuthService } from '@core/auth/auth.service'; import { AuthService } from '@core/auth/auth.service';
import { BulkImportRequest, BulkImportResult } from '@shared/import-export/import-export.models'; import { BulkImportRequest, BulkImportResult } from '@shared/import-export/import-export.models';
import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models'; import { PersistentRpc, RpcStatus } from '@shared/models/rpc.models';
import { ResourcesService } from '@core/services/resources.service'; import { ResourcesService } from '@core/services/resources.service';
import { SaveEntityParams } from '@shared/models/entity.models';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -87,15 +89,19 @@ export class DeviceService {
return this.http.get<DeviceInfo>(`/api/device/info/${deviceId}`, defaultHttpOptionsFromConfig(config)); return this.http.get<DeviceInfo>(`/api/device/info/${deviceId}`, defaultHttpOptionsFromConfig(config));
} }
public saveDevice(device: Device, config?: RequestConfig): Observable<Device> { public saveDevice(device: Device, config?: RequestConfig): Observable<Device>;
return this.http.post<Device>('/api/device', device, defaultHttpOptionsFromConfig(config)); public saveDevice(device: Device, saveParams?: SaveDeviceParams, config?: RequestConfig): Observable<Device>;
public saveDevice(device: Device, saveParamsOrConfig?: SaveDeviceParams | RequestConfig, config?: RequestConfig): Observable<Device> {
return this.http.post<Device>('/api/device', device, createDefaultHttpOptions(saveParamsOrConfig, config));
} }
public saveDeviceWithCredentials(device: Device, credentials: DeviceCredentials, config?: RequestConfig): Observable<Device> { public saveDeviceWithCredentials(device: Device, credentials: DeviceCredentials, config?: RequestConfig): Observable<Device>;
public saveDeviceWithCredentials(device: Device, credentials: DeviceCredentials, saveParams: SaveEntityParams, config?: RequestConfig): Observable<Device>;
public saveDeviceWithCredentials(device: Device, credentials: DeviceCredentials, saveParamsOrConfig?: SaveEntityParams | RequestConfig, config?: RequestConfig): Observable<Device> {
return this.http.post<Device>('/api/device-with-credentials', { return this.http.post<Device>('/api/device-with-credentials', {
device, device,
credentials credentials
}, defaultHttpOptionsFromConfig(config)); }, createDefaultHttpOptions(saveParamsOrConfig, config));
} }
public deleteDevice(deviceId: string, config?: RequestConfig) { public deleteDevice(deviceId: string, config?: RequestConfig) {

View File

@ -15,13 +15,14 @@
/// ///
import { Injectable } from '@angular/core'; import { Injectable } from '@angular/core';
import { defaultHttpOptionsFromConfig, RequestConfig } from './http-utils'; import { createDefaultHttpOptions, defaultHttpOptionsFromConfig, RequestConfig } from './http-utils';
import { Observable } from 'rxjs'; import { Observable } from 'rxjs';
import { HttpClient } from '@angular/common/http'; import { HttpClient } from '@angular/common/http';
import { PageLink } from '@shared/models/page/page-link'; import { PageLink } from '@shared/models/page/page-link';
import { PageData } from '@shared/models/page/page-data'; import { PageData } from '@shared/models/page/page-data';
import { EntitySubtype } from '@app/shared/models/entity-type.models'; import { EntitySubtype } from '@app/shared/models/entity-type.models';
import { EntityView, EntityViewInfo, EntityViewSearchQuery } from '@app/shared/models/entity-view.models'; import { EntityView, EntityViewInfo, EntityViewSearchQuery } from '@app/shared/models/entity-view.models';
import { SaveEntityParams } from '@shared/models/entity.models';
@Injectable({ @Injectable({
providedIn: 'root' providedIn: 'root'
@ -51,8 +52,10 @@ export class EntityViewService {
return this.http.get<EntityViewInfo>(`/api/entityView/info/${entityViewId}`, defaultHttpOptionsFromConfig(config)); return this.http.get<EntityViewInfo>(`/api/entityView/info/${entityViewId}`, defaultHttpOptionsFromConfig(config));
} }
public saveEntityView(entityView: EntityView, config?: RequestConfig): Observable<EntityView> { public saveEntityView(entityView: EntityView, config?: RequestConfig): Observable<EntityView>;
return this.http.post<EntityView>('/api/entityView', entityView, defaultHttpOptionsFromConfig(config)); public saveEntityView(entityView: EntityView, saveParams: SaveEntityParams, config?: RequestConfig): Observable<EntityView>;
public saveEntityView(entityView: EntityView, saveParamsOrConfig?: SaveEntityParams | RequestConfig, config?: RequestConfig): Observable<EntityView> {
return this.http.post<EntityView>('/api/entityView', entityView, createDefaultHttpOptions(saveParamsOrConfig, config));
} }
public deleteEntityView(entityViewId: string, config?: RequestConfig) { public deleteEntityView(entityViewId: string, config?: RequestConfig) {

View File

@ -18,32 +18,56 @@ import { InterceptorHttpParams } from '../interceptors/interceptor-http-params';
import { HttpHeaders } from '@angular/common/http'; import { HttpHeaders } from '@angular/common/http';
import { InterceptorConfig } from '../interceptors/interceptor-config'; import { InterceptorConfig } from '../interceptors/interceptor-config';
export type QueryParams = { [param:string]: any };
export interface RequestConfig { export interface RequestConfig {
ignoreLoading?: boolean; ignoreLoading?: boolean;
ignoreErrors?: boolean; ignoreErrors?: boolean;
resendRequest?: boolean; resendRequest?: boolean;
queryParams?: QueryParams;
}
export function hasRequestConfig(config?: any): boolean {
if (!config) {
return false;
}
return config.hasOwnProperty('ignoreLoading') || config.hasOwnProperty('ignoreErrors') || config.hasOwnProperty('resendRequest') || config.hasOwnProperty('queryParams');
}
export function createDefaultHttpOptions(queryParamsOrConfig?: QueryParams | RequestConfig, config?: RequestConfig) {
if (hasRequestConfig(queryParamsOrConfig)) {
return defaultHttpOptionsFromConfig(queryParamsOrConfig as RequestConfig);
}
const queryParams = queryParamsOrConfig as QueryParams;
const finalConfig = {
...config,
...(queryParams && { queryParams }),
};
return defaultHttpOptionsFromConfig(finalConfig);
} }
export function defaultHttpOptionsFromConfig(config?: RequestConfig) { export function defaultHttpOptionsFromConfig(config?: RequestConfig) {
if (!config) { if (!config) {
config = {}; config = {};
} }
return defaultHttpOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest); return defaultHttpOptions(config.ignoreLoading, config.ignoreErrors, config.resendRequest, config.queryParams);
} }
export function defaultHttpOptions(ignoreLoading: boolean = false, export function defaultHttpOptions(ignoreLoading: boolean = false,
ignoreErrors: boolean = false, ignoreErrors: boolean = false,
resendRequest: boolean = false) { resendRequest: boolean = false,
queryParams?: QueryParams) {
return { return {
headers: new HttpHeaders({'Content-Type': 'application/json'}), headers: new HttpHeaders({'Content-Type': 'application/json'}),
params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest)) params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest), queryParams)
}; };
} }
export function defaultHttpUploadOptions(ignoreLoading: boolean = false, export function defaultHttpUploadOptions(ignoreLoading: boolean = false,
ignoreErrors: boolean = false, ignoreErrors: boolean = false,
resendRequest: boolean = false) { resendRequest: boolean = false,
queryParams?: QueryParams) {
return { return {
params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest)) params: new InterceptorHttpParams(new InterceptorConfig(ignoreLoading, ignoreErrors, resendRequest), queryParams)
}; };
} }

View File

@ -20,7 +20,7 @@ import { InterceptorConfig } from './interceptor-config';
export class InterceptorHttpParams extends HttpParams { export class InterceptorHttpParams extends HttpParams {
constructor( constructor(
public interceptorConfig: InterceptorConfig, public interceptorConfig: InterceptorConfig,
params?: { [param: string]: string | string[] } params?: { [param: string]: string | number | boolean | ReadonlyArray<string | number | boolean>; }
) { ) {
super({ fromObject: params }); super({ fromObject: params });
} }

View File

@ -22,7 +22,7 @@ import { DeviceCredentialsId } from '@shared/models/id/device-credentials-id';
import { EntitySearchQuery } from '@shared/models/relation.models'; import { EntitySearchQuery } from '@shared/models/relation.models';
import { DeviceProfileId } from '@shared/models/id/device-profile-id'; import { DeviceProfileId } from '@shared/models/id/device-profile-id';
import { RuleChainId } from '@shared/models/id/rule-chain-id'; import { RuleChainId } from '@shared/models/id/rule-chain-id';
import { EntityInfoData, HasTenantId, HasVersion } from '@shared/models/entity.models'; import { EntityInfoData, HasTenantId, HasVersion, SaveEntityParams } from '@shared/models/entity.models';
import { FilterPredicateValue, KeyFilter } from '@shared/models/query/query.models'; import { FilterPredicateValue, KeyFilter } from '@shared/models/query/query.models';
import { TimeUnit } from '@shared/models/time/time.models'; import { TimeUnit } from '@shared/models/time/time.models';
import _moment from 'moment'; import _moment from 'moment';
@ -739,6 +739,10 @@ export interface DeviceInfoFilter {
active?: boolean; active?: boolean;
} }
export interface SaveDeviceParams extends SaveEntityParams {
accessToken?: string;
}
export class DeviceInfoQuery { export class DeviceInfoQuery {
pageLink: PageLink; pageLink: PageLink;

View File

@ -209,3 +209,19 @@ export interface EntityTestScriptResult {
} }
export type VersionedEntity = EntityInfoData & HasVersion | RuleChainMetaData; export type VersionedEntity = EntityInfoData & HasVersion | RuleChainMetaData;
export enum NameConflictPolicy {
FAIL = 'FAIL',
UNIQUIFY = 'UNIQUIFY',
}
export enum UniquifyStrategy {
RANDOM = 'RANDOM',
INCREMENTAL = 'INCREMENTAL'
}
export interface SaveEntityParams {
nameConflictPolicy?: NameConflictPolicy;
uniquifyStrategy?: UniquifyStrategy;
uniquifySeparator?: string;
}