Browse Source

Merge branch 'master' into task/4114-gateway-version-compatibility

pull/11516/head
Max Petrov 2 years ago
committed by GitHub
parent
commit
636e7ccd66
No known key found for this signature in database GPG Key ID: B5690EEEBB952194
  1. 1
      application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java
  2. 10
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java
  3. 2
      application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java
  4. 5
      common/cache/src/main/java/org/thingsboard/server/cache/CaffeineTbTransactionalCache.java
  5. 2
      common/cache/src/main/java/org/thingsboard/server/cache/RedisTbCacheTransaction.java
  6. 16
      common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java
  7. 8
      common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java
  8. 26
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedCacheKey.java
  9. 2
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java
  10. 19
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java
  11. 2
      common/cache/src/main/java/org/thingsboard/server/cache/VersionedTbCache.java
  12. 9
      common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java
  13. 9
      dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCacheKey.java
  14. 9
      dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeCacheKey.java
  15. 9
      dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCacheKey.java
  16. 3
      dao/src/main/java/org/thingsboard/server/dao/entity/CachedVersionedEntityService.java
  17. 9
      dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCacheKey.java
  18. 9
      dao/src/main/java/org/thingsboard/server/dao/timeseries/TsLatestCacheKey.java
  19. 1
      rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java
  20. 93
      ui-ngx/src/app/core/services/dynamic-component-factory.service.ts
  21. 71
      ui-ngx/src/app/core/services/resources.service.ts
  22. 13
      ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts
  23. 7
      ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts
  24. 5
      ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts
  25. 2
      ui-ngx/src/app/modules/home/components/widget/widget.component.ts
  26. 4
      ui-ngx/src/app/modules/home/models/widget-component.models.ts
  27. 12
      ui-ngx/src/app/shared/components/markdown.component.ts

1
application/src/main/java/org/thingsboard/server/controller/SystemInfoController.java

@ -164,6 +164,7 @@ public class SystemInfoController extends BaseController {
} else {
infoObject.put("version", "unknown");
}
infoObject.put("type", "CE");
return infoObject;
}
}

10
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbEntityDataSubscriptionService.java

