169 changed files with 6321 additions and 266 deletions
@ -0,0 +1,21 @@ |
|||||
|
-- |
||||
|
-- Copyright © 2016-2022 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. |
||||
|
-- |
||||
|
|
||||
|
DROP PROCEDURE IF EXISTS update_asset_profiles; |
||||
|
|
||||
|
ALTER TABLE asset ALTER COLUMN asset_profile_id SET NOT NULL; |
||||
|
ALTER TABLE asset DROP CONSTRAINT IF EXISTS fk_asset_profile; |
||||
|
ALTER TABLE asset ADD CONSTRAINT fk_asset_profile FOREIGN KEY (asset_profile_id) REFERENCES asset_profile(id); |
||||
@ -0,0 +1,45 @@ |
|||||
|
-- |
||||
|
-- Copyright © 2016-2022 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. |
||||
|
-- |
||||
|
|
||||
|
CREATE TABLE IF NOT EXISTS asset_profile ( |
||||
|
id uuid NOT NULL CONSTRAINT asset_profile_pkey PRIMARY KEY, |
||||
|
created_time bigint NOT NULL, |
||||
|
name varchar(255), |
||||
|
image varchar(1000000), |
||||
|
description varchar, |
||||
|
search_text varchar(255), |
||||
|
is_default boolean, |
||||
|
tenant_id uuid, |
||||
|
default_rule_chain_id uuid, |
||||
|
default_dashboard_id uuid, |
||||
|
default_queue_name varchar(255), |
||||
|
external_id uuid, |
||||
|
CONSTRAINT asset_profile_name_unq_key UNIQUE (tenant_id, name), |
||||
|
CONSTRAINT asset_profile_external_id_unq_key UNIQUE (tenant_id, external_id), |
||||
|
CONSTRAINT fk_default_rule_chain_asset_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id), |
||||
|
CONSTRAINT fk_default_dashboard_asset_profile FOREIGN KEY (default_dashboard_id) REFERENCES dashboard(id) |
||||
|
); |
||||
|
|
||||
|
CREATE OR REPLACE PROCEDURE update_asset_profiles() |
||||
|
LANGUAGE plpgsql AS |
||||
|
$$ |
||||
|
BEGIN |
||||
|
UPDATE asset as a SET asset_profile_id = p.id |
||||
|
FROM |
||||
|
(SELECT id, tenant_id, name from asset_profile) as p |
||||
|
WHERE a.asset_profile_id IS NULL AND p.tenant_id = a.tenant_id AND a.type = p.name; |
||||
|
END; |
||||
|
$$; |
||||
@ -0,0 +1,227 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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 io.swagger.annotations.ApiOperation; |
||||
|
import io.swagger.annotations.ApiParam; |
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.http.HttpStatus; |
||||
|
import org.springframework.security.access.prepost.PreAuthorize; |
||||
|
import org.springframework.web.bind.annotation.PathVariable; |
||||
|
import org.springframework.web.bind.annotation.RequestBody; |
||||
|
import org.springframework.web.bind.annotation.RequestMapping; |
||||
|
import org.springframework.web.bind.annotation.RequestMethod; |
||||
|
import org.springframework.web.bind.annotation.RequestParam; |
||||
|
import org.springframework.web.bind.annotation.ResponseBody; |
||||
|
import org.springframework.web.bind.annotation.ResponseStatus; |
||||
|
import org.springframework.web.bind.annotation.RestController; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfileInfo; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.timeseries.TimeseriesService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.entitiy.asset.profile.TbAssetProfileService; |
||||
|
import org.thingsboard.server.service.security.permission.Operation; |
||||
|
import org.thingsboard.server.service.security.permission.Resource; |
||||
|
|
||||
|
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_ID; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_ID_PARAM_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_INFO_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.NEW_LINE; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_DATA_PARAMETERS; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_NUMBER_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.PAGE_SIZE_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_ALLOWABLE_VALUES; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_ORDER_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.SORT_PROPERTY_DESCRIPTION; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.TENANT_AUTHORITY_PARAGRAPH; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH; |
||||
|
import static org.thingsboard.server.controller.ControllerConstants.UUID_WIKI_LINK; |
||||
|
|
||||
|
@RestController |
||||
|
@TbCoreComponent |
||||
|
@RequestMapping("/api") |
||||
|
@RequiredArgsConstructor |
||||
|
@Slf4j |
||||
|
public class AssetProfileController extends BaseController { |
||||
|
|
||||
|
private final TbAssetProfileService tbAssetProfileService; |
||||
|
|
||||
|
@ApiOperation(value = "Get Asset Profile (getAssetProfileById)", |
||||
|
notes = "Fetch the Asset Profile object based on the provided Asset Profile Id. " + |
||||
|
"The server checks that the asset profile is owned by the same tenant. " + TENANT_AUTHORITY_PARAGRAPH, |
||||
|
produces = "application/json") |
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/assetProfile/{assetProfileId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public AssetProfile getAssetProfileById( |
||||
|
@ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION) |
||||
|
@PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException { |
||||
|
checkParameter(ASSET_PROFILE_ID, strAssetProfileId); |
||||
|
try { |
||||
|
AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); |
||||
|
return checkAssetProfileId(assetProfileId, Operation.READ); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Get Asset Profile Info (getAssetProfileInfoById)", |
||||
|
notes = "Fetch the Asset Profile Info object based on the provided Asset Profile Id. " |
||||
|
+ ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, |
||||
|
produces = "application/json") |
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/assetProfileInfo/{assetProfileId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public AssetProfileInfo getAssetProfileInfoById( |
||||
|
@ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION) |
||||
|
@PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException { |
||||
|
checkParameter(ASSET_PROFILE_ID, strAssetProfileId); |
||||
|
try { |
||||
|
AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); |
||||
|
return new AssetProfileInfo(checkAssetProfileId(assetProfileId, Operation.READ)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Get Default Asset Profile (getDefaultAssetProfileInfo)", |
||||
|
notes = "Fetch the Default Asset Profile Info object. " + |
||||
|
ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, |
||||
|
produces = "application/json") |
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/assetProfileInfo/default", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public AssetProfileInfo getDefaultAssetProfileInfo() throws ThingsboardException { |
||||
|
try { |
||||
|
return checkNotNull(assetProfileService.findDefaultAssetProfileInfo(getTenantId())); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Create Or Update Asset Profile (saveAssetProfile)", |
||||
|
notes = "Create or update the Asset Profile. When creating asset profile, platform generates asset profile id as " + UUID_WIKI_LINK + |
||||
|
"The newly created asset profile id will be present in the response. " + |
||||
|
"Specify existing asset profile id to update the asset profile. " + |
||||
|
"Referencing non-existing asset profile Id will cause 'Not Found' error. " + NEW_LINE + |
||||
|
"Asset profile name is unique in the scope of tenant. Only one 'default' asset profile may exist in scope of tenant. " + |
||||
|
"Remove 'id', 'tenantId' from the request body example (below) to create new Asset Profile entity. " + |
||||
|
TENANT_AUTHORITY_PARAGRAPH, |
||||
|
produces = "application/json", |
||||
|
consumes = "application/json") |
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/assetProfile", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public AssetProfile saveAssetProfile( |
||||
|
@ApiParam(value = "A JSON value representing the asset profile.") |
||||
|
@RequestBody AssetProfile assetProfile) throws Exception { |
||||
|
assetProfile.setTenantId(getTenantId()); |
||||
|
checkEntity(assetProfile.getId(), assetProfile, Resource.ASSET_PROFILE); |
||||
|
return tbAssetProfileService.save(assetProfile, getCurrentUser()); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Delete asset profile (deleteAssetProfile)", |
||||
|
notes = "Deletes the asset profile. Referencing non-existing asset profile Id will cause an error. " + |
||||
|
"Can't delete the asset profile if it is referenced by existing assets." + TENANT_AUTHORITY_PARAGRAPH, |
||||
|
produces = "application/json") |
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/assetProfile/{assetProfileId}", method = RequestMethod.DELETE) |
||||
|
@ResponseStatus(value = HttpStatus.OK) |
||||
|
public void deleteAssetProfile( |
||||
|
@ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION) |
||||
|
@PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException { |
||||
|
checkParameter(ASSET_PROFILE_ID, strAssetProfileId); |
||||
|
AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); |
||||
|
AssetProfile assetProfile = checkAssetProfileId(assetProfileId, Operation.DELETE); |
||||
|
tbAssetProfileService.delete(assetProfile, getCurrentUser()); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Make Asset Profile Default (setDefaultAssetProfile)", |
||||
|
notes = "Marks asset profile as default within a tenant scope." + TENANT_AUTHORITY_PARAGRAPH, |
||||
|
produces = "application/json") |
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/assetProfile/{assetProfileId}/default", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public AssetProfile setDefaultAssetProfile( |
||||
|
@ApiParam(value = ASSET_PROFILE_ID_PARAM_DESCRIPTION) |
||||
|
@PathVariable(ASSET_PROFILE_ID) String strAssetProfileId) throws ThingsboardException { |
||||
|
checkParameter(ASSET_PROFILE_ID, strAssetProfileId); |
||||
|
AssetProfileId assetProfileId = new AssetProfileId(toUUID(strAssetProfileId)); |
||||
|
AssetProfile assetProfile = checkAssetProfileId(assetProfileId, Operation.WRITE); |
||||
|
AssetProfile previousDefaultAssetProfile = assetProfileService.findDefaultAssetProfile(getTenantId()); |
||||
|
return tbAssetProfileService.setDefaultAssetProfile(assetProfile, previousDefaultAssetProfile, getCurrentUser()); |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Get Asset Profiles (getAssetProfiles)", |
||||
|
notes = "Returns a page of asset profile objects owned by tenant. " + |
||||
|
PAGE_DATA_PARAMETERS + TENANT_AUTHORITY_PARAGRAPH, |
||||
|
produces = "application/json") |
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/assetProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<AssetProfile> getAssetProfiles( |
||||
|
@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) |
||||
|
@RequestParam int pageSize, |
||||
|
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) |
||||
|
@RequestParam int page, |
||||
|
@ApiParam(value = ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION) |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ASSET_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES) |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return checkNotNull(assetProfileService.findAssetProfiles(getTenantId(), pageLink)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@ApiOperation(value = "Get Asset Profile infos (getAssetProfileInfos)", |
||||
|
notes = "Returns a page of asset profile info objects owned by tenant. " + |
||||
|
PAGE_DATA_PARAMETERS + ASSET_PROFILE_INFO_DESCRIPTION + TENANT_OR_CUSTOMER_AUTHORITY_PARAGRAPH, |
||||
|
produces = "application/json") |
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/assetProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<AssetProfileInfo> getAssetProfileInfos( |
||||
|
@ApiParam(value = PAGE_SIZE_DESCRIPTION, required = true) |
||||
|
@RequestParam int pageSize, |
||||
|
@ApiParam(value = PAGE_NUMBER_DESCRIPTION, required = true) |
||||
|
@RequestParam int page, |
||||
|
@ApiParam(value = ASSET_PROFILE_TEXT_SEARCH_DESCRIPTION) |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@ApiParam(value = SORT_PROPERTY_DESCRIPTION, allowableValues = ASSET_PROFILE_SORT_PROPERTY_ALLOWABLE_VALUES) |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@ApiParam(value = SORT_ORDER_DESCRIPTION, allowableValues = SORT_ORDER_ALLOWABLE_VALUES) |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return checkNotNull(assetProfileService.findAssetProfileInfos(getTenantId(), pageLink)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,67 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.edge.rpc.constructor; |
||||
|
|
||||
|
import com.google.protobuf.ByteString; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.DataDecodingEncodingService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
import java.nio.charset.StandardCharsets; |
||||
|
|
||||
|
@Component |
||||
|
@TbCoreComponent |
||||
|
public class AssetProfileMsgConstructor { |
||||
|
|
||||
|
@Autowired |
||||
|
private DataDecodingEncodingService dataDecodingEncodingService; |
||||
|
|
||||
|
public AssetProfileUpdateMsg constructAssetProfileUpdatedMsg(UpdateMsgType msgType, AssetProfile assetProfile) { |
||||
|
AssetProfileUpdateMsg.Builder builder = AssetProfileUpdateMsg.newBuilder() |
||||
|
.setMsgType(msgType) |
||||
|
.setIdMSB(assetProfile.getId().getId().getMostSignificantBits()) |
||||
|
.setIdLSB(assetProfile.getId().getId().getLeastSignificantBits()) |
||||
|
.setName(assetProfile.getName()) |
||||
|
.setDefault(assetProfile.isDefault()); |
||||
|
if (assetProfile.getDefaultRuleChainId() != null) { |
||||
|
builder.setDefaultRuleChainIdMSB(assetProfile.getDefaultRuleChainId().getId().getMostSignificantBits()) |
||||
|
.setDefaultRuleChainIdLSB(assetProfile.getDefaultRuleChainId().getId().getLeastSignificantBits()); |
||||
|
} |
||||
|
if (assetProfile.getDefaultQueueName() != null) { |
||||
|
builder.setDefaultQueueName(assetProfile.getDefaultQueueName()); |
||||
|
} |
||||
|
if (assetProfile.getDescription() != null) { |
||||
|
builder.setDescription(assetProfile.getDescription()); |
||||
|
} |
||||
|
if (assetProfile.getImage() != null) { |
||||
|
builder.setImage(ByteString.copyFrom(assetProfile.getImage().getBytes(StandardCharsets.UTF_8))); |
||||
|
} |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
public AssetProfileUpdateMsg constructAssetProfileDeleteMsg(AssetProfileId assetProfileId) { |
||||
|
return AssetProfileUpdateMsg.newBuilder() |
||||
|
.setMsgType(UpdateMsgType.ENTITY_DELETED_RPC_MESSAGE) |
||||
|
.setIdMSB(assetProfileId.getId().getMostSignificantBits()) |
||||
|
.setIdLSB(assetProfileId.getId().getLeastSignificantBits()).build(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,47 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.edge.rpc.fetch; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.server.common.data.EdgeUtils; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.edge.Edge; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventType; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.asset.AssetProfileService; |
||||
|
|
||||
|
@AllArgsConstructor |
||||
|
@Slf4j |
||||
|
public class AssetProfilesEdgeEventFetcher extends BasePageableEdgeEventFetcher<AssetProfile> { |
||||
|
|
||||
|
private final AssetProfileService assetProfileService; |
||||
|
|
||||
|
@Override |
||||
|
PageData<AssetProfile> fetchPageData(TenantId tenantId, Edge edge, PageLink pageLink) { |
||||
|
return assetProfileService.findAssetProfiles(tenantId, pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
EdgeEvent constructEdgeEvent(TenantId tenantId, Edge edge, AssetProfile assetProfile) { |
||||
|
return EdgeUtils.constructEdgeEvent(tenantId, edge.getId(), EdgeEventType.ASSET_PROFILE, |
||||
|
EdgeEventActionType.ADDED, assetProfile.getId(), null); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.edge.rpc.processor; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.EdgeUtils; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEvent; |
||||
|
import org.thingsboard.server.common.data.edge.EdgeEventActionType; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.gen.edge.v1.AssetProfileUpdateMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.DownlinkMsg; |
||||
|
import org.thingsboard.server.gen.edge.v1.UpdateMsgType; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
|
||||
|
@Component |
||||
|
@Slf4j |
||||
|
@TbCoreComponent |
||||
|
public class AssetProfileEdgeProcessor extends BaseEdgeProcessor { |
||||
|
|
||||
|
public DownlinkMsg processAssetProfileToEdge(EdgeEvent edgeEvent, UpdateMsgType msgType, EdgeEventActionType action) { |
||||
|
AssetProfileId assetProfileId = new AssetProfileId(edgeEvent.getEntityId()); |
||||
|
DownlinkMsg downlinkMsg = null; |
||||
|
switch (action) { |
||||
|
case ADDED: |
||||
|
case UPDATED: |
||||
|
AssetProfile assetProfile = assetProfileService.findAssetProfileById(edgeEvent.getTenantId(), assetProfileId); |
||||
|
if (assetProfile != null) { |
||||
|
AssetProfileUpdateMsg assetProfileUpdateMsg = |
||||
|
assetProfileMsgConstructor.constructAssetProfileUpdatedMsg(msgType, assetProfile); |
||||
|
downlinkMsg = DownlinkMsg.newBuilder() |
||||
|
.setDownlinkMsgId(EdgeUtils.nextPositiveInt()) |
||||
|
.addAssetProfileUpdateMsg(assetProfileUpdateMsg) |
||||
|
.build(); |
||||
|
} |
||||
|
break; |
||||
|
case DELETED: |
||||
|
AssetProfileUpdateMsg assetProfileUpdateMsg = |
||||
|
assetProfileMsgConstructor.constructAssetProfileDeleteMsg(assetProfileId); |
||||
|
downlinkMsg = DownlinkMsg.newBuilder() |
||||
|
.setDownlinkMsgId(EdgeUtils.nextPositiveInt()) |
||||
|
.addAssetProfileUpdateMsg(assetProfileUpdateMsg) |
||||
|
.build(); |
||||
|
break; |
||||
|
} |
||||
|
return downlinkMsg; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,110 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset.profile; |
||||
|
|
||||
|
import lombok.AllArgsConstructor; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.audit.ActionType; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardErrorCode; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
||||
|
import org.thingsboard.server.dao.asset.AssetProfileService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.entitiy.AbstractTbEntityService; |
||||
|
|
||||
|
import static org.thingsboard.server.dao.asset.BaseAssetService.TB_SERVICE_QUEUE; |
||||
|
|
||||
|
@Service |
||||
|
@TbCoreComponent |
||||
|
@AllArgsConstructor |
||||
|
@Slf4j |
||||
|
public class DefaultTbAssetProfileService extends AbstractTbEntityService implements TbAssetProfileService { |
||||
|
|
||||
|
private final AssetProfileService assetProfileService; |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile save(AssetProfile assetProfile, User user) throws Exception { |
||||
|
ActionType actionType = assetProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED; |
||||
|
TenantId tenantId = assetProfile.getTenantId(); |
||||
|
try { |
||||
|
if (TB_SERVICE_QUEUE.equals(assetProfile.getName())) { |
||||
|
throw new ThingsboardException("Unable to save asset profile with name " + TB_SERVICE_QUEUE, ThingsboardErrorCode.BAD_REQUEST_PARAMS); |
||||
|
} else if (assetProfile.getId() != null) { |
||||
|
AssetProfile foundAssetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfile.getId()); |
||||
|
if (foundAssetProfile != null && TB_SERVICE_QUEUE.equals(foundAssetProfile.getName())) { |
||||
|
throw new ThingsboardException("Updating asset profile with name " + TB_SERVICE_QUEUE + " is prohibited!", ThingsboardErrorCode.BAD_REQUEST_PARAMS); |
||||
|
} |
||||
|
} |
||||
|
AssetProfile savedAssetProfile = checkNotNull(assetProfileService.saveAssetProfile(assetProfile)); |
||||
|
autoCommit(user, savedAssetProfile.getId()); |
||||
|
tbClusterService.broadcastEntityStateChangeEvent(tenantId, savedAssetProfile.getId(), |
||||
|
actionType.equals(ActionType.ADDED) ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); |
||||
|
|
||||
|
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, savedAssetProfile.getId(), |
||||
|
savedAssetProfile, user, actionType, true, null); |
||||
|
return savedAssetProfile; |
||||
|
} catch (Exception e) { |
||||
|
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), assetProfile, actionType, user, e); |
||||
|
throw e; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void delete(AssetProfile assetProfile, User user) { |
||||
|
AssetProfileId assetProfileId = assetProfile.getId(); |
||||
|
TenantId tenantId = assetProfile.getTenantId(); |
||||
|
try { |
||||
|
assetProfileService.deleteAssetProfile(tenantId, assetProfileId); |
||||
|
|
||||
|
tbClusterService.broadcastEntityStateChangeEvent(tenantId, assetProfileId, ComponentLifecycleEvent.DELETED); |
||||
|
notificationEntityService.notifyCreateOrUpdateOrDelete(tenantId, null, assetProfileId, assetProfile, |
||||
|
user, ActionType.DELETED, true, null, assetProfileId.toString()); |
||||
|
} catch (Exception e) { |
||||
|
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), ActionType.DELETED, |
||||
|
user, e, assetProfileId.toString()); |
||||
|
throw e; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile setDefaultAssetProfile(AssetProfile assetProfile, AssetProfile previousDefaultAssetProfile, User user) throws ThingsboardException { |
||||
|
TenantId tenantId = assetProfile.getTenantId(); |
||||
|
AssetProfileId assetProfileId = assetProfile.getId(); |
||||
|
try { |
||||
|
if (assetProfileService.setDefaultAssetProfile(tenantId, assetProfileId)) { |
||||
|
if (previousDefaultAssetProfile != null) { |
||||
|
previousDefaultAssetProfile = assetProfileService.findAssetProfileById(tenantId, previousDefaultAssetProfile.getId()); |
||||
|
notificationEntityService.logEntityAction(tenantId, previousDefaultAssetProfile.getId(), previousDefaultAssetProfile, |
||||
|
ActionType.UPDATED, user); |
||||
|
} |
||||
|
assetProfile = assetProfileService.findAssetProfileById(tenantId, assetProfileId); |
||||
|
|
||||
|
notificationEntityService.logEntityAction(tenantId, assetProfileId, assetProfile, ActionType.UPDATED, user); |
||||
|
} |
||||
|
return assetProfile; |
||||
|
} catch (Exception e) { |
||||
|
notificationEntityService.logEntityAction(tenantId, emptyId(EntityType.ASSET_PROFILE), ActionType.UPDATED, |
||||
|
user, e, assetProfileId.toString()); |
||||
|
throw e; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset.profile; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.service.entitiy.SimpleTbEntityService; |
||||
|
|
||||
|
public interface TbAssetProfileService extends SimpleTbEntityService<AssetProfile> { |
||||
|
|
||||
|
AssetProfile setDefaultAssetProfile(AssetProfile assetProfile, AssetProfile previousDefaultAssetProfile, User user) throws ThingsboardException; |
||||
|
} |
||||
@ -0,0 +1,162 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.profile; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.asset.Asset; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.id.AssetId; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.dao.asset.AssetProfileService; |
||||
|
import org.thingsboard.server.dao.asset.AssetService; |
||||
|
|
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
import java.util.concurrent.ConcurrentMap; |
||||
|
import java.util.concurrent.locks.Lock; |
||||
|
import java.util.concurrent.locks.ReentrantLock; |
||||
|
import java.util.function.BiConsumer; |
||||
|
import java.util.function.Consumer; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class DefaultTbAssetProfileCache implements TbAssetProfileCache { |
||||
|
|
||||
|
private final Lock assetProfileFetchLock = new ReentrantLock(); |
||||
|
private final AssetProfileService assetProfileService; |
||||
|
private final AssetService assetService; |
||||
|
|
||||
|
private final ConcurrentMap<AssetProfileId, AssetProfile> assetProfilesMap = new ConcurrentHashMap<>(); |
||||
|
private final ConcurrentMap<AssetId, AssetProfileId> assetsMap = new ConcurrentHashMap<>(); |
||||
|
private final ConcurrentMap<TenantId, ConcurrentMap<EntityId, Consumer<AssetProfile>>> profileListeners = new ConcurrentHashMap<>(); |
||||
|
private final ConcurrentMap<TenantId, ConcurrentMap<EntityId, BiConsumer<AssetId, AssetProfile>>> assetProfileListeners = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
public DefaultTbAssetProfileCache(AssetProfileService assetProfileService, AssetService assetService) { |
||||
|
this.assetProfileService = assetProfileService; |
||||
|
this.assetService = assetService; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile get(TenantId tenantId, AssetProfileId assetProfileId) { |
||||
|
AssetProfile profile = assetProfilesMap.get(assetProfileId); |
||||
|
if (profile == null) { |
||||
|
assetProfileFetchLock.lock(); |
||||
|
try { |
||||
|
profile = assetProfilesMap.get(assetProfileId); |
||||
|
if (profile == null) { |
||||
|
profile = assetProfileService.findAssetProfileById(tenantId, assetProfileId); |
||||
|
if (profile != null) { |
||||
|
assetProfilesMap.put(assetProfileId, profile); |
||||
|
log.debug("[{}] Fetch asset profile into cache: {}", profile.getId(), profile); |
||||
|
} |
||||
|
} |
||||
|
} finally { |
||||
|
assetProfileFetchLock.unlock(); |
||||
|
} |
||||
|
} |
||||
|
log.trace("[{}] Found asset profile in cache: {}", assetProfileId, profile); |
||||
|
return profile; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile get(TenantId tenantId, AssetId assetId) { |
||||
|
AssetProfileId profileId = assetsMap.get(assetId); |
||||
|
if (profileId == null) { |
||||
|
Asset asset = assetService.findAssetById(tenantId, assetId); |
||||
|
if (asset != null) { |
||||
|
profileId = asset.getAssetProfileId(); |
||||
|
assetsMap.put(assetId, profileId); |
||||
|
} else { |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
|
return get(tenantId, profileId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void evict(TenantId tenantId, AssetProfileId profileId) { |
||||
|
AssetProfile oldProfile = assetProfilesMap.remove(profileId); |
||||
|
log.debug("[{}] evict asset profile from cache: {}", profileId, oldProfile); |
||||
|
AssetProfile newProfile = get(tenantId, profileId); |
||||
|
if (newProfile != null) { |
||||
|
notifyProfileListeners(newProfile); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void evict(TenantId tenantId, AssetId assetId) { |
||||
|
AssetProfileId old = assetsMap.remove(assetId); |
||||
|
if (old != null) { |
||||
|
AssetProfile newProfile = get(tenantId, assetId); |
||||
|
if (newProfile == null || !old.equals(newProfile.getId())) { |
||||
|
notifyAssetListeners(tenantId, assetId, newProfile); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void addListener(TenantId tenantId, EntityId listenerId, |
||||
|
Consumer<AssetProfile> profileListener, |
||||
|
BiConsumer<AssetId, AssetProfile> assetListener) { |
||||
|
if (profileListener != null) { |
||||
|
profileListeners.computeIfAbsent(tenantId, id -> new ConcurrentHashMap<>()).put(listenerId, profileListener); |
||||
|
} |
||||
|
if (assetListener != null) { |
||||
|
assetProfileListeners.computeIfAbsent(tenantId, id -> new ConcurrentHashMap<>()).put(listenerId, assetListener); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile find(AssetProfileId assetProfileId) { |
||||
|
return assetProfileService.findAssetProfileById(TenantId.SYS_TENANT_ID, assetProfileId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile findOrCreateAssetProfile(TenantId tenantId, String profileName) { |
||||
|
return assetProfileService.findOrCreateAssetProfile(tenantId, profileName); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void removeListener(TenantId tenantId, EntityId listenerId) { |
||||
|
ConcurrentMap<EntityId, Consumer<AssetProfile>> tenantListeners = profileListeners.get(tenantId); |
||||
|
if (tenantListeners != null) { |
||||
|
tenantListeners.remove(listenerId); |
||||
|
} |
||||
|
ConcurrentMap<EntityId, BiConsumer<AssetId, AssetProfile>> assetListeners = assetProfileListeners.get(tenantId); |
||||
|
if (assetListeners != null) { |
||||
|
assetListeners.remove(listenerId); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void notifyProfileListeners(AssetProfile profile) { |
||||
|
ConcurrentMap<EntityId, Consumer<AssetProfile>> tenantListeners = profileListeners.get(profile.getTenantId()); |
||||
|
if (tenantListeners != null) { |
||||
|
tenantListeners.forEach((id, listener) -> listener.accept(profile)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
private void notifyAssetListeners(TenantId tenantId, AssetId assetId, AssetProfile profile) { |
||||
|
if (profile != null) { |
||||
|
ConcurrentMap<EntityId, BiConsumer<AssetId, AssetProfile>> tenantListeners = assetProfileListeners.get(tenantId); |
||||
|
if (tenantListeners != null) { |
||||
|
tenantListeners.forEach((id, listener) -> listener.accept(assetId, profile)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.profile; |
||||
|
|
||||
|
import org.thingsboard.rule.engine.api.RuleEngineAssetProfileCache; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.id.AssetId; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
|
||||
|
public interface TbAssetProfileCache extends RuleEngineAssetProfileCache { |
||||
|
|
||||
|
void evict(TenantId tenantId, AssetProfileId id); |
||||
|
|
||||
|
void evict(TenantId tenantId, AssetId id); |
||||
|
|
||||
|
AssetProfile find(AssetProfileId assetProfileId); |
||||
|
|
||||
|
AssetProfile findOrCreateAssetProfile(TenantId tenantId, String assetType); |
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.sync.ie.exporting.impl; |
||||
|
|
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.sync.ie.EntityExportData; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.sync.vc.data.EntitiesExportCtx; |
||||
|
|
||||
|
import java.util.Set; |
||||
|
|
||||
|
@Service |
||||
|
@TbCoreComponent |
||||
|
public class AssetProfileExportService extends BaseEntityExportService<AssetProfileId, AssetProfile, EntityExportData<AssetProfile>> { |
||||
|
|
||||
|
@Override |
||||
|
protected void setRelatedEntities(EntitiesExportCtx<?> ctx, AssetProfile assetProfile, EntityExportData<AssetProfile> exportData) { |
||||
|
assetProfile.setDefaultDashboardId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultDashboardId())); |
||||
|
assetProfile.setDefaultRuleChainId(getExternalIdOrElseInternal(ctx, assetProfile.getDefaultRuleChainId())); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public Set<EntityType> getSupportedEntityTypes() { |
||||
|
return Set.of(EntityType.ASSET_PROFILE); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,80 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.sync.ie.importing.impl; |
||||
|
|
||||
|
import lombok.RequiredArgsConstructor; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.audit.ActionType; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
||||
|
import org.thingsboard.server.common.data.sync.ie.EntityExportData; |
||||
|
import org.thingsboard.server.dao.asset.AssetProfileService; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.sync.vc.data.EntitiesImportCtx; |
||||
|
|
||||
|
@Service |
||||
|
@TbCoreComponent |
||||
|
@RequiredArgsConstructor |
||||
|
public class AssetProfileImportService extends BaseEntityImportService<AssetProfileId, AssetProfile, EntityExportData<AssetProfile>> { |
||||
|
|
||||
|
private final AssetProfileService assetProfileService; |
||||
|
|
||||
|
@Override |
||||
|
protected void setOwner(TenantId tenantId, AssetProfile assetProfile, IdProvider idProvider) { |
||||
|
assetProfile.setTenantId(tenantId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected AssetProfile prepare(EntitiesImportCtx ctx, AssetProfile assetProfile, AssetProfile old, EntityExportData<AssetProfile> exportData, IdProvider idProvider) { |
||||
|
assetProfile.setDefaultRuleChainId(idProvider.getInternalId(assetProfile.getDefaultRuleChainId())); |
||||
|
assetProfile.setDefaultDashboardId(idProvider.getInternalId(assetProfile.getDefaultDashboardId())); |
||||
|
return assetProfile; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected AssetProfile saveOrUpdate(EntitiesImportCtx ctx, AssetProfile assetProfile, EntityExportData<AssetProfile> exportData, IdProvider idProvider) { |
||||
|
return assetProfileService.saveAssetProfile(assetProfile); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void onEntitySaved(User user, AssetProfile savedAssetProfile, AssetProfile oldAssetProfile) throws ThingsboardException { |
||||
|
clusterService.broadcastEntityStateChangeEvent(user.getTenantId(), savedAssetProfile.getId(), |
||||
|
oldAssetProfile == null ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); |
||||
|
entityNotificationService.notifyCreateOrUpdateOrDelete(savedAssetProfile.getTenantId(), null, |
||||
|
savedAssetProfile.getId(), savedAssetProfile, user, oldAssetProfile == null ? ActionType.ADDED : ActionType.UPDATED, true, null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected AssetProfile deepCopy(AssetProfile assetProfile) { |
||||
|
return new AssetProfile(assetProfile); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void cleanupForComparison(AssetProfile assetProfile) { |
||||
|
super.cleanupForComparison(assetProfile); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public EntityType getEntityType() { |
||||
|
return EntityType.ASSET_PROFILE; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,452 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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 com.fasterxml.jackson.core.type.TypeReference; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.mockito.Mockito; |
||||
|
import org.springframework.boot.test.mock.mockito.SpyBean; |
||||
|
import org.thingsboard.server.common.data.Customer; |
||||
|
import org.thingsboard.server.common.data.Dashboard; |
||||
|
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.asset.Asset; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfileInfo; |
||||
|
import org.thingsboard.server.common.data.audit.ActionType; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChain; |
||||
|
import org.thingsboard.server.common.data.security.Authority; |
||||
|
import org.thingsboard.server.dao.asset.AssetProfileDao; |
||||
|
import org.thingsboard.server.dao.exception.DataValidationException; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Collections; |
||||
|
import java.util.List; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
import static org.hamcrest.Matchers.containsString; |
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
public abstract class BaseAssetProfileControllerTest extends AbstractControllerTest { |
||||
|
|
||||
|
private IdComparator<AssetProfile> idComparator = new IdComparator<>(); |
||||
|
private IdComparator<AssetProfileInfo> assetProfileInfoIdComparator = new IdComparator<>(); |
||||
|
|
||||
|
private Tenant savedTenant; |
||||
|
private User tenantAdmin; |
||||
|
|
||||
|
@SpyBean |
||||
|
private AssetProfileDao assetProfileDao; |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
|
||||
|
Tenant tenant = new Tenant(); |
||||
|
tenant.setTitle("My tenant"); |
||||
|
savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
||||
|
Assert.assertNotNull(savedTenant); |
||||
|
|
||||
|
tenantAdmin = new User(); |
||||
|
tenantAdmin.setAuthority(Authority.TENANT_ADMIN); |
||||
|
tenantAdmin.setTenantId(savedTenant.getId()); |
||||
|
tenantAdmin.setEmail("tenant2@thingsboard.org"); |
||||
|
tenantAdmin.setFirstName("Joe"); |
||||
|
tenantAdmin.setLastName("Downs"); |
||||
|
|
||||
|
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
|
||||
|
afterTestEntityDaoRemoveByIdWithException (assetProfileDao); |
||||
|
|
||||
|
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveAssetProfile() throws Exception { |
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile"); |
||||
|
|
||||
|
Mockito.reset(tbClusterService, auditLogService); |
||||
|
|
||||
|
AssetProfile savedAssetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
Assert.assertNotNull(savedAssetProfile); |
||||
|
Assert.assertNotNull(savedAssetProfile.getId()); |
||||
|
Assert.assertTrue(savedAssetProfile.getCreatedTime() > 0); |
||||
|
Assert.assertEquals(assetProfile.getName(), savedAssetProfile.getName()); |
||||
|
Assert.assertEquals(assetProfile.getDescription(), savedAssetProfile.getDescription()); |
||||
|
Assert.assertEquals(assetProfile.isDefault(), savedAssetProfile.isDefault()); |
||||
|
Assert.assertEquals(assetProfile.getDefaultRuleChainId(), savedAssetProfile.getDefaultRuleChainId()); |
||||
|
|
||||
|
testNotifyEntityBroadcastEntityStateChangeEventOneTime(savedAssetProfile, savedAssetProfile.getId(), savedAssetProfile.getId(), |
||||
|
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), |
||||
|
ActionType.ADDED); |
||||
|
|
||||
|
savedAssetProfile.setName("New asset profile"); |
||||
|
doPost("/api/assetProfile", savedAssetProfile, AssetProfile.class); |
||||
|
AssetProfile foundAssetProfile = doGet("/api/assetProfile/" + savedAssetProfile.getId().getId().toString(), AssetProfile.class); |
||||
|
Assert.assertEquals(savedAssetProfile.getName(), foundAssetProfile.getName()); |
||||
|
|
||||
|
testNotifyEntityBroadcastEntityStateChangeEventOneTime(foundAssetProfile, foundAssetProfile.getId(), foundAssetProfile.getId(), |
||||
|
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), |
||||
|
ActionType.UPDATED); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void saveAssetProfileWithViolationOfValidation() throws Exception { |
||||
|
String msgError = msgErrorFieldLength("name"); |
||||
|
|
||||
|
Mockito.reset(tbClusterService, auditLogService); |
||||
|
|
||||
|
AssetProfile createAssetProfile = this.createAssetProfile(StringUtils.randomAlphabetic(300)); |
||||
|
doPost("/api/assetProfile", createAssetProfile) |
||||
|
.andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString(msgError))); |
||||
|
|
||||
|
testNotifyEntityEqualsOneTimeServiceNeverError(createAssetProfile, savedTenant.getId(), |
||||
|
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindAssetProfileById() throws Exception { |
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile"); |
||||
|
AssetProfile savedAssetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
AssetProfile foundAssetProfile = doGet("/api/assetProfile/" + savedAssetProfile.getId().getId().toString(), AssetProfile.class); |
||||
|
Assert.assertNotNull(foundAssetProfile); |
||||
|
Assert.assertEquals(savedAssetProfile, foundAssetProfile); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void whenGetAssetProfileById_thenPermissionsAreChecked() throws Exception { |
||||
|
AssetProfile assetProfile = createAssetProfile("Asset profile 1"); |
||||
|
assetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
|
||||
|
loginDifferentTenant(); |
||||
|
|
||||
|
doGet("/api/assetProfile/" + assetProfile.getId()) |
||||
|
.andExpect(status().isForbidden()) |
||||
|
.andExpect(statusReason(containsString(msgErrorPermission))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindAssetProfileInfoById() throws Exception { |
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile"); |
||||
|
AssetProfile savedAssetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
AssetProfileInfo foundAssetProfileInfo = doGet("/api/assetProfileInfo/" + savedAssetProfile.getId().getId().toString(), AssetProfileInfo.class); |
||||
|
Assert.assertNotNull(foundAssetProfileInfo); |
||||
|
Assert.assertEquals(savedAssetProfile.getId(), foundAssetProfileInfo.getId()); |
||||
|
Assert.assertEquals(savedAssetProfile.getName(), foundAssetProfileInfo.getName()); |
||||
|
|
||||
|
Customer customer = new Customer(); |
||||
|
customer.setTitle("Customer"); |
||||
|
customer.setTenantId(savedTenant.getId()); |
||||
|
Customer savedCustomer = doPost("/api/customer", customer, Customer.class); |
||||
|
|
||||
|
User customerUser = new User(); |
||||
|
customerUser.setAuthority(Authority.CUSTOMER_USER); |
||||
|
customerUser.setTenantId(savedTenant.getId()); |
||||
|
customerUser.setCustomerId(savedCustomer.getId()); |
||||
|
customerUser.setEmail("customer2@thingsboard.org"); |
||||
|
|
||||
|
createUserAndLogin(customerUser, "customer"); |
||||
|
|
||||
|
foundAssetProfileInfo = doGet("/api/assetProfileInfo/" + savedAssetProfile.getId().getId().toString(), AssetProfileInfo.class); |
||||
|
Assert.assertNotNull(foundAssetProfileInfo); |
||||
|
Assert.assertEquals(savedAssetProfile.getId(), foundAssetProfileInfo.getId()); |
||||
|
Assert.assertEquals(savedAssetProfile.getName(), foundAssetProfileInfo.getName()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void whenGetAssetProfileInfoById_thenPermissionsAreChecked() throws Exception { |
||||
|
AssetProfile assetProfile = createAssetProfile("Asset profile 1"); |
||||
|
assetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
|
||||
|
loginDifferentTenant(); |
||||
|
doGet("/api/assetProfileInfo/" + assetProfile.getId()) |
||||
|
.andExpect(status().isForbidden()) |
||||
|
.andExpect(statusReason(containsString(msgErrorPermission))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindDefaultAssetProfileInfo() throws Exception { |
||||
|
AssetProfileInfo foundDefaultAssetProfileInfo = doGet("/api/assetProfileInfo/default", AssetProfileInfo.class); |
||||
|
Assert.assertNotNull(foundDefaultAssetProfileInfo); |
||||
|
Assert.assertNotNull(foundDefaultAssetProfileInfo.getId()); |
||||
|
Assert.assertNotNull(foundDefaultAssetProfileInfo.getName()); |
||||
|
Assert.assertEquals("default", foundDefaultAssetProfileInfo.getName()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSetDefaultAssetProfile() throws Exception { |
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile 1"); |
||||
|
AssetProfile savedAssetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
|
||||
|
Mockito.reset(tbClusterService, auditLogService); |
||||
|
|
||||
|
AssetProfile defaultAssetProfile = doPost("/api/assetProfile/" + savedAssetProfile.getId().getId().toString() + "/default", AssetProfile.class); |
||||
|
Assert.assertNotNull(defaultAssetProfile); |
||||
|
AssetProfileInfo foundDefaultAssetProfile = doGet("/api/assetProfileInfo/default", AssetProfileInfo.class); |
||||
|
Assert.assertNotNull(foundDefaultAssetProfile); |
||||
|
Assert.assertEquals(savedAssetProfile.getName(), foundDefaultAssetProfile.getName()); |
||||
|
Assert.assertEquals(savedAssetProfile.getId(), foundDefaultAssetProfile.getId()); |
||||
|
|
||||
|
testNotifyEntityOneTimeMsgToEdgeServiceNever(defaultAssetProfile, defaultAssetProfile.getId(), defaultAssetProfile.getId(), |
||||
|
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), |
||||
|
ActionType.UPDATED); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveAssetProfileWithEmptyName() throws Exception { |
||||
|
AssetProfile assetProfile = new AssetProfile(); |
||||
|
|
||||
|
Mockito.reset(tbClusterService, auditLogService); |
||||
|
|
||||
|
String msgError = "Asset profile name " + msgErrorShouldBeSpecified; |
||||
|
doPost("/api/assetProfile", assetProfile) |
||||
|
.andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString(msgError))); |
||||
|
|
||||
|
testNotifyEntityEqualsOneTimeServiceNeverError(assetProfile, savedTenant.getId(), |
||||
|
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveAssetProfileWithSameName() throws Exception { |
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile"); |
||||
|
doPost("/api/assetProfile", assetProfile).andExpect(status().isOk()); |
||||
|
AssetProfile assetProfile2 = this.createAssetProfile("Asset Profile"); |
||||
|
|
||||
|
Mockito.reset(tbClusterService, auditLogService); |
||||
|
|
||||
|
String msgError = "Asset profile with such name already exists"; |
||||
|
doPost("/api/assetProfile", assetProfile2) |
||||
|
.andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString(msgError))); |
||||
|
|
||||
|
testNotifyEntityEqualsOneTimeServiceNeverError(assetProfile, savedTenant.getId(), |
||||
|
tenantAdmin.getId(), tenantAdmin.getEmail(), ActionType.ADDED, new DataValidationException(msgError)); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteAssetProfileWithExistingAsset() throws Exception { |
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile"); |
||||
|
AssetProfile savedAssetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
|
||||
|
Asset asset = new Asset(); |
||||
|
asset.setName("Test asset"); |
||||
|
asset.setAssetProfileId(savedAssetProfile.getId()); |
||||
|
|
||||
|
doPost("/api/asset", asset, Asset.class); |
||||
|
|
||||
|
Mockito.reset(tbClusterService, auditLogService); |
||||
|
|
||||
|
doDelete("/api/assetProfile/" + savedAssetProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("The asset profile referenced by the assets cannot be deleted"))); |
||||
|
|
||||
|
testNotifyEntityNever(savedAssetProfile.getId(), savedAssetProfile); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveAssetProfileWithRuleChainFromDifferentTenant() throws Exception { |
||||
|
loginDifferentTenant(); |
||||
|
RuleChain ruleChain = new RuleChain(); |
||||
|
ruleChain.setName("Different rule chain"); |
||||
|
RuleChain savedRuleChain = doPost("/api/ruleChain", ruleChain, RuleChain.class); |
||||
|
|
||||
|
loginTenantAdmin(); |
||||
|
|
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile"); |
||||
|
assetProfile.setDefaultRuleChainId(savedRuleChain.getId()); |
||||
|
doPost("/api/assetProfile", assetProfile).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Can't assign rule chain from different tenant!"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveAssetProfileWithDashboardFromDifferentTenant() throws Exception { |
||||
|
loginDifferentTenant(); |
||||
|
Dashboard dashboard = new Dashboard(); |
||||
|
dashboard.setTitle("Different dashboard"); |
||||
|
Dashboard savedDashboard = doPost("/api/dashboard", dashboard, Dashboard.class); |
||||
|
|
||||
|
loginTenantAdmin(); |
||||
|
|
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile"); |
||||
|
assetProfile.setDefaultDashboardId(savedDashboard.getId()); |
||||
|
doPost("/api/assetProfile", assetProfile).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Can't assign dashboard from different tenant!"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteAssetProfile() throws Exception { |
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile"); |
||||
|
AssetProfile savedAssetProfile = doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
|
||||
|
Mockito.reset(tbClusterService, auditLogService); |
||||
|
|
||||
|
doDelete("/api/assetProfile/" + savedAssetProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
|
||||
|
String savedAssetProfileIdStr = savedAssetProfile.getId().getId().toString(); |
||||
|
testNotifyEntityBroadcastEntityStateChangeEventOneTime(savedAssetProfile, savedAssetProfile.getId(), savedAssetProfile.getId(), |
||||
|
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), |
||||
|
ActionType.DELETED, savedAssetProfileIdStr); |
||||
|
|
||||
|
doGet("/api/assetProfile/" + savedAssetProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isNotFound()) |
||||
|
.andExpect(statusReason(containsString(msgErrorNoFound("Asset profile", savedAssetProfileIdStr)))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindAssetProfiles() throws Exception { |
||||
|
List<AssetProfile> assetProfiles = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(17); |
||||
|
PageData<AssetProfile> pageData = doGetTypedWithPageLink("/api/assetProfiles?", |
||||
|
new TypeReference<>() { |
||||
|
}, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertEquals(1, pageData.getTotalElements()); |
||||
|
assetProfiles.addAll(pageData.getData()); |
||||
|
|
||||
|
Mockito.reset(tbClusterService, auditLogService); |
||||
|
|
||||
|
int cntEntity = 28; |
||||
|
for (int i = 0; i < cntEntity; i++) { |
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile" + i); |
||||
|
assetProfiles.add(doPost("/api/assetProfile", assetProfile, AssetProfile.class)); |
||||
|
} |
||||
|
|
||||
|
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(new AssetProfile(), new AssetProfile(), |
||||
|
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), |
||||
|
ActionType.ADDED, ActionType.ADDED, cntEntity, cntEntity, cntEntity); |
||||
|
Mockito.reset(tbClusterService, auditLogService); |
||||
|
|
||||
|
List<AssetProfile> loadedAssetProfiles = new ArrayList<>(); |
||||
|
pageLink = new PageLink(17); |
||||
|
do { |
||||
|
pageData = doGetTypedWithPageLink("/api/assetProfiles?", |
||||
|
new TypeReference<>() { |
||||
|
}, pageLink); |
||||
|
loadedAssetProfiles.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(assetProfiles, idComparator); |
||||
|
Collections.sort(loadedAssetProfiles, idComparator); |
||||
|
|
||||
|
Assert.assertEquals(assetProfiles, loadedAssetProfiles); |
||||
|
|
||||
|
for (AssetProfile assetProfile : loadedAssetProfiles) { |
||||
|
if (!assetProfile.isDefault()) { |
||||
|
doDelete("/api/assetProfile/" + assetProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
testNotifyManyEntityManyTimeMsgToEdgeServiceEntityEqAny(loadedAssetProfiles.get(0), loadedAssetProfiles.get(0), |
||||
|
savedTenant.getId(), tenantAdmin.getCustomerId(), tenantAdmin.getId(), tenantAdmin.getEmail(), |
||||
|
ActionType.DELETED, ActionType.DELETED, cntEntity, cntEntity, cntEntity, loadedAssetProfiles.get(0).getId().getId().toString()); |
||||
|
|
||||
|
pageLink = new PageLink(17); |
||||
|
pageData = doGetTypedWithPageLink("/api/assetProfiles?", |
||||
|
new TypeReference<>() { |
||||
|
}, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertEquals(1, pageData.getTotalElements()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindAssetProfileInfos() throws Exception { |
||||
|
List<AssetProfile> assetProfiles = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(17); |
||||
|
PageData<AssetProfile> assetProfilePageData = doGetTypedWithPageLink("/api/assetProfiles?", |
||||
|
new TypeReference<PageData<AssetProfile>>() { |
||||
|
}, pageLink); |
||||
|
Assert.assertFalse(assetProfilePageData.hasNext()); |
||||
|
Assert.assertEquals(1, assetProfilePageData.getTotalElements()); |
||||
|
assetProfiles.addAll(assetProfilePageData.getData()); |
||||
|
|
||||
|
for (int i = 0; i < 28; i++) { |
||||
|
AssetProfile assetProfile = this.createAssetProfile("Asset Profile" + i); |
||||
|
assetProfiles.add(doPost("/api/assetProfile", assetProfile, AssetProfile.class)); |
||||
|
} |
||||
|
|
||||
|
List<AssetProfileInfo> loadedAssetProfileInfos = new ArrayList<>(); |
||||
|
pageLink = new PageLink(17); |
||||
|
PageData<AssetProfileInfo> pageData; |
||||
|
do { |
||||
|
pageData = doGetTypedWithPageLink("/api/assetProfileInfos?", |
||||
|
new TypeReference<>() { |
||||
|
}, pageLink); |
||||
|
loadedAssetProfileInfos.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(assetProfiles, idComparator); |
||||
|
Collections.sort(loadedAssetProfileInfos, assetProfileInfoIdComparator); |
||||
|
|
||||
|
List<AssetProfileInfo> assetProfileInfos = assetProfiles.stream().map(assetProfile -> new AssetProfileInfo(assetProfile.getId(), |
||||
|
assetProfile.getName(), assetProfile.getImage(), assetProfile.getDefaultDashboardId())).collect(Collectors.toList()); |
||||
|
|
||||
|
Assert.assertEquals(assetProfileInfos, loadedAssetProfileInfos); |
||||
|
|
||||
|
for (AssetProfile assetProfile : assetProfiles) { |
||||
|
if (!assetProfile.isDefault()) { |
||||
|
doDelete("/api/assetProfile/" + assetProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pageLink = new PageLink(17); |
||||
|
pageData = doGetTypedWithPageLink("/api/assetProfileInfos?", |
||||
|
new TypeReference<PageData<AssetProfileInfo>>() { |
||||
|
}, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertEquals(1, pageData.getTotalElements()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteAssetProfileWithDeleteRelationsOk() throws Exception { |
||||
|
AssetProfileId assetProfileId = savedAssetProfile("AssetProfile for Test WithRelationsOk").getId(); |
||||
|
testEntityDaoWithRelationsOk(savedTenant.getId(), assetProfileId, "/api/assetProfile/" + assetProfileId); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteAssetProfileExceptionWithRelationsTransactional() throws Exception { |
||||
|
AssetProfileId assetProfileId = savedAssetProfile("AssetProfile for Test WithRelations Transactional Exception").getId(); |
||||
|
testEntityDaoWithRelationsTransactionalException(assetProfileDao, savedTenant.getId(), assetProfileId, "/api/assetProfile/" + assetProfileId); |
||||
|
} |
||||
|
|
||||
|
private AssetProfile savedAssetProfile(String name) { |
||||
|
AssetProfile assetProfile = createAssetProfile(name); |
||||
|
return doPost("/api/assetProfile", assetProfile, AssetProfile.class); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.sql; |
||||
|
|
||||
|
import org.thingsboard.server.controller.BaseAssetProfileControllerTest; |
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class AssetProfileControllerSqlTest extends BaseAssetProfileControllerTest { |
||||
|
} |
||||
@ -0,0 +1,53 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfileInfo; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
|
||||
|
public interface AssetProfileService { |
||||
|
|
||||
|
AssetProfile findAssetProfileById(TenantId tenantId, AssetProfileId assetProfileId); |
||||
|
|
||||
|
AssetProfile findAssetProfileByName(TenantId tenantId, String profileName); |
||||
|
|
||||
|
AssetProfileInfo findAssetProfileInfoById(TenantId tenantId, AssetProfileId assetProfileId); |
||||
|
|
||||
|
AssetProfile saveAssetProfile(AssetProfile assetProfile); |
||||
|
|
||||
|
void deleteAssetProfile(TenantId tenantId, AssetProfileId assetProfileId); |
||||
|
|
||||
|
PageData<AssetProfile> findAssetProfiles(TenantId tenantId, PageLink pageLink); |
||||
|
|
||||
|
PageData<AssetProfileInfo> findAssetProfileInfos(TenantId tenantId, PageLink pageLink); |
||||
|
|
||||
|
AssetProfile findOrCreateAssetProfile(TenantId tenantId, String profileName); |
||||
|
|
||||
|
AssetProfile createDefaultAssetProfile(TenantId tenantId); |
||||
|
|
||||
|
AssetProfile findDefaultAssetProfile(TenantId tenantId); |
||||
|
|
||||
|
AssetProfileInfo findDefaultAssetProfileInfo(TenantId tenantId); |
||||
|
|
||||
|
boolean setDefaultAssetProfile(TenantId tenantId, AssetProfileId assetProfileId); |
||||
|
|
||||
|
void deleteAssetProfilesByTenantId(TenantId tenantId); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,118 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset; |
||||
|
|
||||
|
import io.swagger.annotations.ApiModel; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import lombok.ToString; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.server.common.data.ExportableEntity; |
||||
|
import org.thingsboard.server.common.data.HasName; |
||||
|
import org.thingsboard.server.common.data.HasTenantId; |
||||
|
import org.thingsboard.server.common.data.SearchTextBased; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.DashboardId; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.validation.Length; |
||||
|
import org.thingsboard.server.common.data.validation.NoXss; |
||||
|
|
||||
|
@ApiModel |
||||
|
@Data |
||||
|
@ToString(exclude = {"image"}) |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Slf4j |
||||
|
public class AssetProfile extends SearchTextBased<AssetProfileId> implements HasName, HasTenantId, ExportableEntity<AssetProfileId> { |
||||
|
|
||||
|
private static final long serialVersionUID = 6998485460273302018L; |
||||
|
|
||||
|
@ApiModelProperty(position = 3, value = "JSON object with Tenant Id that owns the profile.", accessMode = ApiModelProperty.AccessMode.READ_ONLY) |
||||
|
private TenantId tenantId; |
||||
|
@NoXss |
||||
|
@Length(fieldName = "name") |
||||
|
@ApiModelProperty(position = 4, value = "Unique Asset Profile Name in scope of Tenant.", example = "Building") |
||||
|
private String name; |
||||
|
@NoXss |
||||
|
@ApiModelProperty(position = 11, value = "Asset Profile description. ") |
||||
|
private String description; |
||||
|
@Length(fieldName = "image", max = 1000000) |
||||
|
@ApiModelProperty(position = 12, value = "Either URL or Base64 data of the icon. Used in the mobile application to visualize set of asset profiles in the grid view. ") |
||||
|
private String image; |
||||
|
private boolean isDefault; |
||||
|
@ApiModelProperty(position = 7, value = "Reference to the rule chain. " + |
||||
|
"If present, the specified rule chain will be used to process all messages related to asset, including asset updates, telemetry, attribute updates, etc. " + |
||||
|
"Otherwise, the root rule chain will be used to process those messages.") |
||||
|
private RuleChainId defaultRuleChainId; |
||||
|
@ApiModelProperty(position = 6, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to asset details.") |
||||
|
private DashboardId defaultDashboardId; |
||||
|
|
||||
|
@NoXss |
||||
|
@ApiModelProperty(position = 8, value = "Rule engine queue name. " + |
||||
|
"If present, the specified queue will be used to store all unprocessed messages related to asset, including asset updates, telemetry, attribute updates, etc. " + |
||||
|
"Otherwise, the 'Main' queue will be used to store those messages.") |
||||
|
private String defaultQueueName; |
||||
|
|
||||
|
private AssetProfileId externalId; |
||||
|
|
||||
|
public AssetProfile() { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
public AssetProfile(AssetProfileId assetProfileId) { |
||||
|
super(assetProfileId); |
||||
|
} |
||||
|
|
||||
|
public AssetProfile(AssetProfile assetProfile) { |
||||
|
super(assetProfile); |
||||
|
this.tenantId = assetProfile.getTenantId(); |
||||
|
this.name = assetProfile.getName(); |
||||
|
this.description = assetProfile.getDescription(); |
||||
|
this.image = assetProfile.getImage(); |
||||
|
this.isDefault = assetProfile.isDefault(); |
||||
|
this.defaultRuleChainId = assetProfile.getDefaultRuleChainId(); |
||||
|
this.defaultDashboardId = assetProfile.getDefaultDashboardId(); |
||||
|
this.defaultQueueName = assetProfile.getDefaultQueueName(); |
||||
|
this.externalId = assetProfile.getExternalId(); |
||||
|
} |
||||
|
|
||||
|
@ApiModelProperty(position = 1, value = "JSON object with the asset profile Id. " + |
||||
|
"Specify this field to update the asset profile. " + |
||||
|
"Referencing non-existing asset profile Id will cause error. " + |
||||
|
"Omit this field to create new asset profile.") |
||||
|
@Override |
||||
|
public AssetProfileId getId() { |
||||
|
return super.getId(); |
||||
|
} |
||||
|
|
||||
|
@ApiModelProperty(position = 2, value = "Timestamp of the profile creation, in milliseconds", example = "1609459200000", accessMode = ApiModelProperty.AccessMode.READ_ONLY) |
||||
|
@Override |
||||
|
public long getCreatedTime() { |
||||
|
return super.getCreatedTime(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getSearchText() { |
||||
|
return getName(); |
||||
|
} |
||||
|
|
||||
|
@ApiModelProperty(position = 5, value = "Used to mark the default profile. Default profile is used when the asset profile is not specified during asset creation.") |
||||
|
public boolean isDefault(){ |
||||
|
return isDefault; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,62 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonCreator; |
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import lombok.ToString; |
||||
|
import lombok.Value; |
||||
|
import org.thingsboard.server.common.data.EntityInfo; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.id.DashboardId; |
||||
|
import org.thingsboard.server.common.data.id.EntityId; |
||||
|
import org.thingsboard.server.common.data.id.EntityIdFactory; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@Value |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@ToString(callSuper = true, exclude = "image") |
||||
|
public class AssetProfileInfo extends EntityInfo { |
||||
|
|
||||
|
@ApiModelProperty(position = 3, value = "Either URL or Base64 data of the icon. Used in the mobile application to visualize set of asset profiles in the grid view. ") |
||||
|
private final String image; |
||||
|
@ApiModelProperty(position = 4, value = "Reference to the dashboard. Used in the mobile application to open the default dashboard when user navigates to asset details.") |
||||
|
private final DashboardId defaultDashboardId; |
||||
|
|
||||
|
@JsonCreator |
||||
|
public AssetProfileInfo(@JsonProperty("id") EntityId id, |
||||
|
@JsonProperty("name") String name, |
||||
|
@JsonProperty("image") String image, |
||||
|
@JsonProperty("defaultDashboardId") DashboardId defaultDashboardId) { |
||||
|
super(id, name); |
||||
|
this.image = image; |
||||
|
this.defaultDashboardId = defaultDashboardId; |
||||
|
} |
||||
|
|
||||
|
public AssetProfileInfo(UUID uuid, String name, String image, UUID defaultDashboardId) { |
||||
|
super(EntityIdFactory.getByTypeAndUuid(EntityType.ASSET_PROFILE, uuid), name); |
||||
|
this.image = image; |
||||
|
this.defaultDashboardId = defaultDashboardId != null ? new DashboardId(defaultDashboardId) : null; |
||||
|
} |
||||
|
|
||||
|
public AssetProfileInfo(AssetProfile profile) { |
||||
|
this(profile.getId(), profile.getName(), profile.getImage(), profile.getDefaultDashboardId()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,43 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.id; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
import com.fasterxml.jackson.annotation.JsonCreator; |
||||
|
import com.fasterxml.jackson.annotation.JsonProperty; |
||||
|
import io.swagger.annotations.ApiModelProperty; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
|
||||
|
public class AssetProfileId extends UUIDBased implements EntityId { |
||||
|
|
||||
|
private static final long serialVersionUID = 1L; |
||||
|
|
||||
|
@JsonCreator |
||||
|
public AssetProfileId(@JsonProperty("id") UUID id) { |
||||
|
super(id); |
||||
|
} |
||||
|
|
||||
|
public static AssetProfileId fromString(String assetProfileId) { |
||||
|
return new AssetProfileId(UUID.fromString(assetProfileId)); |
||||
|
} |
||||
|
|
||||
|
@ApiModelProperty(position = 2, required = true, value = "string", example = "ASSET_PROFILE", allowableValues = "ASSET_PROFILE") |
||||
|
@Override |
||||
|
public EntityType getEntityType() { |
||||
|
return EntityType.ASSET_PROFILE; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
|
||||
|
import java.io.Serializable; |
||||
|
|
||||
|
@Data |
||||
|
public class AssetProfileCacheKey implements Serializable { |
||||
|
|
||||
|
private static final long serialVersionUID = 8220455917177676472L; |
||||
|
|
||||
|
private final TenantId tenantId; |
||||
|
private final String name; |
||||
|
private final AssetProfileId assetProfileId; |
||||
|
private final boolean defaultProfile; |
||||
|
|
||||
|
private AssetProfileCacheKey(TenantId tenantId, String name, AssetProfileId assetProfileId, boolean defaultProfile) { |
||||
|
this.tenantId = tenantId; |
||||
|
this.name = name; |
||||
|
this.assetProfileId = assetProfileId; |
||||
|
this.defaultProfile = defaultProfile; |
||||
|
} |
||||
|
|
||||
|
public static AssetProfileCacheKey fromName(TenantId tenantId, String name) { |
||||
|
return new AssetProfileCacheKey(tenantId, name, null, false); |
||||
|
} |
||||
|
|
||||
|
public static AssetProfileCacheKey fromId(AssetProfileId id) { |
||||
|
return new AssetProfileCacheKey(null, null, id, false); |
||||
|
} |
||||
|
|
||||
|
public static AssetProfileCacheKey defaultProfile(TenantId tenantId) { |
||||
|
return new AssetProfileCacheKey(tenantId, null, null, true); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String toString() { |
||||
|
if (assetProfileId != null) { |
||||
|
return assetProfileId.toString(); |
||||
|
} else if (defaultProfile) { |
||||
|
return tenantId.toString(); |
||||
|
} else { |
||||
|
return tenantId + "_" + name; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,33 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset; |
||||
|
|
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||
|
import org.springframework.cache.CacheManager; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.cache.CaffeineTbTransactionalCache; |
||||
|
import org.thingsboard.server.common.data.CacheConstants; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
|
||||
|
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "caffeine", matchIfMissing = true) |
||||
|
@Service("AssetProfileCache") |
||||
|
public class AssetProfileCaffeineCache extends CaffeineTbTransactionalCache<AssetProfileCacheKey, AssetProfile> { |
||||
|
|
||||
|
public AssetProfileCaffeineCache(CacheManager cacheManager) { |
||||
|
super(cacheManager, CacheConstants.ASSET_PROFILE_CACHE); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,46 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset; |
||||
|
|
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfileInfo; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.Dao; |
||||
|
import org.thingsboard.server.dao.ExportableEntityDao; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
public interface AssetProfileDao extends Dao<AssetProfile>, ExportableEntityDao<AssetProfileId, AssetProfile> { |
||||
|
|
||||
|
AssetProfileInfo findAssetProfileInfoById(TenantId tenantId, UUID assetProfileId); |
||||
|
|
||||
|
AssetProfile save(TenantId tenantId, AssetProfile assetProfile); |
||||
|
|
||||
|
AssetProfile saveAndFlush(TenantId tenantId, AssetProfile assetProfile); |
||||
|
|
||||
|
PageData<AssetProfile> findAssetProfiles(TenantId tenantId, PageLink pageLink); |
||||
|
|
||||
|
PageData<AssetProfileInfo> findAssetProfileInfos(TenantId tenantId, PageLink pageLink); |
||||
|
|
||||
|
AssetProfile findDefaultAssetProfile(TenantId tenantId); |
||||
|
|
||||
|
AssetProfileInfo findDefaultAssetProfileInfo(TenantId tenantId); |
||||
|
|
||||
|
AssetProfile findByName(TenantId tenantId, String profileName); |
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
|
||||
|
@Data |
||||
|
public class AssetProfileEvictEvent { |
||||
|
|
||||
|
private final TenantId tenantId; |
||||
|
private final String newName; |
||||
|
private final String oldName; |
||||
|
private final AssetProfileId assetProfileId; |
||||
|
private final boolean defaultProfile; |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,35 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset; |
||||
|
|
||||
|
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; |
||||
|
import org.springframework.data.redis.connection.RedisConnectionFactory; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.thingsboard.server.cache.CacheSpecsMap; |
||||
|
import org.thingsboard.server.cache.RedisTbTransactionalCache; |
||||
|
import org.thingsboard.server.cache.TBRedisCacheConfiguration; |
||||
|
import org.thingsboard.server.cache.TbFSTRedisSerializer; |
||||
|
import org.thingsboard.server.common.data.CacheConstants; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
|
||||
|
@ConditionalOnProperty(prefix = "cache", value = "type", havingValue = "redis") |
||||
|
@Service("AssetProfileCache") |
||||
|
public class AssetProfileRedisCache extends RedisTbTransactionalCache<AssetProfileCacheKey, AssetProfile> { |
||||
|
|
||||
|
public AssetProfileRedisCache(TBRedisCacheConfiguration configuration, CacheSpecsMap cacheSpecsMap, RedisConnectionFactory connectionFactory) { |
||||
|
super(CacheConstants.ASSET_PROFILE_CACHE, cacheSpecsMap, connectionFactory, configuration, new TbFSTRedisSerializer<>()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,286 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.asset; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.hibernate.exception.ConstraintViolationException; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.stereotype.Service; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
import org.springframework.transaction.event.TransactionalEventListener; |
||||
|
import org.thingsboard.server.common.data.StringUtils; |
||||
|
import org.thingsboard.server.common.data.asset.Asset; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfileInfo; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.entity.AbstractCachedEntityService; |
||||
|
import org.thingsboard.server.dao.exception.DataValidationException; |
||||
|
import org.thingsboard.server.dao.service.DataValidator; |
||||
|
import org.thingsboard.server.dao.service.PaginatedRemover; |
||||
|
import org.thingsboard.server.dao.service.Validator; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.Map; |
||||
|
|
||||
|
import static org.thingsboard.server.dao.service.Validator.validateId; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class AssetProfileServiceImpl extends AbstractCachedEntityService<AssetProfileCacheKey, AssetProfile, AssetProfileEvictEvent> implements AssetProfileService { |
||||
|
|
||||
|
private static final String INCORRECT_TENANT_ID = "Incorrect tenantId "; |
||||
|
|
||||
|
private static final String INCORRECT_ASSET_PROFILE_ID = "Incorrect assetProfileId "; |
||||
|
|
||||
|
private static final String INCORRECT_ASSET_PROFILE_NAME = "Incorrect assetProfileName "; |
||||
|
|
||||
|
private static final String ASSET_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS = "Asset profile with such name already exists!"; |
||||
|
|
||||
|
@Autowired |
||||
|
private AssetProfileDao assetProfileDao; |
||||
|
|
||||
|
@Autowired |
||||
|
private AssetDao assetDao; |
||||
|
|
||||
|
@Autowired |
||||
|
private AssetService assetService; |
||||
|
|
||||
|
@Autowired |
||||
|
private DataValidator<AssetProfile> assetProfileValidator; |
||||
|
|
||||
|
@TransactionalEventListener(classes = AssetProfileEvictEvent.class) |
||||
|
@Override |
||||
|
public void handleEvictEvent(AssetProfileEvictEvent event) { |
||||
|
List<AssetProfileCacheKey> keys = new ArrayList<>(2); |
||||
|
keys.add(AssetProfileCacheKey.fromName(event.getTenantId(), event.getNewName())); |
||||
|
if (event.getAssetProfileId() != null) { |
||||
|
keys.add(AssetProfileCacheKey.fromId(event.getAssetProfileId())); |
||||
|
} |
||||
|
if (event.isDefaultProfile()) { |
||||
|
keys.add(AssetProfileCacheKey.defaultProfile(event.getTenantId())); |
||||
|
} |
||||
|
if (StringUtils.isNotEmpty(event.getOldName()) && !event.getOldName().equals(event.getNewName())) { |
||||
|
keys.add(AssetProfileCacheKey.fromName(event.getTenantId(), event.getOldName())); |
||||
|
} |
||||
|
cache.evict(keys); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile findAssetProfileById(TenantId tenantId, AssetProfileId assetProfileId) { |
||||
|
log.trace("Executing findAssetProfileById [{}]", assetProfileId); |
||||
|
Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); |
||||
|
return cache.getAndPutInTransaction(AssetProfileCacheKey.fromId(assetProfileId), |
||||
|
() -> assetProfileDao.findById(tenantId, assetProfileId.getId()), true); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile findAssetProfileByName(TenantId tenantId, String profileName) { |
||||
|
log.trace("Executing findAssetProfileByName [{}][{}]", tenantId, profileName); |
||||
|
Validator.validateString(profileName, INCORRECT_ASSET_PROFILE_NAME + profileName); |
||||
|
return cache.getAndPutInTransaction(AssetProfileCacheKey.fromName(tenantId, profileName), |
||||
|
() -> assetProfileDao.findByName(tenantId, profileName), true); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfileInfo findAssetProfileInfoById(TenantId tenantId, AssetProfileId assetProfileId) { |
||||
|
log.trace("Executing findAssetProfileInfoById [{}]", assetProfileId); |
||||
|
Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); |
||||
|
return toAssetProfileInfo(findAssetProfileById(tenantId, assetProfileId)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile saveAssetProfile(AssetProfile assetProfile) { |
||||
|
log.trace("Executing saveAssetProfile [{}]", assetProfile); |
||||
|
AssetProfile oldAssetProfile = assetProfileValidator.validate(assetProfile, AssetProfile::getTenantId); |
||||
|
AssetProfile savedAssetProfile; |
||||
|
try { |
||||
|
savedAssetProfile = assetProfileDao.saveAndFlush(assetProfile.getTenantId(), assetProfile); |
||||
|
publishEvictEvent(new AssetProfileEvictEvent(savedAssetProfile.getTenantId(), savedAssetProfile.getName(), |
||||
|
oldAssetProfile != null ? oldAssetProfile.getName() : null, savedAssetProfile.getId(), savedAssetProfile.isDefault())); |
||||
|
} catch (Exception t) { |
||||
|
handleEvictEvent(new AssetProfileEvictEvent(assetProfile.getTenantId(), assetProfile.getName(), |
||||
|
oldAssetProfile != null ? oldAssetProfile.getName() : null, null, assetProfile.isDefault())); |
||||
|
checkConstraintViolation(t, |
||||
|
Map.of("asset_profile_name_unq_key", ASSET_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS, |
||||
|
"asset_profile_external_id_unq_key", "Asset profile with such external id already exists!")); |
||||
|
throw t; |
||||
|
} |
||||
|
if (oldAssetProfile != null && !oldAssetProfile.getName().equals(assetProfile.getName())) { |
||||
|
PageLink pageLink = new PageLink(100); |
||||
|
PageData<Asset> pageData; |
||||
|
do { |
||||
|
pageData = assetDao.findAssetsByTenantIdAndProfileId(assetProfile.getTenantId().getId(), assetProfile.getUuidId(), pageLink); |
||||
|
for (Asset asset : pageData.getData()) { |
||||
|
asset.setType(assetProfile.getName()); |
||||
|
assetService.saveAsset(asset); |
||||
|
} |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} while (pageData.hasNext()); |
||||
|
} |
||||
|
return savedAssetProfile; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
@Transactional |
||||
|
public void deleteAssetProfile(TenantId tenantId, AssetProfileId assetProfileId) { |
||||
|
log.trace("Executing deleteAssetProfile [{}]", assetProfileId); |
||||
|
Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); |
||||
|
AssetProfile assetProfile = assetProfileDao.findById(tenantId, assetProfileId.getId()); |
||||
|
if (assetProfile != null && assetProfile.isDefault()) { |
||||
|
throw new DataValidationException("Deletion of Default Asset Profile is prohibited!"); |
||||
|
} |
||||
|
this.removeAssetProfile(tenantId, assetProfile); |
||||
|
} |
||||
|
|
||||
|
private void removeAssetProfile(TenantId tenantId, AssetProfile assetProfile) { |
||||
|
AssetProfileId assetProfileId = assetProfile.getId(); |
||||
|
try { |
||||
|
deleteEntityRelations(tenantId, assetProfileId); |
||||
|
assetProfileDao.removeById(tenantId, assetProfileId.getId()); |
||||
|
publishEvictEvent(new AssetProfileEvictEvent(assetProfile.getTenantId(), assetProfile.getName(), |
||||
|
null, assetProfile.getId(), assetProfile.isDefault())); |
||||
|
} catch (Exception t) { |
||||
|
ConstraintViolationException e = extractConstraintViolationException(t).orElse(null); |
||||
|
if (e != null && e.getConstraintName() != null && e.getConstraintName().equalsIgnoreCase("fk_asset_profile")) { |
||||
|
throw new DataValidationException("The asset profile referenced by the assets cannot be deleted!"); |
||||
|
} else { |
||||
|
throw t; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<AssetProfile> findAssetProfiles(TenantId tenantId, PageLink pageLink) { |
||||
|
log.trace("Executing findAssetProfiles tenantId [{}], pageLink [{}]", tenantId, pageLink); |
||||
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
||||
|
Validator.validatePageLink(pageLink); |
||||
|
return assetProfileDao.findAssetProfiles(tenantId, pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<AssetProfileInfo> findAssetProfileInfos(TenantId tenantId, PageLink pageLink) { |
||||
|
log.trace("Executing findAssetProfileInfos tenantId [{}], pageLink [{}]", tenantId, pageLink); |
||||
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
||||
|
Validator.validatePageLink(pageLink); |
||||
|
return assetProfileDao.findAssetProfileInfos(tenantId, pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile findOrCreateAssetProfile(TenantId tenantId, String name) { |
||||
|
log.trace("Executing findOrCreateAssetProfile"); |
||||
|
AssetProfile assetProfile = findAssetProfileByName(tenantId, name); |
||||
|
if (assetProfile == null) { |
||||
|
try { |
||||
|
assetProfile = this.doCreateDefaultAssetProfile(tenantId, name, name.equals("default")); |
||||
|
} catch (DataValidationException e) { |
||||
|
if (ASSET_PROFILE_WITH_SUCH_NAME_ALREADY_EXISTS.equals(e.getMessage())) { |
||||
|
assetProfile = findAssetProfileByName(tenantId, name); |
||||
|
} else { |
||||
|
throw e; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return assetProfile; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile createDefaultAssetProfile(TenantId tenantId) { |
||||
|
log.trace("Executing createDefaultAssetProfile tenantId [{}]", tenantId); |
||||
|
return doCreateDefaultAssetProfile(tenantId, "default", true); |
||||
|
} |
||||
|
|
||||
|
private AssetProfile doCreateDefaultAssetProfile(TenantId tenantId, String profileName, boolean defaultProfile) { |
||||
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
||||
|
AssetProfile assetProfile = new AssetProfile(); |
||||
|
assetProfile.setTenantId(tenantId); |
||||
|
assetProfile.setDefault(defaultProfile); |
||||
|
assetProfile.setName(profileName); |
||||
|
assetProfile.setDescription("Default asset profile"); |
||||
|
return saveAssetProfile(assetProfile); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile findDefaultAssetProfile(TenantId tenantId) { |
||||
|
log.trace("Executing findDefaultAssetProfile tenantId [{}]", tenantId); |
||||
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
||||
|
return cache.getAndPutInTransaction(AssetProfileCacheKey.defaultProfile(tenantId), |
||||
|
() -> assetProfileDao.findDefaultAssetProfile(tenantId), true); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfileInfo findDefaultAssetProfileInfo(TenantId tenantId) { |
||||
|
log.trace("Executing findDefaultAssetProfileInfo tenantId [{}]", tenantId); |
||||
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
||||
|
return toAssetProfileInfo(findDefaultAssetProfile(tenantId)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public boolean setDefaultAssetProfile(TenantId tenantId, AssetProfileId assetProfileId) { |
||||
|
log.trace("Executing setDefaultAssetProfile [{}]", assetProfileId); |
||||
|
Validator.validateId(assetProfileId, INCORRECT_ASSET_PROFILE_ID + assetProfileId); |
||||
|
AssetProfile assetProfile = assetProfileDao.findById(tenantId, assetProfileId.getId()); |
||||
|
if (!assetProfile.isDefault()) { |
||||
|
assetProfile.setDefault(true); |
||||
|
AssetProfile previousDefaultAssetProfile = findDefaultAssetProfile(tenantId); |
||||
|
boolean changed = false; |
||||
|
if (previousDefaultAssetProfile == null) { |
||||
|
assetProfileDao.save(tenantId, assetProfile); |
||||
|
publishEvictEvent(new AssetProfileEvictEvent(assetProfile.getTenantId(), assetProfile.getName(), null, assetProfile.getId(), true)); |
||||
|
changed = true; |
||||
|
} else if (!previousDefaultAssetProfile.getId().equals(assetProfile.getId())) { |
||||
|
previousDefaultAssetProfile.setDefault(false); |
||||
|
assetProfileDao.save(tenantId, previousDefaultAssetProfile); |
||||
|
assetProfileDao.save(tenantId, assetProfile); |
||||
|
publishEvictEvent(new AssetProfileEvictEvent(previousDefaultAssetProfile.getTenantId(), previousDefaultAssetProfile.getName(), null, previousDefaultAssetProfile.getId(), false)); |
||||
|
publishEvictEvent(new AssetProfileEvictEvent(assetProfile.getTenantId(), assetProfile.getName(), null, assetProfile.getId(), true)); |
||||
|
changed = true; |
||||
|
} |
||||
|
return changed; |
||||
|
} |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void deleteAssetProfilesByTenantId(TenantId tenantId) { |
||||
|
log.trace("Executing deleteAssetProfilesByTenantId, tenantId [{}]", tenantId); |
||||
|
validateId(tenantId, INCORRECT_TENANT_ID + tenantId); |
||||
|
tenantAssetProfilesRemover.removeEntities(tenantId, tenantId); |
||||
|
} |
||||
|
|
||||
|
private PaginatedRemover<TenantId, AssetProfile> tenantAssetProfilesRemover = |
||||
|
new PaginatedRemover<>() { |
||||
|
|
||||
|
@Override |
||||
|
protected PageData<AssetProfile> findEntities(TenantId tenantId, TenantId id, PageLink pageLink) { |
||||
|
return assetProfileDao.findAssetProfiles(id, pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected void removeEntity(TenantId tenantId, AssetProfile entity) { |
||||
|
removeAssetProfile(tenantId, entity); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
private AssetProfileInfo toAssetProfileInfo(AssetProfile profile) { |
||||
|
return profile == null ? null : new AssetProfileInfo(profile.getId(), profile.getName(), profile.getImage(), |
||||
|
profile.getDefaultDashboardId()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,136 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.model.sql; |
||||
|
|
||||
|
import lombok.Data; |
||||
|
import lombok.EqualsAndHashCode; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.DashboardId; |
||||
|
import org.thingsboard.server.common.data.id.RuleChainId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.dao.model.BaseSqlEntity; |
||||
|
import org.thingsboard.server.dao.model.ModelConstants; |
||||
|
import org.thingsboard.server.dao.model.SearchTextEntity; |
||||
|
|
||||
|
import javax.persistence.Column; |
||||
|
import javax.persistence.Entity; |
||||
|
import javax.persistence.Table; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@Data |
||||
|
@EqualsAndHashCode(callSuper = true) |
||||
|
@Entity |
||||
|
@Table(name = ModelConstants.ASSET_PROFILE_COLUMN_FAMILY_NAME) |
||||
|
public final class AssetProfileEntity extends BaseSqlEntity<AssetProfile> implements SearchTextEntity<AssetProfile> { |
||||
|
|
||||
|
@Column(name = ModelConstants.ASSET_PROFILE_TENANT_ID_PROPERTY) |
||||
|
private UUID tenantId; |
||||
|
|
||||
|
@Column(name = ModelConstants.ASSET_PROFILE_NAME_PROPERTY) |
||||
|
private String name; |
||||
|
|
||||
|
@Column(name = ModelConstants.ASSET_PROFILE_IMAGE_PROPERTY) |
||||
|
private String image; |
||||
|
|
||||
|
@Column(name = ModelConstants.ASSET_PROFILE_DESCRIPTION_PROPERTY) |
||||
|
private String description; |
||||
|
|
||||
|
@Column(name = ModelConstants.SEARCH_TEXT_PROPERTY) |
||||
|
private String searchText; |
||||
|
|
||||
|
@Column(name = ModelConstants.ASSET_PROFILE_IS_DEFAULT_PROPERTY) |
||||
|
private boolean isDefault; |
||||
|
|
||||
|
@Column(name = ModelConstants.ASSET_PROFILE_DEFAULT_RULE_CHAIN_ID_PROPERTY, columnDefinition = "uuid") |
||||
|
private UUID defaultRuleChainId; |
||||
|
|
||||
|
@Column(name = ModelConstants.ASSET_PROFILE_DEFAULT_DASHBOARD_ID_PROPERTY) |
||||
|
private UUID defaultDashboardId; |
||||
|
|
||||
|
@Column(name = ModelConstants.ASSET_PROFILE_DEFAULT_QUEUE_NAME_PROPERTY) |
||||
|
private String defaultQueueName; |
||||
|
|
||||
|
@Column(name = ModelConstants.EXTERNAL_ID_PROPERTY) |
||||
|
private UUID externalId; |
||||
|
|
||||
|
public AssetProfileEntity() { |
||||
|
super(); |
||||
|
} |
||||
|
|
||||
|
public AssetProfileEntity(AssetProfile assetProfile) { |
||||
|
if (assetProfile.getId() != null) { |
||||
|
this.setUuid(assetProfile.getId().getId()); |
||||
|
} |
||||
|
if (assetProfile.getTenantId() != null) { |
||||
|
this.tenantId = assetProfile.getTenantId().getId(); |
||||
|
} |
||||
|
this.setCreatedTime(assetProfile.getCreatedTime()); |
||||
|
this.name = assetProfile.getName(); |
||||
|
this.image = assetProfile.getImage(); |
||||
|
this.description = assetProfile.getDescription(); |
||||
|
this.isDefault = assetProfile.isDefault(); |
||||
|
if (assetProfile.getDefaultRuleChainId() != null) { |
||||
|
this.defaultRuleChainId = assetProfile.getDefaultRuleChainId().getId(); |
||||
|
} |
||||
|
if (assetProfile.getDefaultDashboardId() != null) { |
||||
|
this.defaultDashboardId = assetProfile.getDefaultDashboardId().getId(); |
||||
|
} |
||||
|
this.defaultQueueName = assetProfile.getDefaultQueueName(); |
||||
|
if (assetProfile.getExternalId() != null) { |
||||
|
this.externalId = assetProfile.getExternalId().getId(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public String getSearchTextSource() { |
||||
|
return name; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void setSearchText(String searchText) { |
||||
|
this.searchText = searchText; |
||||
|
} |
||||
|
|
||||
|
public String getSearchText() { |
||||
|
return searchText; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile toData() { |
||||
|
AssetProfile assetProfile = new AssetProfile(new AssetProfileId(this.getUuid())); |
||||
|
assetProfile.setCreatedTime(createdTime); |
||||
|
if (tenantId != null) { |
||||
|
assetProfile.setTenantId(TenantId.fromUUID(tenantId)); |
||||
|
} |
||||
|
assetProfile.setName(name); |
||||
|
assetProfile.setImage(image); |
||||
|
assetProfile.setDescription(description); |
||||
|
assetProfile.setDefault(isDefault); |
||||
|
assetProfile.setDefaultQueueName(defaultQueueName); |
||||
|
if (defaultRuleChainId != null) { |
||||
|
assetProfile.setDefaultRuleChainId(new RuleChainId(defaultRuleChainId)); |
||||
|
} |
||||
|
if (defaultDashboardId != null) { |
||||
|
assetProfile.setDefaultDashboardId(new DashboardId(defaultDashboardId)); |
||||
|
} |
||||
|
if (externalId != null) { |
||||
|
assetProfile.setExternalId(new AssetProfileId(externalId)); |
||||
|
} |
||||
|
|
||||
|
return assetProfile; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,109 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.service.validator; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.context.annotation.Lazy; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.thingsboard.server.common.data.DashboardInfo; |
||||
|
import org.thingsboard.server.common.data.StringUtils; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.queue.Queue; |
||||
|
import org.thingsboard.server.common.data.rule.RuleChain; |
||||
|
import org.thingsboard.server.dao.asset.AssetProfileDao; |
||||
|
import org.thingsboard.server.dao.asset.AssetProfileService; |
||||
|
import org.thingsboard.server.dao.dashboard.DashboardService; |
||||
|
import org.thingsboard.server.dao.exception.DataValidationException; |
||||
|
import org.thingsboard.server.dao.queue.QueueService; |
||||
|
import org.thingsboard.server.dao.rule.RuleChainService; |
||||
|
import org.thingsboard.server.dao.service.DataValidator; |
||||
|
import org.thingsboard.server.dao.tenant.TenantService; |
||||
|
|
||||
|
@Component |
||||
|
public class AssetProfileDataValidator extends DataValidator<AssetProfile> { |
||||
|
|
||||
|
@Autowired |
||||
|
private AssetProfileDao assetProfileDao; |
||||
|
@Autowired |
||||
|
@Lazy |
||||
|
private AssetProfileService assetProfileService; |
||||
|
@Autowired |
||||
|
private TenantService tenantService; |
||||
|
@Lazy |
||||
|
@Autowired |
||||
|
private QueueService queueService; |
||||
|
@Autowired |
||||
|
private RuleChainService ruleChainService; |
||||
|
@Autowired |
||||
|
private DashboardService dashboardService; |
||||
|
|
||||
|
@Override |
||||
|
protected void validateDataImpl(TenantId tenantId, AssetProfile assetProfile) { |
||||
|
if (StringUtils.isEmpty(assetProfile.getName())) { |
||||
|
throw new DataValidationException("Asset profile name should be specified!"); |
||||
|
} |
||||
|
if (assetProfile.getTenantId() == null) { |
||||
|
throw new DataValidationException("Asset profile should be assigned to tenant!"); |
||||
|
} else { |
||||
|
if (!tenantService.tenantExists(assetProfile.getTenantId())) { |
||||
|
throw new DataValidationException("Asset profile is referencing to non-existent tenant!"); |
||||
|
} |
||||
|
} |
||||
|
if (assetProfile.isDefault()) { |
||||
|
AssetProfile defaultAssetProfile = assetProfileService.findDefaultAssetProfile(tenantId); |
||||
|
if (defaultAssetProfile != null && !defaultAssetProfile.getId().equals(assetProfile.getId())) { |
||||
|
throw new DataValidationException("Another default asset profile is present in scope of current tenant!"); |
||||
|
} |
||||
|
} |
||||
|
if (StringUtils.isNotEmpty(assetProfile.getDefaultQueueName())) { |
||||
|
Queue queue = queueService.findQueueByTenantIdAndName(tenantId, assetProfile.getDefaultQueueName()); |
||||
|
if (queue == null) { |
||||
|
throw new DataValidationException("Asset profile is referencing to non-existent queue!"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (assetProfile.getDefaultRuleChainId() != null) { |
||||
|
RuleChain ruleChain = ruleChainService.findRuleChainById(tenantId, assetProfile.getDefaultRuleChainId()); |
||||
|
if (ruleChain == null) { |
||||
|
throw new DataValidationException("Can't assign non-existent rule chain!"); |
||||
|
} |
||||
|
if (!ruleChain.getTenantId().equals(assetProfile.getTenantId())) { |
||||
|
throw new DataValidationException("Can't assign rule chain from different tenant!"); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (assetProfile.getDefaultDashboardId() != null) { |
||||
|
DashboardInfo dashboard = dashboardService.findDashboardInfoById(tenantId, assetProfile.getDefaultDashboardId()); |
||||
|
if (dashboard == null) { |
||||
|
throw new DataValidationException("Can't assign non-existent dashboard!"); |
||||
|
} |
||||
|
if (!dashboard.getTenantId().equals(assetProfile.getTenantId())) { |
||||
|
throw new DataValidationException("Can't assign dashboard from different tenant!"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected AssetProfile validateUpdate(TenantId tenantId, AssetProfile assetProfile) { |
||||
|
AssetProfile old = assetProfileDao.findById(assetProfile.getTenantId(), assetProfile.getId().getId()); |
||||
|
if (old == null) { |
||||
|
throw new DataValidationException("Can't update non existing asset profile!"); |
||||
|
} |
||||
|
return old; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,63 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.sql.asset; |
||||
|
|
||||
|
import org.springframework.data.domain.Page; |
||||
|
import org.springframework.data.domain.Pageable; |
||||
|
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
import org.springframework.data.jpa.repository.Query; |
||||
|
import org.springframework.data.repository.query.Param; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfileInfo; |
||||
|
import org.thingsboard.server.dao.ExportableEntityRepository; |
||||
|
import org.thingsboard.server.dao.model.sql.AssetProfileEntity; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
|
||||
|
public interface AssetProfileRepository extends JpaRepository<AssetProfileEntity, UUID>, ExportableEntityRepository<AssetProfileEntity> { |
||||
|
|
||||
|
@Query("SELECT new org.thingsboard.server.common.data.asset.AssetProfileInfo(a.id, a.name, a.image, a.defaultDashboardId) " + |
||||
|
"FROM AssetProfileEntity a " + |
||||
|
"WHERE a.id = :assetProfileId") |
||||
|
AssetProfileInfo findAssetProfileInfoById(@Param("assetProfileId") UUID assetProfileId); |
||||
|
|
||||
|
@Query("SELECT a FROM AssetProfileEntity a WHERE " + |
||||
|
"a.tenantId = :tenantId AND LOWER(a.searchText) LIKE LOWER(CONCAT('%', :textSearch, '%'))") |
||||
|
Page<AssetProfileEntity> findAssetProfiles(@Param("tenantId") UUID tenantId, |
||||
|
@Param("textSearch") String textSearch, |
||||
|
Pageable pageable); |
||||
|
|
||||
|
@Query("SELECT new org.thingsboard.server.common.data.asset.AssetProfileInfo(a.id, a.name, a.image, a.defaultDashboardId) " + |
||||
|
"FROM AssetProfileEntity a WHERE " + |
||||
|
"a.tenantId = :tenantId AND LOWER(a.searchText) LIKE LOWER(CONCAT('%', :textSearch, '%'))") |
||||
|
Page<AssetProfileInfo> findAssetProfileInfos(@Param("tenantId") UUID tenantId, |
||||
|
@Param("textSearch") String textSearch, |
||||
|
Pageable pageable); |
||||
|
|
||||
|
@Query("SELECT a FROM AssetProfileEntity a " + |
||||
|
"WHERE a.tenantId = :tenantId AND a.isDefault = true") |
||||
|
AssetProfileEntity findByDefaultTrueAndTenantId(@Param("tenantId") UUID tenantId); |
||||
|
|
||||
|
@Query("SELECT new org.thingsboard.server.common.data.asset.AssetProfileInfo(a.id, a.name, a.image, a.defaultDashboardId) " + |
||||
|
"FROM AssetProfileEntity a " + |
||||
|
"WHERE a.tenantId = :tenantId AND a.isDefault = true") |
||||
|
AssetProfileInfo findDefaultAssetProfileInfo(@Param("tenantId") UUID tenantId); |
||||
|
|
||||
|
AssetProfileEntity findByTenantIdAndName(UUID id, String profileName); |
||||
|
|
||||
|
@Query("SELECT externalId FROM AssetProfileEntity WHERE id = :id") |
||||
|
UUID getExternalIdById(@Param("id") UUID id); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,126 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2022 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.sql.asset; |
||||
|
|
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.springframework.data.jpa.repository.JpaRepository; |
||||
|
import org.springframework.stereotype.Component; |
||||
|
import org.springframework.transaction.annotation.Transactional; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfile; |
||||
|
import org.thingsboard.server.common.data.asset.AssetProfileInfo; |
||||
|
import org.thingsboard.server.common.data.id.AssetProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.dao.DaoUtil; |
||||
|
import org.thingsboard.server.dao.asset.AssetProfileDao; |
||||
|
import org.thingsboard.server.dao.model.sql.AssetProfileEntity; |
||||
|
import org.thingsboard.server.dao.sql.JpaAbstractSearchTextDao; |
||||
|
|
||||
|
import java.util.Objects; |
||||
|
import java.util.Optional; |
||||
|
import java.util.UUID; |
||||
|
|
||||
|
@Component |
||||
|
public class JpaAssetProfileDao extends JpaAbstractSearchTextDao<AssetProfileEntity, AssetProfile> implements AssetProfileDao { |
||||
|
|
||||
|
@Autowired |
||||
|
private AssetProfileRepository assetProfileRepository; |
||||
|
|
||||
|
@Override |
||||
|
protected Class<AssetProfileEntity> getEntityClass() { |
||||
|
return AssetProfileEntity.class; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
protected JpaRepository<AssetProfileEntity, UUID> getRepository() { |
||||
|
return assetProfileRepository; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfileInfo findAssetProfileInfoById(TenantId tenantId, UUID assetProfileId) { |
||||
|
return assetProfileRepository.findAssetProfileInfoById(assetProfileId); |
||||
|
} |
||||
|
|
||||
|
@Transactional |
||||
|
@Override |
||||
|
public AssetProfile saveAndFlush(TenantId tenantId, AssetProfile assetProfile) { |
||||
|
AssetProfile result = save(tenantId, assetProfile); |
||||
|
assetProfileRepository.flush(); |
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<AssetProfile> findAssetProfiles(TenantId tenantId, PageLink pageLink) { |
||||
|
return DaoUtil.toPageData( |
||||
|
assetProfileRepository.findAssetProfiles( |
||||
|
tenantId.getId(), |
||||
|
Objects.toString(pageLink.getTextSearch(), ""), |
||||
|
DaoUtil.toPageable(pageLink))); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<AssetProfileInfo> findAssetProfileInfos(TenantId tenantId, PageLink pageLink) { |
||||
|
return DaoUtil.pageToPageData( |
||||
|
assetProfileRepository.findAssetProfileInfos( |
||||
|
tenantId.getId(), |
||||
|
Objects.toString(pageLink.getTextSearch(), ""), |
||||
|
DaoUtil.toPageable(pageLink))); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile findDefaultAssetProfile(TenantId tenantId) { |
||||
|
return DaoUtil.getData(assetProfileRepository.findByDefaultTrueAndTenantId(tenantId.getId())); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfileInfo findDefaultAssetProfileInfo(TenantId tenantId) { |
||||
|
return assetProfileRepository.findDefaultAssetProfileInfo(tenantId.getId()); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile findByName(TenantId tenantId, String profileName) { |
||||
|
return DaoUtil.getData(assetProfileRepository.findByTenantIdAndName(tenantId.getId(), profileName)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile findByTenantIdAndExternalId(UUID tenantId, UUID externalId) { |
||||
|
return DaoUtil.getData(assetProfileRepository.findByTenantIdAndExternalId(tenantId, externalId)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfile findByTenantIdAndName(UUID tenantId, String name) { |
||||
|
return DaoUtil.getData(assetProfileRepository.findByTenantIdAndName(tenantId, name)); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public PageData<AssetProfile> findByTenantId(UUID tenantId, PageLink pageLink) { |
||||
|
return findAssetProfiles(TenantId.fromUUID(tenantId), pageLink); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public AssetProfileId getExternalIdByInternal(AssetProfileId internalId) { |
||||
|
return Optional.ofNullable(assetProfileRepository.getExternalIdById(internalId.getId())) |
||||
|
.map(AssetProfileId::new).orElse(null); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public EntityType getEntityType() { |
||||
|
return EntityType.ASSET_PROFILE; |
||||
|
} |
||||
|
|
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue