Merge pull request #13301 from yuliaklochai/ui-trendz-settings
Added Trendz setting
This commit is contained in:
commit
0e20e27f6f
@ -42,6 +42,7 @@ import org.thingsboard.server.common.data.settings.UserSettings;
|
||||
import org.thingsboard.server.common.data.settings.UserSettingsType;
|
||||
import org.thingsboard.server.common.data.tenant.profile.DefaultTenantProfileConfiguration;
|
||||
import org.thingsboard.server.dao.mobile.QrCodeSettingService;
|
||||
import org.thingsboard.server.dao.trendz.TrendzSettingsService;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
import org.thingsboard.server.service.security.model.SecurityUser;
|
||||
import org.thingsboard.server.service.security.model.UserPrincipal;
|
||||
@ -87,6 +88,9 @@ public class SystemInfoController extends BaseController {
|
||||
@Autowired
|
||||
private DebugModeRateLimitsConfig debugModeRateLimitsConfig;
|
||||
|
||||
@Autowired
|
||||
private TrendzSettingsService trendzSettingsService;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
JsonNode info = buildInfoObject();
|
||||
@ -158,6 +162,7 @@ public class SystemInfoController extends BaseController {
|
||||
}
|
||||
systemParams.setMaxArgumentsPerCF(tenantProfileConfiguration.getMaxArgumentsPerCF());
|
||||
systemParams.setMaxDataPointsPerRollingArg(tenantProfileConfiguration.getMaxDataPointsPerRollingArg());
|
||||
systemParams.setTrendzSettings(trendzSettingsService.findTrendzSettings(currentUser.getTenantId()));
|
||||
}
|
||||
systemParams.setMobileQrEnabled(Optional.ofNullable(qrCodeSettingService.findQrCodeSettings(TenantId.SYS_TENANT_ID))
|
||||
.map(QrCodeSettings::getQrCodeConfig).map(QRCodeConfig::isShowOnHomePage)
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 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.controller;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.trendz.TrendzSettings;
|
||||
import org.thingsboard.server.config.annotations.ApiOperation;
|
||||
import org.thingsboard.server.dao.trendz.TrendzSettingsService;
|
||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||
import org.thingsboard.server.service.security.model.SecurityUser;
|
||||
import org.thingsboard.server.service.security.permission.Operation;
|
||||
import org.thingsboard.server.service.security.permission.Resource;
|
||||
|
||||
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_END;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.MARKDOWN_CODE_BLOCK_START;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH;
|
||||
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH;
|
||||
|
||||
@RestController
|
||||
@TbCoreComponent
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/api")
|
||||
public class TrendzController extends BaseController {
|
||||
|
||||
private final TrendzSettingsService trendzSettingsService;
|
||||
|
||||
@ApiOperation(value = "Save Trendz settings (saveTrendzSettings)",
|
||||
notes = "Saves Trendz settings for this tenant.\n" + NEW_LINE +
|
||||
"Here is an example of the Trendz settings:\n" +
|
||||
MARKDOWN_CODE_BLOCK_START +
|
||||
"{\n" +
|
||||
" \"enabled\": true,\n" +
|
||||
" \"baseUrl\": \"https://some.domain.com:18888/also_necessary_prefix\"\n" +
|
||||
"}" +
|
||||
MARKDOWN_CODE_BLOCK_END +
|
||||
TENANT_AUTHORITY_PARAGRAPH)
|
||||
@PostMapping("/trendz/settings")
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')")
|
||||
public TrendzSettings saveTrendzSettings(@RequestBody TrendzSettings trendzSettings,
|
||||
@AuthenticationPrincipal SecurityUser user) throws ThingsboardException {
|
||||
accessControlService.checkPermission(user, Resource.ADMIN_SETTINGS, Operation.WRITE);
|
||||
TenantId tenantId = user.getTenantId();
|
||||
trendzSettingsService.saveTrendzSettings(tenantId, trendzSettings);
|
||||
return trendzSettings;
|
||||
}
|
||||
|
||||
@ApiOperation(value = "Get Trendz Settings (getTrendzSettings)",
|
||||
notes = "Retrieves Trendz settings for this tenant." +
|
||||
TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH)
|
||||
@GetMapping("/trendz/settings")
|
||||
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||
public TrendzSettings getTrendzSettings(@AuthenticationPrincipal SecurityUser user) {
|
||||
TenantId tenantId = user.getTenantId();
|
||||
return trendzSettingsService.findTrendzSettings(tenantId);
|
||||
}
|
||||
|
||||
}
|
||||
@ -23,7 +23,7 @@ import org.thingsboard.server.common.data.id.UserId;
|
||||
import org.thingsboard.server.common.data.security.Authority;
|
||||
import org.thingsboard.server.service.security.model.SecurityUser;
|
||||
|
||||
@Component(value="sysAdminPermissions")
|
||||
@Component(value = "sysAdminPermissions")
|
||||
public class SysAdminPermissions extends AbstractPermissions {
|
||||
|
||||
public SysAdminPermissions() {
|
||||
|
||||
@ -644,6 +644,9 @@ cache:
|
||||
mobileSecretKey:
|
||||
timeToLiveInMinutes: "${CACHE_MOBILE_SECRET_KEY_TTL:2}" # QR secret key cache TTL
|
||||
maxSize: "${CACHE_MOBILE_SECRET_KEY_MAX_SIZE:10000}" # 0 means the cache is disabled
|
||||
trendzSettings:
|
||||
timeToLiveInMinutes: "${CACHE_SPECS_TRENDZ_SETTINGS_TTL:1440}" # Trendz settings cache TTL
|
||||
maxSize: "${CACHE_SPECS_TRENDZ_SETTINGS_MAX_SIZE:10000}" # 0 means the cache is disabled
|
||||
|
||||
# Deliberately placed outside the 'specs' group above
|
||||
notificationRules:
|
||||
|
||||
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 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.controller;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.thingsboard.server.common.data.trendz.TrendzSettings;
|
||||
import org.thingsboard.server.dao.service.DaoSqlTest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
@DaoSqlTest
|
||||
public class TrendzControllerTest extends AbstractControllerTest {
|
||||
|
||||
private final String trendzUrl = "https://some.domain.com:18888/also_necessary_prefix";
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
loginTenantAdmin();
|
||||
|
||||
TrendzSettings trendzSettings = new TrendzSettings();
|
||||
trendzSettings.setEnabled(true);
|
||||
trendzSettings.setBaseUrl(trendzUrl);
|
||||
|
||||
doPost("/api/trendz/settings", trendzSettings).andExpect(status().isOk());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrendzSettingsWhenTenant() throws Exception {
|
||||
loginTenantAdmin();
|
||||
|
||||
TrendzSettings trendzSettings = doGet("/api/trendz/settings", TrendzSettings.class);
|
||||
|
||||
assertThat(trendzSettings).isNotNull();
|
||||
assertThat(trendzSettings.isEnabled()).isTrue();
|
||||
assertThat(trendzSettings.getBaseUrl()).isEqualTo(trendzUrl);
|
||||
|
||||
String updatedUrl = "https://some.domain.com:18888/tenant_trendz";
|
||||
trendzSettings.setBaseUrl(updatedUrl);
|
||||
|
||||
doPost("/api/trendz/settings", trendzSettings).andExpect(status().isOk());
|
||||
|
||||
TrendzSettings updatedTrendzSettings = doGet("/api/trendz/settings", TrendzSettings.class);
|
||||
assertThat(updatedTrendzSettings).isEqualTo(trendzSettings);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTrendzSettingsWhenCustomer() throws Exception {
|
||||
loginCustomerUser();
|
||||
|
||||
TrendzSettings newTrendzSettings = new TrendzSettings();
|
||||
newTrendzSettings.setEnabled(true);
|
||||
newTrendzSettings.setBaseUrl("https://some.domain.com:18888/customer_trendz");
|
||||
|
||||
doPost("/api/trendz/settings", newTrendzSettings).andExpect(status().isForbidden());
|
||||
|
||||
TrendzSettings fetchedTrendzSettings = doGet("/api/trendz/settings", TrendzSettings.class);
|
||||
assertThat(fetchedTrendzSettings).isNotNull();
|
||||
assertThat(fetchedTrendzSettings.isEnabled()).isTrue();
|
||||
assertThat(fetchedTrendzSettings.getBaseUrl()).isEqualTo(trendzUrl);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 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.dao.trendz;
|
||||
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.trendz.TrendzSettings;
|
||||
|
||||
public interface TrendzSettingsService {
|
||||
|
||||
void saveTrendzSettings(TenantId tenantId, TrendzSettings settings);
|
||||
|
||||
TrendzSettings findTrendzSettings(TenantId tenantId);
|
||||
|
||||
void deleteTrendzSettings(TenantId tenantId);
|
||||
|
||||
}
|
||||
@ -35,6 +35,7 @@ public class CacheConstants {
|
||||
public static final String DEVICE_PROFILE_CACHE = "deviceProfiles";
|
||||
public static final String NOTIFICATION_SETTINGS_CACHE = "notificationSettings";
|
||||
public static final String SENT_NOTIFICATIONS_CACHE = "sentNotifications";
|
||||
public static final String TRENDZ_SETTINGS_CACHE = "trendzSettings";
|
||||
|
||||
public static final String ASSET_PROFILE_CACHE = "assetProfiles";
|
||||
public static final String ATTRIBUTES_CACHE = "attributes";
|
||||
|
||||
@ -17,6 +17,7 @@ package org.thingsboard.server.common.data;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.Data;
|
||||
import org.thingsboard.server.common.data.trendz.TrendzSettings;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -37,4 +38,5 @@ public class SystemParams {
|
||||
String calculatedFieldDebugPerTenantLimitsConfiguration;
|
||||
long maxArgumentsPerCF;
|
||||
long maxDataPointsPerRollingArg;
|
||||
TrendzSettings trendzSettings;
|
||||
}
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 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.common.data.trendz;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class TrendzSettings {
|
||||
|
||||
private boolean enabled;
|
||||
private String baseUrl;
|
||||
|
||||
}
|
||||
@ -44,6 +44,7 @@ import org.thingsboard.server.dao.service.PaginatedRemover;
|
||||
import org.thingsboard.server.dao.service.Validator;
|
||||
import org.thingsboard.server.dao.service.validator.TenantDataValidator;
|
||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||
import org.thingsboard.server.dao.trendz.TrendzSettingsService;
|
||||
import org.thingsboard.server.dao.usagerecord.ApiUsageStateService;
|
||||
import org.thingsboard.server.dao.user.UserService;
|
||||
|
||||
@ -81,6 +82,8 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
|
||||
@Autowired
|
||||
private QrCodeSettingService qrCodeSettingService;
|
||||
@Autowired
|
||||
private TrendzSettingsService trendzSettingsService;
|
||||
@Autowired
|
||||
private TenantDataValidator tenantValidator;
|
||||
@Autowired
|
||||
protected TbTransactionalCache<TenantId, Boolean> existsTenantCache;
|
||||
@ -163,9 +166,10 @@ public class TenantServiceImpl extends AbstractCachedEntityService<TenantId, Ten
|
||||
Validator.validateId(tenantId, id -> INCORRECT_TENANT_ID + id);
|
||||
|
||||
userService.deleteAllByTenantId(tenantId);
|
||||
notificationSettingsService.deleteNotificationSettings(tenantId);
|
||||
trendzSettingsService.deleteTrendzSettings(tenantId);
|
||||
adminSettingsService.deleteAdminSettingsByTenantId(tenantId);
|
||||
qrCodeSettingService.deleteByTenantId(tenantId);
|
||||
notificationSettingsService.deleteNotificationSettings(tenantId);
|
||||
|
||||
tenantDao.removeById(tenantId, tenantId.getId());
|
||||
publishEvictEvent(new TenantEvictEvent(tenantId, true));
|
||||
|
||||
@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 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.dao.trendz;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.thingsboard.common.util.JacksonUtil;
|
||||
import org.thingsboard.server.common.data.AdminSettings;
|
||||
import org.thingsboard.server.common.data.CacheConstants;
|
||||
import org.thingsboard.server.common.data.id.TenantId;
|
||||
import org.thingsboard.server.common.data.trendz.TrendzSettings;
|
||||
import org.thingsboard.server.dao.settings.AdminSettingsService;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Slf4j
|
||||
public class DefaultTrendzSettingsService implements TrendzSettingsService {
|
||||
|
||||
private final AdminSettingsService adminSettingsService;
|
||||
|
||||
private static final String SETTINGS_KEY = "trendz";
|
||||
|
||||
@CacheEvict(cacheNames = CacheConstants.TRENDZ_SETTINGS_CACHE, key = "#tenantId")
|
||||
@Override
|
||||
public void saveTrendzSettings(TenantId tenantId, TrendzSettings settings) {
|
||||
AdminSettings adminSettings = Optional.ofNullable(adminSettingsService.findAdminSettingsByTenantIdAndKey(tenantId, SETTINGS_KEY))
|
||||
.orElseGet(() -> {
|
||||
AdminSettings newAdminSettings = new AdminSettings();
|
||||
newAdminSettings.setTenantId(tenantId);
|
||||
newAdminSettings.setKey(SETTINGS_KEY);
|
||||
return newAdminSettings;
|
||||
});
|
||||
adminSettings.setJsonValue(JacksonUtil.valueToTree(settings));
|
||||
adminSettingsService.saveAdminSettings(tenantId, adminSettings);
|
||||
}
|
||||
|
||||
@Cacheable(cacheNames = CacheConstants.TRENDZ_SETTINGS_CACHE, key = "#tenantId")
|
||||
@Override
|
||||
public TrendzSettings findTrendzSettings(TenantId tenantId) {
|
||||
return Optional.ofNullable(adminSettingsService.findAdminSettingsByTenantIdAndKey(tenantId, SETTINGS_KEY))
|
||||
.map(adminSettings -> JacksonUtil.treeToValue(adminSettings.getJsonValue(), TrendzSettings.class))
|
||||
.orElseGet(TrendzSettings::new);
|
||||
}
|
||||
|
||||
@CacheEvict(cacheNames = CacheConstants.TRENDZ_SETTINGS_CACHE, key = "#tenantId")
|
||||
@Override
|
||||
public void deleteTrendzSettings(TenantId tenantId) {
|
||||
adminSettingsService.deleteAdminSettingsByTenantIdAndKey(tenantId, SETTINGS_KEY);
|
||||
}
|
||||
|
||||
}
|
||||
@ -108,6 +108,9 @@ cache.specs.qrCodeSettings.maxSize=10000
|
||||
cache.specs.mobileSecretKey.timeToLiveInMinutes=1440
|
||||
cache.specs.mobileSecretKey.maxSize=10000
|
||||
|
||||
cache.specs.trendzSettings.timeToLiveInMinutes=1440
|
||||
cache.specs.trendzSettings.maxSize=10000
|
||||
|
||||
redis.connection.host=localhost
|
||||
redis.connection.port=6379
|
||||
redis.connection.db=0
|
||||
|
||||
@ -16,6 +16,7 @@
|
||||
|
||||
import { AuthUser, User } from '@shared/models/user.model';
|
||||
import { UserSettings } from '@shared/models/user-settings.models';
|
||||
import { TrendzSettings } from '@shared/models/trendz-settings.models';
|
||||
|
||||
export interface SysParamsState {
|
||||
userTokenAccessEnabled: boolean;
|
||||
@ -32,6 +33,7 @@ export interface SysParamsState {
|
||||
maxArgumentsPerCF: number;
|
||||
ruleChainDebugPerTenantLimitsConfiguration?: string;
|
||||
calculatedFieldDebugPerTenantLimitsConfiguration?: string;
|
||||
trendzSettings: TrendzSettings;
|
||||
}
|
||||
|
||||
export interface SysParams extends SysParamsState {
|
||||
|
||||
@ -17,6 +17,7 @@
|
||||
import { AuthPayload, AuthState } from './auth.models';
|
||||
import { AuthActions, AuthActionTypes } from './auth.actions';
|
||||
import { initialUserSettings, UserSettings } from '@shared/models/user-settings.models';
|
||||
import { initialTrendzSettings } from '@shared/models/trendz-settings.models';
|
||||
import { unset } from '@core/utils';
|
||||
|
||||
const emptyUserAuthState: AuthPayload = {
|
||||
@ -34,7 +35,8 @@ const emptyUserAuthState: AuthPayload = {
|
||||
maxArgumentsPerCF: 0,
|
||||
maxDataPointsPerRollingArg: 0,
|
||||
maxDebugModeDurationMinutes: 0,
|
||||
userSettings: initialUserSettings
|
||||
userSettings: initialUserSettings,
|
||||
trendzSettings: initialTrendzSettings
|
||||
};
|
||||
|
||||
export const initialState: AuthState = {
|
||||
|
||||
@ -47,3 +47,4 @@ export * from './user.service';
|
||||
export * from './user-settings.service';
|
||||
export * from './widget.service';
|
||||
export * from './usage-info.service';
|
||||
export * from './trendz-settings.service'
|
||||
|
||||
39
ui-ngx/src/app/core/http/trendz-settings.service.ts
Normal file
39
ui-ngx/src/app/core/http/trendz-settings.service.ts
Normal file
@ -0,0 +1,39 @@
|
||||
///
|
||||
/// Copyright © 2016-2025 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 { Observable } from 'rxjs';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { TrendzSettings } from '@shared/models/trendz-settings.models';
|
||||
import { defaultHttpOptionsFromConfig, RequestConfig } from '@core/http/http-utils';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class TrendzSettingsService {
|
||||
|
||||
constructor(
|
||||
private http: HttpClient
|
||||
) {}
|
||||
|
||||
public getTrendzSettings(config?: RequestConfig): Observable<TrendzSettings> {
|
||||
return this.http.get<TrendzSettings>(`/api/trendz/settings`, defaultHttpOptionsFromConfig(config))
|
||||
}
|
||||
|
||||
public saveTrendzSettings(trendzSettings: TrendzSettings, config?: RequestConfig): Observable<TrendzSettings> {
|
||||
return this.http.post<TrendzSettings>(`/api/trendz/settings`, trendzSettings, defaultHttpOptionsFromConfig(config))
|
||||
}
|
||||
}
|
||||
@ -104,7 +104,8 @@ export enum MenuId {
|
||||
features = 'features',
|
||||
otaUpdates = 'otaUpdates',
|
||||
version_control = 'version_control',
|
||||
api_usage = 'api_usage'
|
||||
api_usage = 'api_usage',
|
||||
trendz_settings = 'trendz_settings'
|
||||
}
|
||||
|
||||
declare type MenuFilter = (authState: AuthState) => boolean;
|
||||
@ -684,6 +685,17 @@ export const menuSectionMap = new Map<MenuId, MenuSection>([
|
||||
path: '/usage',
|
||||
icon: 'insert_chart'
|
||||
}
|
||||
],
|
||||
[
|
||||
MenuId.trendz_settings,
|
||||
{
|
||||
id: MenuId.trendz_settings,
|
||||
name: 'admin.trendz',
|
||||
fullName: 'admin.trendz-settings',
|
||||
type: 'link',
|
||||
path: '/settings/trendz',
|
||||
icon: 'trendz-settings'
|
||||
}
|
||||
]
|
||||
]);
|
||||
|
||||
@ -843,7 +855,8 @@ const defaultUserMenuMap = new Map<Authority, MenuReference[]>([
|
||||
{id: MenuId.home_settings},
|
||||
{id: MenuId.notification_settings},
|
||||
{id: MenuId.repository_settings},
|
||||
{id: MenuId.auto_commit_settings}
|
||||
{id: MenuId.auto_commit_settings},
|
||||
{id: MenuId.trendz_settings}
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -946,7 +959,7 @@ const defaultHomeSectionMap = new Map<Authority, HomeSectionReference[]>([
|
||||
},
|
||||
{
|
||||
name: 'admin.system-settings',
|
||||
places: [MenuId.home_settings, MenuId.resources_library, MenuId.repository_settings, MenuId.auto_commit_settings]
|
||||
places: [MenuId.home_settings, MenuId.resources_library, MenuId.repository_settings, MenuId.auto_commit_settings, MenuId.trendz_settings]
|
||||
}
|
||||
]
|
||||
],
|
||||
|
||||
@ -52,6 +52,7 @@ import { UiSettingsService } from '@core/http/ui-settings.service';
|
||||
import { UsageInfoService } from '@core/http/usage-info.service';
|
||||
import { EventService } from '@core/http/event.service';
|
||||
import { AuditLogService } from '@core/http/audit-log.service';
|
||||
import { TrendzSettingsService } from '@core/http/trendz-settings.service';
|
||||
|
||||
export const ServicesMap = new Map<string, Type<any>>(
|
||||
[
|
||||
@ -91,6 +92,7 @@ export const ServicesMap = new Map<string, Type<any>>(
|
||||
['usageInfoService', UsageInfoService],
|
||||
['notificationService', NotificationService],
|
||||
['eventService', EventService],
|
||||
['auditLogService', AuditLogService]
|
||||
['auditLogService', AuditLogService],
|
||||
['trendzSettingsService', TrendzSettingsService]
|
||||
]
|
||||
);
|
||||
|
||||
@ -46,6 +46,7 @@ import { ScadaSymbolData } from '@home/pages/scada-symbol/scada-symbol-editor.mo
|
||||
import { MenuId } from '@core/services/menu.models';
|
||||
import { catchError } from 'rxjs/operators';
|
||||
import { JsLibraryTableConfigResolver } from '@home/pages/admin/resource/js-library-table-config.resolver';
|
||||
import { TrendzSettingsComponent } from '@home/pages/admin/trendz-settings.component';
|
||||
|
||||
export const scadaSymbolResolver: ResolveFn<ScadaSymbolData> =
|
||||
(route: ActivatedRouteSnapshot,
|
||||
@ -349,6 +350,18 @@ const routes: Routes = [
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'trendz',
|
||||
component: TrendzSettingsComponent,
|
||||
canDeactivate: [ConfirmOnExitGuard],
|
||||
data: {
|
||||
auth: [Authority.TENANT_ADMIN],
|
||||
title: 'admin.trendz-settings',
|
||||
breadcrumb: {
|
||||
menuId: MenuId.trendz_settings
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
path: 'security-settings',
|
||||
redirectTo: '/security-settings/general'
|
||||
|
||||
@ -37,6 +37,7 @@ import { OAuth2Module } from '@home/pages/admin/oauth2/oauth2.module';
|
||||
import { JsLibraryTableHeaderComponent } from '@home/pages/admin/resource/js-library-table-header.component';
|
||||
import { JsResourceComponent } from '@home/pages/admin/resource/js-resource.component';
|
||||
import { NgxFlowModule } from '@flowjs/ngx-flow';
|
||||
import { TrendzSettingsComponent } from '@home/pages/admin/trendz-settings.component';
|
||||
|
||||
@NgModule({
|
||||
declarations:
|
||||
@ -55,7 +56,8 @@ import { NgxFlowModule } from '@flowjs/ngx-flow';
|
||||
QueueComponent,
|
||||
RepositoryAdminSettingsComponent,
|
||||
AutoCommitAdminSettingsComponent,
|
||||
TwoFactorAuthSettingsComponent
|
||||
TwoFactorAuthSettingsComponent,
|
||||
TrendzSettingsComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
||||
@ -0,0 +1,51 @@
|
||||
<!--
|
||||
|
||||
Copyright © 2016-2025 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.
|
||||
|
||||
-->
|
||||
<div>
|
||||
<mat-card appearance="outlined" class="settings-card">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
<span class="mat-headline-5" translate>admin.trendz-settings</span>
|
||||
</mat-card-title>
|
||||
<span class="flex-1"></span>
|
||||
<div tb-help="trendzSettings"></div>
|
||||
</mat-card-header>
|
||||
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
||||
</mat-progress-bar>
|
||||
<div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
|
||||
<mat-card-content>
|
||||
<form [formGroup]="trendzSettingsForm" (ngSubmit)="save()">
|
||||
<fieldset [disabled]="isLoading$ | async">
|
||||
<section class="tb-trendz-section flex flex-col gt-sm:flex-row">
|
||||
<mat-form-field class="tb-trendz-url mat-block flex-1" subscriptSizing="dynamic">
|
||||
<mat-label translate>admin.trendz-url</mat-label>
|
||||
<input matInput formControlName="trendzUrl">
|
||||
</mat-form-field>
|
||||
<mat-checkbox class="flex flex-1" formControlName="isTrendzEnabled">
|
||||
{{ 'admin.trendz-enable' | translate }}
|
||||
</mat-checkbox>
|
||||
</section>
|
||||
<div class="flex w-full flex-row flex-wrap items-center justify-end">
|
||||
<button mat-button mat-raised-button color="primary" [disabled]="(isLoading$ | async) || trendzSettingsForm.invalid || !trendzSettingsForm.dirty" type="submit">
|
||||
{{'action.save' | translate}}
|
||||
</button>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
</mat-card-content>
|
||||
</mat-card>
|
||||
</div>
|
||||
@ -0,0 +1,36 @@
|
||||
/**
|
||||
* Copyright © 2016-2025 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 "../../../../../scss/constants";
|
||||
|
||||
:host {
|
||||
.mat-mdc-card-header {
|
||||
min-height: 64px;
|
||||
}
|
||||
|
||||
.tb-trendz-section {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.tb-trendz-url {
|
||||
@media #{$mat-gt-sm} {
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
@media #{$mat-lt-md} {
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
///
|
||||
/// Copyright © 2016-2025 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 { Component, OnInit, DestroyRef } from '@angular/core';
|
||||
import { PageComponent } from '@shared/components/page.component';
|
||||
import { HasConfirmForm } from '@core/guards/confirm-on-exit.guard';
|
||||
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
|
||||
import { TrendzSettingsService } from '@core/http/trendz-settings.service';
|
||||
import { TrendzSettings } from '@shared/models/trendz-settings.models';
|
||||
import { isDefinedAndNotNull } from '@core/utils';
|
||||
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
||||
|
||||
@Component({
|
||||
selector: 'tb-trendz-settings',
|
||||
templateUrl: './trendz-settings.component.html',
|
||||
styleUrls: ['./trendz-settings.component.scss', './settings-card.scss']
|
||||
})
|
||||
export class TrendzSettingsComponent extends PageComponent implements OnInit, HasConfirmForm {
|
||||
|
||||
trendzSettingsForm: FormGroup;
|
||||
|
||||
constructor(private fb: FormBuilder,
|
||||
private trendzSettingsService: TrendzSettingsService,
|
||||
private destroyRef: DestroyRef) {
|
||||
super();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.trendzSettingsForm = this.fb.group({
|
||||
trendzUrl: [null, [Validators.pattern(/^(https?:\/\/)[^\s/$.?#].[^\s]*$/i)]],
|
||||
isTrendzEnabled: [false]
|
||||
});
|
||||
|
||||
this.trendzSettingsService.getTrendzSettings().subscribe((trendzSettings) => {
|
||||
this.setTrendzSettings(trendzSettings);
|
||||
});
|
||||
|
||||
this.trendzSettingsForm.get('isTrendzEnabled').valueChanges
|
||||
.pipe(takeUntilDestroyed(this.destroyRef))
|
||||
.subscribe((enabled: boolean) => this.toggleUrlRequired(enabled));
|
||||
}
|
||||
|
||||
toggleUrlRequired(enabled: boolean) {
|
||||
const trendzUrlControl = this.trendzSettingsForm.get('trendzUrl')!;
|
||||
|
||||
if (enabled) {
|
||||
trendzUrlControl.addValidators(Validators.required);
|
||||
} else {
|
||||
trendzUrlControl.removeValidators(Validators.required);
|
||||
}
|
||||
|
||||
trendzUrlControl.updateValueAndValidity();
|
||||
}
|
||||
|
||||
setTrendzSettings(trendzSettings: TrendzSettings) {
|
||||
this.trendzSettingsForm.reset({
|
||||
trendzUrl: trendzSettings?.baseUrl,
|
||||
isTrendzEnabled: trendzSettings?.enabled ?? false
|
||||
});
|
||||
|
||||
this.toggleUrlRequired(this.trendzSettingsForm.get('isTrendzEnabled').value);
|
||||
}
|
||||
|
||||
confirmForm(): FormGroup {
|
||||
return this.trendzSettingsForm;
|
||||
}
|
||||
|
||||
save(): void {
|
||||
const trendzUrl = this.trendzSettingsForm.get('trendzUrl').value;
|
||||
const isTrendzEnabled = this.trendzSettingsForm.get('isTrendzEnabled').value;
|
||||
|
||||
const trendzSettings: TrendzSettings = {
|
||||
baseUrl: trendzUrl,
|
||||
enabled: isTrendzEnabled
|
||||
};
|
||||
|
||||
this.trendzSettingsService.saveTrendzSettings(trendzSettings).subscribe(() => {
|
||||
this.setTrendzSettings(trendzSettings);
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -200,6 +200,7 @@ export const HelpLinks = {
|
||||
mobileQrCode: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/ui/mobile-qr-code/`,
|
||||
calculatedField: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/calculated-fields/`,
|
||||
timewindowSettings: `${helpBaseUrl}/docs${docPlatformPrefix}/user-guide/dashboards/#time-window`,
|
||||
trendzSettings: `${helpBaseUrl}/docs/trendz/`
|
||||
}
|
||||
};
|
||||
/* eslint-enable max-len */
|
||||
|
||||
@ -62,7 +62,19 @@ export const svgIcons: {[key: string]: string} = {
|
||||
'4.6760606 4.678212,7.3604329 7.3397982,4.6839955 4.6657413,2.0041717 6.6653477,2.2309572e-4 9.3360035,2.6766286 11.997681,' +
|
||||
'0 14.659287,2.6765011 Z m -5.332255,4.0079963 1.999613,2.003945 -7.99844,8.0158157 -1.9996133,-2.004017 z m 1.676684,4.3522483 ' +
|
||||
'1.999613,2.0039454 -6.6654242,6.679793 -1.9996133,-2.003874 z m 2.988987,7.0033574 -1.999544,-2.003945 -4.6658108,4.675848 ' +
|
||||
'1.9996128,2.004015 z"/></svg>'
|
||||
'1.9996128,2.004015 z"/></svg>',
|
||||
'trendz-settings': '<svg viewBox="0 0 25 17"><path fill-rule="evenodd" clip-rule="evenodd" d="M7.04334 0.28949H12.2804V5.7615L11.5894 ' +
|
||||
'6.4537L10.4605 5.32674L7.04334 8.73801V0.28949ZM7.04334 10.0127V11.0075L7.54073 10.5093L7.04334 10.0127ZM7.04334 ' +
|
||||
'12.2649V13.2424L7.53209 12.7545L7.04334 12.2649ZM18.3903 13.243V12.2646L17.901 12.7546L18.3903 13.243ZM18.3903 ' +
|
||||
'11.0079V10.0123L17.8925 10.5093L18.3903 11.0079ZM18.3903 8.73841V3.34443H13.1532V5.76189L13.8438 6.45362L14.9727 ' +
|
||||
'5.32661L17.0542 7.40453L18.3903 8.73841ZM24.8335 1.16233H19.2631V13.8185H24.8335V1.16233ZM0.833481 5.52653H6.1705V13.8185H0.833481V5.52653Z"/>' +
|
||||
'<path fill-rule="evenodd" clip-rule="evenodd" d="M14.9729 6.55688L15.819 7.40149L14.6876 8.53099L15.8137 9.65905L16.947 ' +
|
||||
'8.52767L17.7931 9.37227L16.6583 10.5051L17.7844 11.6331L16.6669 12.7526L17.7931 13.8768L16.947 14.7214L15.8223 13.5986L14.6962 ' +
|
||||
'14.7267L15.819 15.8476L14.973 16.6921L13.8516 15.5727L12.7169 16.7094L11.5821 15.5727L10.4607 16.6921L9.61465 15.8476L10.7375 ' +
|
||||
'14.7267L9.61134 13.5985L8.48664 14.7213L7.64059 13.8767L8.76673 12.7525L7.64928 11.6331L8.77533 10.5051L7.64059 9.37227L8.48664 ' +
|
||||
'8.52767L9.61993 9.65905L10.7461 8.53103L9.61465 7.40158L10.4607 6.55697L11.5907 7.68499L12.7169 6.55688L13.843 7.68494L14.9729 ' +
|
||||
'6.55688ZM12.7169 8.24609L13.5629 9.0907L10.1787 12.4691L9.33268 11.6245L12.7169 8.24609ZM13.4262 10.0805L14.2723 10.9251L11.4521 ' +
|
||||
'13.7404L10.6061 12.8958L13.4262 10.0805ZM14.6909 13.0321L13.8449 12.1875L11.8708 14.1583L12.7168 15.0029L14.6909 13.0321Z"/></svg>'
|
||||
};
|
||||
|
||||
export const svgIconsUrl: { [key: string]: string } = {
|
||||
|
||||
@ -62,3 +62,4 @@ export * from './window-message.model';
|
||||
export * from './usage.models';
|
||||
export * from './query/query.models';
|
||||
export * from './regex.constants';
|
||||
export * from './trendz-settings.models'
|
||||
|
||||
25
ui-ngx/src/app/shared/models/trendz-settings.models.ts
Normal file
25
ui-ngx/src/app/shared/models/trendz-settings.models.ts
Normal file
@ -0,0 +1,25 @@
|
||||
///
|
||||
/// Copyright © 2016-2025 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.
|
||||
///
|
||||
|
||||
export interface TrendzSettings {
|
||||
baseUrl: string,
|
||||
enabled: boolean
|
||||
}
|
||||
|
||||
export const initialTrendzSettings: TrendzSettings = {
|
||||
baseUrl: null,
|
||||
enabled: false
|
||||
}
|
||||
@ -545,7 +545,11 @@
|
||||
"slack-settings": "Slack settings",
|
||||
"mobile-settings": "Mobile settings",
|
||||
"firebase-service-account-file": "Firebase service account credentials JSON file",
|
||||
"select-firebase-service-account-file": "Drag and drop your Firebase service account credentials file or "
|
||||
"select-firebase-service-account-file": "Drag and drop your Firebase service account credentials file or ",
|
||||
"trendz": "Trendz",
|
||||
"trendz-settings": "Trendz settings",
|
||||
"trendz-url": "Trendz URL",
|
||||
"trendz-enable": "Enable Trendz"
|
||||
},
|
||||
"alarm": {
|
||||
"alarm": "Alarm",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user