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