603 changed files with 26763 additions and 3258 deletions
@ -0,0 +1,135 @@ |
|||||
|
{ |
||||
|
"ruleChain": { |
||||
|
"additionalInfo": { |
||||
|
"description": "" |
||||
|
}, |
||||
|
"name": "Device Profile Rule Chain Template", |
||||
|
"firstRuleNodeId": null, |
||||
|
"root": false, |
||||
|
"debugMode": false, |
||||
|
"configuration": null |
||||
|
}, |
||||
|
"metadata": { |
||||
|
"firstNodeIndex": 6, |
||||
|
"nodes": [ |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 822, |
||||
|
"layoutY": 294 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", |
||||
|
"name": "Save Timeseries", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"defaultTTL": 0 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 824, |
||||
|
"layoutY": 221 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", |
||||
|
"name": "Save Client Attributes", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"scope": "CLIENT_SCOPE" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 494, |
||||
|
"layoutY": 309 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeSwitchNode", |
||||
|
"name": "Message Type Switch", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"version": 0 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 824, |
||||
|
"layoutY": 383 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
||||
|
"name": "Log RPC from Device", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 823, |
||||
|
"layoutY": 444 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
||||
|
"name": "Log Other", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"jsScript": "return '\\nIncoming message:\\n' + JSON.stringify(msg) + '\\nIncoming metadata:\\n' + JSON.stringify(metadata);" |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"layoutX": 822, |
||||
|
"layoutY": 507 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.rpc.TbSendRPCRequestNode", |
||||
|
"name": "RPC Call Request", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"timeoutInSeconds": 60 |
||||
|
} |
||||
|
}, |
||||
|
{ |
||||
|
"additionalInfo": { |
||||
|
"description": "", |
||||
|
"layoutX": 209, |
||||
|
"layoutY": 307 |
||||
|
}, |
||||
|
"type": "org.thingsboard.rule.engine.profile.TbDeviceProfileNode", |
||||
|
"name": "Device Profile Node", |
||||
|
"debugMode": false, |
||||
|
"configuration": { |
||||
|
"persistAlarmRulesState": false |
||||
|
} |
||||
|
} |
||||
|
], |
||||
|
"connections": [ |
||||
|
{ |
||||
|
"fromIndex": 2, |
||||
|
"toIndex": 4, |
||||
|
"type": "Other" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 2, |
||||
|
"toIndex": 1, |
||||
|
"type": "Post attributes" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 2, |
||||
|
"toIndex": 0, |
||||
|
"type": "Post telemetry" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 2, |
||||
|
"toIndex": 3, |
||||
|
"type": "RPC Request from Device" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 2, |
||||
|
"toIndex": 5, |
||||
|
"type": "RPC Request to Device" |
||||
|
}, |
||||
|
{ |
||||
|
"fromIndex": 6, |
||||
|
"toIndex": 2, |
||||
|
"type": "Success" |
||||
|
} |
||||
|
], |
||||
|
"ruleChainConnections": null |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,28 @@ |
|||||
|
-- |
||||
|
-- Copyright © 2016-2020 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_tenant_profiles; |
||||
|
DROP PROCEDURE IF EXISTS update_device_profiles; |
||||
|
|
||||
|
ALTER TABLE tenant ALTER COLUMN tenant_profile_id SET NOT NULL; |
||||
|
ALTER TABLE tenant DROP CONSTRAINT IF EXISTS fk_tenant_profile; |
||||
|
ALTER TABLE tenant ADD CONSTRAINT fk_tenant_profile FOREIGN KEY (tenant_profile_id) REFERENCES tenant_profile(id); |
||||
|
ALTER TABLE tenant DROP COLUMN IF EXISTS isolated_tb_core; |
||||
|
ALTER TABLE tenant DROP COLUMN IF EXISTS isolated_tb_rule_engine; |
||||
|
|
||||
|
ALTER TABLE device ALTER COLUMN device_profile_id SET NOT NULL; |
||||
|
ALTER TABLE device DROP CONSTRAINT IF EXISTS fk_device_profile; |
||||
|
ALTER TABLE device ADD CONSTRAINT fk_device_profile FOREIGN KEY (device_profile_id) REFERENCES device_profile(id); |
||||
@ -0,0 +1,81 @@ |
|||||
|
-- |
||||
|
-- Copyright © 2016-2020 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 device_profile ( |
||||
|
id uuid NOT NULL CONSTRAINT device_profile_pkey PRIMARY KEY, |
||||
|
created_time bigint NOT NULL, |
||||
|
name varchar(255), |
||||
|
type varchar(255), |
||||
|
transport_type varchar(255), |
||||
|
profile_data jsonb, |
||||
|
description varchar, |
||||
|
search_text varchar(255), |
||||
|
is_default boolean, |
||||
|
tenant_id uuid, |
||||
|
default_rule_chain_id uuid, |
||||
|
CONSTRAINT device_profile_name_unq_key UNIQUE (tenant_id, name), |
||||
|
CONSTRAINT fk_default_rule_chain_device_profile FOREIGN KEY (default_rule_chain_id) REFERENCES rule_chain(id) |
||||
|
); |
||||
|
|
||||
|
CREATE TABLE IF NOT EXISTS tenant_profile ( |
||||
|
id uuid NOT NULL CONSTRAINT tenant_profile_pkey PRIMARY KEY, |
||||
|
created_time bigint NOT NULL, |
||||
|
name varchar(255), |
||||
|
profile_data jsonb, |
||||
|
description varchar, |
||||
|
search_text varchar(255), |
||||
|
is_default boolean, |
||||
|
isolated_tb_core boolean, |
||||
|
isolated_tb_rule_engine boolean, |
||||
|
CONSTRAINT tenant_profile_name_unq_key UNIQUE (name) |
||||
|
); |
||||
|
|
||||
|
CREATE OR REPLACE PROCEDURE update_tenant_profiles() |
||||
|
LANGUAGE plpgsql AS |
||||
|
$$ |
||||
|
BEGIN |
||||
|
UPDATE tenant as t SET tenant_profile_id = p.id |
||||
|
FROM |
||||
|
(SELECT id from tenant_profile WHERE isolated_tb_core = false AND isolated_tb_rule_engine = false) as p |
||||
|
WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = false AND t.isolated_tb_rule_engine = false; |
||||
|
|
||||
|
UPDATE tenant as t SET tenant_profile_id = p.id |
||||
|
FROM |
||||
|
(SELECT id from tenant_profile WHERE isolated_tb_core = true AND isolated_tb_rule_engine = false) as p |
||||
|
WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = true AND t.isolated_tb_rule_engine = false; |
||||
|
|
||||
|
UPDATE tenant as t SET tenant_profile_id = p.id |
||||
|
FROM |
||||
|
(SELECT id from tenant_profile WHERE isolated_tb_core = false AND isolated_tb_rule_engine = true) as p |
||||
|
WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = false AND t.isolated_tb_rule_engine = true; |
||||
|
|
||||
|
UPDATE tenant as t SET tenant_profile_id = p.id |
||||
|
FROM |
||||
|
(SELECT id from tenant_profile WHERE isolated_tb_core = true AND isolated_tb_rule_engine = true) as p |
||||
|
WHERE t.tenant_profile_id IS NULL AND t.isolated_tb_core = true AND t.isolated_tb_rule_engine = true; |
||||
|
END; |
||||
|
$$; |
||||
|
|
||||
|
CREATE OR REPLACE PROCEDURE update_device_profiles() |
||||
|
LANGUAGE plpgsql AS |
||||
|
$$ |
||||
|
BEGIN |
||||
|
UPDATE device as d SET device_profile_id = p.id, device_data = '{"configuration":{"type":"DEFAULT"}, "transportConfiguration":{"type":"DEFAULT"}}' |
||||
|
FROM |
||||
|
(SELECT id, tenant_id, name from device_profile) as p |
||||
|
WHERE d.device_profile_id IS NULL AND p.tenant_id = d.tenant_id AND d.type = p.name; |
||||
|
END; |
||||
|
$$; |
||||
@ -0,0 +1,203 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.controller; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
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.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileInfo; |
||||
|
import org.thingsboard.server.common.data.EntityType; |
||||
|
import org.thingsboard.server.common.data.audit.ActionType; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.DeviceProfileId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.plugin.ComponentLifecycleEvent; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.security.permission.Operation; |
||||
|
import org.thingsboard.server.service.security.permission.Resource; |
||||
|
|
||||
|
@RestController |
||||
|
@TbCoreComponent |
||||
|
@RequestMapping("/api") |
||||
|
@Slf4j |
||||
|
public class DeviceProfileController extends BaseController { |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public DeviceProfile getDeviceProfileById(@PathVariable("deviceProfileId") String strDeviceProfileId) throws ThingsboardException { |
||||
|
checkParameter("deviceProfileId", strDeviceProfileId); |
||||
|
try { |
||||
|
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); |
||||
|
return checkDeviceProfileId(deviceProfileId, Operation.READ); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/deviceProfileInfo/{deviceProfileId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public DeviceProfileInfo getDeviceProfileInfoById(@PathVariable("deviceProfileId") String strDeviceProfileId) throws ThingsboardException { |
||||
|
checkParameter("deviceProfileId", strDeviceProfileId); |
||||
|
try { |
||||
|
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); |
||||
|
return checkNotNull(deviceProfileService.findDeviceProfileInfoById(getTenantId(), deviceProfileId)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/deviceProfileInfo/default", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public DeviceProfileInfo getDefaultDeviceProfileInfo() throws ThingsboardException { |
||||
|
try { |
||||
|
return checkNotNull(deviceProfileService.findDefaultDeviceProfileInfo(getTenantId())); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/deviceProfile", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public DeviceProfile saveDeviceProfile(@RequestBody DeviceProfile deviceProfile) throws ThingsboardException { |
||||
|
try { |
||||
|
boolean created = deviceProfile.getId() == null; |
||||
|
deviceProfile.setTenantId(getTenantId()); |
||||
|
|
||||
|
checkEntity(deviceProfile.getId(), deviceProfile, Resource.DEVICE_PROFILE); |
||||
|
|
||||
|
DeviceProfile savedDeviceProfile = checkNotNull(deviceProfileService.saveDeviceProfile(deviceProfile)); |
||||
|
|
||||
|
deviceProfileCache.put(savedDeviceProfile); |
||||
|
tbClusterService.onDeviceProfileChange(savedDeviceProfile, null); |
||||
|
tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), savedDeviceProfile.getId(), |
||||
|
created ? ComponentLifecycleEvent.CREATED : ComponentLifecycleEvent.UPDATED); |
||||
|
|
||||
|
logEntityAction(savedDeviceProfile.getId(), savedDeviceProfile, |
||||
|
null, |
||||
|
savedDeviceProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED, null); |
||||
|
|
||||
|
return savedDeviceProfile; |
||||
|
} catch (Exception e) { |
||||
|
logEntityAction(emptyId(EntityType.DEVICE_PROFILE), deviceProfile, |
||||
|
null, deviceProfile.getId() == null ? ActionType.ADDED : ActionType.UPDATED, e); |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/deviceProfile/{deviceProfileId}", method = RequestMethod.DELETE) |
||||
|
@ResponseStatus(value = HttpStatus.OK) |
||||
|
public void deleteDeviceProfile(@PathVariable("deviceProfileId") String strDeviceProfileId) throws ThingsboardException { |
||||
|
checkParameter("deviceProfileId", strDeviceProfileId); |
||||
|
try { |
||||
|
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); |
||||
|
DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.DELETE); |
||||
|
deviceProfileService.deleteDeviceProfile(getTenantId(), deviceProfileId); |
||||
|
deviceProfileCache.evict(deviceProfileId); |
||||
|
|
||||
|
tbClusterService.onDeviceProfileDelete(deviceProfile, null); |
||||
|
tbClusterService.onEntityStateChange(deviceProfile.getTenantId(), deviceProfile.getId(), ComponentLifecycleEvent.DELETED); |
||||
|
|
||||
|
logEntityAction(deviceProfileId, deviceProfile, |
||||
|
null, |
||||
|
ActionType.DELETED, null, strDeviceProfileId); |
||||
|
|
||||
|
} catch (Exception e) { |
||||
|
logEntityAction(emptyId(EntityType.DEVICE_PROFILE), |
||||
|
null, |
||||
|
null, |
||||
|
ActionType.DELETED, e, strDeviceProfileId); |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/deviceProfile/{deviceProfileId}/default", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public DeviceProfile setDefaultDeviceProfile(@PathVariable("deviceProfileId") String strDeviceProfileId) throws ThingsboardException { |
||||
|
checkParameter("deviceProfileId", strDeviceProfileId); |
||||
|
try { |
||||
|
DeviceProfileId deviceProfileId = new DeviceProfileId(toUUID(strDeviceProfileId)); |
||||
|
DeviceProfile deviceProfile = checkDeviceProfileId(deviceProfileId, Operation.WRITE); |
||||
|
DeviceProfile previousDefaultDeviceProfile = deviceProfileService.findDefaultDeviceProfile(getTenantId()); |
||||
|
if (deviceProfileService.setDefaultDeviceProfile(getTenantId(), deviceProfileId)) { |
||||
|
if (previousDefaultDeviceProfile != null) { |
||||
|
previousDefaultDeviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), previousDefaultDeviceProfile.getId()); |
||||
|
|
||||
|
logEntityAction(previousDefaultDeviceProfile.getId(), previousDefaultDeviceProfile, |
||||
|
null, ActionType.UPDATED, null); |
||||
|
} |
||||
|
deviceProfile = deviceProfileService.findDeviceProfileById(getTenantId(), deviceProfileId); |
||||
|
|
||||
|
logEntityAction(deviceProfile.getId(), deviceProfile, |
||||
|
null, ActionType.UPDATED, null); |
||||
|
} |
||||
|
return deviceProfile; |
||||
|
} catch (Exception e) { |
||||
|
logEntityAction(emptyId(EntityType.DEVICE_PROFILE), |
||||
|
null, |
||||
|
null, |
||||
|
ActionType.UPDATED, e, strDeviceProfileId); |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('TENANT_ADMIN')") |
||||
|
@RequestMapping(value = "/deviceProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<DeviceProfile> getDeviceProfiles(@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return checkNotNull(deviceProfileService.findDeviceProfiles(getTenantId(), pageLink)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('TENANT_ADMIN', 'CUSTOMER_USER')") |
||||
|
@RequestMapping(value = "/deviceProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<DeviceProfileInfo> getDeviceProfileInfos(@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return checkNotNull(deviceProfileService.findDeviceProfileInfos(getTenantId(), pageLink)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,162 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
package org.thingsboard.server.controller; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
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.EntityInfo; |
||||
|
import org.thingsboard.server.common.data.TenantProfile; |
||||
|
import org.thingsboard.server.common.data.exception.ThingsboardException; |
||||
|
import org.thingsboard.server.common.data.id.TenantProfileId; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.queue.util.TbCoreComponent; |
||||
|
import org.thingsboard.server.service.security.permission.Operation; |
||||
|
import org.thingsboard.server.service.security.permission.Resource; |
||||
|
|
||||
|
@RestController |
||||
|
@TbCoreComponent |
||||
|
@RequestMapping("/api") |
||||
|
@Slf4j |
||||
|
public class TenantProfileController extends BaseController { |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public TenantProfile getTenantProfileById(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { |
||||
|
checkParameter("tenantProfileId", strTenantProfileId); |
||||
|
try { |
||||
|
TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); |
||||
|
return checkTenantProfileId(tenantProfileId, Operation.READ); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@RequestMapping(value = "/tenantProfileInfo/{tenantProfileId}", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public EntityInfo getTenantProfileInfoById(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { |
||||
|
checkParameter("tenantProfileId", strTenantProfileId); |
||||
|
try { |
||||
|
TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); |
||||
|
return checkNotNull(tenantProfileService.findTenantProfileInfoById(getTenantId(), tenantProfileId)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@RequestMapping(value = "/tenantProfileInfo/default", method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public EntityInfo getDefaultTenantProfileInfo() throws ThingsboardException { |
||||
|
try { |
||||
|
return checkNotNull(tenantProfileService.findDefaultTenantProfileInfo(getTenantId())); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('SYS_ADMIN')") |
||||
|
@RequestMapping(value = "/tenantProfile", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public TenantProfile saveTenantProfile(@RequestBody TenantProfile tenantProfile) throws ThingsboardException { |
||||
|
try { |
||||
|
boolean newTenantProfile = tenantProfile.getId() == null; |
||||
|
if (newTenantProfile) { |
||||
|
accessControlService |
||||
|
.checkPermission(getCurrentUser(), Resource.TENANT_PROFILE, Operation.CREATE); |
||||
|
} else { |
||||
|
checkEntityId(tenantProfile.getId(), Operation.WRITE); |
||||
|
} |
||||
|
|
||||
|
tenantProfile = checkNotNull(tenantProfileService.saveTenantProfile(getTenantId(), tenantProfile)); |
||||
|
return tenantProfile; |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('SYS_ADMIN')") |
||||
|
@RequestMapping(value = "/tenantProfile/{tenantProfileId}", method = RequestMethod.DELETE) |
||||
|
@ResponseStatus(value = HttpStatus.OK) |
||||
|
public void deleteTenantProfile(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { |
||||
|
checkParameter("tenantProfileId", strTenantProfileId); |
||||
|
try { |
||||
|
TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); |
||||
|
checkTenantProfileId(tenantProfileId, Operation.DELETE); |
||||
|
tenantProfileService.deleteTenantProfile(getTenantId(), tenantProfileId); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAnyAuthority('SYS_ADMIN')") |
||||
|
@RequestMapping(value = "/tenantProfile/{tenantProfileId}/default", method = RequestMethod.POST) |
||||
|
@ResponseBody |
||||
|
public TenantProfile setDefaultTenantProfile(@PathVariable("tenantProfileId") String strTenantProfileId) throws ThingsboardException { |
||||
|
checkParameter("tenantProfileId", strTenantProfileId); |
||||
|
try { |
||||
|
TenantProfileId tenantProfileId = new TenantProfileId(toUUID(strTenantProfileId)); |
||||
|
TenantProfile tenantProfile = checkTenantProfileId(tenantProfileId, Operation.WRITE); |
||||
|
tenantProfileService.setDefaultTenantProfile(getTenantId(), tenantProfileId); |
||||
|
return tenantProfile; |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('SYS_ADMIN')") |
||||
|
@RequestMapping(value = "/tenantProfiles", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<TenantProfile> getTenantProfiles(@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return checkNotNull(tenantProfileService.findTenantProfiles(getTenantId(), pageLink)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@PreAuthorize("hasAuthority('SYS_ADMIN')") |
||||
|
@RequestMapping(value = "/tenantProfileInfos", params = {"pageSize", "page"}, method = RequestMethod.GET) |
||||
|
@ResponseBody |
||||
|
public PageData<EntityInfo> getTenantProfileInfos(@RequestParam int pageSize, |
||||
|
@RequestParam int page, |
||||
|
@RequestParam(required = false) String textSearch, |
||||
|
@RequestParam(required = false) String sortProperty, |
||||
|
@RequestParam(required = false) String sortOrder) throws ThingsboardException { |
||||
|
try { |
||||
|
PageLink pageLink = createPageLink(pageSize, page, textSearch, sortProperty, sortOrder); |
||||
|
return checkNotNull(tenantProfileService.findTenantProfileInfos(getTenantId(), pageLink)); |
||||
|
} catch (Exception e) { |
||||
|
throw handleException(e); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,99 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceProfileId; |
||||
|
import org.thingsboard.server.common.data.id.TenantId; |
||||
|
import org.thingsboard.server.dao.device.DeviceProfileService; |
||||
|
import org.thingsboard.server.dao.device.DeviceService; |
||||
|
|
||||
|
import java.util.concurrent.ConcurrentHashMap; |
||||
|
import java.util.concurrent.ConcurrentMap; |
||||
|
import java.util.concurrent.locks.Lock; |
||||
|
import java.util.concurrent.locks.ReentrantLock; |
||||
|
|
||||
|
@Service |
||||
|
@Slf4j |
||||
|
public class DefaultTbDeviceProfileCache implements TbDeviceProfileCache { |
||||
|
|
||||
|
private final Lock deviceProfileFetchLock = new ReentrantLock(); |
||||
|
private final DeviceProfileService deviceProfileService; |
||||
|
private final DeviceService deviceService; |
||||
|
|
||||
|
private final ConcurrentMap<DeviceProfileId, DeviceProfile> deviceProfilesMap = new ConcurrentHashMap<>(); |
||||
|
private final ConcurrentMap<DeviceId, DeviceProfileId> devicesMap = new ConcurrentHashMap<>(); |
||||
|
|
||||
|
public DefaultTbDeviceProfileCache(DeviceProfileService deviceProfileService, DeviceService deviceService) { |
||||
|
this.deviceProfileService = deviceProfileService; |
||||
|
this.deviceService = deviceService; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public DeviceProfile get(TenantId tenantId, DeviceProfileId deviceProfileId) { |
||||
|
DeviceProfile profile = deviceProfilesMap.get(deviceProfileId); |
||||
|
if (profile == null) { |
||||
|
profile = deviceProfilesMap.get(deviceProfileId); |
||||
|
if (profile == null) { |
||||
|
deviceProfileFetchLock.lock(); |
||||
|
try { |
||||
|
profile = deviceProfileService.findDeviceProfileById(tenantId, deviceProfileId); |
||||
|
if (profile != null) { |
||||
|
deviceProfilesMap.put(deviceProfileId, profile); |
||||
|
} |
||||
|
} finally { |
||||
|
deviceProfileFetchLock.unlock(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
return profile; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public DeviceProfile get(TenantId tenantId, DeviceId deviceId) { |
||||
|
DeviceProfileId profileId = devicesMap.get(deviceId); |
||||
|
if (profileId == null) { |
||||
|
Device device = deviceService.findDeviceById(tenantId, deviceId); |
||||
|
if (device != null) { |
||||
|
profileId = device.getDeviceProfileId(); |
||||
|
devicesMap.put(deviceId, profileId); |
||||
|
} |
||||
|
} |
||||
|
return get(tenantId, profileId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void put(DeviceProfile profile) { |
||||
|
if (profile.getId() != null) { |
||||
|
deviceProfilesMap.put(profile.getId(), profile); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void evict(DeviceProfileId profileId) { |
||||
|
deviceProfilesMap.remove(profileId); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void evict(DeviceId deviceId) { |
||||
|
devicesMap.remove(deviceId); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,31 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.RuleEngineDeviceProfileCache; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.id.DeviceId; |
||||
|
import org.thingsboard.server.common.data.id.DeviceProfileId; |
||||
|
|
||||
|
public interface TbDeviceProfileCache extends RuleEngineDeviceProfileCache { |
||||
|
|
||||
|
void put(DeviceProfile profile); |
||||
|
|
||||
|
void evict(DeviceProfileId id); |
||||
|
|
||||
|
void evict(DeviceId id); |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,85 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.queue; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.thingsboard.server.common.data.id.RuleNodeId; |
||||
|
import org.thingsboard.server.common.msg.queue.RuleNodeInfo; |
||||
|
|
||||
|
import java.util.AbstractMap; |
||||
|
import java.util.Map; |
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.atomic.AtomicLong; |
||||
|
import java.util.concurrent.locks.Lock; |
||||
|
import java.util.concurrent.locks.ReentrantLock; |
||||
|
|
||||
|
@Slf4j |
||||
|
public class TbMsgProfilerInfo { |
||||
|
private final UUID msgId; |
||||
|
private AtomicLong totalProcessingTime = new AtomicLong(); |
||||
|
private Lock stateLock = new ReentrantLock(); |
||||
|
private RuleNodeId currentRuleNodeId; |
||||
|
private long stateChangeTime; |
||||
|
|
||||
|
public TbMsgProfilerInfo(UUID msgId) { |
||||
|
this.msgId = msgId; |
||||
|
} |
||||
|
|
||||
|
public void onStart(RuleNodeId ruleNodeId) { |
||||
|
long currentTime = System.currentTimeMillis(); |
||||
|
stateLock.lock(); |
||||
|
try { |
||||
|
currentRuleNodeId = ruleNodeId; |
||||
|
stateChangeTime = currentTime; |
||||
|
} finally { |
||||
|
stateLock.unlock(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public long onEnd(RuleNodeId ruleNodeId) { |
||||
|
long currentTime = System.currentTimeMillis(); |
||||
|
stateLock.lock(); |
||||
|
try { |
||||
|
if (ruleNodeId.equals(currentRuleNodeId)) { |
||||
|
long processingTime = currentTime - stateChangeTime; |
||||
|
stateChangeTime = currentTime; |
||||
|
totalProcessingTime.addAndGet(processingTime); |
||||
|
currentRuleNodeId = null; |
||||
|
return processingTime; |
||||
|
} else { |
||||
|
log.trace("[{}] Invalid sequence of rule node processing detected. Expected [{}] but was [{}]", msgId, currentRuleNodeId, ruleNodeId); |
||||
|
return 0; |
||||
|
} |
||||
|
} finally { |
||||
|
stateLock.unlock(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
public Map.Entry<UUID, Long> onTimeout() { |
||||
|
long currentTime = System.currentTimeMillis(); |
||||
|
stateLock.lock(); |
||||
|
try { |
||||
|
if (currentRuleNodeId != null && stateChangeTime > 0) { |
||||
|
long timeoutTime = currentTime - stateChangeTime; |
||||
|
totalProcessingTime.addAndGet(timeoutTime); |
||||
|
return new AbstractMap.SimpleEntry<>(currentRuleNodeId.getId(), timeoutTime); |
||||
|
} |
||||
|
} finally { |
||||
|
stateLock.unlock(); |
||||
|
} |
||||
|
return null; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,75 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.queue; |
||||
|
|
||||
|
import lombok.Getter; |
||||
|
import org.thingsboard.server.common.msg.queue.RuleNodeInfo; |
||||
|
|
||||
|
import java.util.UUID; |
||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
import java.util.concurrent.atomic.AtomicLong; |
||||
|
|
||||
|
public class TbRuleNodeProfilerInfo { |
||||
|
@Getter |
||||
|
private final UUID ruleNodeId; |
||||
|
@Getter |
||||
|
private final String label; |
||||
|
private AtomicInteger executionCount = new AtomicInteger(0); |
||||
|
private AtomicLong executionTime = new AtomicLong(0); |
||||
|
private AtomicLong maxExecutionTime = new AtomicLong(0); |
||||
|
|
||||
|
public TbRuleNodeProfilerInfo(RuleNodeInfo ruleNodeInfo) { |
||||
|
this.ruleNodeId = ruleNodeInfo.getRuleNodeId().getId(); |
||||
|
this.label = ruleNodeInfo.toString(); |
||||
|
} |
||||
|
|
||||
|
public TbRuleNodeProfilerInfo(UUID ruleNodeId) { |
||||
|
this.ruleNodeId = ruleNodeId; |
||||
|
this.label = ""; |
||||
|
} |
||||
|
|
||||
|
public void record(long processingTime) { |
||||
|
executionCount.incrementAndGet(); |
||||
|
executionTime.addAndGet(processingTime); |
||||
|
while (true) { |
||||
|
long value = maxExecutionTime.get(); |
||||
|
if (value >= processingTime) { |
||||
|
break; |
||||
|
} |
||||
|
if (maxExecutionTime.compareAndSet(value, processingTime)) { |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
int getExecutionCount() { |
||||
|
return executionCount.get(); |
||||
|
} |
||||
|
|
||||
|
long getMaxExecutionTime() { |
||||
|
return maxExecutionTime.get(); |
||||
|
} |
||||
|
|
||||
|
double getAvgExecutionTime() { |
||||
|
double executionCnt = (double) executionCount.get(); |
||||
|
if (executionCnt > 0) { |
||||
|
return executionTime.get() / executionCnt; |
||||
|
} else { |
||||
|
return 0.0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,309 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.Ignore; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileInfo; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileType; |
||||
|
import org.thingsboard.server.common.data.DeviceTransportType; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.page.PageData; |
||||
|
import org.thingsboard.server.common.data.page.PageLink; |
||||
|
import org.thingsboard.server.common.data.security.Authority; |
||||
|
|
||||
|
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 BaseDeviceProfileControllerTest extends AbstractControllerTest { |
||||
|
|
||||
|
private IdComparator<DeviceProfile> idComparator = new IdComparator<>(); |
||||
|
private IdComparator<DeviceProfileInfo> deviceProfileInfoIdComparator = new IdComparator<>(); |
||||
|
|
||||
|
private Tenant savedTenant; |
||||
|
private User tenantAdmin; |
||||
|
|
||||
|
@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(); |
||||
|
|
||||
|
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveDeviceProfile() throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); |
||||
|
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
Assert.assertNotNull(savedDeviceProfile); |
||||
|
Assert.assertNotNull(savedDeviceProfile.getId()); |
||||
|
Assert.assertTrue(savedDeviceProfile.getCreatedTime() > 0); |
||||
|
Assert.assertEquals(deviceProfile.getName(), savedDeviceProfile.getName()); |
||||
|
Assert.assertEquals(deviceProfile.getDescription(), savedDeviceProfile.getDescription()); |
||||
|
Assert.assertEquals(deviceProfile.getProfileData(), savedDeviceProfile.getProfileData()); |
||||
|
Assert.assertEquals(deviceProfile.isDefault(), savedDeviceProfile.isDefault()); |
||||
|
Assert.assertEquals(deviceProfile.getDefaultRuleChainId(), savedDeviceProfile.getDefaultRuleChainId()); |
||||
|
savedDeviceProfile.setName("New device profile"); |
||||
|
doPost("/api/deviceProfile", savedDeviceProfile, DeviceProfile.class); |
||||
|
DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class); |
||||
|
Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfile.getName()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindDeviceProfileById() throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); |
||||
|
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
DeviceProfile foundDeviceProfile = doGet("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString(), DeviceProfile.class); |
||||
|
Assert.assertNotNull(foundDeviceProfile); |
||||
|
Assert.assertEquals(savedDeviceProfile, foundDeviceProfile); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindDeviceProfileInfoById() throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); |
||||
|
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
DeviceProfileInfo foundDeviceProfileInfo = doGet("/api/deviceProfileInfo/"+savedDeviceProfile.getId().getId().toString(), DeviceProfileInfo.class); |
||||
|
Assert.assertNotNull(foundDeviceProfileInfo); |
||||
|
Assert.assertEquals(savedDeviceProfile.getId(), foundDeviceProfileInfo.getId()); |
||||
|
Assert.assertEquals(savedDeviceProfile.getName(), foundDeviceProfileInfo.getName()); |
||||
|
Assert.assertEquals(savedDeviceProfile.getType(), foundDeviceProfileInfo.getType()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindDefaultDeviceProfileInfo() throws Exception { |
||||
|
DeviceProfileInfo foundDefaultDeviceProfileInfo = doGet("/api/deviceProfileInfo/default", DeviceProfileInfo.class); |
||||
|
Assert.assertNotNull(foundDefaultDeviceProfileInfo); |
||||
|
Assert.assertNotNull(foundDefaultDeviceProfileInfo.getId()); |
||||
|
Assert.assertNotNull(foundDefaultDeviceProfileInfo.getName()); |
||||
|
Assert.assertNotNull(foundDefaultDeviceProfileInfo.getType()); |
||||
|
Assert.assertEquals(DeviceProfileType.DEFAULT, foundDefaultDeviceProfileInfo.getType()); |
||||
|
Assert.assertEquals("default", foundDefaultDeviceProfileInfo.getName()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSetDefaultDeviceProfile() throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile 1"); |
||||
|
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
DeviceProfile defaultDeviceProfile = doPost("/api/deviceProfile/"+savedDeviceProfile.getId().getId().toString()+"/default", null, DeviceProfile.class); |
||||
|
Assert.assertNotNull(defaultDeviceProfile); |
||||
|
DeviceProfileInfo foundDefaultDeviceProfile = doGet("/api/deviceProfileInfo/default", DeviceProfileInfo.class); |
||||
|
Assert.assertNotNull(foundDefaultDeviceProfile); |
||||
|
Assert.assertEquals(savedDeviceProfile.getName(), foundDefaultDeviceProfile.getName()); |
||||
|
Assert.assertEquals(savedDeviceProfile.getId(), foundDefaultDeviceProfile.getId()); |
||||
|
Assert.assertEquals(savedDeviceProfile.getType(), foundDefaultDeviceProfile.getType()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveDeviceProfileWithEmptyName() throws Exception { |
||||
|
DeviceProfile deviceProfile = new DeviceProfile(); |
||||
|
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Device profile name should be specified"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveDeviceProfileWithSameName() throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); |
||||
|
doPost("/api/deviceProfile", deviceProfile).andExpect(status().isOk()); |
||||
|
DeviceProfile deviceProfile2 = this.createDeviceProfile("Device Profile"); |
||||
|
doPost("/api/deviceProfile", deviceProfile2).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Device profile with such name already exists"))); |
||||
|
} |
||||
|
|
||||
|
@Ignore |
||||
|
@Test |
||||
|
public void testChangeDeviceProfileTypeWithExistingDevices() throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); |
||||
|
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
Device device = new Device(); |
||||
|
device.setName("Test device"); |
||||
|
device.setType("default"); |
||||
|
device.setDeviceProfileId(savedDeviceProfile.getId()); |
||||
|
doPost("/api/device", device, Device.class); |
||||
|
//TODO uncomment once we have other device types;
|
||||
|
//savedDeviceProfile.setType(DeviceProfileType.LWM2M);
|
||||
|
doPost("/api/deviceProfile", savedDeviceProfile).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Can't change device profile type because devices referenced it"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testChangeDeviceProfileTransportTypeWithExistingDevices() throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); |
||||
|
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
Device device = new Device(); |
||||
|
device.setName("Test device"); |
||||
|
device.setType("default"); |
||||
|
device.setDeviceProfileId(savedDeviceProfile.getId()); |
||||
|
doPost("/api/device", device, Device.class); |
||||
|
savedDeviceProfile.setTransportType(DeviceTransportType.MQTT); |
||||
|
doPost("/api/deviceProfile", savedDeviceProfile).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Can't change device profile transport type because devices referenced it"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteDeviceProfileWithExistingDevice() throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); |
||||
|
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
|
||||
|
Device device = new Device(); |
||||
|
device.setName("Test device"); |
||||
|
device.setType("default"); |
||||
|
device.setDeviceProfileId(savedDeviceProfile.getId()); |
||||
|
|
||||
|
Device savedDevice = doPost("/api/device", device, Device.class); |
||||
|
|
||||
|
doDelete("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("The device profile referenced by the devices cannot be deleted"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteDeviceProfile() throws Exception { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"); |
||||
|
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class); |
||||
|
|
||||
|
doDelete("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
|
||||
|
doGet("/api/deviceProfile/" + savedDeviceProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isNotFound()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindDeviceProfiles() throws Exception { |
||||
|
List<DeviceProfile> deviceProfiles = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(17); |
||||
|
PageData<DeviceProfile> pageData = doGetTypedWithPageLink("/api/deviceProfiles?", |
||||
|
new TypeReference<PageData<DeviceProfile>>(){}, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertEquals(1, pageData.getTotalElements()); |
||||
|
deviceProfiles.addAll(pageData.getData()); |
||||
|
|
||||
|
for (int i=0;i<28;i++) { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i); |
||||
|
deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class)); |
||||
|
} |
||||
|
|
||||
|
List<DeviceProfile> loadedDeviceProfiles = new ArrayList<>(); |
||||
|
pageLink = new PageLink(17); |
||||
|
do { |
||||
|
pageData = doGetTypedWithPageLink("/api/deviceProfiles?", |
||||
|
new TypeReference<PageData<DeviceProfile>>(){}, pageLink); |
||||
|
loadedDeviceProfiles.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(deviceProfiles, idComparator); |
||||
|
Collections.sort(loadedDeviceProfiles, idComparator); |
||||
|
|
||||
|
Assert.assertEquals(deviceProfiles, loadedDeviceProfiles); |
||||
|
|
||||
|
for (DeviceProfile deviceProfile : loadedDeviceProfiles) { |
||||
|
if (!deviceProfile.isDefault()) { |
||||
|
doDelete("/api/deviceProfile/" + deviceProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pageLink = new PageLink(17); |
||||
|
pageData = doGetTypedWithPageLink("/api/deviceProfiles?", |
||||
|
new TypeReference<PageData<DeviceProfile>>(){}, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertEquals(1, pageData.getTotalElements()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindDeviceProfileInfos() throws Exception { |
||||
|
List<DeviceProfile> deviceProfiles = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(17); |
||||
|
PageData<DeviceProfile> deviceProfilePageData = doGetTypedWithPageLink("/api/deviceProfiles?", |
||||
|
new TypeReference<PageData<DeviceProfile>>(){}, pageLink); |
||||
|
Assert.assertFalse(deviceProfilePageData.hasNext()); |
||||
|
Assert.assertEquals(1, deviceProfilePageData.getTotalElements()); |
||||
|
deviceProfiles.addAll(deviceProfilePageData.getData()); |
||||
|
|
||||
|
for (int i=0;i<28;i++) { |
||||
|
DeviceProfile deviceProfile = this.createDeviceProfile("Device Profile"+i); |
||||
|
deviceProfiles.add(doPost("/api/deviceProfile", deviceProfile, DeviceProfile.class)); |
||||
|
} |
||||
|
|
||||
|
List<DeviceProfileInfo> loadedDeviceProfileInfos = new ArrayList<>(); |
||||
|
pageLink = new PageLink(17); |
||||
|
PageData<DeviceProfileInfo> pageData; |
||||
|
do { |
||||
|
pageData = doGetTypedWithPageLink("/api/deviceProfileInfos?", |
||||
|
new TypeReference<PageData<DeviceProfileInfo>>(){}, pageLink); |
||||
|
loadedDeviceProfileInfos.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(deviceProfiles, idComparator); |
||||
|
Collections.sort(loadedDeviceProfileInfos, deviceProfileInfoIdComparator); |
||||
|
|
||||
|
List<DeviceProfileInfo> deviceProfileInfos = deviceProfiles.stream().map(deviceProfile -> new DeviceProfileInfo(deviceProfile.getId(), |
||||
|
deviceProfile.getName(), deviceProfile.getType(), deviceProfile.getTransportType())).collect(Collectors.toList()); |
||||
|
|
||||
|
Assert.assertEquals(deviceProfileInfos, loadedDeviceProfileInfos); |
||||
|
|
||||
|
for (DeviceProfile deviceProfile : deviceProfiles) { |
||||
|
if (!deviceProfile.isDefault()) { |
||||
|
doDelete("/api/deviceProfile/" + deviceProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pageLink = new PageLink(17); |
||||
|
pageData = doGetTypedWithPageLink("/api/deviceProfileInfos?", |
||||
|
new TypeReference<PageData<DeviceProfileInfo>>(){}, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertEquals(1, pageData.getTotalElements()); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,294 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.Test; |
||||
|
import org.springframework.beans.factory.annotation.Autowired; |
||||
|
import org.thingsboard.server.common.data.EntityInfo; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.TenantProfile; |
||||
|
import org.thingsboard.server.common.data.TenantProfileData; |
||||
|
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.tenant.TenantProfileService; |
||||
|
|
||||
|
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 BaseTenantProfileControllerTest extends AbstractControllerTest { |
||||
|
|
||||
|
private IdComparator<TenantProfile> idComparator = new IdComparator<>(); |
||||
|
private IdComparator<EntityInfo> tenantProfileInfoIdComparator = new IdComparator<>(); |
||||
|
|
||||
|
@Autowired |
||||
|
private TenantProfileService tenantProfileService; |
||||
|
|
||||
|
@After |
||||
|
@Override |
||||
|
public void teardown() throws Exception { |
||||
|
super.teardown(); |
||||
|
tenantProfileService.deleteTenantProfiles(TenantId.SYS_TENANT_ID); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveTenantProfile() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); |
||||
|
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); |
||||
|
Assert.assertNotNull(savedTenantProfile); |
||||
|
Assert.assertNotNull(savedTenantProfile.getId()); |
||||
|
Assert.assertTrue(savedTenantProfile.getCreatedTime() > 0); |
||||
|
Assert.assertEquals(tenantProfile.getName(), savedTenantProfile.getName()); |
||||
|
Assert.assertEquals(tenantProfile.getDescription(), savedTenantProfile.getDescription()); |
||||
|
Assert.assertEquals(tenantProfile.getProfileData(), savedTenantProfile.getProfileData()); |
||||
|
Assert.assertEquals(tenantProfile.isDefault(), savedTenantProfile.isDefault()); |
||||
|
Assert.assertEquals(tenantProfile.isIsolatedTbCore(), savedTenantProfile.isIsolatedTbCore()); |
||||
|
Assert.assertEquals(tenantProfile.isIsolatedTbRuleEngine(), savedTenantProfile.isIsolatedTbRuleEngine()); |
||||
|
|
||||
|
savedTenantProfile.setName("New tenant profile"); |
||||
|
doPost("/api/tenantProfile", savedTenantProfile, TenantProfile.class); |
||||
|
TenantProfile foundTenantProfile = doGet("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString(), TenantProfile.class); |
||||
|
Assert.assertEquals(foundTenantProfile.getName(), savedTenantProfile.getName()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindTenantProfileById() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); |
||||
|
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); |
||||
|
TenantProfile foundTenantProfile = doGet("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString(), TenantProfile.class); |
||||
|
Assert.assertNotNull(foundTenantProfile); |
||||
|
Assert.assertEquals(savedTenantProfile, foundTenantProfile); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindTenantProfileInfoById() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); |
||||
|
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); |
||||
|
EntityInfo foundTenantProfileInfo = doGet("/api/tenantProfileInfo/"+savedTenantProfile.getId().getId().toString(), EntityInfo.class); |
||||
|
Assert.assertNotNull(foundTenantProfileInfo); |
||||
|
Assert.assertEquals(savedTenantProfile.getId(), foundTenantProfileInfo.getId()); |
||||
|
Assert.assertEquals(savedTenantProfile.getName(), foundTenantProfileInfo.getName()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindDefaultTenantProfileInfo() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
EntityInfo foundDefaultTenantProfile = doGet("/api/tenantProfileInfo/default", EntityInfo.class); |
||||
|
Assert.assertNotNull(foundDefaultTenantProfile); |
||||
|
Assert.assertEquals("Default", foundDefaultTenantProfile.getName()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSetDefaultTenantProfile() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile 1"); |
||||
|
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); |
||||
|
TenantProfile defaultTenantProfile = doPost("/api/tenantProfile/"+savedTenantProfile.getId().getId().toString()+"/default", null, TenantProfile.class); |
||||
|
Assert.assertNotNull(defaultTenantProfile); |
||||
|
EntityInfo foundDefaultTenantProfile = doGet("/api/tenantProfileInfo/default", EntityInfo.class); |
||||
|
Assert.assertNotNull(foundDefaultTenantProfile); |
||||
|
Assert.assertEquals(savedTenantProfile.getName(), foundDefaultTenantProfile.getName()); |
||||
|
Assert.assertEquals(savedTenantProfile.getId(), foundDefaultTenantProfile.getId()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveTenantProfileWithEmptyName() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = new TenantProfile(); |
||||
|
doPost("/api/tenantProfile", tenantProfile).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Tenant profile name should be specified"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveTenantProfileWithSameName() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); |
||||
|
doPost("/api/tenantProfile", tenantProfile).andExpect(status().isOk()); |
||||
|
TenantProfile tenantProfile2 = this.createTenantProfile("Tenant Profile"); |
||||
|
doPost("/api/tenantProfile", tenantProfile2).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Tenant profile with such name already exists"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveSameTenantProfileWithDifferentIsolatedTbRuleEngine() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); |
||||
|
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); |
||||
|
savedTenantProfile.setIsolatedTbRuleEngine(true); |
||||
|
doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Can't update isolatedTbRuleEngine property"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSaveSameTenantProfileWithDifferentIsolatedTbCore() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); |
||||
|
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); |
||||
|
savedTenantProfile.setIsolatedTbCore(true); |
||||
|
doPost("/api/tenantProfile", savedTenantProfile).andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("Can't update isolatedTbCore property"))); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteTenantProfileWithExistingTenant() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); |
||||
|
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); |
||||
|
|
||||
|
Tenant tenant = new Tenant(); |
||||
|
tenant.setTitle("My tenant with tenant profile"); |
||||
|
tenant.setTenantProfileId(savedTenantProfile.getId()); |
||||
|
Tenant savedTenant = doPost("/api/tenant", tenant, Tenant.class); |
||||
|
|
||||
|
doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isBadRequest()) |
||||
|
.andExpect(statusReason(containsString("The tenant profile referenced by the tenants cannot be deleted"))); |
||||
|
|
||||
|
doDelete("/api/tenant/"+savedTenant.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testDeleteTenantProfile() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"); |
||||
|
TenantProfile savedTenantProfile = doPost("/api/tenantProfile", tenantProfile, TenantProfile.class); |
||||
|
|
||||
|
doDelete("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
|
||||
|
doGet("/api/tenantProfile/" + savedTenantProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isNotFound()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindTenantProfiles() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
List<TenantProfile> tenantProfiles = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(17); |
||||
|
PageData<TenantProfile> pageData = doGetTypedWithPageLink("/api/tenantProfiles?", |
||||
|
new TypeReference<PageData<TenantProfile>>(){}, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertEquals(1, pageData.getTotalElements()); |
||||
|
tenantProfiles.addAll(pageData.getData()); |
||||
|
|
||||
|
for (int i=0;i<28;i++) { |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i); |
||||
|
tenantProfiles.add(doPost("/api/tenantProfile", tenantProfile, TenantProfile.class)); |
||||
|
} |
||||
|
|
||||
|
List<TenantProfile> loadedTenantProfiles = new ArrayList<>(); |
||||
|
pageLink = new PageLink(17); |
||||
|
do { |
||||
|
pageData = doGetTypedWithPageLink("/api/tenantProfiles?", |
||||
|
new TypeReference<PageData<TenantProfile>>(){}, pageLink); |
||||
|
loadedTenantProfiles.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(tenantProfiles, idComparator); |
||||
|
Collections.sort(loadedTenantProfiles, idComparator); |
||||
|
|
||||
|
Assert.assertEquals(tenantProfiles, loadedTenantProfiles); |
||||
|
|
||||
|
for (TenantProfile tenantProfile : loadedTenantProfiles) { |
||||
|
if (!tenantProfile.isDefault()) { |
||||
|
doDelete("/api/tenantProfile/" + tenantProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pageLink = new PageLink(17); |
||||
|
pageData = doGetTypedWithPageLink("/api/tenantProfiles?", |
||||
|
new TypeReference<PageData<TenantProfile>>(){}, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertEquals(1, pageData.getTotalElements()); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testFindTenantProfileInfos() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
List<TenantProfile> tenantProfiles = new ArrayList<>(); |
||||
|
PageLink pageLink = new PageLink(17); |
||||
|
PageData<TenantProfile> tenantProfilePageData = doGetTypedWithPageLink("/api/tenantProfiles?", |
||||
|
new TypeReference<PageData<TenantProfile>>(){}, pageLink); |
||||
|
Assert.assertFalse(tenantProfilePageData.hasNext()); |
||||
|
Assert.assertEquals(1, tenantProfilePageData.getTotalElements()); |
||||
|
tenantProfiles.addAll(tenantProfilePageData.getData()); |
||||
|
|
||||
|
for (int i=0;i<28;i++) { |
||||
|
TenantProfile tenantProfile = this.createTenantProfile("Tenant Profile"+i); |
||||
|
tenantProfiles.add(doPost("/api/tenantProfile", tenantProfile, TenantProfile.class)); |
||||
|
} |
||||
|
|
||||
|
List<EntityInfo> loadedTenantProfileInfos = new ArrayList<>(); |
||||
|
pageLink = new PageLink(17); |
||||
|
PageData<EntityInfo> pageData; |
||||
|
do { |
||||
|
pageData = doGetTypedWithPageLink("/api/tenantProfileInfos?", |
||||
|
new TypeReference<PageData<EntityInfo>>(){}, pageLink); |
||||
|
loadedTenantProfileInfos.addAll(pageData.getData()); |
||||
|
if (pageData.hasNext()) { |
||||
|
pageLink = pageLink.nextPageLink(); |
||||
|
} |
||||
|
} while (pageData.hasNext()); |
||||
|
|
||||
|
Collections.sort(tenantProfiles, idComparator); |
||||
|
Collections.sort(loadedTenantProfileInfos, tenantProfileInfoIdComparator); |
||||
|
|
||||
|
List<EntityInfo> tenantProfileInfos = tenantProfiles.stream().map(tenantProfile -> new EntityInfo(tenantProfile.getId(), |
||||
|
tenantProfile.getName())).collect(Collectors.toList()); |
||||
|
|
||||
|
Assert.assertEquals(tenantProfileInfos, loadedTenantProfileInfos); |
||||
|
|
||||
|
for (TenantProfile tenantProfile : tenantProfiles) { |
||||
|
if (!tenantProfile.isDefault()) { |
||||
|
doDelete("/api/tenantProfile/" + tenantProfile.getId().getId().toString()) |
||||
|
.andExpect(status().isOk()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
pageLink = new PageLink(17); |
||||
|
pageData = doGetTypedWithPageLink("/api/tenantProfileInfos?", |
||||
|
new TypeReference<PageData<EntityInfo>>(){}, pageLink); |
||||
|
Assert.assertFalse(pageData.hasNext()); |
||||
|
Assert.assertEquals(1, pageData.getTotalElements()); |
||||
|
} |
||||
|
|
||||
|
private TenantProfile createTenantProfile(String name) { |
||||
|
TenantProfile tenantProfile = new TenantProfile(); |
||||
|
tenantProfile.setName(name); |
||||
|
tenantProfile.setDescription(name + " Test"); |
||||
|
tenantProfile.setProfileData(new TenantProfileData()); |
||||
|
tenantProfile.setDefault(false); |
||||
|
tenantProfile.setIsolatedTbCore(false); |
||||
|
tenantProfile.setIsolatedTbRuleEngine(false); |
||||
|
return tenantProfile; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.BaseDeviceProfileControllerTest; |
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class DeviceProfileControllerSqlTest extends BaseDeviceProfileControllerTest { |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.BaseTenantProfileControllerTest; |
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class TenantProfileControllerSqlTest extends BaseTenantProfileControllerTest { |
||||
|
} |
||||
@ -0,0 +1,246 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttException; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence; |
||||
|
import org.junit.Assert; |
||||
|
import org.springframework.util.StringUtils; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileType; |
||||
|
import org.thingsboard.server.common.data.DeviceTransportType; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; |
||||
|
import org.thingsboard.server.common.data.security.Authority; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentials; |
||||
|
import org.thingsboard.server.controller.AbstractControllerTest; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
import java.util.function.Supplier; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttIntegrationTest extends AbstractControllerTest { |
||||
|
|
||||
|
protected static final String MQTT_URL = "tcp://localhost:1883"; |
||||
|
|
||||
|
private static final AtomicInteger atomicInteger = new AtomicInteger(2); |
||||
|
|
||||
|
protected Tenant savedTenant; |
||||
|
protected User tenantAdmin; |
||||
|
|
||||
|
protected Device savedDevice; |
||||
|
protected String accessToken; |
||||
|
|
||||
|
protected Device savedGateway; |
||||
|
protected String gatewayAccessToken; |
||||
|
|
||||
|
protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) 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("tenant" + atomicInteger.getAndIncrement() + "@thingsboard.org"); |
||||
|
tenantAdmin.setFirstName("Joe"); |
||||
|
tenantAdmin.setLastName("Downs"); |
||||
|
|
||||
|
tenantAdmin = createUserAndLogin(tenantAdmin, "testPassword1"); |
||||
|
|
||||
|
Device device = new Device(); |
||||
|
device.setName(deviceName); |
||||
|
device.setType("default"); |
||||
|
|
||||
|
Device gateway = new Device(); |
||||
|
gateway.setName(gatewayName); |
||||
|
gateway.setType("default"); |
||||
|
ObjectNode additionalInfo = mapper.createObjectNode(); |
||||
|
additionalInfo.put("gateway", true); |
||||
|
gateway.setAdditionalInfo(additionalInfo); |
||||
|
|
||||
|
if (payloadType != null) { |
||||
|
DeviceProfile mqttDeviceProfile = createMqttDeviceProfile(payloadType, telemetryTopic, attributesTopic); |
||||
|
DeviceProfile savedDeviceProfile = doPost("/api/deviceProfile", mqttDeviceProfile, DeviceProfile.class); |
||||
|
device.setType(savedDeviceProfile.getName()); |
||||
|
device.setDeviceProfileId(savedDeviceProfile.getId()); |
||||
|
gateway.setType(savedDeviceProfile.getName()); |
||||
|
gateway.setDeviceProfileId(savedDeviceProfile.getId()); |
||||
|
} |
||||
|
|
||||
|
savedDevice = doPost("/api/device", device, Device.class); |
||||
|
|
||||
|
DeviceCredentials deviceCredentials = |
||||
|
doGet("/api/device/" + savedDevice.getId().getId().toString() + "/credentials", DeviceCredentials.class); |
||||
|
|
||||
|
savedGateway = doPost("/api/device", gateway, Device.class); |
||||
|
|
||||
|
DeviceCredentials gatewayCredentials = |
||||
|
doGet("/api/device/" + savedGateway.getId().getId().toString() + "/credentials", DeviceCredentials.class); |
||||
|
|
||||
|
assertEquals(savedDevice.getId(), deviceCredentials.getDeviceId()); |
||||
|
accessToken = deviceCredentials.getCredentialsId(); |
||||
|
assertNotNull(accessToken); |
||||
|
|
||||
|
assertEquals(savedGateway.getId(), gatewayCredentials.getDeviceId()); |
||||
|
gatewayAccessToken = gatewayCredentials.getCredentialsId(); |
||||
|
assertNotNull(gatewayAccessToken); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected void processAfterTest() throws Exception { |
||||
|
loginSysAdmin(); |
||||
|
if (savedTenant != null) { |
||||
|
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()).andExpect(status().isOk()); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
protected MqttAsyncClient getMqttAsyncClient(String accessToken) throws MqttException { |
||||
|
String clientId = MqttAsyncClient.generateClientId(); |
||||
|
MqttAsyncClient client = new MqttAsyncClient(MQTT_URL, clientId, new MemoryPersistence()); |
||||
|
|
||||
|
MqttConnectOptions options = new MqttConnectOptions(); |
||||
|
options.setUserName(accessToken); |
||||
|
client.connect(options).waitForCompletion(); |
||||
|
return client; |
||||
|
} |
||||
|
|
||||
|
protected void publishMqttMsg(MqttAsyncClient client, byte[] payload, String topic) throws MqttException { |
||||
|
MqttMessage message = new MqttMessage(); |
||||
|
message.setPayload(payload); |
||||
|
client.publish(topic, message); |
||||
|
} |
||||
|
|
||||
|
protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) { |
||||
|
List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>(); |
||||
|
TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V); |
||||
|
TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V); |
||||
|
TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "3.0", TransportProtos.KeyValueType.DOUBLE_V); |
||||
|
TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "4", TransportProtos.KeyValueType.LONG_V); |
||||
|
TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V); |
||||
|
keyValueProtos.add(strKeyValueProto); |
||||
|
keyValueProtos.add(boolKeyValueProto); |
||||
|
keyValueProtos.add(dblKeyValueProto); |
||||
|
keyValueProtos.add(longKeyValueProto); |
||||
|
keyValueProtos.add(jsonKeyValueProto); |
||||
|
return keyValueProtos; |
||||
|
} |
||||
|
|
||||
|
protected TransportProtos.KeyValueProto getKeyValueProto(String key, String strValue, TransportProtos.KeyValueType type) { |
||||
|
TransportProtos.KeyValueProto.Builder keyValueProtoBuilder = TransportProtos.KeyValueProto.newBuilder(); |
||||
|
keyValueProtoBuilder.setKey(key); |
||||
|
keyValueProtoBuilder.setType(type); |
||||
|
switch (type) { |
||||
|
case BOOLEAN_V: |
||||
|
keyValueProtoBuilder.setBoolV(Boolean.parseBoolean(strValue)); |
||||
|
break; |
||||
|
case LONG_V: |
||||
|
keyValueProtoBuilder.setLongV(Long.parseLong(strValue)); |
||||
|
break; |
||||
|
case DOUBLE_V: |
||||
|
keyValueProtoBuilder.setDoubleV(Double.parseDouble(strValue)); |
||||
|
break; |
||||
|
case STRING_V: |
||||
|
keyValueProtoBuilder.setStringV(strValue); |
||||
|
break; |
||||
|
case JSON_V: |
||||
|
keyValueProtoBuilder.setJsonV(strValue); |
||||
|
break; |
||||
|
} |
||||
|
return keyValueProtoBuilder.build(); |
||||
|
} |
||||
|
|
||||
|
protected DeviceProfile createMqttDeviceProfile(TransportPayloadType transportPayloadType, String telemetryTopic, String attributesTopic) { |
||||
|
DeviceProfile deviceProfile = new DeviceProfile(); |
||||
|
deviceProfile.setName(transportPayloadType.name()); |
||||
|
deviceProfile.setType(DeviceProfileType.DEFAULT); |
||||
|
deviceProfile.setTransportType(DeviceTransportType.MQTT); |
||||
|
deviceProfile.setDescription(transportPayloadType.name() + " Test"); |
||||
|
DeviceProfileData deviceProfileData = new DeviceProfileData(); |
||||
|
DefaultDeviceProfileConfiguration configuration = new DefaultDeviceProfileConfiguration(); |
||||
|
MqttDeviceProfileTransportConfiguration transportConfiguration = new MqttDeviceProfileTransportConfiguration(); |
||||
|
transportConfiguration.setTransportPayloadType(transportPayloadType); |
||||
|
if (!StringUtils.isEmpty(telemetryTopic)) { |
||||
|
transportConfiguration.setDeviceTelemetryTopic(telemetryTopic); |
||||
|
} |
||||
|
if (!StringUtils.isEmpty(attributesTopic)) { |
||||
|
transportConfiguration.setDeviceAttributesTopic(attributesTopic); |
||||
|
} |
||||
|
deviceProfileData.setTransportConfiguration(transportConfiguration); |
||||
|
deviceProfileData.setConfiguration(configuration); |
||||
|
deviceProfile.setProfileData(deviceProfileData); |
||||
|
deviceProfile.setDefault(false); |
||||
|
deviceProfile.setDefaultRuleChainId(null); |
||||
|
return deviceProfile; |
||||
|
} |
||||
|
|
||||
|
protected TransportProtos.PostAttributeMsg getPostAttributeMsg(List<String> expectedKeys) { |
||||
|
List<TransportProtos.KeyValueProto> kvProtos = getKvProtos(expectedKeys); |
||||
|
TransportProtos.PostAttributeMsg.Builder builder = TransportProtos.PostAttributeMsg.newBuilder(); |
||||
|
builder.addAllKv(kvProtos); |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
protected <T> T doExecuteWithRetriesAndInterval(SupplierWithThrowable<T> supplier, int retries, int intervalMs) throws Exception { |
||||
|
int count = 0; |
||||
|
T result = null; |
||||
|
Throwable lastException = null; |
||||
|
while (count < retries) { |
||||
|
try { |
||||
|
result = supplier.get(); |
||||
|
if (result != null) { |
||||
|
return result; |
||||
|
} |
||||
|
} catch (Throwable e) { |
||||
|
lastException = e; |
||||
|
} |
||||
|
count++; |
||||
|
if (count < retries) { |
||||
|
Thread.sleep(intervalMs); |
||||
|
} |
||||
|
} |
||||
|
if (lastException != null) { |
||||
|
throw new RuntimeException(lastException); |
||||
|
} else { |
||||
|
return result; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@FunctionalInterface |
||||
|
public interface SupplierWithThrowable<T> { |
||||
|
T get() throws Throwable; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,111 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttCallback; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.CountDownLatch; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttAttributesIntegrationTest extends AbstractMqttIntegrationTest { |
||||
|
|
||||
|
protected static final String POST_ATTRIBUTES_PAYLOAD = "{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73," + |
||||
|
"\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}"; |
||||
|
|
||||
|
protected void processBeforeTest(String deviceName, String gatewayName, TransportPayloadType payloadType, String telemetryTopic, String attributesTopic) throws Exception { |
||||
|
super.processBeforeTest(deviceName, gatewayName, payloadType, telemetryTopic, attributesTopic); |
||||
|
} |
||||
|
|
||||
|
protected void processAfterTest() throws Exception { |
||||
|
super.processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
protected List<TransportProtos.TsKvProto> getTsKvProtoList() { |
||||
|
TransportProtos.TsKvProto tsKvProtoAttribute1 = getTsKvProto("attribute1", "value1", TransportProtos.KeyValueType.STRING_V); |
||||
|
TransportProtos.TsKvProto tsKvProtoAttribute2 = getTsKvProto("attribute2", "true", TransportProtos.KeyValueType.BOOLEAN_V); |
||||
|
TransportProtos.TsKvProto tsKvProtoAttribute3 = getTsKvProto("attribute3", "42.0", TransportProtos.KeyValueType.DOUBLE_V); |
||||
|
TransportProtos.TsKvProto tsKvProtoAttribute4 = getTsKvProto("attribute4", "73", TransportProtos.KeyValueType.LONG_V); |
||||
|
TransportProtos.TsKvProto tsKvProtoAttribute5 = getTsKvProto("attribute5", "{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}", TransportProtos.KeyValueType.JSON_V); |
||||
|
List<TransportProtos.TsKvProto> tsKvProtoList = new ArrayList<>(); |
||||
|
tsKvProtoList.add(tsKvProtoAttribute1); |
||||
|
tsKvProtoList.add(tsKvProtoAttribute2); |
||||
|
tsKvProtoList.add(tsKvProtoAttribute3); |
||||
|
tsKvProtoList.add(tsKvProtoAttribute4); |
||||
|
tsKvProtoList.add(tsKvProtoAttribute5); |
||||
|
return tsKvProtoList; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected TransportProtos.TsKvProto getTsKvProto(String key, String value, TransportProtos.KeyValueType keyValueType) { |
||||
|
TransportProtos.TsKvProto.Builder tsKvProtoBuilder = TransportProtos.TsKvProto.newBuilder(); |
||||
|
TransportProtos.KeyValueProto keyValueProto = getKeyValueProto(key, value, keyValueType); |
||||
|
tsKvProtoBuilder.setKv(keyValueProto); |
||||
|
return tsKvProtoBuilder.build(); |
||||
|
} |
||||
|
|
||||
|
protected TestMqttCallback getTestMqttCallback() { |
||||
|
CountDownLatch latch = new CountDownLatch(1); |
||||
|
return new TestMqttCallback(latch); |
||||
|
} |
||||
|
|
||||
|
protected static class TestMqttCallback implements MqttCallback { |
||||
|
|
||||
|
private final CountDownLatch latch; |
||||
|
private Integer qoS; |
||||
|
private byte[] payloadBytes; |
||||
|
|
||||
|
TestMqttCallback(CountDownLatch latch) { |
||||
|
this.latch = latch; |
||||
|
} |
||||
|
|
||||
|
public int getQoS() { |
||||
|
return qoS; |
||||
|
} |
||||
|
|
||||
|
public byte[] getPayloadBytes() { |
||||
|
return payloadBytes; |
||||
|
} |
||||
|
|
||||
|
public CountDownLatch getLatch() { |
||||
|
return latch; |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void connectionLost(Throwable throwable) { |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void messageArrived(String requestTopic, MqttMessage mqttMessage) throws Exception { |
||||
|
qoS = mqttMessage.getQos(); |
||||
|
payloadBytes = mqttMessage.getPayload(); |
||||
|
latch.countDown(); |
||||
|
} |
||||
|
|
||||
|
@Override |
||||
|
public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,154 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.request; |
||||
|
|
||||
|
import com.google.protobuf.InvalidProtocolBufferException; |
||||
|
import io.netty.handler.codec.mqtt.MqttQoS; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttCallback; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttException; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
||||
|
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
||||
|
import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest; |
||||
|
|
||||
|
import java.nio.charset.StandardCharsets; |
||||
|
import java.util.concurrent.CountDownLatch; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertFalse; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
import static org.junit.Assert.assertTrue; |
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttAttributesRequestIntegrationTest extends AbstractMqttAttributesIntegrationTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("Test Request attribute values from the server", "Gateway Test Request attribute values from the server", null, null, null); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testRequestAttributesValuesFromTheServer() throws Exception { |
||||
|
processTestRequestAttributesValuesFromTheServer(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testRequestAttributesValuesFromTheServerGateway() throws Exception { |
||||
|
processTestGatewayRequestAttributesValuesFromTheServer(); |
||||
|
} |
||||
|
|
||||
|
protected void processTestRequestAttributesValuesFromTheServer() throws Exception { |
||||
|
|
||||
|
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
||||
|
|
||||
|
postAttributesAndSubscribeToTopic(savedDevice, client); |
||||
|
|
||||
|
Thread.sleep(1000); |
||||
|
|
||||
|
TestMqttCallback callback = getTestMqttCallback(); |
||||
|
client.setCallback(callback); |
||||
|
|
||||
|
validateResponse(client, callback.getLatch(), callback); |
||||
|
} |
||||
|
|
||||
|
protected void processTestGatewayRequestAttributesValuesFromTheServer() throws Exception { |
||||
|
|
||||
|
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
||||
|
|
||||
|
postGatewayDeviceClientAttributes(client); |
||||
|
|
||||
|
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Request Attributes", Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
assertNotNull(savedDevice); |
||||
|
|
||||
|
Thread.sleep(1000); |
||||
|
|
||||
|
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
||||
|
|
||||
|
Thread.sleep(1000); |
||||
|
|
||||
|
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_RESPONSE_TOPIC, MqttQoS.AT_LEAST_ONCE.value()); |
||||
|
|
||||
|
TestMqttCallback clientAttributesCallback = getTestMqttCallback(); |
||||
|
client.setCallback(clientAttributesCallback); |
||||
|
validateClientResponseGateway(client, clientAttributesCallback); |
||||
|
|
||||
|
TestMqttCallback sharedAttributesCallback = getTestMqttCallback(); |
||||
|
client.setCallback(sharedAttributesCallback); |
||||
|
validateSharedResponseGateway(client, sharedAttributesCallback); |
||||
|
} |
||||
|
|
||||
|
protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception { |
||||
|
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
||||
|
client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(POST_ATTRIBUTES_PAYLOAD.getBytes())); |
||||
|
client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
||||
|
} |
||||
|
|
||||
|
protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception { |
||||
|
String postClientAttributes = "{\"" + "Gateway Device Request Attributes" + "\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; |
||||
|
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(postClientAttributes.getBytes())); |
||||
|
} |
||||
|
|
||||
|
protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
||||
|
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
||||
|
String payloadStr = "{\"clientKeys\":\"" + keys + "\", \"sharedKeys\":\"" + keys + "\"}"; |
||||
|
MqttMessage mqttMessage = new MqttMessage(); |
||||
|
mqttMessage.setPayload(payloadStr.getBytes()); |
||||
|
client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); |
||||
|
latch.await(3, TimeUnit.SECONDS); |
||||
|
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); |
||||
|
String expectedRequestPayload = "{\"client\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}},\"shared\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; |
||||
|
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); |
||||
|
} |
||||
|
|
||||
|
protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
||||
|
String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": true, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}"; |
||||
|
MqttMessage mqttMessage = new MqttMessage(); |
||||
|
mqttMessage.setPayload(payloadStr.getBytes()); |
||||
|
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage); |
||||
|
callback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); |
||||
|
String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; |
||||
|
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); |
||||
|
} |
||||
|
|
||||
|
protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
||||
|
String payloadStr = "{\"id\": 1, \"device\": \"" + "Gateway Device Request Attributes" + "\", \"client\": false, \"keys\": [\"attribute1\", \"attribute2\", \"attribute3\", \"attribute4\", \"attribute5\"]}"; |
||||
|
MqttMessage mqttMessage = new MqttMessage(); |
||||
|
mqttMessage.setPayload(payloadStr.getBytes()); |
||||
|
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, mqttMessage); |
||||
|
callback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); |
||||
|
String expectedRequestPayload = "{\"id\":1,\"device\":\"" + "Gateway Device Request Attributes" + "\",\"values\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; |
||||
|
assertEquals(JacksonUtil.toJsonNode(expectedRequestPayload), JacksonUtil.toJsonNode(new String(callback.getPayloadBytes(), StandardCharsets.UTF_8))); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.request; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Ignore; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
import static org.junit.Assert.assertTrue; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttAttributesRequestJsonIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("Test Request attribute values from the server json", "Gateway Test Request attribute values from the server json", TransportPayloadType.JSON, null, null); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testRequestAttributesValuesFromTheServer() throws Exception { |
||||
|
processTestRequestAttributesValuesFromTheServer(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testRequestAttributesValuesFromTheServerGateway() throws Exception { |
||||
|
processTestGatewayRequestAttributesValuesFromTheServer(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,201 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.request; |
||||
|
|
||||
|
import com.google.protobuf.InvalidProtocolBufferException; |
||||
|
import io.netty.handler.codec.mqtt.MqttQoS; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttException; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
||||
|
import org.thingsboard.server.gen.transport.TransportApiProtos; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
|
||||
|
import java.util.ArrayList; |
||||
|
import java.util.Arrays; |
||||
|
import java.util.List; |
||||
|
import java.util.concurrent.CountDownLatch; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertTrue; |
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttAttributesRequestProtoIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("Test Request attribute values from the server proto", "Gateway Test Request attribute values from the server proto", TransportPayloadType.PROTOBUF, null, null); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testRequestAttributesValuesFromTheServer() throws Exception { |
||||
|
processTestRequestAttributesValuesFromTheServer(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
@Test |
||||
|
public void testRequestAttributesValuesFromTheServerGateway() throws Exception { |
||||
|
processTestGatewayRequestAttributesValuesFromTheServer(); |
||||
|
} |
||||
|
|
||||
|
protected void postAttributesAndSubscribeToTopic(Device savedDevice, MqttAsyncClient client) throws Exception { |
||||
|
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
||||
|
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
||||
|
List<String> expectedKeys = Arrays.asList(keys.split(",")); |
||||
|
TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys); |
||||
|
byte[] payload = postAttributeMsg.toByteArray(); |
||||
|
client.publish(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, new MqttMessage(payload)); |
||||
|
client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_RESPONSES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
||||
|
} |
||||
|
|
||||
|
protected void postGatewayDeviceClientAttributes(MqttAsyncClient client) throws Exception { |
||||
|
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
||||
|
List<String> expectedKeys = Arrays.asList(keys.split(",")); |
||||
|
TransportProtos.PostAttributeMsg postAttributeMsg = getPostAttributeMsg(expectedKeys); |
||||
|
TransportApiProtos.AttributesMsg.Builder attributesMsgBuilder = TransportApiProtos.AttributesMsg.newBuilder(); |
||||
|
attributesMsgBuilder.setDeviceName("Gateway Device Request Attributes"); |
||||
|
attributesMsgBuilder.setMsg(postAttributeMsg); |
||||
|
TransportApiProtos.AttributesMsg attributesMsg = attributesMsgBuilder.build(); |
||||
|
TransportApiProtos.GatewayAttributesMsg.Builder gatewayAttributeMsgBuilder = TransportApiProtos.GatewayAttributesMsg.newBuilder(); |
||||
|
gatewayAttributeMsgBuilder.addMsg(attributesMsg); |
||||
|
byte[] bytes = gatewayAttributeMsgBuilder.build().toByteArray(); |
||||
|
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, new MqttMessage(bytes)); |
||||
|
} |
||||
|
|
||||
|
protected void validateResponse(MqttAsyncClient client, CountDownLatch latch, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
||||
|
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
||||
|
TransportApiProtos.AttributesRequest.Builder attributesRequestBuilder = TransportApiProtos.AttributesRequest.newBuilder(); |
||||
|
attributesRequestBuilder.setClientKeys(keys); |
||||
|
attributesRequestBuilder.setSharedKeys(keys); |
||||
|
TransportApiProtos.AttributesRequest attributesRequest = attributesRequestBuilder.build(); |
||||
|
MqttMessage mqttMessage = new MqttMessage(); |
||||
|
mqttMessage.setPayload(attributesRequest.toByteArray()); |
||||
|
client.publish(MqttTopics.DEVICE_ATTRIBUTES_REQUEST_TOPIC_PREFIX + "1", mqttMessage); |
||||
|
latch.await(3, TimeUnit.SECONDS); |
||||
|
assertEquals(MqttQoS.AT_MOST_ONCE.value(), callback.getQoS()); |
||||
|
TransportProtos.GetAttributeResponseMsg expectedAttributesResponse = getExpectedAttributeResponseMsg(); |
||||
|
TransportProtos.GetAttributeResponseMsg actualAttributesResponse = TransportProtos.GetAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); |
||||
|
assertEquals(expectedAttributesResponse.getRequestId(), actualAttributesResponse.getRequestId()); |
||||
|
List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualAttributesResponse.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualAttributesResponse.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); |
||||
|
assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); |
||||
|
} |
||||
|
|
||||
|
protected void validateClientResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
||||
|
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
||||
|
TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, true); |
||||
|
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray())); |
||||
|
callback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); |
||||
|
TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(true); |
||||
|
TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); |
||||
|
assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); |
||||
|
|
||||
|
TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg(); |
||||
|
TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg(); |
||||
|
assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId()); |
||||
|
|
||||
|
List<TransportProtos.KeyValueProto> expectedClientKeyValueProtos = expectedResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
List<TransportProtos.KeyValueProto> actualClientKeyValueProtos = actualResponseMsg.getClientAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
assertTrue(actualClientKeyValueProtos.containsAll(expectedClientKeyValueProtos)); |
||||
|
} |
||||
|
|
||||
|
protected void validateSharedResponseGateway(MqttAsyncClient client, TestMqttCallback callback) throws MqttException, InterruptedException, InvalidProtocolBufferException { |
||||
|
String keys = "attribute1,attribute2,attribute3,attribute4,attribute5"; |
||||
|
TransportApiProtos.GatewayAttributesRequestMsg gatewayAttributesRequestMsg = getGatewayAttributesRequestMsg(keys, false); |
||||
|
client.publish(MqttTopics.GATEWAY_ATTRIBUTES_REQUEST_TOPIC, new MqttMessage(gatewayAttributesRequestMsg.toByteArray())); |
||||
|
callback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
assertEquals(MqttQoS.AT_LEAST_ONCE.value(), callback.getQoS()); |
||||
|
TransportApiProtos.GatewayAttributeResponseMsg expectedGatewayAttributeResponseMsg = getExpectedGatewayAttributeResponseMsg(false); |
||||
|
TransportApiProtos.GatewayAttributeResponseMsg actualGatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.parseFrom(callback.getPayloadBytes()); |
||||
|
assertEquals(expectedGatewayAttributeResponseMsg.getDeviceName(), actualGatewayAttributeResponseMsg.getDeviceName()); |
||||
|
|
||||
|
TransportProtos.GetAttributeResponseMsg expectedResponseMsg = expectedGatewayAttributeResponseMsg.getResponseMsg(); |
||||
|
TransportProtos.GetAttributeResponseMsg actualResponseMsg = actualGatewayAttributeResponseMsg.getResponseMsg(); |
||||
|
assertEquals(expectedResponseMsg.getRequestId(), actualResponseMsg.getRequestId()); |
||||
|
|
||||
|
List<TransportProtos.KeyValueProto> expectedSharedKeyValueProtos = expectedResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
List<TransportProtos.KeyValueProto> actualSharedKeyValueProtos = actualResponseMsg.getSharedAttributeListList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
|
||||
|
assertTrue(actualSharedKeyValueProtos.containsAll(expectedSharedKeyValueProtos)); |
||||
|
} |
||||
|
|
||||
|
private TransportApiProtos.GatewayAttributesRequestMsg getGatewayAttributesRequestMsg(String keys, boolean client) { |
||||
|
return TransportApiProtos.GatewayAttributesRequestMsg.newBuilder() |
||||
|
.setClient(client) |
||||
|
.addAllKeys(Arrays.asList(keys.split(","))) |
||||
|
.setDeviceName("Gateway Device Request Attributes") |
||||
|
.setId(1).build(); |
||||
|
} |
||||
|
|
||||
|
private TransportProtos.GetAttributeResponseMsg getExpectedAttributeResponseMsg() { |
||||
|
TransportProtos.GetAttributeResponseMsg.Builder result = TransportProtos.GetAttributeResponseMsg.newBuilder(); |
||||
|
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); |
||||
|
result.addAllClientAttributeList(tsKvProtoList); |
||||
|
result.addAllSharedAttributeList(tsKvProtoList); |
||||
|
result.setRequestId(1); |
||||
|
return result.build(); |
||||
|
} |
||||
|
|
||||
|
private TransportApiProtos.GatewayAttributeResponseMsg getExpectedGatewayAttributeResponseMsg(boolean client) { |
||||
|
TransportApiProtos.GatewayAttributeResponseMsg.Builder gatewayAttributeResponseMsg = TransportApiProtos.GatewayAttributeResponseMsg.newBuilder(); |
||||
|
TransportProtos.GetAttributeResponseMsg.Builder getAttributeResponseMsgBuilder = TransportProtos.GetAttributeResponseMsg.newBuilder(); |
||||
|
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); |
||||
|
if (client) { |
||||
|
getAttributeResponseMsgBuilder.addAllClientAttributeList(tsKvProtoList); |
||||
|
} else { |
||||
|
getAttributeResponseMsgBuilder.addAllSharedAttributeList(tsKvProtoList); |
||||
|
} |
||||
|
getAttributeResponseMsgBuilder.setRequestId(1); |
||||
|
TransportProtos.GetAttributeResponseMsg getAttributeResponseMsg = getAttributeResponseMsgBuilder.build(); |
||||
|
gatewayAttributeResponseMsg.setDeviceName("Gateway Device Request Attributes"); |
||||
|
gatewayAttributeResponseMsg.setResponseMsg(getAttributeResponseMsg); |
||||
|
return gatewayAttributeResponseMsg.build(); |
||||
|
} |
||||
|
|
||||
|
protected List<TransportProtos.KeyValueProto> getKvProtos(List<String> expectedKeys) { |
||||
|
List<TransportProtos.KeyValueProto> keyValueProtos = new ArrayList<>(); |
||||
|
TransportProtos.KeyValueProto strKeyValueProto = getKeyValueProto(expectedKeys.get(0), "value1", TransportProtos.KeyValueType.STRING_V); |
||||
|
TransportProtos.KeyValueProto boolKeyValueProto = getKeyValueProto(expectedKeys.get(1), "true", TransportProtos.KeyValueType.BOOLEAN_V); |
||||
|
TransportProtos.KeyValueProto dblKeyValueProto = getKeyValueProto(expectedKeys.get(2), "42.0", TransportProtos.KeyValueType.DOUBLE_V); |
||||
|
TransportProtos.KeyValueProto longKeyValueProto = getKeyValueProto(expectedKeys.get(3), "73", TransportProtos.KeyValueType.LONG_V); |
||||
|
TransportProtos.KeyValueProto jsonKeyValueProto = getKeyValueProto(expectedKeys.get(4), "{\"someNumber\": 42, \"someArray\": [1,2,3], \"someNestedObject\": {\"key\": \"value\"}}", TransportProtos.KeyValueType.JSON_V); |
||||
|
keyValueProtos.add(strKeyValueProto); |
||||
|
keyValueProtos.add(boolKeyValueProto); |
||||
|
keyValueProtos.add(dblKeyValueProto); |
||||
|
keyValueProtos.add(longKeyValueProto); |
||||
|
keyValueProtos.add(jsonKeyValueProto); |
||||
|
return keyValueProtos; |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.request.nosql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; |
||||
|
|
||||
|
|
||||
|
@DaoNoSqlTest |
||||
|
public class MqttAttributesRequestNoSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.request.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttAttributesRequestJsonSqlIntegrationTest extends AbstractMqttAttributesRequestJsonIntegrationTest { |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.request.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestJsonIntegrationTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestProtoIntegrationTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttAttributesRequestProtoSqlIntegrationTest extends AbstractMqttAttributesRequestProtoIntegrationTest { |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.request.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.request.AbstractMqttAttributesRequestIntegrationTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttAttributesRequestSqlIntegrationTest extends AbstractMqttAttributesRequestIntegrationTest { |
||||
|
} |
||||
@ -0,0 +1,171 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.updates; |
||||
|
|
||||
|
import com.google.protobuf.InvalidProtocolBufferException; |
||||
|
import io.netty.handler.codec.mqtt.MqttQoS; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttCallback; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttException; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
||||
|
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
||||
|
import org.thingsboard.server.mqtt.attributes.AbstractMqttAttributesIntegrationTest; |
||||
|
|
||||
|
import java.nio.charset.StandardCharsets; |
||||
|
import java.util.concurrent.CountDownLatch; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
import static org.junit.Assert.assertTrue; |
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttAttributesUpdatesIntegrationTest extends AbstractMqttAttributesIntegrationTest { |
||||
|
|
||||
|
private static final String RESPONSE_ATTRIBUTES_PAYLOAD_DELETED = "{\"deleted\":[\"attribute5\"]}"; |
||||
|
|
||||
|
private static String getResponseGatewayAttributesUpdatedPayload() { |
||||
|
return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\"," + |
||||
|
"\"data\":{\"attribute1\":\"value1\",\"attribute2\":true,\"attribute3\":42.0,\"attribute4\":73,\"attribute5\":{\"someNumber\":42,\"someArray\":[1,2,3],\"someNestedObject\":{\"key\":\"value\"}}}}"; |
||||
|
} |
||||
|
|
||||
|
private static String getResponseGatewayAttributesDeletedPayload() { |
||||
|
return "{\"device\":\"" + "Gateway Device Subscribe to attribute updates" + "\",\"data\":{\"deleted\":[\"attribute5\"]}}"; |
||||
|
} |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { |
||||
|
processTestSubscribeToAttributesUpdates(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { |
||||
|
processGatewayTestSubscribeToAttributesUpdates(); |
||||
|
} |
||||
|
|
||||
|
protected void processTestSubscribeToAttributesUpdates() throws Exception { |
||||
|
|
||||
|
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
||||
|
|
||||
|
TestMqttCallback onUpdateCallback = getTestMqttCallback(); |
||||
|
client.setCallback(onUpdateCallback); |
||||
|
|
||||
|
client.subscribe(MqttTopics.DEVICE_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
||||
|
|
||||
|
Thread.sleep(1000); |
||||
|
|
||||
|
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
||||
|
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
|
||||
|
validateUpdateAttributesResponse(onUpdateCallback); |
||||
|
|
||||
|
TestMqttCallback onDeleteCallback = getTestMqttCallback(); |
||||
|
client.setCallback(onDeleteCallback); |
||||
|
|
||||
|
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class); |
||||
|
onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
|
||||
|
validateDeleteAttributesResponse(onDeleteCallback); |
||||
|
} |
||||
|
|
||||
|
protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
||||
|
assertNotNull(callback.getPayloadBytes()); |
||||
|
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); |
||||
|
assertEquals(JacksonUtil.toJsonNode(POST_ATTRIBUTES_PAYLOAD), JacksonUtil.toJsonNode(response)); |
||||
|
} |
||||
|
|
||||
|
protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
||||
|
assertNotNull(callback.getPayloadBytes()); |
||||
|
String response = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); |
||||
|
assertEquals(JacksonUtil.toJsonNode(RESPONSE_ATTRIBUTES_PAYLOAD_DELETED), JacksonUtil.toJsonNode(response)); |
||||
|
} |
||||
|
|
||||
|
protected void processGatewayTestSubscribeToAttributesUpdates() throws Exception { |
||||
|
|
||||
|
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
||||
|
|
||||
|
TestMqttCallback onUpdateCallback = getTestMqttCallback(); |
||||
|
client.setCallback(onUpdateCallback); |
||||
|
|
||||
|
Device device = new Device(); |
||||
|
device.setName("Gateway Device Subscribe to attribute updates"); |
||||
|
device.setType("default"); |
||||
|
|
||||
|
byte[] connectPayloadBytes = getConnectPayloadBytes(); |
||||
|
|
||||
|
publishMqttMsg(client, connectPayloadBytes, MqttTopics.GATEWAY_CONNECT_TOPIC); |
||||
|
|
||||
|
Device savedDevice = doExecuteWithRetriesAndInterval(() -> doGet("/api/tenant/devices?deviceName=" + "Gateway Device Subscribe to attribute updates", Device.class), |
||||
|
20, |
||||
|
100); |
||||
|
|
||||
|
assertNotNull(savedDevice); |
||||
|
|
||||
|
client.subscribe(MqttTopics.GATEWAY_ATTRIBUTES_TOPIC, MqttQoS.AT_MOST_ONCE.value()); |
||||
|
|
||||
|
Thread.sleep(1000); |
||||
|
|
||||
|
doPostAsync("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/attributes/SHARED_SCOPE", POST_ATTRIBUTES_PAYLOAD, String.class, status().isOk()); |
||||
|
onUpdateCallback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
|
||||
|
validateGatewayUpdateAttributesResponse(onUpdateCallback); |
||||
|
|
||||
|
TestMqttCallback onDeleteCallback = getTestMqttCallback(); |
||||
|
client.setCallback(onDeleteCallback); |
||||
|
|
||||
|
doDelete("/api/plugins/telemetry/DEVICE/" + savedDevice.getId().getId() + "/SHARED_SCOPE?keys=attribute5", String.class); |
||||
|
onDeleteCallback.getLatch().await(3, TimeUnit.SECONDS); |
||||
|
|
||||
|
validateGatewayDeleteAttributesResponse(onDeleteCallback); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
||||
|
assertNotNull(callback.getPayloadBytes()); |
||||
|
String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); |
||||
|
assertEquals(getResponseGatewayAttributesUpdatedPayload(), s); |
||||
|
} |
||||
|
|
||||
|
protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
||||
|
assertNotNull(callback.getPayloadBytes()); |
||||
|
String s = new String(callback.getPayloadBytes(), StandardCharsets.UTF_8); |
||||
|
assertEquals(s, getResponseGatewayAttributesDeletedPayload()); |
||||
|
} |
||||
|
|
||||
|
protected byte[] getConnectPayloadBytes() { |
||||
|
String connectPayload = "{\"device\": \"Gateway Device Subscribe to attribute updates\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; |
||||
|
return connectPayload.getBytes(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,51 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.updates; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertFalse; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
import static org.junit.Assert.assertTrue; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttAttributesUpdatesJsonIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.JSON, null, null); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { |
||||
|
processTestSubscribeToAttributesUpdates(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { |
||||
|
processGatewayTestSubscribeToAttributesUpdates(); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,149 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.updates; |
||||
|
|
||||
|
import com.google.protobuf.InvalidProtocolBufferException; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
||||
|
import org.thingsboard.server.gen.transport.TransportApiProtos; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
|
||||
|
import java.nio.charset.StandardCharsets; |
||||
|
import java.util.List; |
||||
|
import java.util.stream.Collectors; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
import static org.junit.Assert.assertTrue; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttAttributesUpdatesProtoIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("Test Subscribe to attribute updates", "Gateway Test Subscribe to attribute updates", TransportPayloadType.PROTOBUF, null, null); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSubscribeToAttributesUpdatesFromTheServer() throws Exception { |
||||
|
processTestSubscribeToAttributesUpdates(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testSubscribeToAttributesUpdatesFromTheServerGateway() throws Exception { |
||||
|
processGatewayTestSubscribeToAttributesUpdates(); |
||||
|
} |
||||
|
|
||||
|
protected void validateUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
||||
|
assertNotNull(callback.getPayloadBytes()); |
||||
|
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); |
||||
|
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); |
||||
|
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); |
||||
|
|
||||
|
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); |
||||
|
TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); |
||||
|
|
||||
|
List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedAttributeUpdateNotificationMsg.getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
|
||||
|
assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size()); |
||||
|
assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList)); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected void validateDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
||||
|
assertNotNull(callback.getPayloadBytes()); |
||||
|
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); |
||||
|
attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5"); |
||||
|
|
||||
|
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); |
||||
|
TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = TransportProtos.AttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); |
||||
|
|
||||
|
assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size()); |
||||
|
assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0)); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected void validateGatewayUpdateAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
||||
|
assertNotNull(callback.getPayloadBytes()); |
||||
|
|
||||
|
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); |
||||
|
List<TransportProtos.TsKvProto> tsKvProtoList = getTsKvProtoList(); |
||||
|
attributeUpdateNotificationMsgBuilder.addAllSharedUpdated(tsKvProtoList); |
||||
|
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); |
||||
|
|
||||
|
TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); |
||||
|
gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates"); |
||||
|
gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(expectedAttributeUpdateNotificationMsg); |
||||
|
|
||||
|
TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build(); |
||||
|
TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); |
||||
|
|
||||
|
assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName()); |
||||
|
|
||||
|
List<TransportProtos.KeyValueProto> actualSharedUpdatedList = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
List<TransportProtos.KeyValueProto> expectedSharedUpdatedList = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg().getSharedUpdatedList().stream().map(TransportProtos.TsKvProto::getKv).collect(Collectors.toList()); |
||||
|
|
||||
|
assertEquals(expectedSharedUpdatedList.size(), actualSharedUpdatedList.size()); |
||||
|
assertTrue(actualSharedUpdatedList.containsAll(expectedSharedUpdatedList)); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected void validateGatewayDeleteAttributesResponse(TestMqttCallback callback) throws InvalidProtocolBufferException { |
||||
|
assertNotNull(callback.getPayloadBytes()); |
||||
|
TransportProtos.AttributeUpdateNotificationMsg.Builder attributeUpdateNotificationMsgBuilder = TransportProtos.AttributeUpdateNotificationMsg.newBuilder(); |
||||
|
attributeUpdateNotificationMsgBuilder.addSharedDeleted("attribute5"); |
||||
|
TransportProtos.AttributeUpdateNotificationMsg attributeUpdateNotificationMsg = attributeUpdateNotificationMsgBuilder.build(); |
||||
|
|
||||
|
TransportApiProtos.GatewayAttributeUpdateNotificationMsg.Builder gatewayAttributeUpdateNotificationMsgBuilder = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.newBuilder(); |
||||
|
gatewayAttributeUpdateNotificationMsgBuilder.setDeviceName("Gateway Device Subscribe to attribute updates"); |
||||
|
gatewayAttributeUpdateNotificationMsgBuilder.setNotificationMsg(attributeUpdateNotificationMsg); |
||||
|
|
||||
|
TransportApiProtos.GatewayAttributeUpdateNotificationMsg expectedGatewayAttributeUpdateNotificationMsg = gatewayAttributeUpdateNotificationMsgBuilder.build(); |
||||
|
TransportApiProtos.GatewayAttributeUpdateNotificationMsg actualGatewayAttributeUpdateNotificationMsg = TransportApiProtos.GatewayAttributeUpdateNotificationMsg.parseFrom(callback.getPayloadBytes()); |
||||
|
|
||||
|
assertEquals(expectedGatewayAttributeUpdateNotificationMsg.getDeviceName(), actualGatewayAttributeUpdateNotificationMsg.getDeviceName()); |
||||
|
|
||||
|
TransportProtos.AttributeUpdateNotificationMsg expectedAttributeUpdateNotificationMsg = expectedGatewayAttributeUpdateNotificationMsg.getNotificationMsg(); |
||||
|
TransportProtos.AttributeUpdateNotificationMsg actualAttributeUpdateNotificationMsg = actualGatewayAttributeUpdateNotificationMsg.getNotificationMsg(); |
||||
|
|
||||
|
assertEquals(expectedAttributeUpdateNotificationMsg.getSharedDeletedList().size(), actualAttributeUpdateNotificationMsg.getSharedDeletedList().size()); |
||||
|
assertEquals("attribute5", actualAttributeUpdateNotificationMsg.getSharedDeletedList().get(0)); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
protected byte[] getConnectPayloadBytes() { |
||||
|
TransportApiProtos.ConnectMsg connectProto = getConnectProto(); |
||||
|
return connectProto.toByteArray(); |
||||
|
} |
||||
|
|
||||
|
private TransportApiProtos.ConnectMsg getConnectProto() { |
||||
|
TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); |
||||
|
builder.setDeviceName("Gateway Device Subscribe to attribute updates"); |
||||
|
builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.updates.nosql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; |
||||
|
|
||||
|
|
||||
|
@DaoNoSqlTest |
||||
|
public class MqttAttributesUpdatesNoSqlIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.updates.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttAttributesUpdatesSqlIntegrationTest extends AbstractMqttAttributesUpdatesIntegrationTest { |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.updates.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesIntegrationTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttAttributesUpdatesSqlJsonIntegrationTest extends AbstractMqttAttributesUpdatesJsonIntegrationTest { |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.attributes.updates.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesJsonIntegrationTest; |
||||
|
import org.thingsboard.server.mqtt.attributes.updates.AbstractMqttAttributesUpdatesProtoIntegrationTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttAttributesUpdatesSqlProtoIntegrationTest extends AbstractMqttAttributesUpdatesProtoIntegrationTest { |
||||
|
} |
||||
@ -0,0 +1,206 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.claim; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Ignore; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.ClaimRequest; |
||||
|
import org.thingsboard.server.common.data.Customer; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
||||
|
import org.thingsboard.server.common.data.security.Authority; |
||||
|
import org.thingsboard.server.dao.device.claim.ClaimResponse; |
||||
|
import org.thingsboard.server.dao.device.claim.ClaimResult; |
||||
|
import org.thingsboard.server.mqtt.AbstractMqttIntegrationTest; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttClaimDeviceTest extends AbstractMqttIntegrationTest { |
||||
|
|
||||
|
protected static final String CUSTOMER_USER_PASSWORD = "customerUser123!"; |
||||
|
|
||||
|
protected User customerAdmin; |
||||
|
protected Customer savedCustomer; |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
super.processBeforeTest("Test Claim device", "Test Claim gateway", null, null, null); |
||||
|
createCustomerAndUser(); |
||||
|
} |
||||
|
|
||||
|
protected void createCustomerAndUser() throws Exception { |
||||
|
Customer customer = new Customer(); |
||||
|
customer.setTenantId(savedTenant.getId()); |
||||
|
customer.setTitle("Test Claiming Customer"); |
||||
|
savedCustomer = doPost("/api/customer", customer, Customer.class); |
||||
|
assertNotNull(savedCustomer); |
||||
|
assertEquals(savedTenant.getId(), savedCustomer.getTenantId()); |
||||
|
|
||||
|
User user = new User(); |
||||
|
user.setAuthority(Authority.CUSTOMER_USER); |
||||
|
user.setTenantId(savedTenant.getId()); |
||||
|
user.setCustomerId(savedCustomer.getId()); |
||||
|
user.setEmail("customer@thingsboard.org"); |
||||
|
|
||||
|
customerAdmin = createUser(user, CUSTOMER_USER_PASSWORD); |
||||
|
assertNotNull(customerAdmin); |
||||
|
assertEquals(customerAdmin.getCustomerId(), savedCustomer.getId()); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
super.processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testClaimingDevice() throws Exception { |
||||
|
processTestClaimingDevice(false); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { |
||||
|
processTestClaimingDevice(true); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayClaimingDevice() throws Exception { |
||||
|
processTestGatewayClaimingDevice("Test claiming gateway device", false); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { |
||||
|
processTestGatewayClaimingDevice("Test claiming gateway device empty payload", true); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { |
||||
|
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
||||
|
byte[] payloadBytes; |
||||
|
byte[] failurePayloadBytes; |
||||
|
if (emptyPayload) { |
||||
|
payloadBytes = "{}".getBytes(); |
||||
|
failurePayloadBytes = "{\"durationMs\":1}".getBytes(); |
||||
|
} else { |
||||
|
payloadBytes = "{\"secretKey\":\"value\", \"durationMs\":60000}".getBytes(); |
||||
|
failurePayloadBytes = "{\"secretKey\":\"value\", \"durationMs\":1}".getBytes(); |
||||
|
} |
||||
|
validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes); |
||||
|
} |
||||
|
|
||||
|
protected void validateClaimResponse(boolean emptyPayload, MqttAsyncClient client, byte[] payloadBytes, byte[] failurePayloadBytes) throws Exception { |
||||
|
client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes)); |
||||
|
|
||||
|
loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD); |
||||
|
ClaimRequest claimRequest; |
||||
|
if (!emptyPayload) { |
||||
|
claimRequest = new ClaimRequest("value"); |
||||
|
} else { |
||||
|
claimRequest = new ClaimRequest(null); |
||||
|
} |
||||
|
|
||||
|
ClaimResponse claimResponse = doExecuteWithRetriesAndInterval( |
||||
|
() -> doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()), |
||||
|
20, |
||||
|
100 |
||||
|
); |
||||
|
|
||||
|
assertEquals(claimResponse, ClaimResponse.FAILURE); |
||||
|
|
||||
|
client.publish(MqttTopics.DEVICE_CLAIM_TOPIC, new MqttMessage(payloadBytes)); |
||||
|
|
||||
|
ClaimResult claimResult = doExecuteWithRetriesAndInterval( |
||||
|
() -> doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResult.class, status().isOk()), |
||||
|
20, |
||||
|
100 |
||||
|
); |
||||
|
assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS); |
||||
|
Device claimedDevice = claimResult.getDevice(); |
||||
|
assertNotNull(claimedDevice); |
||||
|
assertNotNull(claimedDevice.getCustomerId()); |
||||
|
assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId()); |
||||
|
|
||||
|
claimResponse = doPostClaimAsync("/api/customer/device/" + savedDevice.getName() + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); |
||||
|
assertEquals(claimResponse, ClaimResponse.CLAIMED); |
||||
|
} |
||||
|
|
||||
|
protected void validateGatewayClaimResponse(String deviceName, boolean emptyPayload, MqttAsyncClient client, byte[] failurePayloadBytes, byte[] payloadBytes) throws Exception { |
||||
|
client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(failurePayloadBytes)); |
||||
|
|
||||
|
Device savedDevice = doExecuteWithRetriesAndInterval( |
||||
|
() -> doGet("/api/tenant/devices?deviceName=" + deviceName, Device.class), |
||||
|
20, |
||||
|
100 |
||||
|
); |
||||
|
|
||||
|
assertNotNull(savedDevice); |
||||
|
|
||||
|
loginUser(customerAdmin.getName(), CUSTOMER_USER_PASSWORD); |
||||
|
ClaimRequest claimRequest; |
||||
|
if (!emptyPayload) { |
||||
|
claimRequest = new ClaimRequest("value"); |
||||
|
} else { |
||||
|
claimRequest = new ClaimRequest(null); |
||||
|
} |
||||
|
|
||||
|
ClaimResponse claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); |
||||
|
assertEquals(claimResponse, ClaimResponse.FAILURE); |
||||
|
|
||||
|
client.publish(MqttTopics.GATEWAY_CLAIM_TOPIC, new MqttMessage(payloadBytes)); |
||||
|
|
||||
|
ClaimResult claimResult = doExecuteWithRetriesAndInterval( |
||||
|
() -> doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResult.class, status().isOk()), |
||||
|
20, |
||||
|
100 |
||||
|
); |
||||
|
|
||||
|
assertEquals(claimResult.getResponse(), ClaimResponse.SUCCESS); |
||||
|
Device claimedDevice = claimResult.getDevice(); |
||||
|
assertNotNull(claimedDevice); |
||||
|
assertNotNull(claimedDevice.getCustomerId()); |
||||
|
assertEquals(customerAdmin.getCustomerId(), claimedDevice.getCustomerId()); |
||||
|
|
||||
|
claimResponse = doPostClaimAsync("/api/customer/device/" + deviceName + "/claim", claimRequest, ClaimResponse.class, status().isBadRequest()); |
||||
|
assertEquals(claimResponse, ClaimResponse.CLAIMED); |
||||
|
} |
||||
|
|
||||
|
protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception { |
||||
|
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
||||
|
byte[] failurePayloadBytes; |
||||
|
byte[] payloadBytes; |
||||
|
String failurePayload; |
||||
|
String payload; |
||||
|
if (emptyPayload) { |
||||
|
failurePayload = "{\"" + deviceName + "\": " + "{\"durationMs\":1}" + "}"; |
||||
|
payload = "{\"" + deviceName + "\": " + "{}" + "}"; |
||||
|
} else { |
||||
|
failurePayload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":1}" + "}"; |
||||
|
payload = "{\"" + deviceName + "\": " + "{\"secretKey\":\"value\", \"durationMs\":60000}" + "}"; |
||||
|
} |
||||
|
payloadBytes = payload.getBytes(); |
||||
|
failurePayloadBytes = failurePayload.getBytes(); |
||||
|
validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,58 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.claim; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Ignore; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttClaimJsonDeviceTest extends AbstractMqttClaimDeviceTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
super.processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.JSON, null, null); |
||||
|
createCustomerAndUser(); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
super.afterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testClaimingDevice() throws Exception { |
||||
|
processTestClaimingDevice(false); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { |
||||
|
processTestClaimingDevice(true); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayClaimingDevice() throws Exception { |
||||
|
processTestGatewayClaimingDevice("Test claiming gateway device Json", false); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { |
||||
|
processTestGatewayClaimingDevice("Test claiming gateway device empty payload Json", true); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,116 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.claim; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Ignore; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.gen.transport.TransportApiProtos; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttClaimProtoDeviceTest extends AbstractMqttClaimDeviceTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("Test Claim device", "Test Claim gateway", TransportPayloadType.PROTOBUF, null, null); |
||||
|
createCustomerAndUser(); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { super.afterTest(); } |
||||
|
|
||||
|
@Test |
||||
|
public void testClaimingDevice() throws Exception { |
||||
|
processTestClaimingDevice(false); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testClaimingDeviceWithoutSecretAndDuration() throws Exception { |
||||
|
processTestClaimingDevice(true); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayClaimingDevice() throws Exception { |
||||
|
processTestGatewayClaimingDevice("Test claiming gateway device Proto", false); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayClaimingDeviceWithoutSecretAndDuration() throws Exception { |
||||
|
processTestGatewayClaimingDevice("Test claiming gateway device empty payload Proto", true); |
||||
|
} |
||||
|
|
||||
|
protected void processTestClaimingDevice(boolean emptyPayload) throws Exception { |
||||
|
MqttAsyncClient client = getMqttAsyncClient(accessToken); |
||||
|
byte[] payloadBytes; |
||||
|
if (emptyPayload) { |
||||
|
payloadBytes = getClaimDevice(0, emptyPayload).toByteArray(); |
||||
|
} else { |
||||
|
payloadBytes = getClaimDevice(60000, emptyPayload).toByteArray(); |
||||
|
} |
||||
|
byte[] failurePayloadBytes = getClaimDevice(1, emptyPayload).toByteArray(); |
||||
|
validateClaimResponse(emptyPayload, client, payloadBytes, failurePayloadBytes); |
||||
|
} |
||||
|
|
||||
|
protected void processTestGatewayClaimingDevice(String deviceName, boolean emptyPayload) throws Exception { |
||||
|
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
||||
|
byte[] failurePayloadBytes; |
||||
|
byte[] payloadBytes; |
||||
|
if (emptyPayload) { |
||||
|
payloadBytes = getGatewayClaimMsg(deviceName, 0, emptyPayload).toByteArray(); |
||||
|
} else { |
||||
|
payloadBytes = getGatewayClaimMsg(deviceName, 60000, emptyPayload).toByteArray(); |
||||
|
} |
||||
|
failurePayloadBytes = getGatewayClaimMsg(deviceName, 1, emptyPayload).toByteArray(); |
||||
|
|
||||
|
validateGatewayClaimResponse(deviceName, emptyPayload, client, failurePayloadBytes, payloadBytes); |
||||
|
} |
||||
|
|
||||
|
private TransportApiProtos.GatewayClaimMsg getGatewayClaimMsg(String deviceName, long duration, boolean emptyPayload) { |
||||
|
TransportApiProtos.GatewayClaimMsg.Builder gatewayClaimMsgBuilder = TransportApiProtos.GatewayClaimMsg.newBuilder(); |
||||
|
TransportApiProtos.ClaimDeviceMsg.Builder claimDeviceMsgBuilder = TransportApiProtos.ClaimDeviceMsg.newBuilder(); |
||||
|
TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder(); |
||||
|
if (!emptyPayload) { |
||||
|
claimDeviceBuilder.setSecretKey("value"); |
||||
|
} |
||||
|
if (duration > 0) { |
||||
|
claimDeviceBuilder.setDurationMs(duration); |
||||
|
} |
||||
|
TransportApiProtos.ClaimDevice claimDevice = claimDeviceBuilder.build(); |
||||
|
claimDeviceMsgBuilder.setClaimRequest(claimDevice); |
||||
|
claimDeviceMsgBuilder.setDeviceName(deviceName); |
||||
|
TransportApiProtos.ClaimDeviceMsg claimDeviceMsg = claimDeviceMsgBuilder.build(); |
||||
|
gatewayClaimMsgBuilder.addMsg(claimDeviceMsg); |
||||
|
return gatewayClaimMsgBuilder.build(); |
||||
|
} |
||||
|
|
||||
|
private TransportApiProtos.ClaimDevice getClaimDevice(long duration, boolean emptyPayload) { |
||||
|
TransportApiProtos.ClaimDevice.Builder claimDeviceBuilder = TransportApiProtos.ClaimDevice.newBuilder(); |
||||
|
if (!emptyPayload) { |
||||
|
claimDeviceBuilder.setSecretKey("value"); |
||||
|
} |
||||
|
if (duration > 0) { |
||||
|
claimDeviceBuilder.setSecretKey("value"); |
||||
|
claimDeviceBuilder.setDurationMs(duration); |
||||
|
} |
||||
|
return claimDeviceBuilder.build(); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.claim.nosql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; |
||||
|
|
||||
|
|
||||
|
@DaoNoSqlTest |
||||
|
public class MqttClaimDeviceNoSqlTest extends AbstractMqttClaimDeviceTest { |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.claim.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; |
||||
|
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttClaimDeviceJsonSqlTest extends AbstractMqttClaimJsonDeviceTest { |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.claim.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimJsonDeviceTest; |
||||
|
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimProtoDeviceTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttClaimDeviceProtoSqlTest extends AbstractMqttClaimProtoDeviceTest { |
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.claim.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.claim.AbstractMqttClaimDeviceTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttClaimDeviceSqlTest extends AbstractMqttClaimDeviceTest { |
||||
|
} |
||||
@ -0,0 +1,137 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.rpc; |
||||
|
|
||||
|
import com.fasterxml.jackson.databind.JsonNode; |
||||
|
import com.fasterxml.jackson.databind.node.ObjectNode; |
||||
|
import com.google.protobuf.InvalidProtocolBufferException; |
||||
|
import com.nimbusds.jose.util.StandardCharset; |
||||
|
import com.datastax.oss.driver.api.core.uuid.Uuids; |
||||
|
import io.netty.handler.codec.mqtt.MqttQoS; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.apache.commons.lang3.StringUtils; |
||||
|
import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttCallback; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttConnectOptions; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttException; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Assert; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Ignore; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.Device; |
||||
|
import org.thingsboard.server.common.data.DeviceProfile; |
||||
|
import org.thingsboard.server.common.data.DeviceProfileType; |
||||
|
import org.thingsboard.server.common.data.DeviceTransportType; |
||||
|
import org.thingsboard.server.common.data.Tenant; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.common.data.User; |
||||
|
import org.thingsboard.server.common.data.device.profile.DefaultDeviceProfileConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.DeviceProfileData; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttDeviceProfileTransportConfiguration; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
||||
|
import org.thingsboard.server.common.data.security.Authority; |
||||
|
import org.thingsboard.server.common.data.security.DeviceCredentials; |
||||
|
import org.thingsboard.server.controller.AbstractControllerTest; |
||||
|
import org.thingsboard.server.dao.util.mapping.JacksonUtil; |
||||
|
import org.thingsboard.server.service.security.AccessValidator; |
||||
|
|
||||
|
import java.util.Arrays; |
||||
|
import java.util.concurrent.CountDownLatch; |
||||
|
import java.util.concurrent.TimeUnit; |
||||
|
import java.util.concurrent.atomic.AtomicInteger; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
||||
|
|
||||
|
/** |
||||
|
* @author Valerii Sosliuk |
||||
|
*/ |
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttServerSideRpcDefaultIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("RPC test device", "RPC test gateway", null, null, null); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
super.processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttOneWayRpcDeviceOffline() throws Exception { |
||||
|
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"24\",\"value\": 1},\"timeout\": 6000}"; |
||||
|
String deviceId = savedDevice.getId().getId().toString(); |
||||
|
|
||||
|
doPostAsync("/api/plugins/rpc/oneway/" + deviceId, setGpioRequest, String.class, status().is(409), |
||||
|
asyncContextTimeoutToUseRpcPlugin); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttOneWayRpcDeviceDoesNotExist() throws Exception { |
||||
|
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"25\",\"value\": 1}}"; |
||||
|
String nonExistentDeviceId = Uuids.timeBased().toString(); |
||||
|
|
||||
|
String result = doPostAsync("/api/plugins/rpc/oneway/" + nonExistentDeviceId, setGpioRequest, String.class, |
||||
|
status().isNotFound()); |
||||
|
Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttTwoWayRpcDeviceOffline() throws Exception { |
||||
|
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"27\",\"value\": 1},\"timeout\": 6000}"; |
||||
|
String deviceId = savedDevice.getId().getId().toString(); |
||||
|
|
||||
|
doPostAsync("/api/plugins/rpc/twoway/" + deviceId, setGpioRequest, String.class, status().is(409), |
||||
|
asyncContextTimeoutToUseRpcPlugin); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttTwoWayRpcDeviceDoesNotExist() throws Exception { |
||||
|
String setGpioRequest = "{\"method\":\"setGpio\",\"params\":{\"pin\": \"28\",\"value\": 1}}"; |
||||
|
String nonExistentDeviceId = Uuids.timeBased().toString(); |
||||
|
|
||||
|
String result = doPostAsync("/api/plugins/rpc/twoway/" + nonExistentDeviceId, setGpioRequest, String.class, |
||||
|
status().isNotFound()); |
||||
|
Assert.assertEquals(AccessValidator.DEVICE_WITH_REQUESTED_ID_NOT_FOUND, result); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttOneWayRpc() throws Exception { |
||||
|
processOneWayRpcTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttTwoWayRpc() throws Exception { |
||||
|
processTwoWayRpcTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayServerMqttOneWayRpc() throws Exception { |
||||
|
processOneWayRpcTestGateway("Gateway Device OneWay RPC"); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayServerMqttTwoWayRpc() throws Exception { |
||||
|
processTwoWayRpcTestGateway("Gateway Device TwoWay RPC"); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,66 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.rpc; |
||||
|
|
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Ignore; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttServerSideRpcJsonIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.JSON, null, null); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
super.processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttOneWayRpc() throws Exception { |
||||
|
processOneWayRpcTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttTwoWayRpc() throws Exception { |
||||
|
processTwoWayRpcTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayServerMqttOneWayRpc() throws Exception { |
||||
|
processOneWayRpcTestGateway("Gateway Device OneWay RPC Json"); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayServerMqttTwoWayRpc() throws Exception { |
||||
|
processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Json"); |
||||
|
} |
||||
|
|
||||
|
protected void processOneWayRpcTestGateway(String deviceName) throws Exception { |
||||
|
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
||||
|
String payload = "{\"device\": \"" + deviceName + "\", \"type\": \"" + TransportPayloadType.JSON.name() + "\"}"; |
||||
|
byte[] payloadBytes = payload.getBytes(); |
||||
|
validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -0,0 +1,114 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.rpc; |
||||
|
|
||||
|
import com.google.protobuf.InvalidProtocolBufferException; |
||||
|
import lombok.extern.slf4j.Slf4j; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttAsyncClient; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttException; |
||||
|
import org.eclipse.paho.client.mqttv3.MqttMessage; |
||||
|
import org.junit.After; |
||||
|
import org.junit.Before; |
||||
|
import org.junit.Ignore; |
||||
|
import org.junit.Test; |
||||
|
import org.thingsboard.server.common.data.TransportPayloadType; |
||||
|
import org.thingsboard.server.common.data.device.profile.MqttTopics; |
||||
|
import org.thingsboard.server.gen.transport.TransportApiProtos; |
||||
|
import org.thingsboard.server.gen.transport.TransportProtos; |
||||
|
|
||||
|
import static org.junit.Assert.assertEquals; |
||||
|
import static org.junit.Assert.assertNotNull; |
||||
|
|
||||
|
@Slf4j |
||||
|
public abstract class AbstractMqttServerSideRpcProtoIntegrationTest extends AbstractMqttServerSideRpcIntegrationTest { |
||||
|
|
||||
|
@Before |
||||
|
public void beforeTest() throws Exception { |
||||
|
processBeforeTest("RPC test device", "RPC test gateway", TransportPayloadType.PROTOBUF, null, null); |
||||
|
} |
||||
|
|
||||
|
@After |
||||
|
public void afterTest() throws Exception { |
||||
|
super.processAfterTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttOneWayRpc() throws Exception { |
||||
|
processOneWayRpcTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testServerMqttTwoWayRpc() throws Exception { |
||||
|
processTwoWayRpcTest(); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayServerMqttOneWayRpc() throws Exception { |
||||
|
processOneWayRpcTestGateway("Gateway Device OneWay RPC Proto"); |
||||
|
} |
||||
|
|
||||
|
@Test |
||||
|
public void testGatewayServerMqttTwoWayRpc() throws Exception { |
||||
|
processTwoWayRpcTestGateway("Gateway Device TwoWay RPC Proto"); |
||||
|
} |
||||
|
|
||||
|
protected void processTwoWayRpcTestGateway(String deviceName) throws Exception { |
||||
|
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
||||
|
TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); |
||||
|
byte[] payloadBytes = connectMsgProto.toByteArray(); |
||||
|
validateTwoWayRpcGateway(deviceName, client, payloadBytes); |
||||
|
} |
||||
|
|
||||
|
protected void processOneWayRpcTestGateway(String deviceName) throws Exception { |
||||
|
MqttAsyncClient client = getMqttAsyncClient(gatewayAccessToken); |
||||
|
TransportApiProtos.ConnectMsg connectMsgProto = getConnectProto(deviceName); |
||||
|
byte[] payloadBytes = connectMsgProto.toByteArray(); |
||||
|
validateOneWayRpcGatewayResponse(deviceName, client, payloadBytes); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
private TransportApiProtos.ConnectMsg getConnectProto(String deviceName) { |
||||
|
TransportApiProtos.ConnectMsg.Builder builder = TransportApiProtos.ConnectMsg.newBuilder(); |
||||
|
builder.setDeviceName(deviceName); |
||||
|
builder.setDeviceType(TransportPayloadType.PROTOBUF.name()); |
||||
|
return builder.build(); |
||||
|
} |
||||
|
|
||||
|
protected MqttMessage processMessageArrived(String requestTopic, MqttMessage mqttMessage) throws MqttException, InvalidProtocolBufferException { |
||||
|
MqttMessage message = new MqttMessage(); |
||||
|
if (requestTopic.startsWith(MqttTopics.BASE_DEVICE_API_TOPIC)) { |
||||
|
TransportProtos.ToDeviceRpcResponseMsg toDeviceRpcResponseMsg = TransportProtos.ToDeviceRpcResponseMsg.newBuilder() |
||||
|
.setPayload(DEVICE_RESPONSE) |
||||
|
.setRequestId(0) |
||||
|
.build(); |
||||
|
message.setPayload(toDeviceRpcResponseMsg.toByteArray()); |
||||
|
} else { |
||||
|
TransportApiProtos.GatewayDeviceRpcRequestMsg msg = TransportApiProtos.GatewayDeviceRpcRequestMsg.parseFrom(mqttMessage.getPayload()); |
||||
|
String deviceName = msg.getDeviceName(); |
||||
|
int requestId = msg.getRpcRequestMsg().getRequestId(); |
||||
|
TransportApiProtos.GatewayRpcResponseMsg gatewayRpcResponseMsg = TransportApiProtos.GatewayRpcResponseMsg.newBuilder() |
||||
|
.setDeviceName(deviceName) |
||||
|
.setId(requestId) |
||||
|
.setData("{\"success\": true}") |
||||
|
.build(); |
||||
|
message.setPayload(gatewayRpcResponseMsg.toByteArray()); |
||||
|
} |
||||
|
return message; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
} |
||||
@ -0,0 +1,23 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.rpc.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcJsonIntegrationTest; |
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttServerSideRpcJsonSqlIntegrationTest extends AbstractMqttServerSideRpcJsonIntegrationTest { |
||||
|
} |
||||
@ -0,0 +1,24 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2020 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.mqtt.rpc.sql; |
||||
|
|
||||
|
import org.thingsboard.server.dao.service.DaoSqlTest; |
||||
|
import org.thingsboard.server.mqtt.rpc.AbstractMqttServerSideRpcProtoIntegrationTest; |
||||
|
|
||||
|
|
||||
|
@DaoSqlTest |
||||
|
public class MqttServerSideRpcProtoSqlIntegrationTest extends AbstractMqttServerSideRpcProtoIntegrationTest { |
||||
|
} |
||||
Some files were not shown because too many files changed in this diff
Loading…
Reference in new issue