71 changed files with 634 additions and 1407 deletions
@ -1,13 +0,0 @@ |
|||
{ |
|||
"apiToken": "messaging", |
|||
"name": "Demo Device Messaging RPC Plugin", |
|||
"clazz": "org.thingsboard.server.extensions.core.plugin.messaging.DeviceMessagingPlugin", |
|||
"publicAccess": false, |
|||
"state": "ACTIVE", |
|||
"configuration": { |
|||
"maxDeviceCountPerCustomer": 1024, |
|||
"defaultTimeout": 20000, |
|||
"maxTimeout": 60000 |
|||
}, |
|||
"additionalInfo": null |
|||
} |
|||
@ -1,28 +0,0 @@ |
|||
{ |
|||
"apiToken": "mail", |
|||
"name": "Demo Email Plugin", |
|||
"clazz": "org.thingsboard.server.extensions.core.plugin.mail.MailPlugin", |
|||
"publicAccess": true, |
|||
"state": "ACTIVE", |
|||
"configuration": { |
|||
"host": "smtp.sendgrid.net", |
|||
"port": 2525, |
|||
"username": "apikey", |
|||
"password": "your_api_key", |
|||
"otherProperties": [ |
|||
{ |
|||
"key": "mail.smtp.auth", |
|||
"value": "true" |
|||
}, |
|||
{ |
|||
"key": "mail.smtp.timeout", |
|||
"value": "10000" |
|||
}, |
|||
{ |
|||
"key": "mail.smtp.starttls.enable", |
|||
"value": "true" |
|||
} |
|||
] |
|||
}, |
|||
"additionalInfo": null |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
{ |
|||
"apiToken": "time", |
|||
"name": "Demo Time RPC Plugin", |
|||
"clazz": "org.thingsboard.server.extensions.core.plugin.time.TimePlugin", |
|||
"publicAccess": false, |
|||
"state": "ACTIVE", |
|||
"configuration": { |
|||
"timeFormat": "yyyy MM dd HH:mm:ss.SSS" |
|||
}, |
|||
"additionalInfo": null |
|||
} |
|||
@ -1,46 +0,0 @@ |
|||
{ |
|||
"name": "Demo Alarm Rule", |
|||
"state": "ACTIVE", |
|||
"weight": 0, |
|||
"pluginToken": "mail", |
|||
"filters": [ |
|||
{ |
|||
"clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter", |
|||
"name": "MsgTypeFilter", |
|||
"configuration": { |
|||
"messageTypes": [ |
|||
"POST_TELEMETRY", |
|||
"POST_ATTRIBUTES", |
|||
"GET_ATTRIBUTES" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"clazz": "org.thingsboard.server.extensions.core.filter.DeviceTelemetryFilter", |
|||
"name": "Temperature filter", |
|||
"configuration": { |
|||
"filter": "typeof temperature !== 'undefined' && temperature >= 100" |
|||
} |
|||
} |
|||
], |
|||
"processor": { |
|||
"clazz": "org.thingsboard.server.extensions.core.processor.AlarmDeduplicationProcessor", |
|||
"name": "AlarmDeduplicationProcessor", |
|||
"configuration": { |
|||
"alarmIdTemplate": "[$date.get('yyyy-MM-dd HH:mm')] Device $cs.get('serialNumber')($cs.get('model')) temperature is high!", |
|||
"alarmBodyTemplate": "[$date.get('yyyy-MM-dd HH:mm:ss')] Device $cs.get('serialNumber')($cs.get('model')) temperature is $temp.valueAsString!" |
|||
} |
|||
}, |
|||
"action": { |
|||
"clazz": "org.thingsboard.server.extensions.core.action.mail.SendMailAction", |
|||
"name": "Send Mail Action", |
|||
"configuration": { |
|||
"sendFlag": "isNewAlarm", |
|||
"fromTemplate": "thingsboard@gmail.com", |
|||
"toTemplate": "thingsboard@gmail.com", |
|||
"subjectTemplate": "$alarmId", |
|||
"bodyTemplate": "$alarmBody" |
|||
} |
|||
}, |
|||
"additionalInfo": null |
|||
} |
|||
@ -1,35 +0,0 @@ |
|||
{ |
|||
"name": "Demo getTime RPC Rule", |
|||
"state": "ACTIVE", |
|||
"weight": 0, |
|||
"pluginToken": "time", |
|||
"filters": [ |
|||
{ |
|||
"configuration": { |
|||
"messageTypes": [ |
|||
"RPC_REQUEST" |
|||
] |
|||
}, |
|||
"name": "RPC Request Filter", |
|||
"clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter" |
|||
}, |
|||
{ |
|||
"configuration": { |
|||
"methodNames": [ |
|||
{ |
|||
"name": "getTime" |
|||
} |
|||
] |
|||
}, |
|||
"name": "getTime method filter", |
|||
"clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter" |
|||
} |
|||
], |
|||
"processor": null, |
|||
"action": { |
|||
"configuration": {}, |
|||
"clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction", |
|||
"name": "getTimeAction" |
|||
}, |
|||
"additionalInfo": null |
|||
} |
|||
@ -1,38 +0,0 @@ |
|||
{ |
|||
"name": "Demo Messaging RPC Rule", |
|||
"state": "ACTIVE", |
|||
"weight": 0, |
|||
"pluginToken": "messaging", |
|||
"filters": [ |
|||
{ |
|||
"configuration": { |
|||
"messageTypes": [ |
|||
"RPC_REQUEST" |
|||
] |
|||
}, |
|||
"name": "RPC Request Filter", |
|||
"clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter" |
|||
}, |
|||
{ |
|||
"configuration": { |
|||
"methodNames": [ |
|||
{ |
|||
"name": "getDevices" |
|||
}, |
|||
{ |
|||
"name": "sendMsg" |
|||
} |
|||
] |
|||
}, |
|||
"name": "Messaging methods filter", |
|||
"clazz": "org.thingsboard.server.extensions.core.filter.MethodNameFilter" |
|||
} |
|||
], |
|||
"processor": null, |
|||
"action": { |
|||
"configuration": {}, |
|||
"clazz": "org.thingsboard.server.extensions.core.action.rpc.RpcPluginAction", |
|||
"name": "Messaging RPC Action" |
|||
}, |
|||
"additionalInfo": null |
|||
} |
|||
@ -1,11 +0,0 @@ |
|||
{ |
|||
"apiToken": "rpc", |
|||
"name": "System RPC Plugin", |
|||
"clazz": "org.thingsboard.server.extensions.core.plugin.rpc.RpcPlugin", |
|||
"publicAccess": true, |
|||
"state": "ACTIVE", |
|||
"configuration": { |
|||
"defaultTimeout": 20000 |
|||
}, |
|||
"additionalInfo": null |
|||
} |
|||
@ -1,9 +0,0 @@ |
|||
{ |
|||
"apiToken": "telemetry", |
|||
"name": "System Telemetry Plugin", |
|||
"clazz": "org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin", |
|||
"publicAccess": true, |
|||
"state": "ACTIVE", |
|||
"configuration": {}, |
|||
"additionalInfo": null |
|||
} |
|||
@ -1,29 +0,0 @@ |
|||
{ |
|||
"name": "System Telemetry Rule", |
|||
"state": "ACTIVE", |
|||
"weight": 0, |
|||
"pluginToken": "telemetry", |
|||
"filters": [ |
|||
{ |
|||
"clazz": "org.thingsboard.server.extensions.core.filter.MsgTypeFilter", |
|||
"name": "TelemetryFilter", |
|||
"configuration": { |
|||
"messageTypes": [ |
|||
"POST_TELEMETRY", |
|||
"POST_ATTRIBUTES", |
|||
"GET_ATTRIBUTES" |
|||
] |
|||
} |
|||
} |
|||
], |
|||
"processor": null, |
|||
"action": { |
|||
"clazz": "org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction", |
|||
"name": "TelemetryMsgConverterAction", |
|||
"configuration": { |
|||
"timeUnit": "DAYS", |
|||
"ttlValue": 365 |
|||
} |
|||
}, |
|||
"additionalInfo": null |
|||
} |
|||
@ -0,0 +1,102 @@ |
|||
{ |
|||
"ruleChain": { |
|||
"additionalInfo": null, |
|||
"name": "Root Rule Chain", |
|||
"firstRuleNodeId": null, |
|||
"root": true, |
|||
"debugMode": false, |
|||
"configuration": null |
|||
}, |
|||
"metadata": { |
|||
"firstNodeIndex": 2, |
|||
"nodes": [ |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 639, |
|||
"layoutY": 113 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode", |
|||
"name": "PostAttributes", |
|||
"debugMode": true, |
|||
"configuration": { |
|||
"messageTypes": [ |
|||
"POST_ATTRIBUTES_REQUEST" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 638, |
|||
"layoutY": 206 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.filter.TbMsgTypeFilterNode", |
|||
"name": "PostTelemetry", |
|||
"debugMode": true, |
|||
"configuration": { |
|||
"messageTypes": [ |
|||
"POST_TELEMETRY_REQUEST" |
|||
] |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 297, |
|||
"layoutY": 148 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.action.TbLogNode", |
|||
"name": "Log", |
|||
"debugMode": false, |
|||
"configuration": { |
|||
"jsScript": "return 'incoming message = ' + msg;" |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 905, |
|||
"layoutY": 203 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgTimeseriesNode", |
|||
"name": "SaveTS", |
|||
"debugMode": true, |
|||
"configuration": { |
|||
"defaultTTL": 0 |
|||
} |
|||
}, |
|||
{ |
|||
"additionalInfo": { |
|||
"layoutX": 904, |
|||
"layoutY": 110 |
|||
}, |
|||
"type": "org.thingsboard.rule.engine.telemetry.TbMsgAttributesNode", |
|||
"name": "save client attributes", |
|||
"debugMode": true, |
|||
"configuration": { |
|||
"scope": "CLIENT_SCOPE" |
|||
} |
|||
} |
|||
], |
|||
"connections": [ |
|||
{ |
|||
"fromIndex": 0, |
|||
"toIndex": 4, |
|||
"type": "True" |
|||
}, |
|||
{ |
|||
"fromIndex": 1, |
|||
"toIndex": 3, |
|||
"type": "True" |
|||
}, |
|||
{ |
|||
"fromIndex": 2, |
|||
"toIndex": 0, |
|||
"type": "Success" |
|||
}, |
|||
{ |
|||
"fromIndex": 2, |
|||
"toIndex": 1, |
|||
"type": "Success" |
|||
} |
|||
], |
|||
"ruleChainConnections": null |
|||
} |
|||
} |
|||
@ -0,0 +1,182 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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.install; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import lombok.Getter; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Autowired; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.server.common.data.Dashboard; |
|||
import org.thingsboard.server.common.data.id.CustomerId; |
|||
import org.thingsboard.server.common.data.id.TenantId; |
|||
import org.thingsboard.server.common.data.rule.RuleChain; |
|||
import org.thingsboard.server.common.data.rule.RuleChainMetaData; |
|||
import org.thingsboard.server.common.data.widget.WidgetType; |
|||
import org.thingsboard.server.common.data.widget.WidgetsBundle; |
|||
import org.thingsboard.server.dao.dashboard.DashboardService; |
|||
import org.thingsboard.server.dao.rule.RuleChainService; |
|||
import org.thingsboard.server.dao.widget.WidgetTypeService; |
|||
import org.thingsboard.server.dao.widget.WidgetsBundleService; |
|||
|
|||
import java.io.IOException; |
|||
import java.nio.file.DirectoryStream; |
|||
import java.nio.file.Files; |
|||
import java.nio.file.Path; |
|||
import java.nio.file.Paths; |
|||
|
|||
import static org.thingsboard.server.service.install.DatabaseHelper.objectMapper; |
|||
|
|||
/** |
|||
* Created by ashvayka on 18.04.18. |
|||
*/ |
|||
@Component |
|||
@Slf4j |
|||
public class InstallScripts { |
|||
|
|||
public static final String APP_DIR = "application"; |
|||
public static final String SRC_DIR = "src"; |
|||
public static final String MAIN_DIR = "main"; |
|||
public static final String DATA_DIR = "data"; |
|||
public static final String JSON_DIR = "json"; |
|||
public static final String SYSTEM_DIR = "system"; |
|||
public static final String TENANT_DIR = "tenant"; |
|||
public static final String DEMO_DIR = "demo"; |
|||
public static final String RULE_CHAINS_DIR = "rule_chains"; |
|||
public static final String WIDGET_BUNDLES_DIR = "widget_bundles"; |
|||
public static final String DASHBOARDS_DIR = "dashboards"; |
|||
|
|||
public static final String JSON_EXT = ".json"; |
|||
|
|||
@Value("${install.data_dir:}") |
|||
private String dataDir; |
|||
|
|||
@Autowired |
|||
private RuleChainService ruleChainService; |
|||
|
|||
@Autowired |
|||
private DashboardService dashboardService; |
|||
|
|||
@Autowired |
|||
private WidgetTypeService widgetTypeService; |
|||
|
|||
@Autowired |
|||
private WidgetsBundleService widgetsBundleService; |
|||
|
|||
public Path getTenantRuleChainsDir() { |
|||
return Paths.get(getDataDir(), JSON_DIR, TENANT_DIR, RULE_CHAINS_DIR); |
|||
} |
|||
|
|||
public String getDataDir() { |
|||
if (!StringUtils.isEmpty(dataDir)) { |
|||
return dataDir; |
|||
} else { |
|||
String workDir = System.getProperty("user.dir"); |
|||
if (workDir.endsWith("application")) { |
|||
return Paths.get(workDir, SRC_DIR, MAIN_DIR, DATA_DIR).toString(); |
|||
} else { |
|||
Path dataDirPath = Paths.get(workDir, APP_DIR, SRC_DIR, MAIN_DIR, DATA_DIR); |
|||
if (Files.exists(dataDirPath)) { |
|||
return dataDirPath.toString(); |
|||
} else { |
|||
throw new RuntimeException("Not valid working directory: " + workDir + ". Please use either root project directory, application module directory or specify valid \"install.data_dir\" ENV variable to avoid automatic data directory lookup!"); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
public void createDefaultRuleChains(TenantId tenantId) throws IOException { |
|||
Path tenantChainsDir = getTenantRuleChainsDir(); |
|||
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(tenantChainsDir, path -> path.toString().endsWith(InstallScripts.JSON_EXT))) { |
|||
dirStream.forEach( |
|||
path -> { |
|||
try { |
|||
JsonNode ruleChainJson = objectMapper.readTree(path.toFile()); |
|||
RuleChain ruleChain = objectMapper.treeToValue(ruleChainJson.get("ruleChain"), RuleChain.class); |
|||
RuleChainMetaData ruleChainMetaData = objectMapper.treeToValue(ruleChainJson.get("metadata"), RuleChainMetaData.class); |
|||
|
|||
ruleChain.setTenantId(tenantId); |
|||
ruleChain = ruleChainService.saveRuleChain(ruleChain); |
|||
|
|||
ruleChainMetaData.setRuleChainId(ruleChain.getId()); |
|||
ruleChainService.saveRuleChainMetaData(ruleChainMetaData); |
|||
} catch (Exception e) { |
|||
log.error("Unable to load rule chain from json: [{}]", path.toString()); |
|||
throw new RuntimeException("Unable to load rule chain from json", e); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
public void loadSystemWidgets() throws Exception { |
|||
Path widgetBundlesDir = Paths.get(getDataDir(), JSON_DIR, SYSTEM_DIR, WIDGET_BUNDLES_DIR); |
|||
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(widgetBundlesDir, path -> path.toString().endsWith(JSON_EXT))) { |
|||
dirStream.forEach( |
|||
path -> { |
|||
try { |
|||
JsonNode widgetsBundleDescriptorJson = objectMapper.readTree(path.toFile()); |
|||
JsonNode widgetsBundleJson = widgetsBundleDescriptorJson.get("widgetsBundle"); |
|||
WidgetsBundle widgetsBundle = objectMapper.treeToValue(widgetsBundleJson, WidgetsBundle.class); |
|||
WidgetsBundle savedWidgetsBundle = widgetsBundleService.saveWidgetsBundle(widgetsBundle); |
|||
JsonNode widgetTypesArrayJson = widgetsBundleDescriptorJson.get("widgetTypes"); |
|||
widgetTypesArrayJson.forEach( |
|||
widgetTypeJson -> { |
|||
try { |
|||
WidgetType widgetType = objectMapper.treeToValue(widgetTypeJson, WidgetType.class); |
|||
widgetType.setBundleAlias(savedWidgetsBundle.getAlias()); |
|||
widgetTypeService.saveWidgetType(widgetType); |
|||
} catch (Exception e) { |
|||
log.error("Unable to load widget type from json: [{}]", path.toString()); |
|||
throw new RuntimeException("Unable to load widget type from json", e); |
|||
} |
|||
} |
|||
); |
|||
} catch (Exception e) { |
|||
log.error("Unable to load widgets bundle from json: [{}]", path.toString()); |
|||
throw new RuntimeException("Unable to load widgets bundle from json", e); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
public void loadDashboards(TenantId tenantId, CustomerId customerId) throws Exception { |
|||
Path dashboardsDir = Paths.get(getDataDir(), JSON_DIR, DEMO_DIR, DASHBOARDS_DIR); |
|||
try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(dashboardsDir, path -> path.toString().endsWith(JSON_EXT))) { |
|||
dirStream.forEach( |
|||
path -> { |
|||
try { |
|||
JsonNode dashboardJson = objectMapper.readTree(path.toFile()); |
|||
Dashboard dashboard = objectMapper.treeToValue(dashboardJson, Dashboard.class); |
|||
dashboard.setTenantId(tenantId); |
|||
Dashboard savedDashboard = dashboardService.saveDashboard(dashboard); |
|||
if (customerId != null && !customerId.isNullUid()) { |
|||
dashboardService.assignDashboardToCustomer(savedDashboard.getId(), customerId); |
|||
} |
|||
} catch (Exception e) { |
|||
log.error("Unable to load dashboard from json: [{}]", path.toString()); |
|||
throw new RuntimeException("Unable to load dashboard from json", e); |
|||
} |
|||
} |
|||
); |
|||
} |
|||
} |
|||
|
|||
|
|||
} |
|||
@ -1,27 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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.actors; |
|||
|
|||
import org.junit.extensions.cpsuite.ClasspathSuite; |
|||
import org.junit.runner.RunWith; |
|||
|
|||
/** |
|||
* @author Andrew Shvayka |
|||
*/ |
|||
@RunWith(ClasspathSuite.class) |
|||
@ClasspathSuite.ClassnameFilters({"org.thingsboard.server.actors.*Test"}) |
|||
public class ActorsTestSuite { |
|||
} |
|||
@ -1,245 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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.actors; |
|||
|
|||
import static org.mockito.Matchers.any; |
|||
import static org.mockito.Mockito.mock; |
|||
import static org.mockito.Mockito.verify; |
|||
import static org.mockito.Mockito.when; |
|||
|
|||
import java.util.*; |
|||
|
|||
import com.google.common.util.concurrent.Futures; |
|||
import org.thingsboard.server.actors.service.DefaultActorService; |
|||
import org.thingsboard.server.common.data.id.*; |
|||
import org.thingsboard.server.common.data.kv.TsKvEntry; |
|||
import org.thingsboard.server.common.data.page.TextPageData; |
|||
import org.thingsboard.server.common.data.plugin.ComponentDescriptor; |
|||
import org.thingsboard.server.common.data.plugin.ComponentLifecycleState; |
|||
import org.thingsboard.server.common.data.plugin.ComponentType; |
|||
import org.thingsboard.server.common.msg.session.*; |
|||
import org.thingsboard.server.dao.attributes.AttributesService; |
|||
import org.thingsboard.server.dao.event.EventService; |
|||
import org.thingsboard.server.gen.discovery.ServerInstanceProtos; |
|||
import org.thingsboard.server.service.cluster.discovery.DiscoveryService; |
|||
import org.thingsboard.server.service.cluster.discovery.ServerInstance; |
|||
import org.thingsboard.server.service.cluster.routing.ClusterRoutingService; |
|||
import org.thingsboard.server.service.cluster.rpc.ClusterRpcService; |
|||
import org.thingsboard.server.service.component.ComponentDiscoveryService; |
|||
import org.thingsboard.server.common.transport.auth.DeviceAuthResult; |
|||
import org.thingsboard.server.common.transport.auth.DeviceAuthService; |
|||
import org.thingsboard.server.common.data.DataConstants; |
|||
import org.thingsboard.server.common.data.Device; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
|||
import org.thingsboard.server.common.data.kv.KvEntry; |
|||
import org.thingsboard.server.common.data.kv.StringDataEntry; |
|||
import org.thingsboard.server.common.data.plugin.PluginMetaData; |
|||
import org.thingsboard.server.common.data.rule.RuleMetaData; |
|||
import org.thingsboard.server.common.data.security.DeviceCredentialsFilter; |
|||
import org.thingsboard.server.common.data.security.DeviceTokenCredentials; |
|||
import org.thingsboard.server.common.msg.core.BasicTelemetryUploadRequest; |
|||
import org.thingsboard.server.dao.device.DeviceService; |
|||
import org.thingsboard.server.dao.model.ModelConstants; |
|||
import org.thingsboard.server.dao.plugin.PluginService; |
|||
import org.thingsboard.server.dao.rule.RuleService; |
|||
import org.thingsboard.server.dao.tenant.TenantService; |
|||
import org.thingsboard.server.dao.timeseries.TimeseriesService; |
|||
import org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin; |
|||
import org.junit.After; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.mockito.Mockito; |
|||
import org.springframework.test.util.ReflectionTestUtils; |
|||
|
|||
import com.fasterxml.jackson.databind.JsonNode; |
|||
import com.fasterxml.jackson.databind.ObjectMapper; |
|||
|
|||
public class DefaultActorServiceTest { |
|||
|
|||
private static final TenantId SYSTEM_TENANT = new TenantId(ModelConstants.NULL_UUID); |
|||
|
|||
private static final String PLUGIN_ID = "9fb2e951-e298-4acb-913a-db69af8a15f4"; |
|||
private static final String FILTERS_CONFIGURATION = |
|||
"[{\"clazz\":\"org.thingsboard.server.extensions.core.filter.MsgTypeFilter\", \"name\":\"TelemetryFilter\", \"configuration\": {\"messageTypes\":[\"POST_TELEMETRY\",\"POST_ATTRIBUTES\",\"GET_ATTRIBUTES\"]}}]"; |
|||
private static final String ACTION_CONFIGURATION = "{\"pluginToken\":\"telemetry\", \"clazz\":\"org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction\", \"name\":\"TelemetryMsgConverterAction\", \"configuration\":{}}"; |
|||
private static final String PLUGIN_CONFIGURATION = "{}"; |
|||
private DefaultActorService actorService; |
|||
private ActorSystemContext actorContext; |
|||
|
|||
private PluginService pluginService; |
|||
private RuleService ruleService; |
|||
private DeviceAuthService deviceAuthService; |
|||
private DeviceService deviceService; |
|||
private TimeseriesService tsService; |
|||
private TenantService tenantService; |
|||
private ClusterRpcService rpcService; |
|||
private DiscoveryService discoveryService; |
|||
private ClusterRoutingService routingService; |
|||
private AttributesService attributesService; |
|||
private ComponentDiscoveryService componentService; |
|||
private EventService eventService; |
|||
private ServerInstance serverInstance; |
|||
|
|||
private RuleMetaData ruleMock; |
|||
private PluginMetaData pluginMock; |
|||
private RuleId ruleId = new RuleId(UUID.randomUUID()); |
|||
private PluginId pluginId = new PluginId(UUID.fromString(PLUGIN_ID)); |
|||
private TenantId tenantId = new TenantId(UUID.randomUUID()); |
|||
|
|||
|
|||
@Before |
|||
public void before() throws Exception { |
|||
actorService = new DefaultActorService(); |
|||
actorContext = new ActorSystemContext(); |
|||
|
|||
tenantService = mock(TenantService.class); |
|||
pluginService = mock(PluginService.class); |
|||
ruleService = mock(RuleService.class); |
|||
deviceAuthService = mock(DeviceAuthService.class); |
|||
deviceService = mock(DeviceService.class); |
|||
tsService = mock(TimeseriesService.class); |
|||
rpcService = mock(ClusterRpcService.class); |
|||
discoveryService = mock(DiscoveryService.class); |
|||
routingService = mock(ClusterRoutingService.class); |
|||
attributesService = mock(AttributesService.class); |
|||
componentService = mock(ComponentDiscoveryService.class); |
|||
eventService = mock(EventService.class); |
|||
serverInstance = new ServerInstance(ServerInstanceProtos.ServerInfo.newBuilder().setHost("localhost").setPort(8080).build()); |
|||
|
|||
ReflectionTestUtils.setField(actorService, "actorContext", actorContext); |
|||
ReflectionTestUtils.setField(actorService, "rpcService", rpcService); |
|||
ReflectionTestUtils.setField(actorService, "discoveryService", discoveryService); |
|||
|
|||
ReflectionTestUtils.setField(actorContext, "syncSessionTimeout", 10000L); |
|||
ReflectionTestUtils.setField(actorContext, "pluginActorTerminationDelay", 10000L); |
|||
ReflectionTestUtils.setField(actorContext, "pluginErrorPersistFrequency", 10000L); |
|||
ReflectionTestUtils.setField(actorContext, "ruleActorTerminationDelay", 10000L); |
|||
ReflectionTestUtils.setField(actorContext, "ruleErrorPersistFrequency", 10000L); |
|||
ReflectionTestUtils.setField(actorContext, "pluginProcessingTimeout", 60000L); |
|||
ReflectionTestUtils.setField(actorContext, "tenantService", tenantService); |
|||
ReflectionTestUtils.setField(actorContext, "pluginService", pluginService); |
|||
ReflectionTestUtils.setField(actorContext, "ruleService", ruleService); |
|||
ReflectionTestUtils.setField(actorContext, "deviceAuthService", deviceAuthService); |
|||
ReflectionTestUtils.setField(actorContext, "deviceService", deviceService); |
|||
ReflectionTestUtils.setField(actorContext, "tsService", tsService); |
|||
ReflectionTestUtils.setField(actorContext, "rpcService", rpcService); |
|||
ReflectionTestUtils.setField(actorContext, "discoveryService", discoveryService); |
|||
ReflectionTestUtils.setField(actorContext, "tsService", tsService); |
|||
ReflectionTestUtils.setField(actorContext, "routingService", routingService); |
|||
ReflectionTestUtils.setField(actorContext, "attributesService", attributesService); |
|||
ReflectionTestUtils.setField(actorContext, "componentService", componentService); |
|||
ReflectionTestUtils.setField(actorContext, "eventService", eventService); |
|||
|
|||
|
|||
when(routingService.resolveById((EntityId) any())).thenReturn(Optional.empty()); |
|||
|
|||
when(discoveryService.getCurrentServer()).thenReturn(serverInstance); |
|||
|
|||
ruleMock = mock(RuleMetaData.class); |
|||
when(ruleMock.getId()).thenReturn(ruleId); |
|||
when(ruleMock.getState()).thenReturn(ComponentLifecycleState.ACTIVE); |
|||
when(ruleMock.getPluginToken()).thenReturn("telemetry"); |
|||
TextPageData<RuleMetaData> systemRules = new TextPageData<>(Collections.emptyList(), null, false); |
|||
TextPageData<RuleMetaData> tenantRules = new TextPageData<>(Collections.singletonList(ruleMock), null, false); |
|||
when(ruleService.findSystemRules(any())).thenReturn(systemRules); |
|||
when(ruleService.findTenantRules(any(), any())).thenReturn(tenantRules); |
|||
when(ruleService.findRuleById(ruleId)).thenReturn(ruleMock); |
|||
|
|||
pluginMock = mock(PluginMetaData.class); |
|||
when(pluginMock.getTenantId()).thenReturn(SYSTEM_TENANT); |
|||
when(pluginMock.getId()).thenReturn(pluginId); |
|||
when(pluginMock.getState()).thenReturn(ComponentLifecycleState.ACTIVE); |
|||
TextPageData<PluginMetaData> systemPlugins = new TextPageData<>(Collections.singletonList(pluginMock), null, false); |
|||
TextPageData<PluginMetaData> tenantPlugins = new TextPageData<>(Collections.emptyList(), null, false); |
|||
when(pluginService.findSystemPlugins(any())).thenReturn(systemPlugins); |
|||
when(pluginService.findTenantPlugins(any(), any())).thenReturn(tenantPlugins); |
|||
when(pluginService.findPluginByApiToken("telemetry")).thenReturn(pluginMock); |
|||
when(pluginService.findPluginById(pluginId)).thenReturn(pluginMock); |
|||
|
|||
TextPageData<Tenant> tenants = new TextPageData<>(Collections.emptyList(), null, false); |
|||
when(tenantService.findTenants(any())).thenReturn(tenants); |
|||
} |
|||
|
|||
private void initActorSystem() { |
|||
actorService.initActorSystem(); |
|||
} |
|||
|
|||
@After |
|||
public void after() { |
|||
actorService.stopActorSystem(); |
|||
} |
|||
|
|||
@Test |
|||
public void testBasicPostWithSyncSession() throws Exception { |
|||
SessionContext ssnCtx = mock(SessionContext.class); |
|||
KvEntry entry1 = new StringDataEntry("key1", "value1"); |
|||
KvEntry entry2 = new StringDataEntry("key2", "value2"); |
|||
BasicTelemetryUploadRequest telemetry = new BasicTelemetryUploadRequest(); |
|||
long ts = 42; |
|||
telemetry.add(ts, entry1); |
|||
telemetry.add(ts, entry2); |
|||
BasicAdaptorToSessionActorMsg msg = new BasicAdaptorToSessionActorMsg(ssnCtx, telemetry); |
|||
|
|||
DeviceId deviceId = new DeviceId(UUID.randomUUID()); |
|||
|
|||
DeviceCredentialsFilter filter = new DeviceTokenCredentials("token1"); |
|||
Device device = mock(Device.class); |
|||
|
|||
when(device.getId()).thenReturn(deviceId); |
|||
when(device.getTenantId()).thenReturn(tenantId); |
|||
when(ssnCtx.getSessionId()).thenReturn(new DummySessionID("session1")); |
|||
when(ssnCtx.getSessionType()).thenReturn(SessionType.SYNC); |
|||
when(deviceAuthService.process(filter)).thenReturn(DeviceAuthResult.of(deviceId)); |
|||
when(deviceService.findDeviceById(deviceId)).thenReturn(device); |
|||
|
|||
ObjectMapper ruleMapper = new ObjectMapper(); |
|||
when(ruleMock.getFilters()).thenReturn(ruleMapper.readTree(FILTERS_CONFIGURATION)); |
|||
when(ruleMock.getAction()).thenReturn(ruleMapper.readTree(ACTION_CONFIGURATION)); |
|||
|
|||
ComponentDescriptor filterComp = new ComponentDescriptor(); |
|||
filterComp.setClazz("org.thingsboard.server.extensions.core.filter.MsgTypeFilter"); |
|||
filterComp.setType(ComponentType.FILTER); |
|||
when(componentService.getComponent("org.thingsboard.server.extensions.core.filter.MsgTypeFilter")) |
|||
.thenReturn(Optional.of(filterComp)); |
|||
|
|||
ComponentDescriptor actionComp = new ComponentDescriptor(); |
|||
actionComp.setClazz("org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction"); |
|||
actionComp.setType(ComponentType.ACTION); |
|||
when(componentService.getComponent("org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction")) |
|||
.thenReturn(Optional.of(actionComp)); |
|||
|
|||
ObjectMapper pluginMapper = new ObjectMapper(); |
|||
JsonNode pluginAdditionalInfo = pluginMapper.readTree(PLUGIN_CONFIGURATION); |
|||
when(pluginMock.getConfiguration()).thenReturn(pluginAdditionalInfo); |
|||
when(pluginMock.getClazz()).thenReturn(TelemetryStoragePlugin.class.getName()); |
|||
|
|||
when(attributesService.findAll(deviceId, DataConstants.CLIENT_SCOPE)).thenReturn(Futures.immediateFuture(Collections.emptyList())); |
|||
when(attributesService.findAll(deviceId, DataConstants.SHARED_SCOPE)).thenReturn(Futures.immediateFuture(Collections.emptyList())); |
|||
when(attributesService.findAll(deviceId, DataConstants.SERVER_SCOPE)).thenReturn(Futures.immediateFuture(Collections.emptyList())); |
|||
|
|||
initActorSystem(); |
|||
Thread.sleep(1000); |
|||
actorService.process(new BasicToDeviceActorSessionMsg(device, msg)); |
|||
|
|||
// Check that device data was saved to DB;
|
|||
List<TsKvEntry> expected = new ArrayList<>(); |
|||
expected.add(new BasicTsKvEntry(ts, entry1)); |
|||
expected.add(new BasicTsKvEntry(ts, entry2)); |
|||
verify(tsService, Mockito.timeout(5000)).save(deviceId, expected, 0L); |
|||
} |
|||
|
|||
} |
|||
@ -1,63 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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.actors; |
|||
|
|||
import org.thingsboard.server.common.data.id.SessionId; |
|||
|
|||
public class DummySessionID implements SessionId { |
|||
|
|||
@Override |
|||
public String toString() { |
|||
return id; |
|||
} |
|||
|
|||
private final String id; |
|||
|
|||
public DummySessionID(String id) { |
|||
this.id = id; |
|||
} |
|||
|
|||
@Override |
|||
public String toUidStr() { |
|||
return id; |
|||
} |
|||
|
|||
@Override |
|||
public int hashCode() { |
|||
final int prime = 31; |
|||
int result = 1; |
|||
result = prime * result + ((id == null) ? 0 : id.hashCode()); |
|||
return result; |
|||
} |
|||
|
|||
@Override |
|||
public boolean equals(Object obj) { |
|||
if (this == obj) |
|||
return true; |
|||
if (obj == null) |
|||
return false; |
|||
if (getClass() != obj.getClass()) |
|||
return false; |
|||
DummySessionID other = (DummySessionID) obj; |
|||
if (id == null) { |
|||
if (other.id != null) |
|||
return false; |
|||
} else if (!id.equals(other.id)) |
|||
return false; |
|||
return true; |
|||
} |
|||
|
|||
} |
|||
@ -1,232 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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 com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.page.TextPageData; |
|||
import org.thingsboard.server.common.data.page.TextPageLink; |
|||
import org.thingsboard.server.common.data.plugin.PluginMetaData; |
|||
import org.thingsboard.server.common.data.rule.RuleMetaData; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
import org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
public abstract class BasePluginControllerTest extends AbstractControllerTest { |
|||
|
|||
private IdComparator<PluginMetaData> idComparator = new IdComparator<>(); |
|||
|
|||
private final ObjectMapper mapper = new ObjectMapper(); |
|||
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 testSavePlugin() throws Exception { |
|||
PluginMetaData plugin = new PluginMetaData(); |
|||
doPost("/api/plugin", plugin).andExpect(status().isBadRequest()); |
|||
plugin.setName("My plugin"); |
|||
doPost("/api/plugin", plugin).andExpect(status().isBadRequest()); |
|||
plugin.setApiToken("myplugin"); |
|||
doPost("/api/plugin", plugin).andExpect(status().isBadRequest()); |
|||
plugin.setConfiguration(mapper.readTree("{}")); |
|||
doPost("/api/plugin", plugin).andExpect(status().isBadRequest()); |
|||
plugin.setClazz(TelemetryStoragePlugin.class.getName()); |
|||
PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); |
|||
|
|||
Assert.assertNotNull(savedPlugin); |
|||
Assert.assertNotNull(savedPlugin.getId()); |
|||
Assert.assertTrue(savedPlugin.getCreatedTime() > 0); |
|||
Assert.assertEquals(savedTenant.getId(), savedPlugin.getTenantId()); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindPluginById() throws Exception { |
|||
PluginMetaData plugin = new PluginMetaData(); |
|||
plugin.setName("My plugin"); |
|||
plugin.setApiToken("myplugin"); |
|||
plugin.setConfiguration(mapper.readTree("{}")); |
|||
plugin.setClazz(TelemetryStoragePlugin.class.getName()); |
|||
|
|||
PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); |
|||
PluginMetaData foundPlugin = doGet("/api/plugin/" + savedPlugin.getId().getId().toString(), PluginMetaData.class); |
|||
Assert.assertNotNull(foundPlugin); |
|||
Assert.assertEquals(savedPlugin, foundPlugin); |
|||
} |
|||
|
|||
@Test |
|||
public void testActivatePlugin() throws Exception { |
|||
PluginMetaData plugin = new PluginMetaData(); |
|||
plugin.setName("My plugin"); |
|||
plugin.setApiToken("myplugin"); |
|||
plugin.setConfiguration(mapper.readTree("{}")); |
|||
plugin.setClazz(TelemetryStoragePlugin.class.getName()); |
|||
|
|||
PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); |
|||
|
|||
doPost("/api/plugin/" + savedPlugin.getId().getId().toString() + "/activate").andExpect(status().isOk()); |
|||
} |
|||
|
|||
@Test |
|||
public void testSuspendPlugin() throws Exception { |
|||
PluginMetaData plugin = new PluginMetaData(); |
|||
plugin.setName("My plugin"); |
|||
plugin.setApiToken("myplugin"); |
|||
plugin.setConfiguration(mapper.readTree("{}")); |
|||
plugin.setClazz(TelemetryStoragePlugin.class.getName()); |
|||
|
|||
PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); |
|||
|
|||
doPost("/api/plugin/" + savedPlugin.getId().getId().toString() + "/activate").andExpect(status().isOk()); |
|||
|
|||
RuleMetaData rule = BaseRuleControllerTest.createRuleMetaData(savedPlugin); |
|||
RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); |
|||
doPost("/api/rule/" + savedRule.getId().getId().toString() + "/activate").andExpect(status().isOk()); |
|||
|
|||
doPost("/api/plugin/" + savedPlugin.getId().getId().toString() + "/suspend").andExpect(status().isBadRequest()); |
|||
|
|||
doPost("/api/rule/" + savedRule.getId().getId().toString() + "/suspend").andExpect(status().isOk()); |
|||
|
|||
doPost("/api/plugin/" + savedPlugin.getId().getId().toString() + "/suspend").andExpect(status().isOk()); |
|||
} |
|||
|
|||
@Test |
|||
public void testDeletePluginById() throws Exception { |
|||
PluginMetaData plugin = new PluginMetaData(); |
|||
plugin.setName("My plugin"); |
|||
plugin.setApiToken("myplugin"); |
|||
plugin.setConfiguration(mapper.readTree("{}")); |
|||
plugin.setClazz(TelemetryStoragePlugin.class.getName()); |
|||
|
|||
PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); |
|||
|
|||
RuleMetaData rule = BaseRuleControllerTest.createRuleMetaData(savedPlugin); |
|||
RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); |
|||
|
|||
doDelete("/api/plugin/" + savedPlugin.getId().getId()).andExpect(status().isBadRequest()); |
|||
|
|||
doDelete("/api/rule/" + savedRule.getId().getId()).andExpect(status().isOk()); |
|||
|
|||
doDelete("/api/plugin/" + savedPlugin.getId().getId()).andExpect(status().isOk()); |
|||
doGet("/api/plugin/" + savedPlugin.getId().getId().toString()).andExpect(status().isNotFound()); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindPluginByToken() throws Exception { |
|||
PluginMetaData plugin = new PluginMetaData(); |
|||
plugin.setName("My plugin"); |
|||
plugin.setApiToken("myplugin"); |
|||
plugin.setConfiguration(mapper.readTree("{}")); |
|||
plugin.setClazz(TelemetryStoragePlugin.class.getName()); |
|||
|
|||
PluginMetaData savedPlugin = doPost("/api/plugin", plugin, PluginMetaData.class); |
|||
PluginMetaData foundPlugin = doGet("/api/plugin/token/" + "myplugin", PluginMetaData.class); |
|||
Assert.assertNotNull(foundPlugin); |
|||
Assert.assertEquals(savedPlugin, foundPlugin); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindCurrentTenantPlugins() throws Exception { |
|||
List<PluginMetaData> plugins = testPluginsCreation("/api/plugin"); |
|||
for (PluginMetaData plugin : plugins) { |
|||
doDelete("/api/plugin/" + plugin.getId().getId()).andExpect(status().isOk()); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testFindSystemPlugins() throws Exception { |
|||
loginSysAdmin(); |
|||
List<PluginMetaData> plugins = testPluginsCreation("/api/plugin/system"); |
|||
for (PluginMetaData plugin : plugins) { |
|||
doDelete("/api/plugin/" + plugin.getId().getId()).andExpect(status().isOk()); |
|||
} |
|||
} |
|||
|
|||
private List<PluginMetaData> testPluginsCreation(String url) throws Exception { |
|||
List<PluginMetaData> plugins = new ArrayList<>(); |
|||
for (int i = 0; i < 111; i++) { |
|||
PluginMetaData plugin = new PluginMetaData(); |
|||
plugin.setName("My plugin"); |
|||
plugin.setApiToken("myplugin" + i); |
|||
plugin.setConfiguration(mapper.readTree("{}")); |
|||
plugin.setClazz(TelemetryStoragePlugin.class.getName()); |
|||
plugins.add(doPost("/api/plugin", plugin, PluginMetaData.class)); |
|||
} |
|||
|
|||
List<PluginMetaData> loadedPlugins = new ArrayList<>(); |
|||
TextPageLink pageLink = new TextPageLink(23); |
|||
TextPageData<PluginMetaData> pageData; |
|||
do { |
|||
pageData = doGetTypedWithPageLink(url + "?", |
|||
new TypeReference<TextPageData<PluginMetaData>>() { |
|||
}, pageLink); |
|||
loadedPlugins.addAll(pageData.getData()); |
|||
if (pageData.hasNext()) { |
|||
pageLink = pageData.getNextPageLink(); |
|||
} |
|||
} while (pageData.hasNext()); |
|||
|
|||
loadedPlugins = loadedPlugins.stream() |
|||
.filter(p -> !p.getName().equals("System Telemetry Plugin")) |
|||
.filter(p -> !p.getName().equals("Mail Sender Plugin")) |
|||
.filter(p -> !p.getName().equals("System RPC Plugin")) |
|||
.collect(Collectors.toList()); |
|||
|
|||
Collections.sort(plugins, idComparator); |
|||
Collections.sort(loadedPlugins, idComparator); |
|||
|
|||
Assert.assertEquals(plugins, loadedPlugins); |
|||
return loadedPlugins; |
|||
} |
|||
} |
|||
@ -1,247 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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 com.fasterxml.jackson.databind.ObjectMapper; |
|||
import org.junit.After; |
|||
import org.junit.Assert; |
|||
import org.junit.Before; |
|||
import org.junit.Test; |
|||
import org.thingsboard.server.common.data.Tenant; |
|||
import org.thingsboard.server.common.data.User; |
|||
import org.thingsboard.server.common.data.page.TextPageData; |
|||
import org.thingsboard.server.common.data.page.TextPageLink; |
|||
import org.thingsboard.server.common.data.plugin.PluginMetaData; |
|||
import org.thingsboard.server.common.data.rule.RuleMetaData; |
|||
import org.thingsboard.server.common.data.security.Authority; |
|||
import org.thingsboard.server.extensions.core.plugin.telemetry.TelemetryStoragePlugin; |
|||
|
|||
import java.io.IOException; |
|||
import java.util.ArrayList; |
|||
import java.util.Collections; |
|||
import java.util.List; |
|||
import java.util.stream.Collectors; |
|||
|
|||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; |
|||
|
|||
public abstract class BaseRuleControllerTest extends AbstractControllerTest { |
|||
|
|||
private IdComparator<RuleMetaData> idComparator = new IdComparator<>(); |
|||
|
|||
private static final ObjectMapper mapper = new ObjectMapper(); |
|||
private Tenant savedTenant; |
|||
private User tenantAdmin; |
|||
private PluginMetaData sysPlugin; |
|||
private PluginMetaData tenantPlugin; |
|||
|
|||
@Before |
|||
public void beforeTest() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
sysPlugin = new PluginMetaData(); |
|||
sysPlugin.setName("Sys plugin"); |
|||
sysPlugin.setApiToken("sysplugin"); |
|||
sysPlugin.setConfiguration(mapper.readTree("{}")); |
|||
sysPlugin.setClazz(TelemetryStoragePlugin.class.getName()); |
|||
sysPlugin = doPost("/api/plugin", sysPlugin, PluginMetaData.class); |
|||
|
|||
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"); |
|||
|
|||
tenantPlugin = new PluginMetaData(); |
|||
tenantPlugin.setName("My plugin"); |
|||
tenantPlugin.setApiToken("myplugin"); |
|||
tenantPlugin.setConfiguration(mapper.readTree("{}")); |
|||
tenantPlugin.setClazz(TelemetryStoragePlugin.class.getName()); |
|||
tenantPlugin = doPost("/api/plugin", tenantPlugin, PluginMetaData.class); |
|||
} |
|||
|
|||
@After |
|||
public void afterTest() throws Exception { |
|||
loginSysAdmin(); |
|||
|
|||
doDelete("/api/tenant/" + savedTenant.getId().getId().toString()) |
|||
.andExpect(status().isOk()); |
|||
|
|||
doDelete("/api/plugin/" + sysPlugin.getId().getId()).andExpect(status().isOk()); |
|||
} |
|||
|
|||
@Test |
|||
public void testSaveRule() throws Exception { |
|||
RuleMetaData rule = new RuleMetaData(); |
|||
doPost("/api/rule", rule).andExpect(status().isBadRequest()); |
|||
rule.setName("My Rule"); |
|||
doPost("/api/rule", rule).andExpect(status().isBadRequest()); |
|||
rule.setPluginToken(tenantPlugin.getApiToken()); |
|||
doPost("/api/rule", rule).andExpect(status().isBadRequest()); |
|||
rule.setFilters(mapper.readTree("[{\"clazz\":\"org.thingsboard.server.extensions.core.filter.MsgTypeFilter\", " + |
|||
"\"name\":\"TelemetryFilter\", " + |
|||
"\"configuration\": {\"messageTypes\":[\"POST_TELEMETRY\",\"POST_ATTRIBUTES\",\"GET_ATTRIBUTES\"]}}]")); |
|||
doPost("/api/rule", rule).andExpect(status().isBadRequest()); |
|||
rule.setAction(mapper.readTree("{\"clazz\":\"org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction\", \"name\":\"TelemetryMsgConverterAction\", \"configuration\":{\"timeUnit\":\"DAYS\", \"ttlValue\":1}}")); |
|||
|
|||
RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); |
|||
Assert.assertNotNull(savedRule); |
|||
Assert.assertNotNull(savedRule.getId()); |
|||
Assert.assertTrue(savedRule.getCreatedTime() > 0); |
|||
Assert.assertEquals(savedTenant.getId(), savedRule.getTenantId()); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindRuleById() throws Exception { |
|||
RuleMetaData rule = createRuleMetaData(tenantPlugin); |
|||
RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); |
|||
|
|||
RuleMetaData foundRule = doGet("/api/rule/" + savedRule.getId().getId().toString(), RuleMetaData.class); |
|||
Assert.assertNotNull(foundRule); |
|||
Assert.assertEquals(savedRule, foundRule); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindRuleByPluginToken() throws Exception { |
|||
RuleMetaData rule = createRuleMetaData(tenantPlugin); |
|||
RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); |
|||
|
|||
List<RuleMetaData> foundRules = doGetTyped("/api/rule/token/" + savedRule.getPluginToken(), |
|||
new TypeReference<List<RuleMetaData>>() { |
|||
}); |
|||
Assert.assertNotNull(foundRules); |
|||
Assert.assertEquals(1, foundRules.size()); |
|||
Assert.assertEquals(savedRule, foundRules.get(0)); |
|||
} |
|||
|
|||
@Test |
|||
public void testActivateRule() throws Exception { |
|||
RuleMetaData rule = createRuleMetaData(tenantPlugin); |
|||
RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); |
|||
|
|||
doPost("/api/rule/" + savedRule.getId().getId().toString() + "/activate").andExpect(status().isBadRequest()); |
|||
|
|||
doPost("/api/plugin/" + tenantPlugin.getId().getId().toString() + "/activate").andExpect(status().isOk()); |
|||
|
|||
doPost("/api/rule/" + savedRule.getId().getId().toString() + "/activate").andExpect(status().isOk()); |
|||
} |
|||
|
|||
@Test |
|||
public void testSuspendRule() throws Exception { |
|||
RuleMetaData rule = createRuleMetaData(tenantPlugin); |
|||
RuleMetaData savedRule = doPost("/api/rule", rule, RuleMetaData.class); |
|||
|
|||
doPost("/api/plugin/" + tenantPlugin.getId().getId().toString() + "/activate").andExpect(status().isOk()); |
|||
doPost("/api/rule/" + savedRule.getId().getId().toString() + "/activate").andExpect(status().isOk()); |
|||
doPost("/api/rule/" + savedRule.getId().getId().toString() + "/suspend").andExpect(status().isOk()); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindSystemRules() throws Exception { |
|||
loginSysAdmin(); |
|||
List<RuleMetaData> rules = testRulesCreation("/api/rule/system", sysPlugin); |
|||
for (RuleMetaData rule : rules) { |
|||
doDelete("/api/rule/" + rule.getId().getId()).andExpect(status().isOk()); |
|||
} |
|||
loginTenantAdmin(); |
|||
} |
|||
|
|||
@Test |
|||
public void testFindCurrentTenantPlugins() throws Exception { |
|||
List<RuleMetaData> rules = testRulesCreation("/api/rule", tenantPlugin); |
|||
for (RuleMetaData rule : rules) { |
|||
doDelete("/api/rule/" + rule.getId().getId()).andExpect(status().isOk()); |
|||
} |
|||
} |
|||
|
|||
@Test |
|||
public void testFindTenantPlugins() throws Exception { |
|||
List<RuleMetaData> rules = testRulesCreation("/api/rule", tenantPlugin); |
|||
loginSysAdmin(); |
|||
List<RuleMetaData> loadedRules = new ArrayList<>(); |
|||
TextPageLink pageLink = new TextPageLink(3); |
|||
TextPageData<RuleMetaData> pageData; |
|||
do { |
|||
pageData = doGetTypedWithPageLink("/api/rule/tenant/" + savedTenant.getId().getId().toString() + "?", |
|||
new TypeReference<TextPageData<RuleMetaData>>() { |
|||
}, pageLink); |
|||
loadedRules.addAll(pageData.getData()); |
|||
if (pageData.hasNext()) { |
|||
pageLink = pageData.getNextPageLink(); |
|||
} |
|||
} while (pageData.hasNext()); |
|||
|
|||
Collections.sort(rules, idComparator); |
|||
Collections.sort(loadedRules, idComparator); |
|||
|
|||
Assert.assertEquals(rules, loadedRules); |
|||
|
|||
for (RuleMetaData rule : rules) { |
|||
doDelete("/api/rule/" + rule.getId().getId()).andExpect(status().isOk()); |
|||
} |
|||
} |
|||
|
|||
private List<RuleMetaData> testRulesCreation(String url, PluginMetaData plugin) throws Exception { |
|||
List<RuleMetaData> rules = new ArrayList<>(); |
|||
for (int i = 0; i < 6; i++) { |
|||
RuleMetaData rule = createRuleMetaData(plugin); |
|||
rule.setPluginToken(plugin.getApiToken()); |
|||
rule.setName(rule.getName() + i); |
|||
rules.add(doPost("/api/rule", rule, RuleMetaData.class)); |
|||
} |
|||
|
|||
List<RuleMetaData> loadedRules = new ArrayList<>(); |
|||
TextPageLink pageLink = new TextPageLink(3); |
|||
TextPageData<RuleMetaData> pageData; |
|||
do { |
|||
pageData = doGetTypedWithPageLink(url + "?", |
|||
new TypeReference<TextPageData<RuleMetaData>>() { |
|||
}, pageLink); |
|||
loadedRules.addAll(pageData.getData()); |
|||
if (pageData.hasNext()) { |
|||
pageLink = pageData.getNextPageLink(); |
|||
} |
|||
} while (pageData.hasNext()); |
|||
|
|||
loadedRules = loadedRules.stream().filter(p -> !p.getName().equals("System Telemetry Rule")).collect(Collectors.toList()); |
|||
|
|||
Collections.sort(rules, idComparator); |
|||
Collections.sort(loadedRules, idComparator); |
|||
|
|||
Assert.assertEquals(rules, loadedRules); |
|||
return loadedRules; |
|||
} |
|||
|
|||
public static RuleMetaData createRuleMetaData(PluginMetaData plugin) throws IOException { |
|||
RuleMetaData rule = new RuleMetaData(); |
|||
rule.setName("My Rule"); |
|||
rule.setPluginToken(plugin.getApiToken()); |
|||
rule.setFilters(mapper.readTree("[{\"clazz\":\"org.thingsboard.server.extensions.core.filter.MsgTypeFilter\", " + |
|||
"\"name\":\"TelemetryFilter\", " + |
|||
"\"configuration\": {\"messageTypes\":[\"POST_TELEMETRY\",\"POST_ATTRIBUTES\",\"GET_ATTRIBUTES\"]}}]")); |
|||
rule.setAction(mapper.readTree("{\"clazz\":\"org.thingsboard.server.extensions.core.action.telemetry.TelemetryPluginAction\", \"name\":\"TelemetryMsgConverterAction\", " + |
|||
"\"configuration\":{\"timeUnit\":\"DAYS\", \"ttlValue\":1}}")); |
|||
return rule; |
|||
} |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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.nosql; |
|||
|
|||
import org.thingsboard.server.controller.BasePluginControllerTest; |
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
|
|||
/** |
|||
* Created by Valerii Sosliuk on 6/28/2017. |
|||
*/ |
|||
@DaoNoSqlTest |
|||
public class PluginControllerNoSqlTest extends BasePluginControllerTest { |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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.nosql; |
|||
|
|||
import org.thingsboard.server.controller.BaseRuleControllerTest; |
|||
import org.thingsboard.server.dao.service.DaoNoSqlTest; |
|||
|
|||
/** |
|||
* Created by Valerii Sosliuk on 6/28/2017. |
|||
*/ |
|||
@DaoNoSqlTest |
|||
public class RuleControllerNoSqlTest extends BaseRuleControllerTest { |
|||
} |
|||
@ -1,26 +0,0 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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.BasePluginControllerTest; |
|||
import org.thingsboard.server.dao.service.DaoSqlTest; |
|||
|
|||
/** |
|||
* Created by Valerii Sosliuk on 6/28/2017. |
|||
*/ |
|||
@DaoSqlTest |
|||
public class PluginControllerSqlTest extends BasePluginControllerTest { |
|||
} |
|||
@ -0,0 +1,76 @@ |
|||
/** |
|||
* Copyright © 2016-2018 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.rule.engine.telemetry; |
|||
|
|||
import com.google.gson.JsonParser; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.util.StringUtils; |
|||
import org.thingsboard.rule.engine.TbNodeUtils; |
|||
import org.thingsboard.rule.engine.api.RuleNode; |
|||
import org.thingsboard.rule.engine.api.TbContext; |
|||
import org.thingsboard.rule.engine.api.TbNode; |
|||
import org.thingsboard.rule.engine.api.TbNodeConfiguration; |
|||
import org.thingsboard.rule.engine.api.TbNodeException; |
|||
import org.thingsboard.server.common.data.kv.AttributeKvEntry; |
|||
import org.thingsboard.server.common.data.kv.BasicTsKvEntry; |
|||
import org.thingsboard.server.common.data.kv.KvEntry; |
|||
import org.thingsboard.server.common.data.kv.TsKvEntry; |
|||
import org.thingsboard.server.common.data.plugin.ComponentType; |
|||
import org.thingsboard.server.common.msg.TbMsg; |
|||
import org.thingsboard.server.common.msg.core.AttributesUpdateRequest; |
|||
import org.thingsboard.server.common.msg.core.TelemetryUploadRequest; |
|||
import org.thingsboard.server.common.msg.session.SessionMsgType; |
|||
import org.thingsboard.server.common.transport.adaptor.JsonConverter; |
|||
|
|||
import java.util.ArrayList; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
import java.util.Set; |
|||
|
|||
@Slf4j |
|||
@RuleNode( |
|||
type = ComponentType.ACTION, |
|||
name = "save attributes", |
|||
configClazz = TbMsgAttributesNodeConfiguration.class, |
|||
nodeDescription = "Saves attributes data", |
|||
nodeDetails = "Saves entity attributes based on configurable scope parameter. Expects messages with 'POST_ATTRIBUTES_REQUEST' message type" |
|||
) |
|||
public class TbMsgAttributesNode implements TbNode { |
|||
|
|||
private TbMsgAttributesNodeConfiguration config; |
|||
|
|||
@Override |
|||
public void init(TbContext ctx, TbNodeConfiguration configuration) throws TbNodeException { |
|||
this.config = TbNodeUtils.convert(configuration, TbMsgAttributesNodeConfiguration.class); |
|||
} |
|||
|
|||
@Override |
|||
public void onMsg(TbContext ctx, TbMsg msg) { |
|||
if (!msg.getType().equals(SessionMsgType.POST_ATTRIBUTES_REQUEST.name())) { |
|||
ctx.tellError(msg, new IllegalArgumentException("Unsupported msg type: " + msg.getType())); |
|||
return; |
|||
} |
|||
|
|||
String src = msg.getData(); |
|||
Set<AttributeKvEntry> attributes = JsonConverter.convertToAttributes(new JsonParser().parse(src)).getAttributes(); |
|||
ctx.getTelemetryService().saveAndNotify(msg.getOriginator(), config.getScope(), new ArrayList<>(attributes), new TelemetryNodeCallback(ctx, msg)); |
|||
} |
|||
|
|||
@Override |
|||
public void destroy() { |
|||
} |
|||
|
|||
} |
|||
Loading…
Reference in new issue