@ -94,7 +94,7 @@ import java.util.stream.Collectors;
public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubscriptionService {
private static final int DEFAULT_LIMIT = 100;
private final Map<String, Map<Integer, TbAbstractSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>();
private final ConcurrentMap<String, ConcurrentMap<Integer, TbAbstractSubCtx>> subscriptionsBySessionId = new ConcurrentHashMap<>();
@Autowired
@Lazy
@ -495,7 +495,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
}
private TbEntityDataSubCtx createSubCtx(WebSocketSessionRef sessionRef, EntityDataCmd cmd) {
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new ConcurrentHashMap<>());
TbEntityDataSubCtx ctx = new TbEntityDataSubCtx(serviceId, wsService, entityService, localSubscriptionService,
attributesService, stats, sessionRef, cmd.getCmdId(), maxEntitiesPerDataSubscription);
if (cmd.getQuery() != null) {
@ -506,7 +506,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
}
private TbEntityCountSubCtx createSubCtx(WebSocketSessionRef sessionRef, EntityCountCmd cmd) {
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new ConcurrentHashMap<>());
TbEntityCountSubCtx ctx = new TbEntityCountSubCtx(serviceId, wsService, entityService, localSubscriptionService,
attributesService, stats, sessionRef, cmd.getCmdId());
if (cmd.getQuery() != null) {
@ -518,7 +518,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
private TbAlarmDataSubCtx createSubCtx(WebSocketSessionRef sessionRef, AlarmDataCmd cmd) {
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new ConcurrentHashMap<>());
TbAlarmDataSubCtx ctx = new TbAlarmDataSubCtx(serviceId, wsService, entityService, localSubscriptionService,
attributesService, stats, alarmService, sessionRef, cmd.getCmdId(), maxEntitiesPerAlarmSubscription,
maxAlarmQueriesPerRefreshInterval);
@ -528,7 +528,7 @@ public class DefaultTbEntityDataSubscriptionService implements TbEntityDataSubsc
}
private TbAlarmCountSubCtx createSubCtx(WebSocketSessionRef sessionRef, AlarmCountCmd cmd) {
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new HashMap<>());
Map<Integer, TbAbstractSubCtx> sessionSubs = subscriptionsBySessionId.computeIfAbsent(sessionRef.getSessionId(), k -> new ConcurrentHashMap<>());
TbAlarmCountSubCtx ctx = new TbAlarmCountSubCtx(serviceId, wsService, entityService, localSubscriptionService,
attributesService, stats, alarmService, sessionRef, cmd.getCmdId());
if (cmd.getQuery() != null) {

2
application/src/main/java/org/thingsboard/server/service/subscription/DefaultTbLocalSubscriptionService.java

@ -85,7 +85,7 @@ import java.util.stream.Collectors;
@Service
public class DefaultTbLocalSubscriptionService implements TbLocalSubscriptionService {
private final ConcurrentMap<String, Map<Integer, TbSubscription<?>>> subscriptionsBySessionId = new ConcurrentHashMap<>();
private final ConcurrentMap<String, ConcurrentMap<Integer, TbSubscription<?>>> subscriptionsBySessionId = new ConcurrentHashMap<>();
private final ConcurrentMap<UUID, TbEntityLocalSubsInfo> subscriptionsByEntityId = new ConcurrentHashMap<>();
private final ConcurrentMap<UUID, TbEntityUpdatesInfo> entityUpdates = new ConcurrentHashMap<>();

5
common/cache/src/main/java/org/thingsboard/server/cache/CaffeineTbTransactionalCache.java

@ -54,11 +54,6 @@ public abstract class CaffeineTbTransactionalCache<K extends Serializable, V ext
return SimpleTbCacheValueWrapper.wrap(cache.get(key));
}
@Override
public TbCacheValueWrapper<V> get(K key, boolean transactionMode) {
return get(key);
}
@Override
public void put(K key, V value) {
lock.lock();

2
common/cache/src/main/java/org/thingsboard/server/cache/RedisTbCacheTransaction.java

@ -31,7 +31,7 @@ public class RedisTbCacheTransaction<K extends Serializable, V extends Serializa
@Override
public void put(K key, V value) {
cache.put(key, value, connection, true);
cache.put(key, value, connection);
}
@Override

16
common/cache/src/main/java/org/thingsboard/server/cache/RedisTbTransactionalCache.java

@ -87,17 +87,11 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
@Override
public TbCacheValueWrapper<V> get(K key) {
return get(key, false);
}
@Override
public TbCacheValueWrapper<V> get(K key, boolean transactionMode) {
if (!cacheEnabled) {
return null;
}
try (var connection = connectionFactory.getConnection()) {
byte[] rawKey = getRawKey(key);
byte[] rawValue = doGet(connection, rawKey, transactionMode);
byte[] rawValue = doGet(key, connection);
if (rawValue == null || rawValue.length == 0) {
return null;
} else if (Arrays.equals(rawValue, BINARY_NULL_VALUE)) {
@ -114,8 +108,8 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
}
}
protected byte[] doGet(RedisConnection connection, byte[] rawKey, boolean transactionMode) {
return connection.stringCommands().get(rawKey);
protected byte[] doGet(K key, RedisConnection connection) {
return connection.stringCommands().get(getRawKey(key));
}
@Override
@ -124,11 +118,11 @@ public abstract class RedisTbTransactionalCache<K extends Serializable, V extend
return;
}
try (var connection = connectionFactory.getConnection()) {
put(key, value, connection, false);
put(key, value, connection);
}
}
public void put(K key, V value, RedisConnection connection, boolean transactionMode) {
public void put(K key, V value, RedisConnection connection) {
put(connection, key, value, RedisStringCommands.SetOption.UPSERT);
}

8
common/cache/src/main/java/org/thingsboard/server/cache/TbTransactionalCache.java

@ -27,8 +27,6 @@ public interface TbTransactionalCache<K extends Serializable, V extends Serializ
TbCacheValueWrapper<V> get(K key);
TbCacheValueWrapper<V> get(K key, boolean transactionMode);
void put(K key, V value);
void putIfAbsent(K key, V value);
@ -53,7 +51,7 @@ public interface TbTransactionalCache<K extends Serializable, V extends Serializ
if (putToCache) {
return getAndPutInTransaction(key, dbCall, cacheNullValue);
} else {
TbCacheValueWrapper<V> cacheValueWrapper = get(key, true);
TbCacheValueWrapper<V> cacheValueWrapper = get(key);
if (cacheValueWrapper != null) {
return cacheValueWrapper.get();
}
@ -66,7 +64,7 @@ public interface TbTransactionalCache<K extends Serializable, V extends Serializ
}
default <R> R getAndPutInTransaction(K key, Supplier<R> dbCall, Function<V, R> cacheValueToResult, Function<R, V> dbValueToCacheValue, boolean cacheNullValue) {
TbCacheValueWrapper<V> cacheValueWrapper = get(key, true);
TbCacheValueWrapper<V> cacheValueWrapper = get(key);
if (cacheValueWrapper != null) {
V cacheValue = cacheValueWrapper.get();
return cacheValue != null ? cacheValueToResult.apply(cacheValue) : null;
@ -92,7 +90,7 @@ public interface TbTransactionalCache<K extends Serializable, V extends Serializ
if (putToCache) {
return getAndPutInTransaction(key, dbCall, cacheValueToResult, dbValueToCacheValue, cacheNullValue);
} else {
TbCacheValueWrapper<V> cacheValueWrapper = get(key, true);
TbCacheValueWrapper<V> cacheValueWrapper = get(key);
if (cacheValueWrapper != null) {
var cacheValue = cacheValueWrapper.get();
return cacheValue == null ? null : cacheValueToResult.apply(cacheValue);

26
common/cache/src/main/java/org/thingsboard/server/cache/VersionedCacheKey.java

@ -0,0 +1,26 @@
/**
* 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.
*/
package org.thingsboard.server.cache;
import java.io.Serializable;
public interface VersionedCacheKey extends Serializable {
default boolean isVersioned() {
return false;
}
}

2
common/cache/src/main/java/org/thingsboard/server/cache/VersionedCaffeineTbCache.java

@ -22,7 +22,7 @@ import org.thingsboard.server.common.data.util.TbPair;
import java.io.Serializable;
public abstract class VersionedCaffeineTbCache<K extends Serializable, V extends Serializable & HasVersion> extends CaffeineTbTransactionalCache<K, V> implements VersionedTbCache<K, V> {
public abstract class VersionedCaffeineTbCache<K extends VersionedCacheKey, V extends Serializable & HasVersion> extends CaffeineTbTransactionalCache<K, V> implements VersionedTbCache<K, V> {
public VersionedCaffeineTbCache(CacheManager cacheManager, String cacheName) {
super(cacheManager, cacheName);

19
common/cache/src/main/java/org/thingsboard/server/cache/VersionedRedisTbCache.java

@ -30,7 +30,7 @@ import java.io.Serializable;
import java.util.Arrays;
@Slf4j
public abstract class VersionedRedisTbCache<K extends Serializable, V extends Serializable & HasVersion> extends RedisTbTransactionalCache<K, V> implements VersionedTbCache<K, V> {
public abstract class VersionedRedisTbCache<K extends VersionedCacheKey, V extends Serializable & HasVersion> extends RedisTbTransactionalCache<K, V> implements VersionedTbCache<K, V> {
private static final int VERSION_SIZE = 8;
private static final int VALUE_END_OFFSET = -1;
@ -79,15 +79,20 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
}
@Override
protected byte[] doGet(RedisConnection connection, byte[] rawKey, boolean transactionMode) {
if (transactionMode) {
return super.doGet(connection, rawKey, true);
protected byte[] doGet(K key, RedisConnection connection) {
if (!key.isVersioned()) {
return super.doGet(key, connection);
}
byte[] rawKey = getRawKey(key);
return connection.stringCommands().getRange(rawKey, VERSION_SIZE, VALUE_END_OFFSET);
}
@Override
public void put(K key, V value) {
if (!key.isVersioned()) {
super.put(key, value);
return;
}
Long version = getVersion(value);
if (version == null) {
return;
@ -96,9 +101,9 @@ public abstract class VersionedRedisTbCache<K extends Serializable, V extends Se
}
@Override
public void put(K key, V value, RedisConnection connection, boolean transactionMode) {
if (transactionMode) {
super.put(key, value, connection, true); // because scripting commands are not supported in transaction mode
public void put(K key, V value, RedisConnection connection) {
if (!key.isVersioned()) {
super.put(key, value, connection); // because scripting commands are not supported in transaction mode
return;
}
Long version = getVersion(value);

2
common/cache/src/main/java/org/thingsboard/server/cache/VersionedTbCache.java

@ -22,7 +22,7 @@ import java.util.Collection;
import java.util.Optional;
import java.util.function.Supplier;
public interface VersionedTbCache<K extends Serializable, V extends Serializable & HasVersion> extends TbTransactionalCache<K, V> {
public interface VersionedTbCache<K extends VersionedCacheKey, V extends Serializable & HasVersion> extends TbTransactionalCache<K, V> {
TbCacheValueWrapper<V> get(K key);

9
common/cache/src/main/java/org/thingsboard/server/cache/device/DeviceCacheKey.java

@ -19,17 +19,17 @@ import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import org.thingsboard.server.cache.VersionedCacheKey;
import org.thingsboard.server.common.data.id.DeviceId;
import org.thingsboard.server.common.data.id.TenantId;
import java.io.Serial;
import java.io.Serializable;
@Getter
@EqualsAndHashCode
@RequiredArgsConstructor
@Builder
public class DeviceCacheKey implements Serializable {
public class DeviceCacheKey implements VersionedCacheKey {
@Serial
private static final long serialVersionUID = 6366389552842340207L;
@ -61,4 +61,9 @@ public class DeviceCacheKey implements Serializable {
}
}
@Override
public boolean isVersioned() {
return deviceId != null;
}
}

9
dao/src/main/java/org/thingsboard/server/dao/asset/AssetProfileCacheKey.java

@ -16,14 +16,14 @@
package org.thingsboard.server.dao.asset;
import lombok.Data;
import org.thingsboard.server.cache.VersionedCacheKey;
import org.thingsboard.server.common.data.id.AssetProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import java.io.Serial;
import java.io.Serializable;
@Data
public class AssetProfileCacheKey implements Serializable {
public class AssetProfileCacheKey implements VersionedCacheKey {
@Serial
private static final long serialVersionUID = 8220455917177676472L;
@ -63,4 +63,9 @@ public class AssetProfileCacheKey implements Serializable {
}
}
@Override
public boolean isVersioned() {
return assetProfileId != null;
}
}

9
dao/src/main/java/org/thingsboard/server/dao/attributes/AttributeCacheKey.java

@ -18,16 +18,16 @@ package org.thingsboard.server.dao.attributes;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.thingsboard.server.cache.VersionedCacheKey;
import org.thingsboard.server.common.data.AttributeScope;
import org.thingsboard.server.common.data.id.EntityId;
import java.io.Serial;
import java.io.Serializable;
@EqualsAndHashCode
@Getter
@AllArgsConstructor
public class AttributeCacheKey implements Serializable {
public class AttributeCacheKey implements VersionedCacheKey {
@Serial
private static final long serialVersionUID = 2013369077925351881L;
@ -41,4 +41,9 @@ public class AttributeCacheKey implements Serializable {
return "{" + entityId + "}" + scope + "_" + key;
}
@Override
public boolean isVersioned() {
return true;
}
}

9
dao/src/main/java/org/thingsboard/server/dao/device/DeviceProfileCacheKey.java

@ -16,15 +16,15 @@
package org.thingsboard.server.dao.device;
import lombok.Data;
import org.thingsboard.server.cache.VersionedCacheKey;
import org.thingsboard.server.common.data.StringUtils;
import org.thingsboard.server.common.data.id.DeviceProfileId;
import org.thingsboard.server.common.data.id.TenantId;
import java.io.Serial;
import java.io.Serializable;
@Data
public class DeviceProfileCacheKey implements Serializable {
public class DeviceProfileCacheKey implements VersionedCacheKey {
@Serial
private static final long serialVersionUID = 8220455917177676472L;
@ -74,4 +74,9 @@ public class DeviceProfileCacheKey implements Serializable {
return tenantId + "_" + name;
}
@Override
public boolean isVersioned() {
return deviceProfileId != null;
}
}

3
dao/src/main/java/org/thingsboard/server/dao/entity/CachedVersionedEntityService.java

@ -16,12 +16,13 @@
package org.thingsboard.server.dao.entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.thingsboard.server.cache.VersionedCacheKey;
import org.thingsboard.server.cache.VersionedTbCache;
import org.thingsboard.server.common.data.HasVersion;
import java.io.Serializable;
public abstract class CachedVersionedEntityService<K extends Serializable, V extends Serializable & HasVersion, E> extends AbstractCachedEntityService<K, V, E> {
public abstract class CachedVersionedEntityService<K extends VersionedCacheKey, V extends Serializable & HasVersion, E> extends AbstractCachedEntityService<K, V, E> {
@Autowired
protected VersionedTbCache<K, V> cache;

9
dao/src/main/java/org/thingsboard/server/dao/entityview/EntityViewCacheKey.java

@ -18,17 +18,17 @@ package org.thingsboard.server.dao.entityview;
import lombok.Builder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.thingsboard.server.cache.VersionedCacheKey;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.EntityViewId;
import org.thingsboard.server.common.data.id.TenantId;
import java.io.Serial;
import java.io.Serializable;
@Getter
@EqualsAndHashCode
@Builder
public class EntityViewCacheKey implements Serializable {
public class EntityViewCacheKey implements VersionedCacheKey {
@Serial
private static final long serialVersionUID = 5986277528222738163L;
@ -68,4 +68,9 @@ public class EntityViewCacheKey implements Serializable {
}
}
@Override
public boolean isVersioned() {
return entityViewId != null;
}
}

9
dao/src/main/java/org/thingsboard/server/dao/timeseries/TsLatestCacheKey.java

@ -18,15 +18,15 @@ package org.thingsboard.server.dao.timeseries;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import org.thingsboard.server.cache.VersionedCacheKey;
import org.thingsboard.server.common.data.id.EntityId;
import java.io.Serial;
import java.io.Serializable;
@EqualsAndHashCode
@Getter
@AllArgsConstructor
public class TsLatestCacheKey implements Serializable {
public class TsLatestCacheKey implements VersionedCacheKey {
@Serial
private static final long serialVersionUID = 2024369077925351881L;
@ -39,4 +39,9 @@ public class TsLatestCacheKey implements Serializable {
return "{" + entityId + "}" + key;
}
@Override
public boolean isVersioned() {
return true;
}
}

1
rule-engine/rule-engine-components/src/main/java/org/thingsboard/rule/engine/rest/TbHttpClient.java

@ -135,7 +135,6 @@ public class TbHttpClient {
this.webClient = WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.defaultHeader(HttpHeaders.CONNECTION, "close") //In previous realization this header was present! (Added for hotfix "Connection reset")
.codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(
(config.getMaxInMemoryBufferSizeInKb() > 0 ? config.getMaxInMemoryBufferSizeInKb() : 256) * 1024))
.build();

93
ui-ngx/src/app/core/services/dynamic-component-factory.service.ts

@ -14,49 +14,17 @@
/// limitations under the License.
///
import {
Compiler,
Component,
Injectable,
Injector,
NgModule,
NgModuleRef,
OnDestroy,
Type, ɵNG_COMP_DEF,
ɵresetCompiledComponents
} from '@angular/core';
import { Component, Injectable, Type, ɵComponentDef, ɵNG_COMP_DEF } from '@angular/core';
import { from, Observable, of } from 'rxjs';
import { CommonModule } from '@angular/common';
import { mergeMap } from 'rxjs/operators';
@NgModule()
export abstract class DynamicComponentModule implements OnDestroy {
// eslint-disable-next-line @angular-eslint/contextual-lifecycle
ngOnDestroy(): void {
}
}
interface DynamicComponentData<T> {
componentType: Type<T>;
componentModuleRef: NgModuleRef<DynamicComponentModule>;
}
interface DynamicComponentModuleData {
moduleRef: NgModuleRef<DynamicComponentModule>;
moduleType: Type<DynamicComponentModule>;
}
@Injectable({
providedIn: 'root'
})
export class DynamicComponentFactoryService {
private dynamicComponentModulesMap = new Map<Type<any>, DynamicComponentModuleData>();
constructor(private compiler: Compiler,
private injector: Injector) {
constructor() {
}
public createDynamicComponent<T>(
@ -64,59 +32,38 @@ export class DynamicComponentFactoryService {
template: string,
modules?: Type<any>[],
preserveWhitespaces?: boolean,
styles?: string[]): Observable<DynamicComponentData<T>> {
styles?: string[]): Observable<Type<T>> {
return from(import('@angular/compiler')).pipe(
mergeMap(() => {
const comp = this._createDynamicComponent(componentType, template, preserveWhitespaces, styles);
let moduleImports: Type<any>[] = [CommonModule];
let componentImports: Type<any>[] = [CommonModule];
if (modules) {
moduleImports = [...moduleImports, ...modules];
componentImports = [...componentImports, ...modules];
}
// noinspection AngularInvalidImportedOrDeclaredSymbol
const dynamicComponentInstanceModule = NgModule({
declarations: [comp],
imports: moduleImports
})(class DynamicComponentInstanceModule extends DynamicComponentModule {});
const module = this.compiler.compileModuleSync(dynamicComponentInstanceModule);
let moduleRef: NgModuleRef<any>;
try {
moduleRef = module.create(this.injector);
// eslint-disable-next-line
comp[ɵNG_COMP_DEF];
} catch (e) {
this.compiler.clearCacheFor(module.moduleType);
ɵresetCompiledComponents();
throw e;
}
this.dynamicComponentModulesMap.set(comp, {
moduleRef,
moduleType: module.moduleType
});
return of( {
componentType: comp,
componentModuleRef: moduleRef
});
const comp = this.createAndCompileDynamicComponent(componentType, template, componentImports, preserveWhitespaces, styles);
return of(comp.type);
})
);
}
public destroyDynamicComponent<T>(componentType: Type<T>) {
const moduleData = this.dynamicComponentModulesMap.get(componentType);
if (moduleData) {
moduleData.moduleRef.destroy();
this.compiler.clearCacheFor(moduleData.moduleType);
this.dynamicComponentModulesMap.delete(componentType);
}
public destroyDynamicComponent<T>(_componentType: Type<T>) {
}
public getComponentDef<T>(componentType: Type<T>): ɵComponentDef<T> {
return componentType[ɵNG_COMP_DEF];
}
private _createDynamicComponent<T>(componentType: Type<T>, template: string, preserveWhitespaces?: boolean, styles?: string[]): Type<T> {
private createAndCompileDynamicComponent<T>(componentType: Type<T>, template: string, imports: Type<any>[],
preserveWhitespaces?: boolean, styles?: string[]): ɵComponentDef<T> {
// noinspection AngularMissingOrInvalidDeclarationInModule
return Component({
const comp = Component({
template,
imports,
preserveWhitespaces,
styles
styles,
standalone: true
})(componentType);
// Trigger component compilation
return comp[ɵNG_COMP_DEF];
}
}

71
ui-ngx/src/app/core/services/resources.service.ts

@ -21,7 +21,7 @@ import {
Injectable,
Injector,
ModuleWithComponentFactories,
Type
Type, ɵNG_MOD_DEF
} from '@angular/core';
import { DOCUMENT } from '@angular/common';
import { forkJoin, Observable, ReplaySubject, throwError } from 'rxjs';
@ -50,7 +50,6 @@ export class ResourcesService {
private loadedJsonResources: { [url: string]: ReplaySubject<any> } = {};
private loadedResources: { [url: string]: ReplaySubject<void> } = {};
private loadedModules: { [url: string]: ReplaySubject<Type<any>[]> } = {};
private loadedModulesAndFactories: { [url: string]: ReplaySubject<ModulesWithFactories> } = {};
private anchor = this.document.getElementsByTagName('head')[0] || this.document.getElementsByTagName('body')[0];
@ -201,71 +200,6 @@ export class ResourcesService {
);
}
public loadModules(resourceId: string | TbResourceId, modulesMap: IModulesMap): Observable<Type<any>[]> {
const url = this.getDownloadUrl(resourceId);
if (this.loadedModules[url]) {
return this.loadedModules[url].asObservable();
}
modulesMap.init();
const meta = this.getMetaInfo(resourceId);
const subject = new ReplaySubject<Type<any>[]>();
this.loadedModules[url] = subject;
import('@angular/compiler').then(
() => {
System.import(url, undefined, meta).then(
(module) => {
try {
let modules;
try {
modules = this.extractNgModules(module);
} catch (e) {
console.error(e);
}
if (modules && modules.length) {
const tasks: Promise<ModuleWithComponentFactories<any>>[] = [];
for (const m of modules) {
tasks.push(this.compiler.compileModuleAndAllComponentsAsync(m));
}
forkJoin(tasks).subscribe((compiled) => {
try {
for (const c of compiled) {
c.ngModuleFactory.create(this.injector);
}
this.loadedModules[url].next(modules);
this.loadedModules[url].complete();
} catch (e) {
this.loadedModules[url].error(new Error(`Unable to init module from url: ${url}`));
}
},
(e) => {
this.loadedModules[url].error(new Error(`Unable to compile module from url: ${url}`));
});
} else {
this.loadedModules[url].error(new Error(`Module '${url}' doesn't have default export or not NgModule!`));
}
} catch (e) {
this.loadedModules[url].error(new Error(`Unable to load module from url: ${url}`));
}
},
(e) => {
this.loadedModules[url].error(new Error(`Unable to load module from url: ${url}`));
console.error(`Unable to load module from url: ${url}`, e);
}
);
}
);
return subject.asObservable().pipe(
tap({
next: () => System.delete(url),
error: () => {
delete this.loadedModulesAndFactories[url];
System.delete(url);
},
complete: () => System.delete(url)
})
);
}
private extractNgModules(module: any, modules: Type<any>[] = []): Type<any>[] {
try {
let potentialModules = [module];
@ -274,7 +208,7 @@ export class ResourcesService {
while (potentialModules.length && currentScanDepth < 10) {
const newPotentialModules = [];
for (const potentialModule of potentialModules) {
if (potentialModule && ('ɵmod' in potentialModule)) {
if (potentialModule && (ɵNG_MOD_DEF in potentialModule)) {
modules.push(potentialModule);
} else {
for (const k of Object.keys(potentialModule)) {
@ -349,7 +283,6 @@ export class ResourcesService {
}
private clearModulesCache() {
this.loadedModules = {};
this.loadedModulesAndFactories = {};
}
}

13
ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog-container.component.ts

@ -17,11 +17,12 @@
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog';
import {
Component,
ComponentFactory,
ComponentRef, HostBinding,
ComponentRef,
HostBinding,
Inject,
Injector, NgModuleRef,
OnDestroy, Type,
Injector,
OnDestroy,
Type,
ViewContainerRef
} from '@angular/core';
import { DialogComponent } from '@shared/components/dialog.component';
@ -35,13 +36,11 @@ import {
} from '@home/components/widget/dialog/custom-dialog.component';
import { DialogService } from '@core/services/dialog.service';
import { TranslateService } from '@ngx-translate/core';
import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service';
export interface CustomDialogContainerData {
controller: (instance: CustomDialogComponent) => void;
data?: any;
customComponentType: Type<CustomDialogComponent>;
customComponentModuleRef: NgModuleRef<DynamicComponentModule>;
}
@Component({
@ -80,7 +79,7 @@ export class CustomDialogContainerComponent extends DialogComponent<CustomDialog
});
try {
this.customComponentRef = this.viewContainerRef.createComponent(this.data.customComponentType,
{index: 0, injector, ngModuleRef: this.data.customComponentModuleRef});
{index: 0, injector});
} catch (e: any) {
let message;
if (e.message?.startsWith('NG0')) {

7
ui-ngx/src/app/modules/home/components/widget/dialog/custom-dialog.service.ts

@ -60,11 +60,10 @@ export class CustomDialogService {
}
return this.dynamicComponentFactoryService.createDynamicComponent(
class CustomDialogComponentInstance extends CustomDialogComponent {}, template, modules).pipe(
mergeMap((componentData) => {
mergeMap((componentType) => {
const dialogData: CustomDialogContainerData = {
controller,
customComponentType: componentData.componentType,
customComponentModuleRef: componentData.componentModuleRef,
customComponentType: componentType,
data
};
let dialogConfig: MatDialogConfig = {
@ -79,7 +78,7 @@ export class CustomDialogService {
CustomDialogContainerComponent,
dialogConfig).afterClosed().pipe(
tap(() => {
this.dynamicComponentFactoryService.destroyDynamicComponent(componentData.componentType);
this.dynamicComponentFactoryService.destroyDynamicComponent(componentType);
})
);
}

5
ui-ngx/src/app/modules/home/components/widget/widget-component.service.ts

@ -381,9 +381,8 @@ export class WidgetComponentService {
widgetInfo.templateHtml,
resolvedModules.modules
).pipe(
map((componentData) => {
widgetInfo.componentType = componentData.componentType;
widgetInfo.componentModuleRef = componentData.componentModuleRef;
map((componentType) => {
widgetInfo.componentType = componentType;
return null;
}),
catchError(e => {

2
ui-ngx/src/app/modules/home/components/widget/widget.component.ts

@ -760,7 +760,7 @@ export class WidgetComponent extends PageComponent implements OnInit, AfterViewI
try {
this.dynamicWidgetComponentRef = this.widgetContentContainer.createComponent(this.widgetInfo.componentType,
{index: 0, injector, ngModuleRef: this.widgetInfo.componentModuleRef});
{index: 0, injector});
this.cd.detectChanges();
} catch (e) {
if (this.dynamicWidgetComponentRef) {

4
ui-ngx/src/app/modules/home/models/widget-component.models.ts

@ -47,7 +47,7 @@ import {
WidgetActionsApi,
WidgetSubscriptionApi
} from '@core/api/widget-api.models';
import { ChangeDetectorRef, Injector, NgModuleRef, NgZone, Type } from '@angular/core';
import { ChangeDetectorRef, Injector, NgZone, Type } from '@angular/core';
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
import { RafService } from '@core/services/raf.service';
import { WidgetTypeId } from '@shared/models/id/widget-type-id';
@ -101,7 +101,6 @@ import { AlarmQuery, AlarmSearchStatus, AlarmStatus } from '@app/shared/models/a
import { ImagePipe, MillisecondsToTimeStringPipe, TelemetrySubscriber } from '@app/shared/public-api';
import { UserId } from '@shared/models/id/user-id';
import { UserSettingsService } from '@core/http/user-settings.service';
import { DynamicComponentModule } from '@core/services/dynamic-component-factory.service';
import { DataKeySettingsFunction } from '@home/components/widget/config/data-keys.component.models';
import { UtilsService } from '@core/services/utils.service';
@ -549,7 +548,6 @@ export interface WidgetInfo extends WidgetTypeDescriptor, WidgetControllerDescri
description?: string;
tags?: string[];
componentType?: Type<IDynamicWidgetComponent>;
componentModuleRef?: NgModuleRef<DynamicComponentModule>;
}
export interface WidgetConfigComponentData {

12
ui-ngx/src/app/shared/components/markdown.component.ts

@ -22,7 +22,8 @@ import {
EventEmitter,
Inject,
Injector,
Input, NgZone,
Input,
NgZone,
OnChanges,
Output,
Renderer2,
@ -132,7 +133,8 @@ export class TbMarkdownComponent implements OnChanges {
let readyObservable: Observable<void>;
if (this.applyDefaultMarkdownStyle) {
if (!defaultMarkdownStyle) {
defaultMarkdownStyle = deepClone(TbMarkdownComponent['ɵcmp'].styles)[0].replace(/\[_nghost\-%COMP%\]/g, '')
const compDef = this.dynamicComponentFactoryService.getComponentDef(TbMarkdownComponent);
defaultMarkdownStyle = deepClone(compDef.styles[0]).replace(/\[_nghost\-%COMP%\]/g, '')
.replace(/\[_ngcontent\-%COMP%\]/g, '');
}
styles.push(defaultMarkdownStyle);
@ -161,13 +163,13 @@ export class TbMarkdownComponent implements OnChanges {
template,
compileModules,
true, styles
).subscribe((componentData) => {
this.tbMarkdownInstanceComponentType = componentData.componentType;
).subscribe((componentType) => {
this.tbMarkdownInstanceComponentType = componentType;
const injector: Injector = Injector.create({providers: [], parent: this.markdownContainer.injector});
try {
this.tbMarkdownInstanceComponentRef =
this.markdownContainer.createComponent(this.tbMarkdownInstanceComponentType,
{index: 0, injector, ngModuleRef: componentData.componentModuleRef});
{index: 0, injector});
if (this.context) {
for (const propName of Object.keys(this.context)) {
this.tbMarkdownInstanceComponentRef.instance[propName] = this.context[propName];

Loading…
Cancel
Save