Basic implementation of the Starred and Last visited Info
This commit is contained in:
parent
3086ed46a0
commit
5d30fefbcb
@ -167,11 +167,12 @@ CREATE INDEX IF NOT EXISTS idx_notification_recipient_id_created_time ON notific
|
|||||||
ALTER TABLE tb_user ADD COLUMN IF NOT EXISTS phone VARCHAR(255);
|
ALTER TABLE tb_user ADD COLUMN IF NOT EXISTS phone VARCHAR(255);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS user_settings (
|
CREATE TABLE IF NOT EXISTS user_settings (
|
||||||
user_id uuid NOT NULL CONSTRAINT user_settings_pkey PRIMARY KEY,
|
user_id uuid NOT NULL,
|
||||||
settings varchar(100000),
|
type VARCHAR(50) NOT NULL,
|
||||||
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE
|
settings varchar(10000),
|
||||||
|
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT user_settings_pkey PRIMARY KEY (user_id, type)
|
||||||
);
|
);
|
||||||
|
|
||||||
-- ALARM INFO VIEW
|
-- ALARM INFO VIEW
|
||||||
|
|
||||||
DROP VIEW IF EXISTS alarm_info CASCADE;
|
DROP VIEW IF EXISTS alarm_info CASCADE;
|
||||||
|
|||||||
@ -146,6 +146,7 @@ import org.thingsboard.server.service.component.ComponentDiscoveryService;
|
|||||||
import org.thingsboard.server.service.edge.instructions.EdgeInstallService;
|
import org.thingsboard.server.service.edge.instructions.EdgeInstallService;
|
||||||
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
|
import org.thingsboard.server.service.edge.rpc.EdgeRpcService;
|
||||||
import org.thingsboard.server.service.entitiy.TbNotificationEntityService;
|
import org.thingsboard.server.service.entitiy.TbNotificationEntityService;
|
||||||
|
import org.thingsboard.server.service.entitiy.user.TbUserSettingsService;
|
||||||
import org.thingsboard.server.service.ota.OtaPackageStateService;
|
import org.thingsboard.server.service.ota.OtaPackageStateService;
|
||||||
import org.thingsboard.server.service.profile.TbAssetProfileCache;
|
import org.thingsboard.server.service.profile.TbAssetProfileCache;
|
||||||
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
|
import org.thingsboard.server.service.profile.TbDeviceProfileCache;
|
||||||
@ -202,7 +203,7 @@ public abstract class BaseController {
|
|||||||
protected UserService userService;
|
protected UserService userService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected UserSettingsService userSettingsService;
|
protected TbUserSettingsService userSettingsService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
protected DeviceService deviceService;
|
protected DeviceService deviceService;
|
||||||
|
|||||||
@ -38,12 +38,14 @@ import org.springframework.web.bind.annotation.ResponseStatus;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.rule.engine.api.MailService;
|
import org.thingsboard.rule.engine.api.MailService;
|
||||||
|
import org.thingsboard.server.common.data.DashboardInfo;
|
||||||
import org.thingsboard.server.common.data.EntityType;
|
import org.thingsboard.server.common.data.EntityType;
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.UserEmailInfo;
|
import org.thingsboard.server.common.data.UserEmailInfo;
|
||||||
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode;
|
||||||
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
import org.thingsboard.server.common.data.exception.ThingsboardException;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
|
import org.thingsboard.server.common.data.id.DashboardId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
import org.thingsboard.server.common.data.page.PageData;
|
import org.thingsboard.server.common.data.page.PageData;
|
||||||
@ -55,7 +57,10 @@ import org.thingsboard.server.common.data.query.EntityTypeFilter;
|
|||||||
import org.thingsboard.server.common.data.query.TsValue;
|
import org.thingsboard.server.common.data.query.TsValue;
|
||||||
import org.thingsboard.server.common.data.security.Authority;
|
import org.thingsboard.server.common.data.security.Authority;
|
||||||
import org.thingsboard.server.common.data.security.UserCredentials;
|
import org.thingsboard.server.common.data.security.UserCredentials;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.LastVisitedDashboardInfo;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserDashboardAction;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserDashboardsInfo;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
|
import org.thingsboard.server.common.data.security.event.UserCredentialsInvalidationEvent;
|
||||||
import org.thingsboard.server.common.data.security.model.JwtPair;
|
import org.thingsboard.server.common.data.security.model.JwtPair;
|
||||||
import org.thingsboard.server.queue.util.TbCoreComponent;
|
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||||
@ -76,6 +81,7 @@ import java.util.Map;
|
|||||||
import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD;
|
import static org.thingsboard.server.common.data.query.EntityKeyType.ENTITY_FIELD;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
|
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
|
import static org.thingsboard.server.controller.ControllerConstants.CUSTOMER_ID_PARAM_DESCRIPTION;
|
||||||
|
import static org.thingsboard.server.controller.ControllerConstants.DASHBOARD_ID_PARAM_DESCRIPTION;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.DEFAULT_DASHBOARD;
|
import static org.thingsboard.server.controller.ControllerConstants.DEFAULT_DASHBOARD;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.HOME_DASHBOARD;
|
import static org.thingsboard.server.controller.ControllerConstants.HOME_DASHBOARD;
|
||||||
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS;
|
||||||
@ -445,6 +451,7 @@ public class UserController extends BaseController {
|
|||||||
SecurityUser currentUser = getCurrentUser();
|
SecurityUser currentUser = getCurrentUser();
|
||||||
|
|
||||||
UserSettings userSettings = new UserSettings();
|
UserSettings userSettings = new UserSettings();
|
||||||
|
userSettings.setType(UserSettings.GENERAL);
|
||||||
userSettings.setSettings(settings);
|
userSettings.setSettings(settings);
|
||||||
userSettings.setUserId(currentUser.getId());
|
userSettings.setUserId(currentUser.getId());
|
||||||
return userSettingsService.saveUserSettings(currentUser.getTenantId(), userSettings).getSettings();
|
return userSettingsService.saveUserSettings(currentUser.getTenantId(), userSettings).getSettings();
|
||||||
@ -485,4 +492,37 @@ public class UserController extends BaseController {
|
|||||||
userSettingsService.deleteUserSettings(currentUser.getTenantId(), currentUser.getId(), Arrays.asList(paths.split(",")));
|
userSettingsService.deleteUserSettings(currentUser.getTenantId(), currentUser.getId(), Arrays.asList(paths.split(",")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "Get information about last visited and starred dashboards (getLastVisitedDashboards)",
|
||||||
|
notes = "Fetch the list of last visited and starred dashboards. Both lists are limited to 10 items.")
|
||||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||||
|
@GetMapping(value = "/user/dashboards")
|
||||||
|
public UserDashboardsInfo getUserDashboardsInfo() throws ThingsboardException {
|
||||||
|
SecurityUser currentUser = getCurrentUser();
|
||||||
|
return userSettingsService.findUserDashboardsInfo(currentUser.getTenantId(), currentUser.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@ApiOperation(value = "Report action of User over the dashboard (reportUserDashboardAction)",
|
||||||
|
notes = "Enables or Disables user credentials. Useful when you would like to block user account without deleting it. " + PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH)
|
||||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')")
|
||||||
|
@RequestMapping(value = "/user/dashboards/{dashboardId}/{action}", method = RequestMethod.POST)
|
||||||
|
@ResponseBody
|
||||||
|
public UserDashboardsInfo reportUserDashboardAction(
|
||||||
|
@ApiParam(value = DASHBOARD_ID_PARAM_DESCRIPTION)
|
||||||
|
@PathVariable(DashboardController.DASHBOARD_ID) String strDashboardId,
|
||||||
|
@ApiParam(value = "Dashboard action, one of: \"visit\", \"star\" or \"unstar\".")
|
||||||
|
@PathVariable("action") String strAction) throws ThingsboardException {
|
||||||
|
checkParameter(DashboardController.DASHBOARD_ID, strDashboardId);
|
||||||
|
checkParameter("action", strAction);
|
||||||
|
DashboardId dashboardId = new DashboardId(toUUID(strDashboardId));
|
||||||
|
DashboardInfo dashboard = checkDashboardInfoId(dashboardId, Operation.READ);
|
||||||
|
UserDashboardAction action;
|
||||||
|
try {
|
||||||
|
action = UserDashboardAction.valueOf(strAction.toUpperCase());
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
throw new ThingsboardException("Action: " + strAction + " is not supported!", ThingsboardErrorCode.BAD_REQUEST_PARAMS);
|
||||||
|
}
|
||||||
|
SecurityUser currentUser = getCurrentUser();
|
||||||
|
return userSettingsService.reportUserDashboardAction(currentUser.getTenantId(), currentUser.getId(), dashboardId, action);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,186 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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.service.entitiy.user;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
|
import org.thingsboard.server.common.data.HasTitle;
|
||||||
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
|
import org.thingsboard.server.common.data.id.DashboardId;
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
|
import org.thingsboard.server.common.data.settings.AbstractUserDashboardInfo;
|
||||||
|
import org.thingsboard.server.common.data.settings.LastVisitedDashboardInfo;
|
||||||
|
import org.thingsboard.server.common.data.settings.StarredDashboardInfo;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserDashboardAction;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserDashboardsInfo;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
|
import org.thingsboard.server.dao.dashboard.DashboardService;
|
||||||
|
import org.thingsboard.server.dao.user.UserSettingsService;
|
||||||
|
import org.thingsboard.server.queue.util.TbCoreComponent;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
@TbCoreComponent
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Slf4j
|
||||||
|
public class DefaultTbUserSettingsService implements TbUserSettingsService {
|
||||||
|
|
||||||
|
private static final int MAX_DASHBOARD_INFO_LIST_SIZE = 10;
|
||||||
|
private static final Predicate<HasTitle> EMPTY_TITLE = i -> StringUtils.isNotEmpty(i.getTitle());
|
||||||
|
|
||||||
|
private final UserSettingsService settingsService;
|
||||||
|
private final DashboardService dashboardService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserSettings saveUserSettings(TenantId tenantId, UserSettings userSettings) {
|
||||||
|
return settingsService.saveUserSettings(tenantId, userSettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateUserSettings(TenantId tenantId, UserId userId, JsonNode settings) {
|
||||||
|
updateUserSettings(tenantId, userId, UserSettings.GENERAL, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateUserSettings(TenantId tenantId, UserId userId, String type, JsonNode settings) {
|
||||||
|
settingsService.updateUserSettings(tenantId, userId, type, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserSettings findUserSettings(TenantId tenantId, UserId userId) {
|
||||||
|
return findUserSettings(tenantId, userId, UserSettings.GENERAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserSettings findUserSettings(TenantId tenantId, UserId userId, String type) {
|
||||||
|
return settingsService.findUserSettings(tenantId, userId, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteUserSettings(TenantId tenantId, UserId userId, List<String> jsonPaths) {
|
||||||
|
deleteUserSettings(tenantId, userId, UserSettings.GENERAL, jsonPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void deleteUserSettings(TenantId tenantId, UserId userId, String type, List<String> jsonPaths) {
|
||||||
|
settingsService.deleteUserSettings(tenantId, userId, type, jsonPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDashboardsInfo findUserDashboardsInfo(TenantId tenantId, UserId id) {
|
||||||
|
UserSettings us = findUserSettings(tenantId, id, UserSettings.STARRED_DASHBOARDS);
|
||||||
|
if (us == null) {
|
||||||
|
return UserDashboardsInfo.EMPTY;
|
||||||
|
}
|
||||||
|
UserDashboardsInfo stored = JacksonUtil.convertValue(us.getSettings(), UserDashboardsInfo.class);
|
||||||
|
if (stored == null) {
|
||||||
|
return UserDashboardsInfo.EMPTY;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!stored.getLast().isEmpty()) {
|
||||||
|
stored.getLast().forEach(i -> setTitleIfEmpty(tenantId, i));
|
||||||
|
stored.getLast().removeIf(EMPTY_TITLE);
|
||||||
|
}
|
||||||
|
if (!stored.getStarred().isEmpty()) {
|
||||||
|
Map<UUID, LastVisitedDashboardInfo> lastMap = stored.getLast().stream().collect(Collectors.toMap(LastVisitedDashboardInfo::getId, Function.identity()));
|
||||||
|
stored.getStarred().forEach(i -> {
|
||||||
|
var last = lastMap.get(i.getId());
|
||||||
|
i.setTitle(last != null ? last.getTitle() : null);
|
||||||
|
});
|
||||||
|
stored.getStarred().forEach(i -> setTitleIfEmpty(tenantId, i));
|
||||||
|
stored.getStarred().removeIf(EMPTY_TITLE);
|
||||||
|
}
|
||||||
|
return stored;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public UserDashboardsInfo reportUserDashboardAction(TenantId tenantId, UserId id, DashboardId dashboardId, UserDashboardAction action) {
|
||||||
|
UserSettings us = findUserSettings(tenantId, id, UserSettings.STARRED_DASHBOARDS);
|
||||||
|
UserDashboardsInfo stored = null;
|
||||||
|
if (us != null) {
|
||||||
|
stored = JacksonUtil.convertValue(us.getSettings(), UserDashboardsInfo.class);
|
||||||
|
}
|
||||||
|
if (stored == null) {
|
||||||
|
stored = new UserDashboardsInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (action) {
|
||||||
|
case STAR:
|
||||||
|
addToStarred(stored, dashboardId);
|
||||||
|
break;
|
||||||
|
case UNSTAR:
|
||||||
|
removeFromStarred(stored, dashboardId);
|
||||||
|
break;
|
||||||
|
case VISIT:
|
||||||
|
addToVisited(stored, dashboardId);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
us = new UserSettings();
|
||||||
|
us.setUserId(id);
|
||||||
|
us.setType(UserSettings.STARRED_DASHBOARDS);
|
||||||
|
us.setSettings(JacksonUtil.valueToTree(stored));
|
||||||
|
saveUserSettings(tenantId, us);
|
||||||
|
return stored;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToVisited(UserDashboardsInfo stored, DashboardId dashboardId) {
|
||||||
|
UUID id = dashboardId.getId();
|
||||||
|
stored.getStarred().removeIf(d -> id.equals(d.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void removeFromStarred(UserDashboardsInfo stored, DashboardId dashboardId) {
|
||||||
|
UUID id = dashboardId.getId();
|
||||||
|
stored.getStarred().removeIf(d -> id.equals(d.getId()));
|
||||||
|
stored.getLast().stream().filter(d -> id.equals(d.getId())).findFirst().ifPresent(d -> d.setStarred(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addToStarred(UserDashboardsInfo stored, DashboardId dashboardId) {
|
||||||
|
UUID id = dashboardId.getId();
|
||||||
|
long ts = System.currentTimeMillis();
|
||||||
|
var opt = stored.getStarred().stream().filter(d -> id.equals(d.getId())).findFirst();
|
||||||
|
if (opt.isPresent()) {
|
||||||
|
opt.get().setStarredAt(ts);
|
||||||
|
} else {
|
||||||
|
var newInfo = new StarredDashboardInfo();
|
||||||
|
newInfo.setId(id);
|
||||||
|
newInfo.setStarredAt(System.currentTimeMillis());
|
||||||
|
stored.getStarred().add(newInfo);
|
||||||
|
}
|
||||||
|
stored.getLast().stream().filter(d -> id.equals(d.getId())).forEach(d -> d.setStarred(true));
|
||||||
|
//TODO: self-heal if some of the dashboards were deleted.
|
||||||
|
//TODO: limit by size.
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setTitleIfEmpty(TenantId tenantId, AbstractUserDashboardInfo i) {
|
||||||
|
if (StringUtils.isEmpty(i.getTitle())) {
|
||||||
|
var dashboardInfo = dashboardService.findDashboardInfoById(tenantId, new DashboardId(i.getId()));
|
||||||
|
i.setTitle(dashboardInfo != null ? dashboardInfo.getTitle() : null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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.service.entitiy.user;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import org.thingsboard.server.common.data.id.DashboardId;
|
||||||
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserDashboardAction;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserDashboardsInfo;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface TbUserSettingsService {
|
||||||
|
|
||||||
|
void updateUserSettings(TenantId tenantId, UserId userId, JsonNode settings);
|
||||||
|
|
||||||
|
void updateUserSettings(TenantId tenantId, UserId userId, String type, JsonNode settings);
|
||||||
|
|
||||||
|
UserSettings saveUserSettings(TenantId tenantId, UserSettings userSettings);
|
||||||
|
|
||||||
|
UserSettings findUserSettings(TenantId tenantId, UserId userId);
|
||||||
|
|
||||||
|
UserSettings findUserSettings(TenantId tenantId, UserId userId, String type);
|
||||||
|
|
||||||
|
void deleteUserSettings(TenantId tenantId, UserId userId, List<String> jsonPaths);
|
||||||
|
|
||||||
|
void deleteUserSettings(TenantId tenantId, UserId userId, String type, List<String> jsonPaths);
|
||||||
|
|
||||||
|
UserDashboardsInfo findUserDashboardsInfo(TenantId tenantId, UserId id);
|
||||||
|
|
||||||
|
UserDashboardsInfo reportUserDashboardAction(TenantId tenantId, UserId id, DashboardId dashboardId, UserDashboardAction action);
|
||||||
|
}
|
||||||
@ -18,18 +18,18 @@ package org.thingsboard.server.dao.user;
|
|||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public interface UserSettingsService {
|
public interface UserSettingsService {
|
||||||
|
|
||||||
void updateUserSettings(TenantId tenantId, UserId userId, JsonNode settings);
|
void updateUserSettings(TenantId tenantId, UserId userId, String type, JsonNode settings);
|
||||||
|
|
||||||
UserSettings saveUserSettings(TenantId tenantId, UserSettings userSettings);
|
UserSettings saveUserSettings(TenantId tenantId, UserSettings userSettings);
|
||||||
|
|
||||||
UserSettings findUserSettings(TenantId tenantId, UserId userId);
|
UserSettings findUserSettings(TenantId tenantId, UserId userId, String type);
|
||||||
|
|
||||||
void deleteUserSettings(TenantId tenantId, UserId userId, List<String> jsonPaths);
|
void deleteUserSettings(TenantId tenantId, UserId userId, String type, List<String> jsonPaths);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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.settings;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import org.thingsboard.server.common.data.HasTitle;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@ApiModel
|
||||||
|
@Data
|
||||||
|
public abstract class AbstractUserDashboardInfo implements HasTitle, Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6461562426034242608L;
|
||||||
|
|
||||||
|
@ApiModelProperty(position = 1, value = "JSON object with Dashboard id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
||||||
|
private UUID id;
|
||||||
|
@ApiModelProperty(position = 2, value = "Title of the dashboard.")
|
||||||
|
private String title;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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.settings;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ApiModel
|
||||||
|
@Data
|
||||||
|
public class LastVisitedDashboardInfo extends AbstractUserDashboardInfo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -6461562426034242608L;
|
||||||
|
|
||||||
|
@ApiModelProperty(position = 3, value = "Starred flag")
|
||||||
|
private boolean starred;
|
||||||
|
@ApiModelProperty(position = 4, value = "Last visit timestamp")
|
||||||
|
private long lastVisited;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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.settings;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.EqualsAndHashCode;
|
||||||
|
import org.thingsboard.server.common.data.HasTitle;
|
||||||
|
import org.thingsboard.server.common.data.id.DashboardId;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
@EqualsAndHashCode(callSuper = true)
|
||||||
|
@ApiModel
|
||||||
|
@Data
|
||||||
|
public class StarredDashboardInfo extends AbstractUserDashboardInfo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -7830828696329673361L;
|
||||||
|
@ApiModelProperty(position = 4, value = "Starred timestamp")
|
||||||
|
private long starredAt;
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,22 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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.settings;
|
||||||
|
|
||||||
|
public enum UserDashboardAction {
|
||||||
|
|
||||||
|
VISIT, STAR, UNSTAR
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,46 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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.settings;
|
||||||
|
|
||||||
|
import io.swagger.annotations.ApiModel;
|
||||||
|
import io.swagger.annotations.ApiModelProperty;
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@ApiModel
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class UserDashboardsInfo implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 2628320657987010348L;
|
||||||
|
public static final UserDashboardsInfo EMPTY = new UserDashboardsInfo(Collections.emptyList(), Collections.emptyList());
|
||||||
|
|
||||||
|
@ApiModelProperty(position = 1, value = "List of last visited dashboards.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
||||||
|
private List<LastVisitedDashboardInfo> last;
|
||||||
|
|
||||||
|
@ApiModelProperty(position = 2, value = "List of starred dashboards.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
||||||
|
private List<StarredDashboardInfo> starred;
|
||||||
|
|
||||||
|
public UserDashboardsInfo() {
|
||||||
|
this(new ArrayList<>(), new ArrayList<>());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -13,7 +13,7 @@
|
|||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.thingsboard.server.common.data.security;
|
package org.thingsboard.server.common.data.settings;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
@ -40,10 +40,18 @@ public class UserSettings implements Serializable {
|
|||||||
|
|
||||||
private static final long serialVersionUID = 2628320657987010348L;
|
private static final long serialVersionUID = 2628320657987010348L;
|
||||||
|
|
||||||
|
public static final String GENERAL = "general";
|
||||||
|
public static final String STARRED_DASHBOARDS = "starred_dashboards";
|
||||||
|
|
||||||
@ApiModelProperty(position = 1, value = "JSON object with User id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
@ApiModelProperty(position = 1, value = "JSON object with User id.", accessMode = ApiModelProperty.AccessMode.READ_ONLY)
|
||||||
private UserId userId;
|
private UserId userId;
|
||||||
|
|
||||||
@ApiModelProperty(position = 2, value = "JSON object with user settings.", dataType = "com.fasterxml.jackson.databind.JsonNode")
|
@ApiModelProperty(position = 2, value = "Type of the settings.")
|
||||||
|
@NoXss
|
||||||
|
@Length(fieldName = "type", max = 50)
|
||||||
|
private transient String type;
|
||||||
|
|
||||||
|
@ApiModelProperty(position = 3, value = "JSON object with user settings.", dataType = "com.fasterxml.jackson.databind.JsonNode")
|
||||||
@NoXss
|
@NoXss
|
||||||
@Length(fieldName = "settings", max = 100000)
|
@Length(fieldName = "settings", max = 100000)
|
||||||
private transient JsonNode settings;
|
private transient JsonNode settings;
|
||||||
@ -0,0 +1,44 @@
|
|||||||
|
/**
|
||||||
|
* Copyright © 2016-2023 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.settings;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class UserSettingsCompositeKey implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -7883642552545291489L;
|
||||||
|
|
||||||
|
private UUID userId;
|
||||||
|
private String type;
|
||||||
|
|
||||||
|
public UserSettingsCompositeKey(UserSettings userSettings) {
|
||||||
|
this.userId = userSettings.getUserId().getId();
|
||||||
|
this.type = userSettings.getType();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return userId.toString() + "_" + type;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -94,6 +94,7 @@ public class ModelConstants {
|
|||||||
*/
|
*/
|
||||||
public static final String USER_SETTINGS_COLUMN_FAMILY_NAME = "user_settings";
|
public static final String USER_SETTINGS_COLUMN_FAMILY_NAME = "user_settings";
|
||||||
public static final String USER_SETTINGS_USER_ID_PROPERTY = USER_ID_PROPERTY;
|
public static final String USER_SETTINGS_USER_ID_PROPERTY = USER_ID_PROPERTY;
|
||||||
|
public static final String USER_SETTINGS_TYPE_PROPERTY = "type";
|
||||||
public static final String USER_SETTINGS_SETTINGS = "settings";
|
public static final String USER_SETTINGS_SETTINGS = "settings";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -21,7 +21,8 @@ import lombok.NoArgsConstructor;
|
|||||||
import org.hibernate.annotations.Type;
|
import org.hibernate.annotations.Type;
|
||||||
import org.hibernate.annotations.TypeDef;
|
import org.hibernate.annotations.TypeDef;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserSettingsCompositeKey;
|
||||||
import org.thingsboard.server.dao.model.ModelConstants;
|
import org.thingsboard.server.dao.model.ModelConstants;
|
||||||
import org.thingsboard.server.dao.model.ToData;
|
import org.thingsboard.server.dao.model.ToData;
|
||||||
import org.thingsboard.server.dao.util.mapping.JsonStringType;
|
import org.thingsboard.server.dao.util.mapping.JsonStringType;
|
||||||
@ -29,6 +30,7 @@ import org.thingsboard.server.dao.util.mapping.JsonStringType;
|
|||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.IdClass;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
@ -37,17 +39,22 @@ import java.util.UUID;
|
|||||||
@TypeDef(name = "json", typeClass = JsonStringType.class)
|
@TypeDef(name = "json", typeClass = JsonStringType.class)
|
||||||
@Entity
|
@Entity
|
||||||
@Table(name = ModelConstants.USER_SETTINGS_COLUMN_FAMILY_NAME)
|
@Table(name = ModelConstants.USER_SETTINGS_COLUMN_FAMILY_NAME)
|
||||||
|
@IdClass(UserSettingsCompositeKey.class)
|
||||||
public class UserSettingsEntity implements ToData<UserSettings> {
|
public class UserSettingsEntity implements ToData<UserSettings> {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@Column(name = ModelConstants.USER_SETTINGS_USER_ID_PROPERTY)
|
@Column(name = ModelConstants.USER_SETTINGS_USER_ID_PROPERTY)
|
||||||
private UUID userId;
|
private UUID userId;
|
||||||
|
@Id
|
||||||
|
@Column(name = ModelConstants.USER_SETTINGS_TYPE_PROPERTY)
|
||||||
|
private String type;
|
||||||
@Type(type = "json")
|
@Type(type = "json")
|
||||||
@Column(name = ModelConstants.USER_SETTINGS_SETTINGS)
|
@Column(name = ModelConstants.USER_SETTINGS_SETTINGS)
|
||||||
private JsonNode settings;
|
private JsonNode settings;
|
||||||
|
|
||||||
public UserSettingsEntity(UserSettings userSettings) {
|
public UserSettingsEntity(UserSettings userSettings) {
|
||||||
this.userId = userSettings.getUserId().getId();
|
this.userId = userSettings.getUserId().getId();
|
||||||
|
this.type = userSettings.getType();
|
||||||
if (userSettings.getSettings() != null) {
|
if (userSettings.getSettings() != null) {
|
||||||
this.settings = userSettings.getSettings();
|
this.settings = userSettings.getSettings();
|
||||||
}
|
}
|
||||||
@ -57,6 +64,7 @@ public class UserSettingsEntity implements ToData<UserSettings> {
|
|||||||
public UserSettings toData() {
|
public UserSettings toData() {
|
||||||
UserSettings userSettings = new UserSettings();
|
UserSettings userSettings = new UserSettings();
|
||||||
userSettings.setUserId(new UserId(userId));
|
userSettings.setUserId(new UserId(userId));
|
||||||
|
userSettings.setType(type);
|
||||||
if (settings != null) {
|
if (settings != null) {
|
||||||
userSettings.setSettings(settings);
|
userSettings.setSettings(settings);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,7 +20,8 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserSettingsCompositeKey;
|
||||||
import org.thingsboard.server.dao.DaoUtil;
|
import org.thingsboard.server.dao.DaoUtil;
|
||||||
import org.thingsboard.server.dao.model.sql.UserSettingsEntity;
|
import org.thingsboard.server.dao.model.sql.UserSettingsEntity;
|
||||||
import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
|
import org.thingsboard.server.dao.sql.JpaAbstractDaoListeningExecutorService;
|
||||||
@ -41,13 +42,13 @@ public class JpaUserSettingsDao extends JpaAbstractDaoListeningExecutorService i
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserSettings findById(TenantId tenantId, UserId userId) {
|
public UserSettings findById(TenantId tenantId, UserId userId, String type) {
|
||||||
return DaoUtil.getData(userSettingsRepository.findById(userId.getId()));
|
return DaoUtil.getData(userSettingsRepository.findById(new UserSettingsCompositeKey(userId.getId(), type)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeById(TenantId tenantId, UserId userId) {
|
public void removeById(TenantId tenantId, UserId userId, String type) {
|
||||||
userSettingsRepository.deleteById(userId.getId());
|
userSettingsRepository.deleteById(new UserSettingsCompositeKey(userId.getId(), type));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,10 +16,11 @@
|
|||||||
package org.thingsboard.server.dao.sql.user;
|
package org.thingsboard.server.dao.sql.user;
|
||||||
|
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserSettingsCompositeKey;
|
||||||
import org.thingsboard.server.dao.model.sql.UserSettingsEntity;
|
import org.thingsboard.server.dao.model.sql.UserSettingsEntity;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
public interface UserSettingsRepository extends JpaRepository<UserSettingsEntity, UUID> {
|
public interface UserSettingsRepository extends JpaRepository<UserSettingsEntity, UserSettingsCompositeKey> {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,14 +20,13 @@ import org.springframework.cache.CacheManager;
|
|||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
|
import org.thingsboard.server.cache.CaffeineTbTransactionalCache;
|
||||||
import org.thingsboard.server.common.data.CacheConstants;
|
import org.thingsboard.server.common.data.CacheConstants;
|
||||||
import org.thingsboard.server.common.data.asset.Asset;
|
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
import org.thingsboard.server.dao.asset.AssetCacheKey;
|
import org.thingsboard.server.common.data.settings.UserSettingsCompositeKey;
|
||||||
|
|
||||||
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
|
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true)
|
||||||
@Service("UserSettingsCache")
|
@Service("UserSettingsCache")
|
||||||
public class UserSettingsCaffeineCache extends CaffeineTbTransactionalCache<UserId, UserSettings> {
|
public class UserSettingsCaffeineCache extends CaffeineTbTransactionalCache<UserSettingsCompositeKey, UserSettings> {
|
||||||
|
|
||||||
public UserSettingsCaffeineCache(CacheManager cacheManager) {
|
public UserSettingsCaffeineCache(CacheManager cacheManager) {
|
||||||
super(cacheManager, CacheConstants.USER_SETTINGS_CACHE);
|
super(cacheManager, CacheConstants.USER_SETTINGS_CACHE);
|
||||||
|
|||||||
@ -17,14 +17,15 @@ package org.thingsboard.server.dao.user;
|
|||||||
|
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserSettingsCompositeKey;
|
||||||
|
|
||||||
public interface UserSettingsDao {
|
public interface UserSettingsDao {
|
||||||
|
|
||||||
UserSettings save(TenantId tenantId, UserSettings userSettings);
|
UserSettings save(TenantId tenantId, UserSettings userSettings);
|
||||||
|
|
||||||
UserSettings findById(TenantId tenantId, UserId userId);
|
UserSettings findById(TenantId tenantId, UserSettingsCompositeKey key);
|
||||||
|
|
||||||
void removeById(TenantId tenantId, UserId userId);
|
void removeById(TenantId tenantId, UserSettingsCompositeKey key);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,8 +17,9 @@ package org.thingsboard.server.dao.user;
|
|||||||
|
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserSettingsCompositeKey;
|
||||||
|
|
||||||
@Data
|
@Data
|
||||||
public class UserSettingsEvictEvent {
|
public class UserSettingsEvictEvent {
|
||||||
private final UserId userId;
|
private final UserSettingsCompositeKey key;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -23,14 +23,13 @@ import org.thingsboard.server.cache.RedisTbTransactionalCache;
|
|||||||
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
|
import org.thingsboard.server.cache.TBRedisCacheConfiguration;
|
||||||
import org.thingsboard.server.cache.TbFSTRedisSerializer;
|
import org.thingsboard.server.cache.TbFSTRedisSerializer;
|
||||||
import org.thingsboard.server.common.data.CacheConstants;
|
import org.thingsboard.server.common.data.CacheConstants;
|
||||||
import org.thingsboard.server.common.data.asset.Asset;
|
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
import org.thingsboard.server.dao.asset.AssetCacheKey;
|
import org.thingsboard.server.common.data.settings.UserSettingsCompositeKey;
|
||||||
|
|
||||||
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
|
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis")
|
||||||
@Service("UserSettingsCache")
|
@Service("UserSettingsCache")
|
||||||
public class UserSettingsRedisCache extends RedisTbTransactionalCache<UserId, UserSettings> {
|
public class UserSettingsRedisCache extends RedisTbTransactionalCache<UserSettingsCompositeKey, UserSettings> {
|
||||||
|
|
||||||
public UserSettingsRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
|
public UserSettingsRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) {
|
||||||
super(CacheConstants.USER_SETTINGS_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>());
|
super(CacheConstants.USER_SETTINGS_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>());
|
||||||
|
|||||||
@ -15,38 +15,35 @@
|
|||||||
*/
|
*/
|
||||||
package org.thingsboard.server.dao.user;
|
package org.thingsboard.server.dao.user;
|
||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
|
||||||
import com.fasterxml.jackson.databind.JsonNode;
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import com.github.fge.jackson.NodeType;
|
|
||||||
import com.jayway.jsonpath.DocumentContext;
|
import com.jayway.jsonpath.DocumentContext;
|
||||||
import com.jayway.jsonpath.JsonPath;
|
import com.jayway.jsonpath.JsonPath;
|
||||||
import com.jayway.jsonpath.PathNotFoundException;
|
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
import org.springframework.transaction.event.TransactionalEventListener;
|
import org.springframework.transaction.event.TransactionalEventListener;
|
||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
|
import org.thingsboard.server.common.data.settings.UserSettingsCompositeKey;
|
||||||
import org.thingsboard.server.dao.entity.AbstractCachedService;
|
import org.thingsboard.server.dao.entity.AbstractCachedService;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
|
import org.thingsboard.server.dao.service.ConstraintValidator;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static org.thingsboard.server.dao.service.Validator.validateId;
|
import static org.thingsboard.server.dao.service.Validator.validateId;
|
||||||
|
|
||||||
@Service("UserSettingsDaoService")
|
@Service("UserSettingsDaoService")
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class UserSettingsServiceImpl extends AbstractCachedService<UserId, UserSettings, UserSettingsEvictEvent> implements UserSettingsService {
|
public class UserSettingsServiceImpl extends AbstractCachedService<UserSettingsCompositeKey, UserSettings, UserSettingsEvictEvent> implements UserSettingsService {
|
||||||
public static final String INCORRECT_USER_ID = "Incorrect userId ";
|
public static final String INCORRECT_USER_ID = "Incorrect userId ";
|
||||||
private final UserSettingsDao userSettingsDao;
|
private final UserSettingsDao userSettingsDao;
|
||||||
|
|
||||||
@ -58,33 +55,37 @@ public class UserSettingsServiceImpl extends AbstractCachedService<UserId, UserS
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void updateUserSettings(TenantId tenantId, UserId userId, JsonNode settings) {
|
public void updateUserSettings(TenantId tenantId, UserId userId, String type, JsonNode settings) {
|
||||||
log.trace("Executing updateUserSettings for user [{}], [{}]", userId, settings);
|
log.trace("Executing updateUserSettings for user [{}], [{}]", userId, settings);
|
||||||
validateId(userId, INCORRECT_USER_ID + userId);
|
validateId(userId, INCORRECT_USER_ID + userId);
|
||||||
|
|
||||||
UserSettings oldSettings = userSettingsDao.findById(tenantId, userId);
|
var key = new UserSettingsCompositeKey(userId.getId(), type);
|
||||||
|
UserSettings oldSettings = userSettingsDao.findById(tenantId, key);
|
||||||
JsonNode oldSettingsJson = oldSettings != null ? oldSettings.getSettings() : JacksonUtil.newObjectNode();
|
JsonNode oldSettingsJson = oldSettings != null ? oldSettings.getSettings() : JacksonUtil.newObjectNode();
|
||||||
|
|
||||||
UserSettings newUserSettings = new UserSettings();
|
UserSettings newUserSettings = new UserSettings();
|
||||||
newUserSettings.setUserId(userId);
|
newUserSettings.setUserId(userId);
|
||||||
|
newUserSettings.setType(type);
|
||||||
newUserSettings.setSettings(update(oldSettingsJson, settings));
|
newUserSettings.setSettings(update(oldSettingsJson, settings));
|
||||||
doSaveUserSettings(tenantId, newUserSettings);
|
doSaveUserSettings(tenantId, newUserSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserSettings findUserSettings(TenantId tenantId, UserId userId) {
|
public UserSettings findUserSettings(TenantId tenantId, UserId userId, String type) {
|
||||||
log.trace("Executing findUserSettings for user [{}]", userId);
|
log.trace("Executing findUserSettings for user [{}]", userId);
|
||||||
validateId(userId, INCORRECT_USER_ID + userId);
|
validateId(userId, INCORRECT_USER_ID + userId);
|
||||||
|
|
||||||
return cache.getAndPutInTransaction(userId,
|
var key = new UserSettingsCompositeKey(userId.getId(), type);
|
||||||
() -> userSettingsDao.findById(tenantId, userId), true);
|
return cache.getAndPutInTransaction(key,
|
||||||
|
() -> userSettingsDao.findById(tenantId, key), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deleteUserSettings(TenantId tenantId, UserId userId, List<String> jsonPaths) {
|
public void deleteUserSettings(TenantId tenantId, UserId userId, String type, List<String> jsonPaths) {
|
||||||
log.trace("Executing deleteUserSettings for user [{}]", userId);
|
log.trace("Executing deleteUserSettings for user [{}]", userId);
|
||||||
validateId(userId, INCORRECT_USER_ID + userId);
|
validateId(userId, INCORRECT_USER_ID + userId);
|
||||||
UserSettings userSettings = userSettingsDao.findById(tenantId, userId);
|
var key = new UserSettingsCompositeKey(userId.getId(), type);
|
||||||
|
UserSettings userSettings = userSettingsDao.findById(tenantId, key);
|
||||||
if (userSettings == null) {
|
if (userSettings == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -95,7 +96,7 @@ public class UserSettingsServiceImpl extends AbstractCachedService<UserId, UserS
|
|||||||
}
|
}
|
||||||
userSettings.setSettings(new ObjectMapper().readValue(dcSettings.jsonString(), ObjectNode.class));
|
userSettings.setSettings(new ObjectMapper().readValue(dcSettings.jsonString(), ObjectNode.class));
|
||||||
} catch (Exception t) {
|
} catch (Exception t) {
|
||||||
handleEvictEvent(new UserSettingsEvictEvent(userSettings.getUserId()));
|
handleEvictEvent(new UserSettingsEvictEvent(key));
|
||||||
throw new RuntimeException(t);
|
throw new RuntimeException(t);
|
||||||
}
|
}
|
||||||
doSaveUserSettings(tenantId, userSettings);
|
doSaveUserSettings(tenantId, userSettings);
|
||||||
@ -103,12 +104,13 @@ public class UserSettingsServiceImpl extends AbstractCachedService<UserId, UserS
|
|||||||
|
|
||||||
private UserSettings doSaveUserSettings(TenantId tenantId, UserSettings userSettings) {
|
private UserSettings doSaveUserSettings(TenantId tenantId, UserSettings userSettings) {
|
||||||
try {
|
try {
|
||||||
|
ConstraintValidator.validateFields(userSettings);
|
||||||
validateJsonKeys(userSettings.getSettings());
|
validateJsonKeys(userSettings.getSettings());
|
||||||
UserSettings saved = userSettingsDao.save(tenantId, userSettings);
|
UserSettings saved = userSettingsDao.save(tenantId, userSettings);
|
||||||
publishEvictEvent(new UserSettingsEvictEvent(userSettings.getUserId()));
|
publishEvictEvent(new UserSettingsEvictEvent(new UserSettingsCompositeKey(userSettings)));
|
||||||
return saved;
|
return saved;
|
||||||
} catch (Exception t) {
|
} catch (Exception t) {
|
||||||
handleEvictEvent(new UserSettingsEvictEvent(userSettings.getUserId()));
|
handleEvictEvent(new UserSettingsEvictEvent(new UserSettingsCompositeKey(userSettings)));
|
||||||
throw t;
|
throw t;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,9 +118,7 @@ public class UserSettingsServiceImpl extends AbstractCachedService<UserId, UserS
|
|||||||
@TransactionalEventListener(classes = UserSettingsEvictEvent.class)
|
@TransactionalEventListener(classes = UserSettingsEvictEvent.class)
|
||||||
@Override
|
@Override
|
||||||
public void handleEvictEvent(UserSettingsEvictEvent event) {
|
public void handleEvictEvent(UserSettingsEvictEvent event) {
|
||||||
List<UserId> keys = new ArrayList<>();
|
cache.evict(event.getKey());
|
||||||
keys.add(event.getUserId());
|
|
||||||
cache.evict(keys);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateJsonKeys(JsonNode userSettings) {
|
private void validateJsonKeys(JsonNode userSettings) {
|
||||||
|
|||||||
@ -850,9 +850,11 @@ CREATE TABLE IF NOT EXISTS notification (
|
|||||||
) PARTITION BY RANGE (created_time);
|
) PARTITION BY RANGE (created_time);
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS user_settings (
|
CREATE TABLE IF NOT EXISTS user_settings (
|
||||||
user_id uuid NOT NULL CONSTRAINT user_settings_pkey PRIMARY KEY,
|
user_id uuid NOT NULL,
|
||||||
|
type VARCHAR(50) NOT NULL,
|
||||||
settings varchar(10000),
|
settings varchar(10000),
|
||||||
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE
|
CONSTRAINT fk_user_id FOREIGN KEY (user_id) REFERENCES tb_user(id) ON DELETE CASCADE,
|
||||||
|
CONSTRAINT user_settings_pkey PRIMARY KEY (user_id, type)
|
||||||
);
|
);
|
||||||
|
|
||||||
DROP VIEW IF EXISTS alarm_info CASCADE;
|
DROP VIEW IF EXISTS alarm_info CASCADE;
|
||||||
|
|||||||
@ -23,7 +23,6 @@ import org.springframework.beans.factory.annotation.Autowired;
|
|||||||
import org.thingsboard.common.util.JacksonUtil;
|
import org.thingsboard.common.util.JacksonUtil;
|
||||||
import org.thingsboard.server.common.data.Customer;
|
import org.thingsboard.server.common.data.Customer;
|
||||||
import org.thingsboard.server.common.data.StringUtils;
|
import org.thingsboard.server.common.data.StringUtils;
|
||||||
import org.thingsboard.server.common.data.Tenant;
|
|
||||||
import org.thingsboard.server.common.data.User;
|
import org.thingsboard.server.common.data.User;
|
||||||
import org.thingsboard.server.common.data.id.CustomerId;
|
import org.thingsboard.server.common.data.id.CustomerId;
|
||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
@ -32,7 +31,7 @@ import org.thingsboard.server.common.data.page.PageData;
|
|||||||
import org.thingsboard.server.common.data.page.PageLink;
|
import org.thingsboard.server.common.data.page.PageLink;
|
||||||
import org.thingsboard.server.common.data.security.Authority;
|
import org.thingsboard.server.common.data.security.Authority;
|
||||||
import org.thingsboard.server.common.data.security.UserCredentials;
|
import org.thingsboard.server.common.data.security.UserCredentials;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
import org.thingsboard.server.dao.customer.CustomerService;
|
import org.thingsboard.server.dao.customer.CustomerService;
|
||||||
import org.thingsboard.server.dao.exception.DataValidationException;
|
import org.thingsboard.server.dao.exception.DataValidationException;
|
||||||
import org.thingsboard.server.dao.user.UserService;
|
import org.thingsboard.server.dao.user.UserService;
|
||||||
|
|||||||
@ -27,7 +27,7 @@ import org.thingsboard.server.common.data.id.CustomerId;
|
|||||||
import org.thingsboard.server.common.data.id.TenantId;
|
import org.thingsboard.server.common.data.id.TenantId;
|
||||||
import org.thingsboard.server.common.data.id.UserId;
|
import org.thingsboard.server.common.data.id.UserId;
|
||||||
import org.thingsboard.server.common.data.security.Authority;
|
import org.thingsboard.server.common.data.security.Authority;
|
||||||
import org.thingsboard.server.common.data.security.UserSettings;
|
import org.thingsboard.server.common.data.settings.UserSettings;
|
||||||
import org.thingsboard.server.dao.AbstractJpaDaoTest;
|
import org.thingsboard.server.dao.AbstractJpaDaoTest;
|
||||||
import org.thingsboard.server.dao.service.AbstractServiceTest;
|
import org.thingsboard.server.dao.service.AbstractServiceTest;
|
||||||
import org.thingsboard.server.dao.user.UserDao;
|
import org.thingsboard.server.dao.user.UserDao;
|
||||||
@ -66,17 +66,18 @@ public class JpaUserSettingsDaoTest extends AbstractJpaDaoTest {
|
|||||||
public void testFindSettingsByUserId() {
|
public void testFindSettingsByUserId() {
|
||||||
UserSettings userSettings = createUserSettings(user.getId());
|
UserSettings userSettings = createUserSettings(user.getId());
|
||||||
|
|
||||||
UserSettings retrievedUserSettings = userSettingsDao.findById(SYSTEM_TENANT_ID, user.getId());
|
UserSettings retrievedUserSettings = userSettingsDao.findById(SYSTEM_TENANT_ID, user.getId(), UserSettings.GENERAL);
|
||||||
assertEquals(retrievedUserSettings.getSettings(), userSettings.getSettings());
|
assertEquals(retrievedUserSettings.getSettings(), userSettings.getSettings());
|
||||||
|
|
||||||
userSettingsDao.removeById(SYSTEM_TENANT_ID, user.getId());
|
userSettingsDao.removeById(SYSTEM_TENANT_ID, user.getId(), UserSettings.GENERAL);
|
||||||
|
|
||||||
UserSettings retrievedUserSettings2 = userSettingsDao.findById(SYSTEM_TENANT_ID, user.getId());
|
UserSettings retrievedUserSettings2 = userSettingsDao.findById(SYSTEM_TENANT_ID, user.getId(), UserSettings.GENERAL);
|
||||||
assertNull(retrievedUserSettings2);
|
assertNull(retrievedUserSettings2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private UserSettings createUserSettings(UserId userId) {
|
private UserSettings createUserSettings(UserId userId) {
|
||||||
UserSettings userSettings = new UserSettings();
|
UserSettings userSettings = new UserSettings();
|
||||||
|
userSettings.setType(UserSettings.GENERAL);
|
||||||
userSettings.setSettings(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10)));
|
userSettings.setSettings(JacksonUtil.newObjectNode().put("text", RandomStringUtils.randomAlphanumeric(10)));
|
||||||
userSettings.setUserId(userId);
|
userSettings.setUserId(userId);
|
||||||
return userSettingsDao.save(SYSTEM_TENANT_ID, userSettings);
|
return userSettingsDao.save(SYSTEM_TENANT_ID, userSettings);
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user