diff --git a/integration-test/iot-community/pom.xml b/integration-test/iot-community/pom.xml
new file mode 100644
index 0000000..71974af
--- /dev/null
+++ b/integration-test/iot-community/pom.xml
@@ -0,0 +1,238 @@
+
+
+
+
+ io.entgra.product.community
+ integration-test
+ 5.1.3-SNAPSHOT
+ ../pom.xml
+
+
+ 4.0.0
+ io.entgra.product.community.iot.test.integration
+ Entgra IoT Community - Integration Test Module
+ Integration Tests for Entgra IoT Community Product
+ jar
+
+
+
+
+ maven-surefire-plugin
+ false
+
+ -Xmx1024m -XX:PermSize=256m -XX:MaxPermSize=512m
+
+
+ src/test/resources/testng-server-mgt.xml
+ src/test/resources/testng.xml
+
+
+ ${skipTests}
+
+
+
+ maven.test.haltafterfailure
+ false
+
+
+ java.io.tmpdir
+ ${project.build.directory}/
+
+
+ carbon.zip
+
+ ${basedir}/../../distribution/iot-community/target/${entgra-iot-community}.zip
+
+
+
+ framework.resource.location
+
+ ${basedir}/src/test/resources/
+
+
+
+ server.list
+
+ IOT
+
+
+
+ usedefaultlisteners
+ false
+
+ ${project.build.directory}/security-verifier/
+ ${basedir}/src/test/resources/instrumentation.txt
+ ${basedir}/src/test/resources/filters.txt
+
+ ${basedir}/target
+
+
+
+ maven-dependency-plugin
+
+
+ copy-secVerifier
+ compile
+
+ copy-dependencies
+
+
+ ${project.build.directory}/security-verifier
+ aar
+ SecVerifier
+ true
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-jar-plugin
+ 2.4
+
+
+
+ test-jar
+
+
+
+
+
+ maven-resources-plugin
+ 2.6
+
+
+ copy-resources-jks
+ compile
+
+ copy-resources
+
+
+ ${basedir}/src/test/resources/keystores/products
+
+
+
+
+ ${project.build.directory}/tobeCopied/${entgra-iot-community}/repository/resources/security/
+
+
+ **/*.jks
+
+
+
+
+
+
+ copy-stratos-jks
+ compile
+
+ copy-resources
+
+
+ ${basedir}/src/test/resources/keystores/stratos
+
+
+
+
+ ${project.build.directory}/tobeCopied/${entgra-iot-community}/repository/resources/security/
+
+
+ **/*.jks
+
+
+
+
+
+
+ copy-axis2files
+ compile
+
+ copy-resources
+
+
+ ${basedir}/src/test/resources/axis2config
+
+
+
+
+ ${project.build.directory}/tobeCopied/${entgra-iot-community}/repository/conf/axis2/
+
+
+ **/*.xml
+
+
+
+
+
+
+ copy-resources-mar
+ compile
+
+ copy-resources
+
+
+ ${basedir}/src/test/resources/client/modules
+
+
+
+
+ ${project.build.directory}/tobeCopied/${entgra-iot-community}/repository/deployment/client/modules
+
+
+ **/*.mar
+
+
+
+
+
+
+
+
+
+
+
+
+ org.wso2.carbon.automation
+ org.wso2.carbon.automation.engine
+
+
+ org.wso2.carbon.automation
+ org.wso2.carbon.automation.test.utils
+
+
+ org.wso2.carbon.automationutils
+ org.wso2.carbon.integration.common.extensions
+
+
+ com.google.code.gson
+ gson
+
+
+ org.wso2.carbon.automation
+ org.wso2.carbon.automation.extensions
+
+
+
+
+ false
+
+
+
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/AssertUtil.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/AssertUtil.java
new file mode 100644
index 0000000..8736739
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/AssertUtil.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.testng.Assert;
+
+/**
+ * This class contains methods to make assertions easier and which are not available out of the box, from testng.
+ */
+public class AssertUtil {
+ /**
+ * This can be used to compare if to json strings are matched or not.
+ *
+ * @param expectedJsonPayload the expected json string.
+ * @param realPayload real json string.
+ * @param mustMatch If the real and expected must match, in order to become the test successful or not.
+ */
+ public static void jsonPayloadCompare(String expectedJsonPayload, String realPayload, boolean mustMatch) {
+ JsonElement jsonElement = new JsonParser().parse(expectedJsonPayload);
+ JsonObject expectedPayloadObject = jsonElement.getAsJsonObject();
+ jsonElement = new JsonParser().parse(realPayload);
+ JsonObject realPayloadObject = jsonElement.getAsJsonObject();
+ if (mustMatch) {
+ Assert.assertTrue(realPayloadObject.equals(expectedPayloadObject));
+ } else {
+ Assert.assertFalse(realPayloadObject.equals(expectedPayloadObject));
+ }
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/Constants.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/Constants.java
new file mode 100644
index 0000000..c1ec412
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/Constants.java
@@ -0,0 +1,510 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common;
+
+import java.io.File;
+
+/**
+ * Constants used through out the test suite are defined here.
+ */
+public final class Constants {
+ public static final String DEVICE_ID = "d24f870f390352a41234";
+ public static final String NUMBER_NOT_EQUAL_TO_DEVICE_ID = "1111";
+ public static final String DEVICE_IMEI = "123123123";
+ public static final String AUTOMATION_CONTEXT = "IOT";
+ public static final String APPLICATION_JSON = "application/json";
+ public static final String APPLICATION_ZIP = "application/zip";
+ public static final String APPLICATION_URL_ENCODED = "application/x-www-form-urlencoded";
+ public static final String OAUTH_CLIENT_ID = "client_id";
+ public static final String OAUTH_CLIENT_SECRET = "client_secret";
+ public static final String OAUTH_ACCESS_TOKEN = "access_token";
+ public static final String SCOPE = "scope";
+ public static final String ANDROID_DEVICE_TYPE = "android";
+ public static final String HTTP_METHOD_POST = "POST";
+ public static final String HTTP_METHOD_PUT = "PUT";
+ public static final String HTTP_METHOD_GET = "GET";
+ public static final String HTTP_METHOD_DELETE = "DELETE";
+ public static final String DEVICE_IDENTIFIER_KEY = "deviceIdentifier";
+ public static final String DEVICE_IDENTIFIERS_KEY = "deviceIDs";
+ public static final String CONTENT_TYPE = "Content-Type";
+ public static final String APPLICATION_SOAP_XML = "application/soap+xml; charset=utf-8";
+ public static final String UTF8 = "UTF-8";
+ public static final int HTTPS_ANALYTICS_PORT = 9445;
+
+ public static final String HTTPS = "https";
+ public static final String HTTP = "http";
+
+ public static final int HTTPS_GATEWAY_PORT = 8243;
+ public static final int HTTP_GATEWAY_PORT = 8280;
+
+ public static final class APIApplicationRegistration {
+ public static final String API_APP_REGISTRATION_ENDPOINT = "/api-application-registration/register";
+ public static final String TOKEN_ENDPOINT = "/token";
+ public static final String MULTI_TENANT_OAUTH_TOKEN_PAYLOAD = "&grant_type=password&scope=perm:android:enroll"
+ + " perm:android:wipe perm:android:ring perm:android:lock-devices perm:android:configure-vpn "
+ + "perm:android:configure-wifi perm:android:enroll perm:android:uninstall-application "
+ + "perm:android:manage-configuration perm:android:location perm:android:install-application "
+ + "perm:android:mute perm:android:change-lock-code perm:android:blacklist-applications "
+ + "perm:android:set-password-policy perm:android:encrypt-storage perm:android:clear-password "
+ + "perm:android:enterprise-wipe perm:android:info perm:android:view-configuration "
+ + "perm:android:upgrade-firmware perm:android:set-webclip perm:android:send-notification "
+ + "perm:android:disenroll perm:android:update-application perm:android:unlock-devices "
+ + "perm:android:control-camera perm:android:reboot perm:android:logcat appm:read appm:subscribe "
+ + "perm:sign-csr perm:admin:devices:view perm:roles:add perm:roles:add-users perm:roles:update "
+ +
+ "perm:roles:permissions perm:roles:details perm:roles:view perm:roles:create-combined-role " +
+ "perm:roles:delete "
+ + "perm:dashboard:vulnerabilities perm:dashboard:non-compliant-count perm:dashboard:non-compliant "
+ + "perm:dashboard:by-groups perm:dashboard:device-counts perm:dashboard:feature-non-compliant "
+ +
+ "perm:dashboard:count-overview perm:dashboard:filtered-count perm:dashboard:details perm:get-activity "
+ +
+ "perm:devices:delete perm:devices:applications perm:devices:effective-policy " +
+ "perm:devices:compliance-data "
+ +
+ "perm:devices:features perm:devices:operations perm:devices:search perm:devices:details " +
+ "perm:devices:update "
+ + "perm:devices:view perm:view-configuration perm:manage-configuration perm:policies:remove "
+ +
+ "perm:policies:priorities perm:policies:deactivate perm:policies:get-policy-details " +
+ "perm:policies:manage "
+ + "perm:policies:activate perm:policies:update perm:policies:changes perm:policies:get-details "
+ +
+ "perm:users:add perm:users:details perm:users:count perm:users:delete perm:users:roles " +
+ "perm:users:user-details "
+ +
+ "perm:users:credentials perm:users:search perm:users:is-exist perm:users:update " +
+ "perm:users:send-invitation "
+ + "perm:admin-users:view perm:groups:devices perm:groups:update perm:groups:add perm:groups:device "
+ +
+ "perm:groups:devices-count perm:groups:remove perm:groups:groups perm:groups:groups-view " +
+ "perm:groups:share "
+ +
+ "perm:groups:count perm:groups:roles perm:groups:devices-remove perm:groups:devices-add " +
+ "perm:groups:assign "
+ +
+ "perm:device-types:features perm:device-types:types perm:applications:install " +
+ "perm:applications:uninstall "
+ +
+ "perm:admin-groups:count perm:admin-groups:view perm:notifications:mark-checked " +
+ "perm:notifications:view "
+ + "perm:admin:certificates:delete perm:admin:certificates:details perm:admin:certificates:view "
+ + "perm:admin:certificates:add perm:admin:certificates:verify perm:ios:enroll perm:ios:view-device "
+ +
+ "perm:ios:apn perm:ios:ldap perm:ios:enterprise-app perm:ios:store-application " +
+ "perm:ios:remove-application "
+ + "perm:ios:app-list perm:ios:profile-list perm:ios:lock perm:ios:enterprise-wipe perm:ios:device-info "
+ +
+ "perm:ios:restriction perm:ios:email perm:ios:cellular perm:ios:applications perm:ios:wifi " +
+ "perm:ios:ring "
+ + "perm:ios:location perm:ios:notification perm:ios:airplay perm:ios:caldav perm:ios:cal-subscription "
+ +
+ "perm:ios:passcode-policy perm:ios:webclip perm:ios:vpn perm:ios:per-app-vpn " +
+ "perm:ios:app-to-per-app-vpn "
+ + "perm:ios:app-lock perm:ios:clear-passcode perm:ios:remove-profile perm:ios:get-restrictions "
+ + "perm:ios:wipe-data perm:admin perm:android:applications perm:devicetype:deployment "
+ + "perm:android-sense:enroll perm:admin:device-type perm:device-types:events "
+ + "perm:device-types:events:view perm:device-types:types perm:device:enroll perm:device:disenroll "
+ +
+ "perm:device:modify perm:device:operations perm:device:publish-event perm:devices:operations " +
+ "perm:devices:operations perm:firealarm:enroll ";
+
+ public static final String OAUTH_TOKEN_PAYLOAD = "username=admin&password=admin" +
+ MULTI_TENANT_OAUTH_TOKEN_PAYLOAD;
+ private static StringBuffer dynamicClientPayloadBuffer = new StringBuffer();
+ public static final String API_APP_REGISTRATION_PAYLOAD = dynamicClientPayloadBuffer.append("{ \n"
+ +
+ " \"applicationName\":\"app_123456\",\n" +
+ " \"isAllowedToAllDomains\":false,\n"
+ +
+ " \"tags\":[\"android\", \"device_management\"],\n" +
+ " \"isMappingAnExistingOAuthApp\":false\n"
+ + "}")
+ .toString();
+ public static final String PERMISSION_LIST =
+ "appm:read appm:subscribe perm:admin-groups:count perm:admin-groups:view perm:admin-users:view " +
+ "perm:admin:certificates:add perm:admin:certificates:delete perm:admin:certificates:details " +
+ "perm:admin:certificates:verify perm:admin:certificates:view perm:admin:device-type " +
+ "perm:admin:devices:view perm:android-sense:enroll perm:android:applications " +
+ "perm:android:blacklist-applications perm:android:change-lock-code " +
+ "perm:android:clear-password perm:android:configure-vpn perm:android:configure-wifi " +
+ "perm:android:control-camera perm:android:disenroll perm:android:encrypt-storage " +
+ "perm:android:enroll perm:android:enterprise-wipe perm:android:info " +
+ "perm:android:install-application perm:android:location perm:android:lock-devices " +
+ "perm:android:logcat perm:android:manage-configuration perm:android:mute perm:android:reboot " +
+ "perm:android:ring perm:android:send-notification perm:android:set-password-policy " +
+ "perm:android:set-webclip perm:android:uninstall-application perm:android:unlock-devices " +
+ "perm:android:update-application perm:android:upgrade-firmware " +
+ "perm:android:view-configuration perm:android:wipe perm:applications:install " +
+ "perm:applications:uninstall perm:device-types:events perm:device-types:events:view " +
+ "perm:device-types:features perm:device-types:types perm:device:disenroll perm:device:enroll " +
+ "perm:device:modify perm:device:operations perm:device:publish-event " +
+ "perm:devices:applications perm:devices:compliance-data perm:devices:delete " +
+ "perm:devices:details perm:devices:effective-policy perm:devices:features " +
+ "perm:devices:operations perm:devices:search perm:devices:update perm:devices:view " +
+ "perm:devicetype:deployment perm:firealarm:enroll perm:get-activity perm:groups:add " +
+ "perm:groups:assign perm:groups:count perm:groups:device perm:groups:devices " +
+ "perm:groups:devices-add perm:groups:devices-count perm:groups:devices-remove " +
+ "perm:groups:groups perm:groups:groups-view perm:groups:remove perm:groups:roles " +
+ "perm:groups:share perm:groups:update perm:manage-configuration " +
+ "perm:notifications:mark-checked perm:notifications:view perm:policies:activate " +
+ "perm:policies:changes perm:policies:deactivate perm:policies:get-details " +
+ "perm:policies:get-policy-details perm:policies:manage perm:policies:priorities " +
+ "perm:policies:remove perm:policies:update perm:roles:add perm:roles:add-users " +
+ "perm:roles:create-combined-role perm:roles:delete perm:roles:details perm:roles:permissions " +
+ "perm:roles:update perm:roles:view perm:users:add perm:users:count perm:users:credentials " +
+ "perm:users:delete perm:users:details perm:users:is-exist perm:users:roles perm:users:search " +
+ "perm:users:send-invitation perm:users:update perm:users:user-details perm:view-configuration";
+
+ private APIApplicationRegistration() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class AndroidEnrollment {
+ public static final String ENROLLMENT_PAYLOAD_FILE_NAME = "android-enrollment-payloads.json";
+ public static final String ENROLLMENT_RESPONSE_PAYLOAD_FILE_NAME = "android-enrollment-response-payloads.json";
+ public static final String ENROLLMENT_ENDPOINT = "/api/device-mgt/android/v1.0/devices";
+ public static final String ENROLLMENT_GROUP = "android-enrollment";
+ public static final String UPDATE_APPLICATION_METHOD = "UPDATE_APPLICATIONS";
+ public static final String ANDROID_DEVICE_TYPE = "android";
+ public static final String GET_PENDING_OPERATIONS_METHOD = "GET_PENDING_OPERATIONS";
+
+ private AndroidEnrollment() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class AndroidPolicy {
+ public static final String POLICY_RESPONSE_PAYLOAD_FILE_NAME = "android-policy-response-payloads.json";
+ public static final String POLICY_ENDPOINT = "/mdm-android-agent/policy/";
+ public static final String POLICY_GROUP = "android-policy";
+ public static final String GET_EFFECTIVE_POLICY = "getEffectivePolicy";
+
+ private AndroidPolicy() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class WindowsEnrollment {
+ public static final String DISCOVERY_GET_URL = "/mdm-windows-agent/services/discovery/get";
+ public static final String DISCOVERY_POST_URL = "/mdm-windows-agent/services/discovery/post";
+ public static final String BSD_URL = "/mdm-windows-agent/services/federated/bst/authentication";
+ public static final String MS_EXCEP = "/mdm-windows-agent/services/certificatepolicy/xcep";
+ public static final String WINDOWS_ENROLLMENT_GROUP = "windows-enrollment";
+ public static final String WSTEP_URL = "/mdm-windows-agent/services/deviceenrolment/wstep";
+ public static final String SYNC_ML_URL = "/mdm-windows-agent/services/syncml/devicemanagement/request";
+ public static final String DISCOVERY_POST_FILE = "windows" + File.separator + "enrollment" + File
+ .separator + "discovery-post.xml";
+ public static final String MS_XCEP_FILE =
+ "windows" + File.separator + "enrollment" + File.separator + "ms_xcep.xml";
+ public static final String WS_STEP_FILE =
+ "windows" + File.separator + "enrollment" + File.separator + "wstep.xml";
+ public static final String BSD_PAYLOAD =
+ "{\"credentials\" : {\"username\" : \"admin\", \"email\" : \"admin@wso2.com\", " +
+ "\"password\" : \"admin\", \"ownership\" : \"BYOD\", " +
+ "\"token\" : \"cbe53efd46ec612c456540f8dfef5428\"}}";
+
+ private WindowsEnrollment() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class AndroidOperations {
+ public static final String PAYLOAD_COMMON = "[" + DEVICE_ID + "]";
+
+ public static final String OPERATION_PAYLOAD_FILE_NAME = "android-operation-payloads.json";
+ public static final String OPERATIONS_GROUP = "operations";
+
+ public static final String CAMERA_OPERATION = "control-camera";
+ public static final String CAMERA_OPERATION_PAYLOAD = "{\n" + " \"operation\": {\n"
+ + " \"enabled\": false\n" + " },\n" + " \"deviceIDs\": [\n" + " \"" + DEVICE_ID + "\" \n"
+ + " ]\n" + "}";
+
+ public static final String WIPE_DATA_OPERATION = "wipe_data";
+ public static final String WIPE_DATA_OPERATION_PAYLOAD = "wipe_data";
+
+ public static final String INSTALL_APPS_OPERATION = "install_apps";
+ public static final String NOTIFICATION_OPERATION = "notification";
+ public static final String WIFI_OPERATION = "wifi";
+ public static final String ENCRYPT_OPERATION = "encrypt";
+ public static final String CHANGE_LOCK_OPERATION = "unlock-devices";
+ public static final String PASSWORD_POLICY_OPERATION = "password_policy";
+ public static final String WEB_CLIP_OPERATION = "web_clip";
+ public static final String OPERATION_ENDPOINT = "/api/device-mgt/android/v1.0/admin/devices/";
+ public static final String UNLOCK_ENDPOINT = "unlock-devices";
+ public static final String UNLOCK_OPERATION_PAYLOAD = PAYLOAD_COMMON;
+
+ public static final String LOCK_ENDPOINT = "lock-devices";
+ public static final String LOCK_OPERATION_PAYLOAD = "{ \"deviceIDs\": [\"" + DEVICE_ID + "\"],"
+ + "\"operation\": { \"message\": \"string\", \"hardLockEnabled\": false }}";
+
+ public static final String LOCATION_ENDPOINT = "location";
+ public static final String LOCATION_PAYLOAD = PAYLOAD_COMMON;
+
+ public static final String CLEAR_PASSWORD_ENDPOINT = "clear-password";
+ public static final String CLEAR_PASSWORD_PAYLOAD = PAYLOAD_COMMON;
+
+ public static final String DEVICE_INFO_ENDPOINT = "info";
+ public static final String DEVICE_INFO_PAYLOAD = "[\"" + DEVICE_ID + "\"]";
+
+ public static final String DEVICE_LOGCAT_ENDPOINT = "logcat";
+
+ public static final String ENTERPRISE_WIPE_ENDPOINT = "enterprise-wipe";
+ public static final String ENTERPRISE_WIPE_PAYLOAD = PAYLOAD_COMMON;
+
+ public static final String WIPE_DATA_ENDPOINT = "wipe";
+ public static final String WIPE_DATA_PAYLOAD = "{\n" + " \"operation\": {\n" + " \"pin\": \"string\"\n"
+ + " },\n" + " \"deviceIDs\": [\n" + " \"" + DEVICE_ID + "\"\n" + " ]\n" + "}";
+
+ public static final String APPLICATION_LIST_ENDPOINT = "applications";
+ public static final String APPLICATION_LIST_PAYLOAD = PAYLOAD_COMMON;
+
+ public static final String RING_ENDPOINT = "ring";
+ public static final String RING_PAYLOAD = PAYLOAD_COMMON;
+
+ public static final String MUTE_ENDPOINT = "mute";
+ public static final String MUTE_PAYLOAD = PAYLOAD_COMMON;
+
+ public static final String INSTALL_APPS_ENDPOINT = "install-application";
+ public static final String UPDATE_APPS_ENDPOINT = "update-application";
+ public static final String INSTALL_APPS_PAYLOAD = "{\n" + " \"deviceIDs\": [\n" + " \"" + DEVICE_ID + "\"\n"
+ + " ],\n" + " \"operation\": {\n" + " \"appIdentifier\": \"string\",\n"
+ + " \"type\": \"string\",\n" + " \"url\": \"string\"\n" + " }\n" + "}";
+
+ public static final String UNINSTALL_APPS_ENDPOINT = "uninstall-application";
+ public static final String UNINSTALL_APPS_PAYLOAD = "{\n" + " \"deviceIDs\": [\n"
+ + " \"" + DEVICE_ID + "\"\n" + " ],\n" + " \"operation\": {\n"
+ + " \"appIdentifier\": \"string\",\n" + " \"type\": \"enterprise\"} }";
+
+ public static final String BLACKLIST_APPS_ENDPOINT = "blacklist-applications";
+ public static final String BLACKLIST_OPERATION = "black_list_application";
+
+ public static final String UPGRADE_FIRMWARE_ENDPOINT = "upgrade-firmware";
+ public static final String UPGRADE_FIRMWARE_OPERATION = "upgrade-firmware";
+
+ public static final String VPN_ENDPOINT = "configure-vpn";
+ public static final String VPN_OPERATION = "vpn";
+
+ public static final String NOTIFICATION_ENDPOINT = "send-notification";
+ public static final String NOTIFICATION_PAYLOAD = "{\n" + " \"deviceIDs\": [\n" + " \"" + DEVICE_ID + "\"\n"
+ + " ],\n" + " \"operation\": {\n" + " \"messageText\": \"string\",\n"
+ + " \"messageTitle\": \"string\"\n" + " }\n" + "}";
+
+ public static final String WIFI_ENDPOINT = "configure-wifi";
+ public static final String WIFI_PAYLOAD = "{\n" + " \"operation\": {\n" + " \"ssid\": \"string\",\n"
+ + " \"password\": \"string\"\n" + " },\n" + " \"deviceIDs\": [\n" + " \"" + DEVICE_ID + "\"\n"
+ + " ]\n" + "}";
+
+ public static final String ENCRYPT_ENDPOINT = "encrypt-storage";
+ public static final String ENCRYPT_PAYLOAD = "{\n" + " \"operation\": {\n" + " \"encrypted\": false\n"
+ + " },\n" + " \"deviceIDs\": [\n" + " \"" + DEVICE_ID + "\"\n" + " ]\n" + "}";
+
+ public static final String REBOOT_ENDPOINT = "reboot";
+ public static final String REBOOT_PAYLOAD = PAYLOAD_COMMON;
+ ;
+
+ public static final String CHANGE_LOCK_ENDPOINT = "change-lock-code";
+ public static final String CHANGE_LOCK_PAYLOAD = "{\n" + " \"operation\": {\n" + " \"lockCode\": \"0000\"\n"
+ + " },\n" + " \"deviceIDs\": [\n" + " \"" + DEVICE_ID + "\"\n" + " ]\n" + "}\n";
+
+ public static final String PASSWORD_POLICY_ENDPOINT = "set-password-policy";
+ public static final String PASSWORD_POLICY_PAYLOAD = "{\n" + " \"operation\": {\n"
+ + " \"maxFailedAttempts\": 0,\n" + " \"minLength\": 0,\n" + " \"pinHistory\": 0,\n"
+ + " \"minComplexChars\": 0,\n" + " \"maxPINAgeInDays\": 0,\n"
+ + " \"requireAlphanumeric\": false,\n" + " \"allowSimple\": false\n" + " },\n"
+ + " \"deviceIDs\": [\n" + " \"" + DEVICE_ID + "\"\n" + " ]\n" + "}";
+
+ public static final String WEB_CLIP_ENDPOINT = "set-webclip";
+ public static final String WEB_CLIP_PAYLOAD = "{\n" + " \"operation\": {\n" + " \"identity\": \"string\",\n"
+ + " \"title\": \"string\",\n" + " \"type\": \"string\"\n" + " },\n" + " \"deviceIDs\": [\n"
+ + " \"" + DEVICE_ID + "\"\n" + " ]\n" + "}";
+
+ private AndroidOperations() {
+ throw new AssertionError();
+ }
+ }
+
+
+ public static final class AndroidConfigurationManagement {
+ public static final String DEVICE_CONFIGURATION_GROUP = "android-config-mgt";
+ public static final String CONFIG_MGT_ENDPOINT = "/api/device-mgt/android/v1.0/configuration/";
+ public static final String LICENSE_ENDPOINT = "license";
+ public static final String PAYLOAD_FILE_NAME = "android-configuration-payloads.json";
+ public static final String RESPONSE_PAYLOAD_FILE_NAME = "android-config-response-payloads.json";
+
+ private AndroidConfigurationManagement() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class OperationManagement {
+ public static final String PATH_APPS = "/apps";
+ public static final String OPERATION_MANAGEMENT_GROUP = "api-policy-mgt";
+ public static final String GET_DEVICE_APPS_ENDPOINT = "/mdm-admin/operations/android/";
+ public static final String GET_DEVICE_OPERATIONS_ENDPOINT = "/mdm-admin/operations/android/";
+
+ private OperationManagement() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class MobileDeviceManagement {
+ public static final String MOBILE_DEVICE_MANAGEMENT_GROUP = "mobile-device-mgt";
+ public static final String GET_DEVICE_COUNT_ENDPOINT = "/api/device-mgt/v1.0/devices";
+ public static final String CHANGE_DEVICE_STATUS_ENDPOINT = "/api/device-mgt/v1.0/devices/";
+ public static final String NO_OF_DEVICES = "1";
+ public static final String GET_ALL_DEVICES_ENDPOINT = "/api/device-mgt/v1.0/devices/";
+ public static final String USER_DEVICE_ENDPOINT = "user-devices";
+ public static final String ADVANCE_SEARCH_ENDPOINT = "search-devices";
+ public static final String ADVANCE_SEARCH_OPERATION = "ADVANCE_SEARCH";
+ public static final String REQUEST_PAYLOAD_FILE_NAME = "mobile-device-mgt-payloads.json";
+ public static final String UPDATE_PAYLOAD_OPERATION = "UPDATE_DEVICE_INFO";
+ public static final String VIEW_DEVICE_TYPES_ENDPOINT = "/mdm-admin/devices/types";
+ public static final String NO_DEVICE = "{\"devices\":[],\"count\":0}";
+
+ private MobileDeviceManagement() {
+ throw new AssertionError();
+ }
+ }
+
+
+ public static final class UserManagement {
+ public static final String USER_MANAGEMENT_GROUP = "user-mgt";
+ public static final String USER_NAME = "username123";
+ public static final String USER_ENDPOINT = "/api/device-mgt/v1.0/users";
+ public static final String USER_PAYLOAD_FILE_NAME = "user-payloads.json";
+ public static final String USER_RESPONSE_PAYLOAD_FILE_NAME = "user-response-payloads.json";
+ public static final String GET_ROLES_METHOD = "GET_ROLES";
+ public static final String RESET_PASSWORD_PAYLOAD = "RESET_PASSWORD";
+
+ private UserManagement() {
+ throw new AssertionError();
+ }
+
+ }
+
+ public static final class RoleManagement {
+ public static final String ROLE_MANAGEMENT_GROUP = "role-mgt";
+ public static final String ROLE_MANAGEMENT_END_POINT = "/api/device-mgt/v1.0/roles";
+ public static final String ROLE_PAYLOAD_FILE_NAME = "role-payloads.json";
+ public static final String ROLE_RESPONSE_PAYLOAD_FILE_NAME = "role-response-payloads.json";
+ public static final String GET_FILTERED_ROLED_METHOD = "GET_FILTERED_ROLES";
+ public static final String UPDATE_ROLES_METHOD = "UPDATE_USERS";
+
+ private RoleManagement() {
+ throw new AssertionError();
+ }
+
+ }
+
+ public static final class PolicyManagement {
+ public static final String POLICY_MANAGEMENT_GROUP = "policy-mgt";
+ public static final String ADD_POLICY_ENDPOINT = "/mdm-admin/policies/active-policy";
+ public static final String POLICY_PAYLOAD_FILE_NAME = "policy-payloads.json";
+ public static final String POLICY_RESPONSE_PAYLOAD_FILE_NAME = "policy-response-payloads.json";
+ public static final String UPDATE_POLICY_ENDPOINT = "/mdm-admin/policies/1";
+ public static final String REMOVE_POLICY_ENDPOINT = "/mdm-admin/policies/bulk-remove";
+ public static final String REMOVE_POLICY_PAYLOAD_FILE_NAME = "[1]";
+ public static final String VIEW_POLICY_LIST_ENDPOINT = "/api/device-mgt/v1.0/policies";
+
+ private PolicyManagement() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class FeatureManagement {
+ public static final String FEATURE_MANAGEMENT_GROUP = "feature-mgt";
+ public static final String VIEW_FEATURES_ENDPOINT = "/mdm-admin/features/android";
+
+ private FeatureManagement() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class LicenseManagement {
+ public static final String LICENSE_MANAGEMENT_GROUP = "license-mgt";
+ public static final String GET_LICENSE_ENDPOINT = "/mdm-admin/license/android/en_US";
+ public static final String LICENSE_RESPONSE_PAYLOAD_FILE_NAME = "license-response-payloads.json";
+
+ private LicenseManagement() {
+ throw new AssertionError();
+ }
+
+ }
+
+ public static final class ConfigurationManagement {
+ public static final String CONFIGURATION_MANAGEMENT_GROUP = "configuration-mgt";
+ public static final String CONFIGURATION_ENDPOINT = "/mdm-admin/configuration";
+ public static final String CONFIGURATION_PAYLOAD_FILE_NAME = "configuration-payloads.json";
+ public static final String CONFIGURATION_RESPONSE_PAYLOAD_FILE_NAME = "configuration-response-payloads.json";
+
+ private ConfigurationManagement() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class NotificationManagement {
+ public static final String NOTIFICATION_MANAGEMENT_GROUP = "notification-mgt";
+ public static final String NOTIFICATION_ENDPOINT = "/mdm-admin/notifications";
+ public static final String NOTIFICATION_PAYLOAD_FILE_NAME = "notification-payloads.json";
+ public static final String NOTIFICATION_RESPONSE_PAYLOAD_FILE_NAME = "notification-response-payloads.json";
+ public static final String NOTIFICATION_UPDATE_ENDPOINT = "/mdm-admin/notifications/1234/NEW";
+
+ private NotificationManagement() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class AndroidSenseEnrollment {
+ public static final String ENROLLMENT_ENDPOINT = "/android_sense/1.0.0/device/";
+ public static final String RETRIEVER_ENDPOINT = "analytics/tables/";
+ public static final String ANALYTICS_ARTIFACTS_DEPLOYMENT_ENDPOINT =
+ "/api/device-mgt/v1.0/admin/publish-artifact/1.0.0/deploy/android_sense";
+ public static final String ENROLLMENT_PAYLOAD_FILE_NAME = "android-sense-enrollment-payloads.json";
+ public static final String PUBLISH_DATA_OPERATION = "PUBLISH_DATA";
+ public static final String BATTERY_STATS_TABLE_NAME = "ORG_WSO2_IOT_ANDROID_BATTERY_STATS";
+ public static final String IS_TABLE_EXIST_CHECK_URL = "analytics/table_exists";
+
+ private AndroidSenseEnrollment() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class QSGManagement {
+ public static final String GET_MOBILE_APPS_ENDPONT = "/api/appm/publisher/v1.1/apps/mobileapp";
+
+ private QSGManagement() {
+ throw new AssertionError();
+ }
+ }
+
+ public static final class VirtualFireAlarmConstants {
+ public static final String ENROLLMENT_ENDPOINT = "/virtual_firealarm/1.0.0/device/download";
+ public static final String STATS_ENDPOINT = "/virtual_firealarm/1.0.0/device/stats";
+ public static final String PAYLOAD_FILE = "virtual-fire-alarm-enrollment-payloads.json";
+ public static final String ANALYTICS_ARTIFACTS_DEPLOYMENT_ENDPOINT =
+ "/api/device-mgt/v1.0/admin/publish-artifact/1.0.0/deploy/virtual_firealarm";
+ public static final String POLICY_DATA = "POLICY_DATA";
+ public static final String ACTIVATE_POLICY_ENDPOINT = "/api/device-mgt/v1.0/policies/activate-policy";
+ public static final String APPLY_CHANGES_ENDPOINT = "/api/device-mgt/v1.0/policies/apply-changes";
+
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/IOTHttpClient.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/IOTHttpClient.java
new file mode 100644
index 0000000..4f6b518
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/IOTHttpClient.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common;
+
+
+import org.apache.commons.httpclient.DefaultHttpMethodRetryHandler;
+import org.apache.commons.httpclient.HttpClient;
+import org.apache.commons.httpclient.contrib.ssl.EasySSLProtocolSocketFactory;
+import org.apache.commons.httpclient.methods.DeleteMethod;
+import org.apache.commons.httpclient.methods.GetMethod;
+import org.apache.commons.httpclient.methods.PostMethod;
+import org.apache.commons.httpclient.methods.PutMethod;
+import org.apache.commons.httpclient.methods.StringRequestEntity;
+import org.apache.commons.httpclient.params.HttpMethodParams;
+import org.apache.commons.httpclient.protocol.Protocol;
+import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import java.io.IOException;
+import java.security.GeneralSecurityException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * This class creates a customised Http Client Class
+ */
+public class IOTHttpClient {
+
+ private static final String AUTHORIZATION = "Authorization";
+ private static Log log = LogFactory.getLog(IOTHttpClient.class);
+ private String backEndUrl;
+ private String authorizationString;
+ private Map requestHeaders = new HashMap();
+
+ public IOTHttpClient(String backEndUrl, String contentType, String authorization) {
+
+ this.backEndUrl = backEndUrl;
+ this.requestHeaders.put(Constants.CONTENT_TYPE, contentType);
+ if (authorization != null || !authorization.isEmpty()) {
+ this.authorizationString = authorization;
+ this.requestHeaders.put(AUTHORIZATION, authorization);
+ }
+ }
+
+ public String getAuthorizationString() {
+ return authorizationString;
+ }
+
+ public void setAuthorizationString(String authorizationString) {
+ this.authorizationString = authorizationString;
+ }
+
+ public void setHttpHeader(String headerName, String value) {
+ this.requestHeaders.put(headerName, value);
+ }
+
+ public String getHttpHeader(String headerName) {
+ return this.requestHeaders.get(headerName);
+ }
+
+ public void removeHttpHeader(String headerName) {
+ this.requestHeaders.remove(headerName);
+ }
+
+ public IOTResponse post(String endpoint, String body) {
+ HttpClient client = new HttpClient();
+ try {
+ ProtocolSocketFactory socketFactory = new EasySSLProtocolSocketFactory();
+ Protocol https = new Protocol(Constants.HTTPS, socketFactory, Constants.HTTPS_GATEWAY_PORT);
+ Protocol.registerProtocol(Constants.HTTPS, https);
+ String url = backEndUrl + endpoint;
+ PostMethod method = new PostMethod(url);
+ method.setRequestHeader(AUTHORIZATION, authorizationString);
+ StringRequestEntity requestEntity = new StringRequestEntity(body,
+ requestHeaders.get(Constants.CONTENT_TYPE), Constants.UTF8);
+ method.setRequestEntity(requestEntity);
+ IOTResponse iotResponse = new IOTResponse();
+ iotResponse.setStatus(client.executeMethod(method));
+ iotResponse.setBody(method.getResponseBodyAsString());
+ return iotResponse;
+
+ } catch (GeneralSecurityException e) {
+ log.error("Failure occurred at IOTResponse post for GeneralSecurityException", e);
+ } catch (IOException e) {
+ log.error("Failure occurred at IOTResponse post for IOException", e);
+ }
+ return null;
+ }
+
+ public IOTResponse put(String endpoint, String body) {
+ HttpClient client = new HttpClient();
+ try {
+ ProtocolSocketFactory socketFactory = new EasySSLProtocolSocketFactory();
+ Protocol https = new Protocol(Constants.HTTPS, socketFactory, Constants.HTTPS_GATEWAY_PORT);
+ Protocol.registerProtocol(Constants.HTTPS, https);
+ String url = backEndUrl + endpoint;
+ PutMethod method = new PutMethod(url);
+ method.setRequestHeader(AUTHORIZATION, authorizationString);
+ StringRequestEntity requestEntity = new StringRequestEntity(
+ body, requestHeaders.get(Constants.CONTENT_TYPE), Constants.UTF8);
+ method.setRequestEntity(requestEntity);
+ IOTResponse iotResponse = new IOTResponse();
+ iotResponse.setStatus(client.executeMethod(method));
+ iotResponse.setBody(method.getResponseBodyAsString());
+ return iotResponse;
+
+ } catch (GeneralSecurityException e) {
+ log.error("Failure occurred at IOTResponse put for GeneralSecurityException", e);
+ } catch (IOException e) {
+ log.error("Failure occurred at IOTResponse put for IO Exception", e);
+ }
+ return null;
+ }
+
+ public IOTResponse get(String endpoint) {
+ HttpClient client = new HttpClient();
+ try {
+ ProtocolSocketFactory socketFactory = new EasySSLProtocolSocketFactory();
+
+ Protocol https = new Protocol(Constants.HTTPS, socketFactory, Constants.HTTPS_GATEWAY_PORT);
+ Protocol.registerProtocol(Constants.HTTPS, https);
+ String url = backEndUrl + endpoint;
+ GetMethod method = new GetMethod(url);
+ method.setRequestHeader(AUTHORIZATION, authorizationString);
+ method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
+ new DefaultHttpMethodRetryHandler(3, false));
+ IOTResponse iotResponse = new IOTResponse();
+ iotResponse.setStatus(client.executeMethod(method));
+ iotResponse.setBody(new String(method.getResponseBody()));
+ return iotResponse;
+
+ } catch (GeneralSecurityException e) {
+ log.error("Failure occurred at IOTResponse get for GeneralSecurityException", e);
+ } catch (IOException e) {
+ log.error("Failure occurred at IOTResponse get for IOException", e);
+ }
+
+ return null;
+ }
+
+ public IOTResponse delete(String endpoint) {
+
+ HttpClient client = new HttpClient();
+
+ try {
+ ProtocolSocketFactory socketFactory = new EasySSLProtocolSocketFactory();
+
+ Protocol https = new Protocol(Constants.HTTPS, socketFactory, Constants.HTTPS_GATEWAY_PORT);
+ Protocol.registerProtocol(Constants.HTTPS, https);
+
+ String url = backEndUrl + endpoint;
+
+ DeleteMethod method = new DeleteMethod(url);
+ method.setRequestHeader(AUTHORIZATION, authorizationString);
+ method.setRequestHeader(Constants.CONTENT_TYPE, requestHeaders.get(Constants.CONTENT_TYPE));
+ method.getParams().setParameter(HttpMethodParams.RETRY_HANDLER,
+ new DefaultHttpMethodRetryHandler(3, false));
+
+ IOTResponse iotResponse = new IOTResponse();
+ iotResponse.setStatus(client.executeMethod(method));
+ iotResponse.setBody(method.getResponseBodyAsString());
+ return iotResponse;
+
+ } catch (GeneralSecurityException e) {
+ log.error("Failure occurred at IOTResponse delete for GeneralSecurityException", e);
+ } catch (IOException e) {
+ log.error("Failure occurred at IOTResponse delete for IOException", e);
+ }
+ return null;
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/IOTResponse.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/IOTResponse.java
new file mode 100644
index 0000000..732d5a3
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/IOTResponse.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common;
+
+/**
+ * This class contains the functions to handle a HTTP Response
+ */
+public class IOTResponse {
+
+ private int status;
+ private String body;
+
+ public int getStatus() {
+ return status;
+ }
+
+ public void setStatus(int status) {
+ this.status = status;
+ }
+
+ public String getBody() {
+ return body;
+ }
+
+ public void setBody(String body) {
+ this.body = body;
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/OAuthUtil.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/OAuthUtil.java
new file mode 100644
index 0000000..2b162b1
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/OAuthUtil.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common;
+
+import org.apache.commons.net.util.Base64;
+import org.json.JSONObject;
+import org.wso2.carbon.automation.test.utils.http.client.HttpResponse;
+
+/**
+ * This Util class holds method necessary to get an OAuth token.
+ */
+public class OAuthUtil {
+
+
+ public static String getScopes(String backendHTTPURL, String backendHTTPSURL) throws Exception {
+ return getOAuthTokenPair(backendHTTPURL, backendHTTPSURL).get(Constants.SCOPE).toString();
+ }
+
+ public static String getOAuthToken(String backendHTTPURL, String backendHTTPSURL) throws Exception {
+ return getOAuthTokenPair(backendHTTPURL, backendHTTPSURL).get(Constants.OAUTH_ACCESS_TOKEN).toString();
+ }
+
+ public static JSONObject getOAuthTokenPair(String backendHTTPURL, String backendHTTPSURL) throws Exception {
+ String AuthString = "Basic YWRtaW46YWRtaW4=";
+ RestClient client = new RestClient(backendHTTPURL, Constants.APPLICATION_JSON, AuthString);
+ HttpResponse oAuthData = client.post(Constants.APIApplicationRegistration.API_APP_REGISTRATION_ENDPOINT,
+ Constants.APIApplicationRegistration.API_APP_REGISTRATION_PAYLOAD);
+ JSONObject jsonObj = new JSONObject(oAuthData.getData());
+ String clientId = jsonObj.get(Constants.OAUTH_CLIENT_ID).toString();
+ String clientSecret = jsonObj.get(Constants.OAUTH_CLIENT_SECRET).toString();
+ byte[] bytesEncoded = Base64.encodeBase64((clientId + ":" + clientSecret).getBytes());
+ String basicAuthString = "Basic " + new String(bytesEncoded);
+ //Initiate a RestClient to get OAuth token
+ client = new RestClient(backendHTTPSURL, Constants.APPLICATION_URL_ENCODED, basicAuthString);
+ oAuthData = client.post(Constants.APIApplicationRegistration.TOKEN_ENDPOINT,
+ Constants.APIApplicationRegistration.OAUTH_TOKEN_PAYLOAD);
+ jsonObj = new JSONObject(oAuthData.getData());
+ return jsonObj;
+ }
+
+ /**
+ * To get the oauth token pair for the given auth string which is encoded in base64 format.
+ * @param authString encoded auth string
+ * @param backendHTTPURL backend http URL
+ * @param backendHTTPSURL backend https URL
+ * @return a JSON object which consist of oauth token pair
+ * @throws Exception Exception
+ */
+ public static String getOAuthTokenPair(String authString, String backendHTTPURL, String backendHTTPSURL,
+ String username, String password) throws Exception {
+ RestClient client = new RestClient(backendHTTPURL, Constants.APPLICATION_JSON, "Basic " + authString);
+ HttpResponse oAuthData = client.post(Constants.APIApplicationRegistration.API_APP_REGISTRATION_ENDPOINT,
+ Constants.APIApplicationRegistration.API_APP_REGISTRATION_PAYLOAD);
+ JSONObject jsonObj = new JSONObject(oAuthData.getData());
+ String clientId = jsonObj.get(Constants.OAUTH_CLIENT_ID).toString();
+ String clientSecret = jsonObj.get(Constants.OAUTH_CLIENT_SECRET).toString();
+ byte[] bytesEncoded = Base64.encodeBase64((clientId + ":" + clientSecret).getBytes());
+ String basicAuthString = "Basic " + new String(bytesEncoded);
+ //Initiate a RestClient to get OAuth token
+ client = new RestClient(backendHTTPSURL, Constants.APPLICATION_URL_ENCODED, basicAuthString);
+ oAuthData = client.post(Constants.APIApplicationRegistration.TOKEN_ENDPOINT,
+ "username=" + username + "&password=" + password + Constants.APIApplicationRegistration.MULTI_TENANT_OAUTH_TOKEN_PAYLOAD);
+ jsonObj = new JSONObject(oAuthData.getData());
+ return jsonObj.get(Constants.OAUTH_ACCESS_TOKEN).toString();
+
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/PayloadGenerator.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/PayloadGenerator.java
new file mode 100644
index 0000000..9f3a6ce
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/PayloadGenerator.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common;
+
+import com.google.gson.JsonArray;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import org.apache.commons.io.IOUtils;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * This class is used to load the necessary payloads from payload files for integration tests.
+ */
+public class PayloadGenerator {
+
+ private static final String PAYLOAD_LOCATION = "payloads/";
+ private static JsonParser parser = new JsonParser();
+
+ public static JsonObject getJsonPayload(String fileName, String method)
+ throws FileNotFoundException {
+ URL url = PayloadGenerator.class.getClassLoader().getResource(PAYLOAD_LOCATION + fileName);
+ JsonObject jsonObject = parser.parse(new FileReader(url.getPath())).getAsJsonObject();
+ return jsonObject.get(method).getAsJsonObject();
+ }
+
+ public static String getJsonPayloadToString(String fileName) throws IOException {
+ URL url = Thread.currentThread().getContextClassLoader().getResource(PAYLOAD_LOCATION + fileName);
+ FileInputStream fisTargetFile = new FileInputStream(new File(url.getPath()));
+ String returnString = IOUtils.toString(fisTargetFile, Constants.UTF8);
+ return returnString;
+ }
+
+ /**
+ * Create a Json Array from a specific method in the file
+ * @param fileName Name of the file
+ * @param method Method name
+ * @return Json Arry created from the specific method in the file
+ * @throws FileNotFoundException File Not found exception
+ */
+ public static JsonArray getJsonArray(String fileName, String method)
+ throws FileNotFoundException {
+ URL url = PayloadGenerator.class.getClassLoader().getResource(PAYLOAD_LOCATION + fileName);
+ JsonObject jsonObject = parser.parse(new FileReader(url.getPath())).getAsJsonObject();
+ return jsonObject.get(method).getAsJsonArray();
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/RestClient.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/RestClient.java
new file mode 100644
index 0000000..c4f09f0
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/RestClient.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common;
+
+import org.wso2.carbon.automation.engine.exceptions.AutomationFrameworkException;
+import org.wso2.carbon.automation.test.utils.http.client.HttpRequestUtil;
+import org.wso2.carbon.automation.test.utils.http.client.HttpResponse;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * This is the rest client that is used to calls to APIs.
+ */
+public class RestClient {
+
+ private static final String AUTHORIZATION = "Authorization";
+ private String backEndUrl;
+ private String authrizationString;
+ private Map requestHeaders = new HashMap();
+
+ public RestClient(String backEndUrl, String contentType) {
+ this.backEndUrl = backEndUrl;
+ this.requestHeaders.put(Constants.CONTENT_TYPE, contentType);
+ }
+
+ public RestClient(String backEndUrl, String contentType, String authorization) {
+ this.backEndUrl = backEndUrl;
+ this.requestHeaders.put(Constants.CONTENT_TYPE, contentType);
+ if (authorization != null || !authorization.isEmpty()) {
+ this.authrizationString = authorization;
+ this.requestHeaders.put(AUTHORIZATION, authorization);
+ }
+ }
+
+ public String getAuthrizationString() {
+ return authrizationString;
+ }
+
+ public void setAuthrizationString(String authrizationString) {
+ this.authrizationString = authrizationString;
+ }
+
+ public void setHttpHeader(String headerName, String value) {
+ this.requestHeaders.put(headerName, value);
+ }
+
+ public String getHttpHeader(String headerName) {
+ return this.requestHeaders.get(headerName);
+ }
+
+ public void removeHttpHeader(String headerName) {
+ this.requestHeaders.remove(headerName);
+ }
+
+ public HttpResponse post(String endpoint, String body) throws MalformedURLException, AutomationFrameworkException {
+ return HttpRequestUtil.doPost(new URL(backEndUrl + endpoint), body, requestHeaders);
+ }
+
+ public HttpResponse put(String endpoint, String body) throws Exception {
+ HttpURLConnection urlConnection = null;
+ try {
+ urlConnection = (HttpURLConnection) new URL(backEndUrl + endpoint).openConnection();
+ try {
+ urlConnection.setRequestMethod("PUT");
+ } catch (ProtocolException e) {
+ throw new Exception("Shouldn\'t happen: HttpURLConnection doesn\'t support POST?? " + e.getMessage(),
+ e);
+ }
+
+ urlConnection.setDoOutput(true);
+ urlConnection.setDoInput(true);
+ urlConnection.setUseCaches(false);
+ urlConnection.setAllowUserInteraction(false);
+ Iterator entryIterator = this.requestHeaders.entrySet().iterator();
+ while (entryIterator.hasNext()) {
+ Map.Entry sb = (Map.Entry) entryIterator.next();
+ urlConnection.setRequestProperty((String) sb.getKey(), (String) sb.getValue());
+ }
+ OutputStream outputStream = urlConnection.getOutputStream();
+ try {
+ OutputStreamWriter sb1 = new OutputStreamWriter(outputStream, "UTF-8");
+ sb1.write(body);
+ sb1.close();
+ } catch (IOException e) {
+ throw new Exception("IOException while sending data " + e.getMessage(), e);
+ } finally {
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ }
+ StringBuilder sb2 = new StringBuilder();
+ BufferedReader rd = null;
+ try {
+ rd = new BufferedReader(
+ new InputStreamReader(urlConnection.getInputStream(), Charset.defaultCharset()));
+ String itr;
+ while ((itr = rd.readLine()) != null) {
+ sb2.append(itr);
+ }
+ } catch (FileNotFoundException e) {
+ throw new Exception("IOException while reading put request data " + e.getMessage(), e);
+ } finally {
+ if (rd != null) {
+ rd.close();
+ }
+ }
+ Iterator iterator = urlConnection.getHeaderFields().keySet().iterator();
+ HashMap responseHeaders = new HashMap();
+ while (iterator.hasNext()) {
+ String key = (String) iterator.next();
+ if (key != null) {
+ responseHeaders.put(key, urlConnection.getHeaderField(key));
+ }
+ }
+ HttpResponse httpResponse =
+ new HttpResponse(sb2.toString(), urlConnection.getResponseCode(), responseHeaders);
+ return httpResponse;
+ } catch (IOException e) {
+ throw new Exception("Connection error (Is server running at " + endpoint + " ?): " + e.getMessage(), e);
+ } finally {
+ if (urlConnection != null) {
+ urlConnection.disconnect();
+ }
+
+ }
+ }
+
+ public HttpResponse get(String endpoint) throws Exception {
+ return HttpRequestUtil.doGet(backEndUrl + endpoint, requestHeaders);
+ }
+
+ public HttpResponse delete(String endpoint) throws Exception {
+ HttpURLConnection conn = null;
+
+ HttpResponse httpResponse1;
+ try {
+ URL url = new URL(backEndUrl + endpoint);
+ conn = (HttpURLConnection) url.openConnection();
+ conn.setRequestMethod("DELETE");
+ conn.setDoOutput(true);
+ conn.setReadTimeout(30000);
+ Iterator entryIterator = this.requestHeaders.entrySet().iterator();
+
+ while (entryIterator.hasNext()) {
+ Map.Entry rd = (Map.Entry) entryIterator.next();
+ conn.setRequestProperty((String) rd.getKey(), (String) rd.getValue());
+ }
+
+ conn.connect();
+ StringBuilder sb1 = new StringBuilder();
+ BufferedReader rd1 = null;
+
+ HttpResponse httpResponse;
+ try {
+ rd1 = new BufferedReader(new InputStreamReader(conn.getInputStream(), Charset.defaultCharset()));
+
+ String ignored;
+ while ((ignored = rd1.readLine()) != null) {
+ sb1.append(ignored);
+ }
+
+ httpResponse = new HttpResponse(sb1.toString(), conn.getResponseCode());
+ httpResponse.setResponseMessage(conn.getResponseMessage());
+ } catch (IOException e) {
+ rd1 = new BufferedReader(new InputStreamReader(conn.getErrorStream(), Charset.defaultCharset()));
+
+ String line;
+ while ((line = rd1.readLine()) != null) {
+ sb1.append(line);
+ }
+
+ httpResponse = new HttpResponse(sb1.toString(), conn.getResponseCode());
+ httpResponse.setResponseMessage(conn.getResponseMessage());
+ } finally {
+ if (rd1 != null) {
+ rd1.close();
+ }
+
+ }
+
+ httpResponse1 = httpResponse;
+ } finally {
+ if (conn != null) {
+ conn.disconnect();
+ }
+
+ }
+
+ return httpResponse1;
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/TestBase.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/TestBase.java
new file mode 100644
index 0000000..e75d496
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/TestBase.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common;
+
+import org.apache.commons.net.util.Base64;
+import org.testng.annotations.DataProvider;
+import org.wso2.carbon.automation.engine.context.AutomationContext;
+import org.wso2.carbon.automation.engine.context.TestUserMode;
+import org.wso2.carbon.automation.engine.context.beans.User;
+import org.wso2.carbon.automation.engine.frameworkutils.FrameworkPathUtil;
+import org.wso2.carbon.integration.common.utils.LoginLogoutClient;
+
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ * This is the base test class that provides common details necessary for other test cases.
+ */
+public class TestBase {
+ protected AutomationContext automationContext;
+ protected String backendHTTPSURL;
+ protected String backendHTTPURL;
+ protected String accessTokenString;
+ protected String accessToken;
+ protected TestUserMode userMode;
+
+ protected void init(TestUserMode userMode) throws Exception {
+ automationContext = new AutomationContext(Constants.AUTOMATION_CONTEXT, userMode);
+ String tenantDomain = automationContext.getContextTenant().getDomain();
+ backendHTTPSURL = automationContext.getContextUrls().getWebAppURLHttps().replace("9443", String.valueOf(Constants
+ .HTTPS_GATEWAY_PORT)).replace("/t/" + tenantDomain , "");
+ backendHTTPURL = automationContext.getContextUrls().getWebAppURL().replace("9763", String.valueOf(Constants
+ .HTTP_GATEWAY_PORT)).replace("/t/" + tenantDomain , "");
+ User currentUser = getAutomationContext().getContextTenant().getContextUser();
+ byte[] bytesEncoded = Base64
+ .encodeBase64((currentUser.getUserName() + ":" + currentUser.getPassword()).getBytes());
+ String encoded = new String(bytesEncoded);
+ accessToken = OAuthUtil.getOAuthTokenPair(encoded, backendHTTPSURL, backendHTTPSURL, currentUser.getUserName(),
+ currentUser.getPassword());
+ accessTokenString = "Bearer " + accessToken;
+ }
+
+ protected void initPublisher(String productGroupName, String instanceName,
+ TestUserMode userMode)
+ throws XPathExpressionException {
+ automationContext = new AutomationContext(productGroupName, instanceName, userMode);
+ backendHTTPSURL = automationContext.getContextUrls().getBackEndUrl();
+ }
+
+ public String getBackendHTTPURL() {
+ return backendHTTPURL;
+ }
+
+ public void setBackendHTTPURL(String backendHTTPURL) {
+ this.backendHTTPURL = backendHTTPURL;
+ }
+
+ protected String getBackendHTTPSURL() throws XPathExpressionException {
+ return backendHTTPSURL;
+ }
+
+ protected String getSessionCookie() throws Exception {
+ LoginLogoutClient loginLogoutClient = new LoginLogoutClient(automationContext);
+ return loginLogoutClient.login();
+ }
+
+ protected String getServiceURL() throws XPathExpressionException {
+ return automationContext.getContextUrls().getServiceUrl();
+ }
+
+ protected AutomationContext getAutomationContext() {
+ return automationContext;
+ }
+
+ protected String getTestArtifactLocation() {
+ return FrameworkPathUtil.getSystemResourceLocation();
+ }
+
+ @DataProvider
+ public static Object[][] userModeProvider() {
+ return new TestUserMode[][]{
+ new TestUserMode[]{TestUserMode.SUPER_TENANT_ADMIN},
+ new TestUserMode[]{TestUserMode.TENANT_ADMIN}
+ };
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/AnalyticsServerExtension.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/AnalyticsServerExtension.java
new file mode 100644
index 0000000..a765718
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/AnalyticsServerExtension.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common.extensions;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.automation.engine.annotations.ExecutionEnvironment;
+import org.wso2.carbon.automation.engine.context.AutomationContext;
+import org.wso2.carbon.automation.engine.context.ContextXpathConstants;
+import org.wso2.carbon.automation.engine.context.TestUserMode;
+import org.wso2.carbon.automation.engine.extensions.ExecutionListenerExtension;
+import org.wso2.carbon.automation.extensions.ExtensionConstants;
+import org.wso2.carbon.automation.extensions.servers.carbonserver.CarbonServerExtension;
+
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ * Test Automation server extension to start the DAS.
+ * This will set the carbon_home to {carbonHome}/core and port offset : 2
+ */
+public class AnalyticsServerExtension extends ExecutionListenerExtension {
+
+ private CustomTestServerManager serverManager;
+ private static final Log log = LogFactory.getLog(CarbonServerExtension.class);
+ private String executionEnvironment;
+ private AutomationContext automationContext;
+ private final String ANALYTICS_PORT_OFFSET = "2";
+
+ @Override
+ public void initiate() {
+ try {
+ automationContext = new AutomationContext("IOT", TestUserMode.SUPER_TENANT_USER);
+ if (getParameters().get(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND) == null) {
+ getParameters().put(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND, ANALYTICS_PORT_OFFSET);
+ }
+ serverManager = new CustomTestServerManager(getAutomationContext(), null, getParameters());
+ executionEnvironment =
+ automationContext.getConfigurationValue(ContextXpathConstants.EXECUTION_ENVIRONMENT);
+
+ } catch (XPathExpressionException e) {
+ throw new RuntimeException("Error while initiating test environment", e);
+ }
+ }
+
+ @Override
+ public void onExecutionStart() {
+ try {
+ if (executionEnvironment.equalsIgnoreCase(ExecutionEnvironment.STANDALONE.name())) {
+ String carbonHome = serverManager.startServer("analytics");
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Fail to start carbon server ", e);
+ }
+ }
+
+ @Override
+ public void onExecutionFinish() {
+ try {
+ if (executionEnvironment.equalsIgnoreCase(ExecutionEnvironment.STANDALONE.name())) {
+ serverManager.stopServer();
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Fail to stop carbon server ", e);
+ }
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/BrokerServerExtension.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/BrokerServerExtension.java
new file mode 100644
index 0000000..3052044
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/BrokerServerExtension.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common.extensions;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.automation.engine.annotations.ExecutionEnvironment;
+import org.wso2.carbon.automation.engine.context.AutomationContext;
+import org.wso2.carbon.automation.engine.context.ContextXpathConstants;
+import org.wso2.carbon.automation.engine.context.TestUserMode;
+import org.wso2.carbon.automation.engine.extensions.ExecutionListenerExtension;
+import org.wso2.carbon.automation.extensions.ExtensionConstants;
+import org.wso2.carbon.automation.extensions.servers.carbonserver.CarbonServerExtension;
+
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ * Test Automation server extension to start the Broker.
+ * This will set the carbon_home to {carbonHome}/core and port offset : 3
+ */
+public class BrokerServerExtension extends ExecutionListenerExtension {
+
+ private CustomTestServerManager serverManager;
+ private static final Log log = LogFactory.getLog(CarbonServerExtension.class);
+ private String executionEnvironment;
+ private AutomationContext automationContext;
+ private final String BROKER_PORT_OFFSET = "3";
+
+ @Override
+ public void initiate() {
+ try {
+ automationContext = new AutomationContext("IOT", TestUserMode.SUPER_TENANT_USER);
+ if (getParameters().get(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND) == null) {
+ getParameters().put(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND, BROKER_PORT_OFFSET);
+ }
+ serverManager = new CustomTestServerManager(getAutomationContext(), null, getParameters());
+ executionEnvironment =
+ automationContext.getConfigurationValue(ContextXpathConstants.EXECUTION_ENVIRONMENT);
+
+ } catch (XPathExpressionException e) {
+ throw new RuntimeException("Error while initiating test environment", e);
+ }
+ }
+
+ @Override
+ public void onExecutionStart() {
+ try {
+ if (executionEnvironment.equalsIgnoreCase(ExecutionEnvironment.STANDALONE.name())) {
+ String carbonHome = serverManager.startServer("broker");
+ log.info(carbonHome);
+ System.setProperty(ExtensionConstants.CARBON_HOME, carbonHome);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Fail to start carbon server ", e);
+ }
+ }
+
+ @Override
+ public void onExecutionFinish() {
+ try {
+ if (executionEnvironment.equalsIgnoreCase(ExecutionEnvironment.STANDALONE.name())) {
+ serverManager.stopServer();
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Fail to stop carbon server ", e);
+ }
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/CarbonServerManagerExtension.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/CarbonServerManagerExtension.java
new file mode 100644
index 0000000..ad8423d
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/CarbonServerManagerExtension.java
@@ -0,0 +1,428 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common.extensions;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.filefilter.DirectoryFileFilter;
+import org.apache.commons.io.filefilter.RegexFileFilter;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.automation.engine.context.AutomationContext;
+import org.wso2.carbon.automation.engine.context.beans.User;
+import org.wso2.carbon.automation.engine.exceptions.AutomationFrameworkException;
+import org.wso2.carbon.automation.engine.frameworkutils.CodeCoverageUtils;
+import org.wso2.carbon.automation.engine.frameworkutils.FrameworkPathUtil;
+import org.wso2.carbon.automation.engine.frameworkutils.ReportGenerator;
+import org.wso2.carbon.automation.engine.frameworkutils.TestFrameworkUtils;
+import org.wso2.carbon.automation.extensions.servers.utils.ArchiveExtractor;
+import org.wso2.carbon.automation.extensions.servers.utils.ClientConnectionUtil;
+import org.wso2.carbon.automation.extensions.servers.utils.FileManipulator;
+import org.wso2.carbon.automation.extensions.servers.utils.ServerLogReader;
+
+import javax.xml.xpath.XPathExpressionException;
+import java.io.File;
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+
+
+public class CarbonServerManagerExtension {
+ private static final Log log = LogFactory.getLog(CarbonServerManagerExtension.class);
+ private Process process;
+ private String carbonHome;
+ private AutomationContext automationContext;
+ private ServerLogReader inputStreamHandler;
+ private ServerLogReader errorStreamHandler;
+ private boolean isCoverageEnable = false;
+ private String coverageDumpFilePath;
+ private int portOffset = 0;
+ private static final String SERVER_SHUTDOWN_MESSAGE = "Halting JVM";
+ private static final String SERVER_STARTUP_MESSAGE = "Mgt Console URL";
+ private static final long DEFAULT_START_STOP_WAIT_MS = 900000L;
+ private static final String CMD_ARG = "cmdArg";
+ private static int defaultHttpPort = Integer.parseInt("9763");
+ private static int defaultHttpsPort = Integer.parseInt("9443");
+ private static final long COVERAGE_DUMP_WAIT_TIME = 30000;
+
+ public CarbonServerManagerExtension(AutomationContext context) {
+ this.automationContext = context;
+ }
+
+ public synchronized void startServerUsingCarbonHome(String carbonHome, Map commandMap) throws AutomationFrameworkException {
+ if(this.process == null) {
+ this.portOffset = this.checkPortAvailability(commandMap);
+ Process tempProcess = null;
+
+ try {
+ if(!commandMap.isEmpty() && this.getPortOffsetFromCommandMap(commandMap) == 0) {
+ System.setProperty("carbon.home", carbonHome);
+ }
+
+ File commandDir = new File(carbonHome);
+ log.info("Starting carbon server............. ");
+ String scriptName = TestFrameworkUtils.getStartupScriptFileName(carbonHome);
+ String[] parameters = this.expandServerStartupCommandList(commandMap);
+ String[] cmdArray;
+ if(System.getProperty("os.name").toLowerCase().contains("windows")) {
+ commandDir = new File(carbonHome + File.separator + "bin");
+ cmdArray = new String[]{"cmd.exe", "/c", scriptName + ".bat"};
+ cmdArray = this.mergePropertiesToCommandArray(parameters, cmdArray);
+ tempProcess = Runtime.getRuntime().exec(cmdArray, (String[])null, commandDir);
+ } else {
+ cmdArray = new String[]{"sh", "bin/" + scriptName + ".sh"};
+ cmdArray = this.mergePropertiesToCommandArray(parameters, cmdArray);
+ tempProcess = Runtime.getRuntime().exec(cmdArray, (String[])null, commandDir);
+ }
+
+ this.errorStreamHandler = new ServerLogReader("errorStream", tempProcess.getErrorStream());
+ this.inputStreamHandler = new ServerLogReader("inputStream", tempProcess.getInputStream());
+ this.inputStreamHandler.start();
+ this.errorStreamHandler.start();
+ Runtime.getRuntime().addShutdownHook(new Thread() {
+ public void run() {
+ try {
+ CarbonServerManagerExtension.this.serverShutdown(CarbonServerManagerExtension.this.portOffset);
+ } catch (Exception var2) {
+ CarbonServerManagerExtension.log.error("Error while server shutdown ..", var2);
+ }
+
+ }
+ });
+ ClientConnectionUtil.waitForPort(defaultHttpPort + this.portOffset, DEFAULT_START_STOP_WAIT_MS, false, (String)this
+ .automationContext.getInstance().getHosts().get("default"));
+ long time = System.currentTimeMillis() + 60000L;
+
+// while(true) {
+// if(this.inputStreamHandler.getOutput().contains("Mgt Console URL") || System.currentTimeMillis() >= time) {
+// int httpsPort = defaultHttpsPort + this.portOffset;
+// String backendURL = this.automationContext.getContextUrls().getSecureServiceUrl().replaceAll("(:\\d+)", ":" + httpsPort);
+// User superUser = this.automationContext.getSuperTenant().getTenantAdmin();
+// ClientConnectionUtil.waitForLogin(backendURL, superUser);
+// log.info("Server started successfully.");
+// break;
+// }
+// }
+ int httpsPort = defaultHttpsPort + this.portOffset;
+ String backendURL = this.automationContext.getContextUrls().getSecureServiceUrl().replaceAll("(:\\d+)", ":" + httpsPort);
+ User superUser = this.automationContext.getSuperTenant().getTenantAdmin();
+ ClientConnectionUtil.waitForLogin(backendURL, superUser);
+ } catch (XPathExpressionException | IOException var13) {
+ throw new IllegalStateException("Unable to start server", var13);
+ }
+
+ this.process = tempProcess;
+ }
+ }
+
+ private int checkPortAvailability(Map commandMap) throws AutomationFrameworkException {
+ int portOffset = this.getPortOffsetFromCommandMap(commandMap);
+ if(ClientConnectionUtil.isPortOpen(defaultHttpPort + portOffset)) {
+ throw new AutomationFrameworkException("Unable to start carbon server on port " + (defaultHttpPort + portOffset) + " : Port already in use");
+ } else if(ClientConnectionUtil.isPortOpen(defaultHttpsPort + portOffset)) {
+ throw new AutomationFrameworkException("Unable to start carbon server on port " + (defaultHttpsPort + portOffset) + " : Port already in use");
+ } else {
+ return portOffset;
+ }
+ }
+
+ private String[] mergePropertiesToCommandArray(String[] parameters, String[] cmdArray) {
+ if(parameters != null) {
+ cmdArray = this.mergerArrays(cmdArray, parameters);
+ }
+
+ return cmdArray;
+ }
+
+ /**
+ * Unzip carbon zip file and return the carbon home. Based on the coverage configuration in automation.xml
+ * This method will inject jacoco agent to the carbon server startup scripts.
+ *
+ * @param carbonServerZipFile - Carbon zip file, which should be specified in test module pom
+ * @return - carbonHome - carbon home
+ * @throws IOException - If pack extraction fails
+ */
+ public synchronized String setUpCarbonHome(String carbonServerZipFile) throws IOException, AutomationFrameworkException {
+ if(this.process != null) {
+ return this.carbonHome;
+ } else {
+ int indexOfZip = carbonServerZipFile.lastIndexOf(".zip");
+ if(indexOfZip == -1) {
+ throw new IllegalArgumentException(carbonServerZipFile + " is not a zip file");
+ } else {
+ String fileSeparator = File.separator.equals("\\")?"\\":"/";
+ if(fileSeparator.equals("\\")) {
+ carbonServerZipFile = carbonServerZipFile.replace("/", "\\");
+ }
+
+ String extractedCarbonDir = carbonServerZipFile.substring(carbonServerZipFile.lastIndexOf(fileSeparator) + 1, indexOfZip);
+ FileManipulator.deleteDir(extractedCarbonDir);
+ String extractDir = "carbontmp" + System.currentTimeMillis();
+ String baseDir = System.getProperty("basedir", ".") + File.separator + "target";
+ log.info("Extracting carbon zip file.. ");
+ (new ArchiveExtractor()).extractFile(carbonServerZipFile, baseDir + File.separator + extractDir);
+ this.carbonHome = (new File(baseDir)).getAbsolutePath() + File.separator + extractDir + File.separator + extractedCarbonDir;
+
+ try {
+ this.isCoverageEnable = Boolean.parseBoolean(this.automationContext.getConfigurationValue("//coverage"));
+ } catch (XPathExpressionException var8) {
+ throw new AutomationFrameworkException("Coverage configuration not found in automation.xml", var8);
+ }
+
+ if(this.isCoverageEnable) {
+ this.instrumentForCoverage();
+ }
+
+ return this.carbonHome;
+ }
+ }
+ }
+
+ public synchronized void serverShutdown(int portOffset) throws AutomationFrameworkException {
+ if(this.process != null) {
+ log.info("Shutting down server..");
+ if(ClientConnectionUtil.isPortOpen(Integer.parseInt("9443") + portOffset)) {
+ int httpsPort = defaultHttpsPort + portOffset;
+ String url = null;
+
+ try {
+ url = this.automationContext.getContextUrls().getBackEndUrl();
+ } catch (XPathExpressionException var10) {
+ throw new AutomationFrameworkException("Get context failed", var10);
+ }
+
+ String backendURL = url.replaceAll("(:\\d+)", ":" + httpsPort);
+
+ try {
+ ClientConnectionUtil.sendForcefulShutDownRequest(backendURL, this.automationContext.getSuperTenant().getContextUser().getUserName(), this.automationContext.getSuperTenant().getContextUser().getPassword());
+ } catch (AutomationFrameworkException var8) {
+ throw new AutomationFrameworkException("Get context failed", var8);
+ } catch (XPathExpressionException var9) {
+ throw new AutomationFrameworkException("Get context failed", var9);
+ }
+
+ long time = System.currentTimeMillis() + 300000L;
+
+// while(!this.inputStreamHandler.getOutput().contains("Halting JVM") && System.currentTimeMillis() < time) {
+// ;
+// }
+
+ log.info("Server stopped successfully...");
+ }
+
+ this.inputStreamHandler.stop();
+ this.errorStreamHandler.stop();
+ this.process.destroy();
+ this.process = null;
+ if(this.isCoverageEnable) {
+ try {
+ log.info("Generating Jacoco code coverage...");
+ this.generateCoverageReport(new File(this.carbonHome + File.separator + "repository"
+ + File.separator + "components" + File.separator + "plugins" + File.separator));
+ } catch (IOException var7) {
+ log.error("Failed to generate code coverage ", var7);
+ throw new AutomationFrameworkException("Failed to generate code coverage ", var7);
+ }
+ }
+
+ if(portOffset == 0) {
+ System.clearProperty("carbon.home");
+ }
+ }
+
+ }
+
+ private void generateCoverageReport(File classesDir) throws IOException, AutomationFrameworkException {
+ checkJacocoDataFileSizes(FrameworkPathUtil.getJacocoCoverageHome());
+ CodeCoverageUtils.executeMerge(FrameworkPathUtil.getJacocoCoverageHome(), FrameworkPathUtil.getCoverageMergeFilePath());
+ ReportGenerator reportGenerator = new ReportGenerator(new File(FrameworkPathUtil.getCoverageMergeFilePath()), classesDir, new File(CodeCoverageUtils.getJacocoReportDirectory()), (File)null);
+ reportGenerator.create();
+ log.info("Jacoco coverage dump file path : " + FrameworkPathUtil.getCoverageDumpFilePath());
+ log.info("Jacoco class file path : " + classesDir);
+ log.info("Jacoco coverage HTML report path : " + CodeCoverageUtils.getJacocoReportDirectory() + File.separator + "index.html");
+ }
+
+ public synchronized void restartGracefully() throws AutomationFrameworkException {
+ try {
+ int httpsPort = defaultHttpsPort + this.portOffset;
+ String backendURL = this.automationContext.getContextUrls().getSecureServiceUrl().replaceAll("(:\\d+)", ":" + httpsPort);
+ User superUser = this.automationContext.getSuperTenant().getTenantAdmin();
+ ClientConnectionUtil.sendGraceFullRestartRequest(backendURL, superUser.getUserName(), superUser.getPassword());
+ } catch (XPathExpressionException var5) {
+ throw new AutomationFrameworkException("restart failed", var5);
+ }
+
+ long time = System.currentTimeMillis() + 300000L;
+
+// while(!this.inputStreamHandler.getOutput().contains("Halting JVM") && System.currentTimeMillis() < time) {
+// ;
+// }
+
+ time = System.currentTimeMillis();
+
+ while(System.currentTimeMillis() < time + 5000L) {
+ ;
+ }
+
+ try {
+ ClientConnectionUtil.waitForPort(Integer.parseInt((String)this.automationContext.getInstance().getPorts().get("https")), (String)this.automationContext.getInstance().getHosts().get("default"));
+ ClientConnectionUtil.waitForLogin(this.automationContext);
+ } catch (XPathExpressionException var4) {
+ throw new AutomationFrameworkException("Connection attempt to carbon server failed", var4);
+ }
+ }
+
+ private String[] expandServerStartupCommandList(Map commandMap) {
+ if(commandMap != null && commandMap.size() != 0) {
+ String[] cmdParaArray = null;
+ String cmdArg = null;
+ if(commandMap.containsKey("cmdArg")) {
+ cmdArg = (String)commandMap.get("cmdArg");
+ cmdParaArray = cmdArg.trim().split("\\s+");
+ commandMap.remove("cmdArg");
+ }
+
+ String[] parameterArray = new String[commandMap.size()];
+ int arrayIndex = 0;
+ Set> entries = commandMap.entrySet();
+
+ String parameter;
+ for(Iterator i$ = entries.iterator(); i$.hasNext(); parameterArray[arrayIndex++] = parameter) {
+ Map.Entry entry = (Map.Entry)i$.next();
+ String key = (String)entry.getKey();
+ String value = (String)entry.getValue();
+ if(value != null && !value.isEmpty()) {
+ parameter = key + "=" + value;
+ } else {
+ parameter = key;
+ }
+ }
+
+ if(cmdArg != null) {
+ commandMap.put("cmdArg", cmdArg);
+ }
+
+ if(cmdParaArray != null && cmdParaArray.length != 0) {
+ return (String[]) ArrayUtils.addAll(parameterArray, cmdParaArray);
+ } else {
+ return parameterArray;
+ }
+ } else {
+ return null;
+ }
+ }
+
+ private int getPortOffsetFromCommandMap(Map commandMap) {
+ return commandMap.containsKey("-DportOffset")?Integer.parseInt((String)commandMap.get("-DportOffset")):0;
+ }
+
+ private String[] mergerArrays(String[] array1, String[] array2) {
+ return (String[])ArrayUtils.addAll(array1, array2);
+ }
+
+ /**
+ * This methods will insert jacoco agent settings into startup script under JAVA_OPTS
+ *
+ * @param scriptName - Name of the startup script
+ * @throws IOException - throws if shell script edit fails
+ */
+ private void insertJacocoAgentToShellScript(String scriptName) throws IOException {
+ String jacocoAgentFile = CodeCoverageUtils.getJacocoAgentJarLocation();
+ this.coverageDumpFilePath = FrameworkPathUtil.getCoverageDumpFilePath();
+ CodeCoverageUtils.insertStringToFile(new File(this.carbonHome + File.separator + "bin" + File.separator + scriptName + ".sh"), new File(this.carbonHome + File.separator + "tmp" + File.separator + scriptName + ".sh"), "-Dwso2.server.standalone=true", "-javaagent:" + jacocoAgentFile + "=destfile=" + this.coverageDumpFilePath + "" + ",append=true,includes=" + CodeCoverageUtils.getInclusionJarsPattern(":") + " \\");
+ }
+
+ /**
+ * This methods will insert jacoco agent settings into windows bat script
+ *
+ * @param scriptName - Name of the startup script
+ * @throws IOException - throws if shell script edit fails
+ */
+ private void insertJacocoAgentToBatScript(String scriptName) throws IOException {
+ String jacocoAgentFile = CodeCoverageUtils.getJacocoAgentJarLocation();
+ this.coverageDumpFilePath = FrameworkPathUtil.getCoverageDumpFilePath();
+ CodeCoverageUtils.insertJacocoAgentToStartupBat(new File(this.carbonHome + File.separator + "bin" + File.separator + scriptName + ".bat"), new File(this.carbonHome + File.separator + "tmp" + File.separator + scriptName + ".bat"), "-Dcatalina.base", "-javaagent:" + jacocoAgentFile + "=destfile=" + this.coverageDumpFilePath + "" + ",append=true,includes=" + CodeCoverageUtils.getInclusionJarsPattern(":"));
+ }
+
+ /**
+ * This method will check the OS and edit server startup script to inject jacoco agent
+ *
+ * @throws IOException - If agent insertion fails.
+ */
+ private void instrumentForCoverage() throws IOException, AutomationFrameworkException {
+ String scriptName = TestFrameworkUtils.getStartupScriptFileName(this.carbonHome);
+ if(System.getProperty("os.name").toLowerCase().contains("windows")) {
+ this.insertJacocoAgentToBatScript(scriptName);
+ if(log.isDebugEnabled()) {
+ log.debug("Included files " + CodeCoverageUtils.getInclusionJarsPattern(":"));
+ log.debug("Excluded files " + CodeCoverageUtils.getExclusionJarsPattern(":"));
+ }
+ } else {
+ this.insertJacocoAgentToShellScript(scriptName);
+ }
+
+ }
+
+ /**
+ * To check jacoco file sizes and wait for them to get created..
+ *
+ * @param filePath File Path of the jacoco data files.
+ */
+ private void checkJacocoDataFileSizes(String filePath) {
+ Collection fileSetsCollection = FileUtils
+ .listFiles(new File(filePath), new RegexFileFilter("[^s]+(." + "(?i)(exec))$"),
+ DirectoryFileFilter.DIRECTORY);
+
+ for (File inputFile : fileSetsCollection) {
+ if (inputFile.isDirectory()) {
+ continue;
+ }
+ //retry to check whether exec data file is non empty.
+ waitForCoverageDumpFileCreation(inputFile);
+ }
+ }
+
+ /**
+ * This is to wait for jacoco exe file creation.
+ *
+ * @param file File that need to be created.
+ */
+ private void waitForCoverageDumpFileCreation(File file) {
+ long currentTime = System.currentTimeMillis();
+ long waitTime = currentTime + COVERAGE_DUMP_WAIT_TIME;
+
+ while (waitTime > System.currentTimeMillis()) {
+ if (file.length() > 0) {
+ log.info("Execution data file non empty file size in KB : " + file.length() / 1024);
+ break;
+ } else {
+ try {
+ log.warn("Execution data file is empty file size in KB : " + file.length() / 1024);
+ Thread.sleep(500);
+ } catch (InterruptedException ignored) {
+ log.warn("Sleep interrupted ", ignored);
+ }
+ }
+ }
+ }
+
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/CustomTestServerManager.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/CustomTestServerManager.java
new file mode 100644
index 0000000..6595dc7
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/CustomTestServerManager.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common.extensions;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.automation.engine.FrameworkConstants;
+import org.wso2.carbon.automation.engine.context.AutomationContext;
+import org.wso2.carbon.automation.engine.exceptions.AutomationFrameworkException;
+import org.wso2.carbon.automation.extensions.ExtensionConstants;
+
+import javax.xml.xpath.XPathExpressionException;
+import java.io.File;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class CustomTestServerManager {
+ protected CarbonServerManagerExtension carbonServer;
+ protected String carbonZip;
+ protected int portOffset;
+ protected Map commandMap = new HashMap();
+ private static final Log log = LogFactory.getLog(CustomTestServerManager.class);
+ protected String carbonHome;
+
+ public CustomTestServerManager(AutomationContext context) {
+ carbonServer = new CarbonServerManagerExtension(context);
+ }
+
+ public CustomTestServerManager(AutomationContext context, String carbonZip) {
+ carbonServer = new CarbonServerManagerExtension(context);
+ this.carbonZip = carbonZip;
+ }
+
+ public CustomTestServerManager(AutomationContext context, int portOffset) {
+ carbonServer = new CarbonServerManagerExtension(context);
+ this.portOffset = portOffset;
+ commandMap.put(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND, String.valueOf(portOffset));
+ }
+
+ public CustomTestServerManager(AutomationContext context, String carbonZip,
+ Map commandMap) {
+ carbonServer = new CarbonServerManagerExtension(context);
+ this.carbonZip = carbonZip;
+ if (commandMap.get(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND) != null) {
+ this.portOffset = Integer.parseInt(commandMap.get(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND));
+ } else {
+ throw new IllegalArgumentException("portOffset value must be set in command list");
+ }
+ this.commandMap = commandMap;
+ }
+
+ public String getCarbonZip() {
+ return carbonZip;
+ }
+
+ public String getCarbonHome() {
+ return carbonHome;
+ }
+
+ public int getPortOffset() {
+ return portOffset;
+ }
+
+ public void configureServer() throws AutomationFrameworkException {
+
+ }
+
+ public Map getCommands() {
+ return commandMap;
+ }
+
+ /**
+ * This method is called for starting a Carbon server in preparation for execution of a
+ * TestSuite
+ *
+ * Add the @BeforeSuite TestNG annotation in the method overriding this method
+ *
+ * @param server : The server which needs to be start.
+ * @return The CARBON_HOME
+ * @throws IOException If an error occurs while copying the deployment artifacts into the
+ * Carbon server
+ */
+ public synchronized String startServer(String server)
+ throws AutomationFrameworkException, IOException, XPathExpressionException, InterruptedException {
+ if (carbonHome == null) {
+ if (carbonZip == null) {
+ carbonZip = System.getProperty(FrameworkConstants.SYSTEM_PROPERTY_CARBON_ZIP_LOCATION);
+ }
+ if (carbonZip == null) {
+ throw new IllegalArgumentException("carbon zip file cannot find in the given location");
+ }
+ String extractedDir = getExistingExtractedDir();
+ if (server.equalsIgnoreCase("core")) {
+ if (extractedDir == null) {
+ carbonHome = carbonServer.setUpCarbonHome(carbonZip);
+ } else {
+ carbonHome = extractedDir;
+ }
+ } else if (server.equalsIgnoreCase("analytics") || server.equalsIgnoreCase("broker")) {
+ if (extractedDir == null) {
+ carbonHome = carbonServer.setUpCarbonHome(carbonZip) + File.separator + "wso2" + File.separator + server;
+ } else {
+ carbonHome = extractedDir + File.separator + "wso2" + File.separator + server;
+ }
+ } else {
+ throw new IllegalArgumentException("Unsupported server type provided - " + server);
+ }
+ configureServer();
+ }
+ log.info("Carbon Home - " + carbonHome);
+ if (commandMap.get(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND) != null) {
+ this.portOffset = Integer.parseInt(commandMap.get(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND));
+ } else {
+ this.portOffset = 0;
+ }
+
+ carbonServer.startServerUsingCarbonHome(carbonHome, commandMap);
+ return carbonHome;
+ }
+
+ private String getExistingExtractedDir() {
+ File zipDir = new File(System.getProperty("basedir", ".") + File.separator + "target");
+ File[] subFiles = zipDir.listFiles();
+ if (subFiles != null) {
+ for (File subFile : subFiles) {
+ if (subFile.getName().startsWith("carbontmp")) {
+ File[] carbonServerFiles = subFile.listFiles();
+ if (carbonServerFiles != null) {
+ for (File file : carbonServerFiles) {
+ if (file.getName().startsWith("entgra-iot")) {
+ return file.getAbsolutePath();
+ }
+ }
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Restarting server already started by the method startServer
+ *
+ * @throws AutomationFrameworkException
+ */
+ public void restartGracefully() throws AutomationFrameworkException {
+ if (carbonHome == null) {
+ throw new AutomationFrameworkException("No Running Server found to restart. " +
+ "Please make sure whether server is started");
+ }
+ carbonServer.restartGracefully();
+ }
+
+ /**
+ * This method is called for stopping a Carbon server
+ *
+ * Add the @AfterSuite annotation in the method overriding this method
+ *
+ * @throws AutomationFrameworkException If an error occurs while shutting down the server
+ */
+ public void stopServer() throws AutomationFrameworkException {
+ carbonServer.serverShutdown(portOffset);
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/IOTServerExtension.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/IOTServerExtension.java
new file mode 100644
index 0000000..34a1037
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/common/extensions/IOTServerExtension.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.common.extensions;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.wso2.carbon.automation.engine.annotations.ExecutionEnvironment;
+import org.wso2.carbon.automation.engine.context.AutomationContext;
+import org.wso2.carbon.automation.engine.context.ContextXpathConstants;
+import org.wso2.carbon.automation.engine.context.TestUserMode;
+import org.wso2.carbon.automation.engine.extensions.ExecutionListenerExtension;
+import org.wso2.carbon.automation.extensions.ExtensionConstants;
+import org.wso2.carbon.automation.extensions.servers.carbonserver.CarbonServerExtension;
+
+import javax.xml.xpath.XPathExpressionException;
+
+/**
+ * Test Automation server extension to start the IOT core.
+ * This will set the carbon_home to {carbonHome}/core and port offset : 0
+ */
+public class IOTServerExtension extends ExecutionListenerExtension {
+
+ private CustomTestServerManager serverManager;
+ private static final Log log = LogFactory.getLog(CarbonServerExtension.class);
+ private String executionEnvironment;
+ private AutomationContext automationContext;
+ private final String IOT_CORE_PORT_OFFSET = "0";
+
+ @Override
+ public void initiate() {
+ try {
+ automationContext = new AutomationContext("IOT", TestUserMode.SUPER_TENANT_USER);
+ if (getParameters().get(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND) == null) {
+ getParameters().put(ExtensionConstants.SERVER_STARTUP_PORT_OFFSET_COMMAND, IOT_CORE_PORT_OFFSET);
+ }
+ serverManager = new CustomTestServerManager(getAutomationContext(), null, getParameters());
+ executionEnvironment =
+ automationContext.getConfigurationValue(ContextXpathConstants.EXECUTION_ENVIRONMENT);
+
+ } catch (XPathExpressionException e) {
+ throw new RuntimeException("Error while initiating test environment", e);
+ }
+ }
+
+ @Override
+ public void onExecutionStart() {
+ try {
+ if (executionEnvironment.equalsIgnoreCase(ExecutionEnvironment.STANDALONE.name())) {
+ String carbonHome = serverManager.startServer("core");
+ log.info(carbonHome);
+ System.setProperty(ExtensionConstants.CARBON_HOME, carbonHome);
+
+ // Need to give time for the apis to be added to the synapse configurations.
+ Thread.sleep(200000);
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Fail to start carbon server ", e);
+ }
+ }
+
+ @Override
+ public void onExecutionFinish() {
+ try {
+ if (executionEnvironment.equalsIgnoreCase(ExecutionEnvironment.STANDALONE.name())) {
+ serverManager.stopServer();
+ }
+ } catch (Exception e) {
+ throw new RuntimeException("Fail to stop carbon server ", e);
+ }
+ }
+}
diff --git a/integration-test/iot-community/src/test/java/org/wso2/iot/integration/jmeter/DeviceMgtCoreAPIJmeterTestCase.java b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/jmeter/DeviceMgtCoreAPIJmeterTestCase.java
new file mode 100644
index 0000000..4405c8a
--- /dev/null
+++ b/integration-test/iot-community/src/test/java/org/wso2/iot/integration/jmeter/DeviceMgtCoreAPIJmeterTestCase.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2018 - 2022 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+ *
+ * Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+ *
+ * Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://entgra.io/licenses/entgra-commercial/1.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.wso2.iot.integration.jmeter;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.testng.annotations.Test;
+import org.wso2.carbon.automation.engine.exceptions.AutomationFrameworkException;
+import org.wso2.carbon.automation.extensions.jmeter.JMeterTest;
+import org.wso2.carbon.automation.extensions.jmeter.JMeterTestManager;
+
+import java.io.File;
+import java.net.URL;
+
+/**
+ * JMeter Test cases for Device Management APIs.
+ */
+public class DeviceMgtCoreAPIJmeterTestCase {
+ private static Log log = LogFactory.getLog(DeviceMgtCoreAPIJmeterTestCase.class);
+
+ @Test(description = "Testing the device-mgt core functionalities")
+ public void permutationTest() throws AutomationFrameworkException {
+ URL url = Thread.currentThread().getContextClassLoader().getResource(
+ "jmeter-scripts" + File.separator + "DeviceMgtCoreTestPlan.jmx");
+ JMeterTest script = new JMeterTest(new File(url.getPath()));
+ JMeterTestManager manager = new JMeterTestManager();
+ log.info("Running permutation test using jmeter scripts");
+ manager.runTest(script);
+ }
+}
diff --git a/integration-test/iot-community/src/test/resources/automation.xml b/integration-test/iot-community/src/test/resources/automation.xml
new file mode 100644
index 0000000..8d21bb5
--- /dev/null
+++ b/integration-test/iot-community/src/test/resources/automation.xml
@@ -0,0 +1,225 @@
+
+
+
+
+
+
+
+
+ 100000
+
+ standalone
+
+ false
+
+ false
+
+
+
+
+
+
+ http://10.100.2.51:4444/wd/hub/
+
+
+
+ firefox
+
+ /home/test/name/webDriver
+
+
+
+
+
+
+ jdbc:h2:testDB
+ wso2carbon
+ wso2carbon
+ org.h2.Driver
+
+
+ jdbc:h2:testDB
+ wso2carbon
+ wso2carbon
+ org.h2.Driver
+
+
+
+
+
+
+ keystores/products/wso2carbon.jks
+
+ JKS
+
+ wso2carbon
+
+ wso2carbon
+
+ wso2carbon
+
+
+
+
+ client-truststore.jks
+
+ JKS
+
+ wso2carbon
+
+
+
+
+
+ https://wso2.org/repo
+ file:///home/krishantha/test
+
+
+
+
+
+
+
+
+
+ admin
+ admin
+
+
+
+
+ testuser11
+ testuser11
+
+
+ testuser21
+ testuser21
+
+
+
+
+
+
+
+
+ admin
+ admin
+
+
+
+
+ testuser11
+ testuser11
+
+
+ testuser21
+ testuser21
+
+
+
+
+
+
+
+
+
+
+
+
+ localhost
+
+
+ 9763
+
+ 9443
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ org.wso2.iot.integration.common.extensions.IOTServerExtension
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integration-test/iot-community/src/test/resources/automationSchema.xsd b/integration-test/iot-community/src/test/resources/automationSchema.xsd
new file mode 100644
index 0000000..c3bd1b3
--- /dev/null
+++ b/integration-test/iot-community/src/test/resources/automationSchema.xsd
@@ -0,0 +1,565 @@
+
+
+
+
+
+
+
+
+ =================================================
+ Parameters =================================================
+ Browser type with used by framework to execute UI test, supported types
+ - chrome|firefox|opera|ie|htmlUnit
+
+
+
+
+
+
+ Change this to edit wait time for test
+ artifact deployment
+
+
+
+
+
+ Change this to product|platform/cloud to
+ execute test on specific environment
+
+
+
+
+
+ Change this to user/tenant to execute test
+ with user mode or tenant mode
+
+
+
+
+
+ Change this to true if you want to generate
+ coverage statistics
+
+
+
+
+
+ Change this to true if you want to enable
+ framework dashboard
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Change to enable remote webDriver
+ URL of remote webDriver server
+
+
+
+
+
+
+
+
+
+
+
+
+ Type of the browser selenium tests
+ are running"
+
+
+
+
+
+
+
+ path to webDriver
+ executable - required only for
+ chrome
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ KeyStore which will be used for
+ encrypting/decrypting passwords
+ and other sensitive information.
+
+
+
+
+
+
+ Keystore file location
+
+
+
+
+
+ Keystore type (JKS/PKCS12 etc.)
+
+
+
+
+
+ Keystore password
+
+
+
+
+
+ Private Key alias
+
+
+
+
+
+ Private Key password
+
+
+
+
+
+
+
+
+
+ System wide trust-store which is used to
+ maintain the certificates of all
+ the trusted parties.
+
+
+
+
+
+
+ trust-store file location
+
+
+
+
+
+ trust-store type (JKS/PKCS12
+ etc.)
+
+
+
+
+
+ trust-store password
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/integration-test/iot-community/src/test/resources/client/modules/addressing-1.6.1-wso2v20.mar b/integration-test/iot-community/src/test/resources/client/modules/addressing-1.6.1-wso2v20.mar
new file mode 100644
index 0000000..d28b3a0
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/client/modules/addressing-1.6.1-wso2v20.mar differ
diff --git a/integration-test/iot-community/src/test/resources/filters.txt b/integration-test/iot-community/src/test/resources/filters.txt
new file mode 100644
index 0000000..8813e63
--- /dev/null
+++ b/integration-test/iot-community/src/test/resources/filters.txt
@@ -0,0 +1,23 @@
+-org.wso2.carbon.caching.core.identity.*
+-org.wso2.carbon.caching.core.permissiontree.*
+-org.wso2.carbon.caching.infinispan.*
+-org.wso2.carbon.event.core.internal.delivery.jms.*
+-org.wso2.carbon.event.core.qpid.*
+-org.wso2.carbon.registry.synchronization.*
+-*.stub*
+-*.stub_
+-*.stub_4.0.0
+-*.stub-
+-org.eclipse.*
+-*.equinox.*
+-org.wso2.carbon.user.core.*
+-samples.*
+-*.log4j*
+-*.axis2*
+-*.ui*
+-*.tenant*
+-*.stratos*
+-*.eventing*
+-*tests-transports*
+-org.wso2.carbon.mediation.statistics*
+-*startup*
diff --git a/integration-test/iot-community/src/test/resources/instrumentation.txt b/integration-test/iot-community/src/test/resources/instrumentation.txt
new file mode 100644
index 0000000..4d643d7
--- /dev/null
+++ b/integration-test/iot-community/src/test/resources/instrumentation.txt
@@ -0,0 +1,8 @@
+io.entgra.power.meter.logger_
+io.entgra.meter.request_
+io.entgra.device.mgt.plugin.power.meter_
+io.entgra.tableau.integration_
+io.entgra.iot.ami.communication.adaptor_
+io.entgra.iot.dlms_
+io.entgra.iot.iec_
+io.entgra.iot.sms_
\ No newline at end of file
diff --git a/integration-test/iot-community/src/test/resources/jmeter-scripts/DeviceMgtCoreTestPlan.jmx b/integration-test/iot-community/src/test/resources/jmeter-scripts/DeviceMgtCoreTestPlan.jmx
new file mode 100644
index 0000000..b6bdd88
--- /dev/null
+++ b/integration-test/iot-community/src/test/resources/jmeter-scripts/DeviceMgtCoreTestPlan.jmx
@@ -0,0 +1,788 @@
+
+
+
+
+
+ false
+ true
+
+
+
+ ServerHost
+ localhost
+ =
+
+
+ GatewayHost
+ localhost
+ =
+
+
+ GatewayPort
+ 8243
+ =
+
+
+ ServerPort
+ 9443
+ =
+
+
+ user
+ admin
+ =
+
+
+ password
+ admin
+ =
+
+
+
+
+
+
+
+ continue
+
+ false
+ 1
+
+ 1
+ 1
+ 1644164770000
+ 1644164770000
+ false
+ 60
+ 60
+ true
+
+
+
+ false
+
+
+
+ true
+
+
+
+ false
+ {
+ "applicationName": "admin",
+ "tags": ["device_management", "transport_management"]
+}
+ =
+
+
+
+ ${GatewayHost}
+ ${GatewayPort}
+ https
+
+ /api-application-registration/register
+ POST
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ https://${GatewayHost}:${GatewayPort}/api-application-registration/register
+ admin
+ admin
+
+
+
+
+ false
+
+
+
+
+
+ content-type
+ application/json
+
+
+
+
+
+ false
+ ClientId
+ "client_id":"(.+?)"}
+ $1$
+
+
+ expires_in
+
+
+
+ false
+ ClientSecret
+ {"client_secret":"(.+?)"
+ $1$
+
+
+
+
+
+
+
+
+
+
+ false
+ grant_type
+ password
+ =
+ true
+ application/x-www-form-urlencoded
+
+
+ false
+ default perm:app:review:view perm:app:review:update perm:app:publisher:view perm:app:publisher:update perm:app:store:view perm:app:subscription:install perm:app:subscription:uninstall perm:admin:app:review:update perm:admin:app:review:view perm:admin:app:publisher:update perm:admin:app:review:update perm:admin:app:subscription:view perm:device-types:types perm:enterprise:modify perm:enterprise:view perm:android-work:customer perm:android-work:admin perm:application-command:modify perm:sign-csr perm:admin:devices:view perm:admin:topics:view perm:roles:add perm:roles:add-users perm:roles:update perm:roles:permissions perm:roles:details perm:roles:view perm:roles:create-combined-role perm:roles:delete perm:dashboard:vulnerabilities perm:dashboard:non-compliant-count perm:dashboard:non-compliant perm:dashboard:by-groups perm:dashboard:device-counts perm:dashboard:feature-non-compliant perm:dashboard:count-overview perm:dashboard:filtered-count perm:dashboard:details perm:get-activity perm:devices:delete perm:devices:applications perm:devices:effective-policy perm:devices:compliance-data perm:devices:features perm:devices:operations perm:devices:search perm:devices:details perm:devices:update perm:devices:view perm:view-configuration perm:manage-configuration perm:policies:remove perm:policies:priorities perm:policies:deactivate perm:policies:get-policy-details perm:policies:manage perm:policies:activate perm:policies:update perm:policies:changes perm:policies:get-details perm:users:add perm:users:details perm:users:count perm:users:delete perm:users:roles perm:users:user-details perm:users:credentials perm:users:search perm:users:is-exist perm:users:update perm:users:send-invitation perm:admin-users:view perm:admin:devices:update-enrollment perm:groups:devices perm:groups:update perm:groups:add perm:groups:device perm:groups:devices-count perm:groups:remove perm:groups:groups perm:groups:groups-view perm:groups:share perm:groups:count perm:groups:roles perm:groups:devices-remove perm:groups:devices-add perm:groups:assign perm:device-types:configs perm:device-types:features perm:device-types:types perm:applications:install perm:applications:uninstall perm:admin-groups:count perm:admin-groups:view perm:admin-groups:add perm:notifications:mark-checked perm:notifications:view perm:admin:certificates:delete perm:admin:certificates:details perm:admin:certificates:view perm:admin:certificates:add perm:admin:certificates:verify perm:admin perm:devicetype:deployment perm:admin:device-type perm:admin:device-type:view perm:admin:device-type:configs perm:device:enroll perm:geo-service:analytics-view perm:geo-service:alerts-manage appm:read perm:devices:permanent-delete perm:android:manage-configuration perm:android:view-configuration perm:user:permission-view perm:ios:view-configuration perm:ios:manage-configuration perm:ios:dep-view perm:ios:dep-add perm:windows:view-configuration perm:windows:manage-configuration perm:android:lock-devices perm:android:unlock-devices perm:android:location perm:android:clear-password perm:android:control-camera perm:android:enterprise-wipe perm:android:wipe perm:android:ring perm:android:applications perm:android:reboot perm:android:change-LockTask perm:android:mute perm:android:configure-display-message perm:android:send-app-restrictions perm:android:file-transfer perm:android:set-webclip perm:android:set-password-policy perm:android:change-lock-code perm:android:upgrade-firmware perm:android:send-notification perm:geo-service:geo-fence perm:metadata:view perm:metadata:create perm:metadata:update perm:sms-handler:view-configuration perm:com-module:admin:configure perm:com-module:admin:enrol perm:com-module:admin:status-update perm:com-module:view perm:rmr:admin:add-schedule perm:rmr:admin:assign-meter perm:rmr:admin:attach-com perm:rmr:admin:billing-registers-retrieve perm:rmr:admin:status-retrieve perm:rmr:admin:delete-schedule perm:rmr:admin:detach-com perm:rmr:admin:detach-meter perm:rmr:admin:enrol perm:rmr:admin:load-profile-retrieve perm:rmr:admin:remote-disconnection perm:rmr:admin:replace-meter perm:rmr:admin:self-test perm:rmr:admin:time-sync perm:rmr:admin:update-meter-mode perm:rmr:admin:update-meter-state perm:rmr:admin:update-schedule perm:rmr:admin:view-meter-reading perm:rmr:admin:view-schedule perm:rmr:device:assign perm:rmr:device:enrol perm:rmr:device:install perm:rmr:device:unassign perm:rmr:device:validate perm:rmr:device:view perm:rmr:sms perm:sim:admin:enrol perm:sim:admin:permanent-delete perm:sim:admin:update perm:sim:view perm:power-meter-dlms:event-mgt perm:rmr:admin:upload-credentials perm:rmr:admin:remote-configure perm:rmr:view-configuration perm:rmr:manage-configuration perm:com-module:types-view perm:com-module:signal-view perm:com-module:admin:attach-sim perm:com-module:admin:detach-sim perm:rmr:device:report-event perm:meter-allocation:view perm:meter-allocation:create perm:meter-allocation:cancel perm:meter-allocation:first-review perm:meter-allocation:second-review perm:meter-allocation:assign perm:meter-allocation:dispatch perm:meter-allocation:receive perm:meter-request:admin:update perm:rmr:admin:update-customer-assign perm:rmr:device:view-install-data perm:rmr:self-test perm:rmr:user:branches perm:meter-return:view perm:meter-return:create perm:meter-return:assign-meters perm:meter-return:cancel perm:meter-return:assign perm:meter-return:dispatch perm:meter-return:receive perm:rmr:admin:branch-overview perm:rmr:admin:branch-assignment perm:rmr:admin:reachability-overview perm:rmr:admin:signal-reception perm:rmr:admin:customer-overview perm:rmr:admin:customer-locations perm:rmr:admin:system-dashboard perm:rmr:admin:meter-inventory perm:rmr:admin:meter-reachability-report perm:sim:types:view perm:rmr:device:types:view perm:com-allocation:view perm:com-allocation:create perm:com-allocation:cancel perm:com-allocation:first-review perm:com-allocation:second-review perm:com-allocation:reject perm:com-allocation:assign perm:com-allocation:dispatch perm:com-allocation:receive perm:com-allocation:add-device perm:com-return-request:view perm:com-return-request:create perm:com-return-request:cancel perm:com-return-request:assign perm:com-return-request:dispatch perm:com-return-request:receive perm:field-test-request:view perm:field-test-request:create perm:field-test-request:cancel perm:field-test-request:assign perm:field-test-request:dispatch perm:field-test-request:complete perm:load-profile-request:view perm:load-profile-request:create perm:load-profile-request:cancel perm:load-profile-request:execute perm:readout-request:view perm:readout-request:create perm:readout-request:cancel perm:readout-request:execute perm:disconnection-request:view perm:disconnection-request:create perm:disconnection-request:cancel perm:disconnection-request:execute perm:disconnection-request:first-review perm:disconnection-request:reject perm:disconnection-request:assign perm:reconnection-request:view perm:reconnection-request:create perm:reconnection-request:cancel perm:reconnection-request:execute perm:reconnection-request:first-review perm:reconnection-request:reject perm:reconnection-request:assign perm:rmr:admin:meter-user perm:rmr:admin:delete-credential perm:meter-return:create-without-devices perm:com-return-request:assign-com perm:com-return-request:create-without-devices perm:rmr:admin:remote-reconnection perm:rmr:admin:reports:view-daily perm:rmr:admin:reports:view-weekly perm:rmr:admin:reports:view-monthly perm:sim:communication:activate perm:sim:communication:deactivate perm:rmr:admin:meter-operations-view perm:rmr:admin:billing-registers-retrieve-individual perm:rmr:admin:self-test-individual perm:rmr:admin:load-profile-retrieve-individual perm:rmr:admin:test-history perm:rmr:admin:reachability-overview-include-branch perm:rmr:admin:meter-reachability-report-include-branch perm:meter-allocation:branch-cancel perm:com-allocation:branch-cancel perm:rmr:admin:assign-branch perm:rmr:admin:detach-branch perm:com-module:admin:assign-branch perm:com-module:admin:detach-branch perm:rmr:admin:replace-com perm:rmr:admin:replace-sim perm:rmr:admin:device:view
+ =
+ true
+ scope
+ application/x-www-form-urlencoded
+
+
+ false
+ admin
+ =
+ true
+ username
+ application/x-www-form-urlencoded
+
+
+ false
+ admin
+ =
+ true
+ password
+ application/x-www-form-urlencoded
+
+
+
+ ${GatewayHost}
+ ${GatewayPort}
+ https
+
+ /token
+ POST
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ https://${GatewayHost}:${GatewayPort}/token
+ ${ClientId}
+ ${ClientSecret}
+
+
+
+
+ false
+
+
+
+
+
+ content-type
+ application/x-www-form-urlencoded
+
+
+
+
+
+ false
+ Token
+ {"access_token":"(.+?)"
+ $1$
+
+
+
+
+
+
+ false
+ expires_in
+ "expires_in":(.+?)}
+ $1$
+
+
+ expires_in
+
+
+
+ true
+
+
+ var expires_in = vars.get("expires_in") * 1000;
+log.info("zzzzzzzzzzzzzzz Expire in: " + expires_in);
+expires_in += Date.now();
+vars.put("exp", expires_in);
+log.info("zzzzzzzzzzzzzzz Expire on: " + expires_in);
+var token = vars.get("Token");
+log.info("zzzzzzzzzzzzzzz Token: " + token);
+props.put("Token", token);
+ javascript
+
+
+
+
+
+
+ continue
+
+ false
+ 1
+
+ 1
+ 1
+ 1644164949000
+ 1644164949000
+ false
+
+
+ true
+
+
+
+
+
+ vars.put("Token", props.get("Token"));
+ javascript
+ true
+
+
+
+ false
+ false
+
+
+
+ true
+
+
+
+ false
+ {
+ "id": 0,
+ "name": "string",
+ "type": "string",
+ "description": "string",
+ "deviceIdentifier": "string",
+ "enrolmentInfo": {
+ "id": 0,
+ "dateOfEnrolment": 0,
+ "dateOfLastUpdate": 0,
+ "lastBilledDate": 0,
+ "ownership": "BYOD",
+ "status": "CREATED",
+ "owner": "string",
+ "transferred": false
+ },
+ "features": [
+ {
+ "id": 0,
+ "code": "string",
+ "name": "string",
+ "description": "string",
+ "type": "string",
+ "hidden": false,
+ "deviceType": "string",
+ "metadataEntries": [
+ {
+ "id": 0,
+ "name": "string",
+ "value": {}
+ }
+ ]
+ }
+ ],
+ "properties": [
+ {
+ "name": "string",
+ "value": "string"
+ }
+ ],
+ "deviceInfo": {
+ "deviceModel": "string",
+ "vendor": "string",
+ "osVersion": "string",
+ "osBuildDate": "string",
+ "batteryLevel": 0,
+ "internalTotalMemory": 0,
+ "internalAvailableMemory": 0,
+ "externalTotalMemory": 0,
+ "externalAvailableMemory": 0,
+ "operator": "string",
+ "connectionType": "string",
+ "mobileSignalStrength": 0,
+ "ssid": "string",
+ "cpuUsage": 0,
+ "totalRAMMemory": 0,
+ "availableRAMMemory": 0,
+ "pluggedIn": false,
+ "updatedTime": "2023-02-15T09:56:04.735Z",
+ "location": {
+ "deviceId": 0,
+ "deviceIdentifier": {
+ "id": "string",
+ "type": "string"
+ },
+ "latitude": 0,
+ "longitude": 0,
+ "street1": "string",
+ "street2": "string",
+ "city": "string",
+ "state": "string",
+ "zip": "string",
+ "country": "string",
+ "updatedTime": "2023-02-15T09:56:04.735Z",
+ "altitude": 0,
+ "speed": 0,
+ "bearing": 0,
+ "distance": 0
+ },
+ "deviceDetailsMap": {
+ "additionalProp1": "string",
+ "additionalProp2": "string",
+ "additionalProp3": "string"
+ }
+ },
+ "applications": [
+ {
+ "id": 0,
+ "platform": "string",
+ "category": "string",
+ "name": "string",
+ "locationUrl": "string",
+ "imageUrl": "string",
+ "version": "string",
+ "type": "string",
+ "appProperties": {
+ "additionalProp1": {},
+ "additionalProp2": {},
+ "additionalProp3": {}
+ },
+ "applicationIdentifier": "string",
+ "memoryUsage": 0,
+ "hourlyUsage": 0,
+ "dailyUsage": 0,
+ "applicationEvents": [
+ {
+ "eventData": "string",
+ "timestamp": 0
+ }
+ ],
+ "active": false
+ }
+ ],
+ "cost": 0,
+ "daysUsed": 0,
+ "deviceStatusInfo": [
+ {
+ "status": "CREATED",
+ "updateTime": "2023-02-15T09:56:04.736Z",
+ "enrolmentId": 0,
+ "deviceId": 0,
+ "changedBy": "string"
+ }
+ ],
+ "historySnapshot": {
+ "fullSnapshot": {
+ "locationHistorySnapshots": [
+ [
+ {
+ "deviceId": 0,
+ "deviceIdentifier": {
+ "id": "string",
+ "type": "string"
+ },
+ "latitude": 0,
+ "longitude": 0,
+ "street1": "string",
+ "street2": "string",
+ "city": "string",
+ "state": "string",
+ "zip": "string",
+ "country": "string",
+ "updatedTime": "2023-02-15T09:56:04.736Z",
+ "altitude": 0,
+ "speed": 0,
+ "bearing": 0,
+ "distance": 0,
+ "geoHash": "string",
+ "tenantId": 0,
+ "owner": "string"
+ }
+ ]
+ ]
+ },
+ "pathSnapshot": [
+ {}
+ ]
+ }
+}
+ =
+
+
+
+ ${GatewayHost}
+ ${GatewayPort}
+ https
+
+ /api/device-mgt//v1.0/device/agent/enroll
+ POST
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ Authorization
+ Bearer ${Token}
+
+
+ Content-Type
+ application/json
+
+
+
+
+
+
+ 200
+
+
+
+
+ Assertion.response_code
+ false
+ 8
+
+
+
+
+
+
+
+ ${GatewayHost}
+ ${GatewayPort}
+ https
+
+ /api/device-mgt/v1.0/devices
+ GET
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ Authorization
+ Bearer ${Token}
+
+
+
+
+
+
+ 200
+
+
+
+ Assertion.response_code
+ false
+ 8
+
+
+
+
+
+
+
+ ${GatewayHost}
+ ${GatewayPort}
+ https
+
+ /api/device-mgt/v1.0//devices/type/any/id/{id}
+ GET
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ Authorization
+ Bearer ${Token}
+
+
+
+
+
+
+ 200
+
+
+
+ Assertion.response_code
+ false
+ 8
+
+
+
+
+
+
+
+ ${GatewayHost}
+ ${GatewayPort}
+ https
+
+ /api/device-mgt/v1.0//devices/type/any/id/{id}
+ GET
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ Authorization
+ Bearer ${Token}
+
+
+
+
+
+
+ 200
+
+
+
+ Assertion.response_code
+ false
+ 8
+
+
+
+
+ true
+
+
+
+ false
+ {
+ "properties": {
+ "additionalProp1": "string",
+ "additionalProp2": "string",
+ "additionalProp3": "string"
+ }
+}
+ =
+
+
+
+ ${GatewayHost}
+ ${GatewayPort}
+ https
+
+ /api/device-mgt//v1.0/devices/query-devices
+ POST
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ Authorization
+ Bearer ${Token}
+
+
+ Content-Type
+ application/json
+
+
+
+
+
+
+ 200
+
+
+
+
+ Assertion.response_code
+ false
+ 8
+
+
+
+
+ true
+
+
+
+ false
+ {
+ "conditions": [
+ {
+ "key": "string",
+ "value": "string",
+ "operator": "string",
+ "state": "AND"
+ }
+ ]
+}
+ =
+
+
+
+ ${GatewayHost}
+ ${GatewayPort}
+ https
+
+ /api/device-mgt//v1.0/devices/query-devices
+ POST
+ true
+ false
+ true
+ false
+
+
+
+
+
+
+
+
+ Authorization
+ Bearer ${Token}
+
+
+ Content-Type
+ application/json
+
+
+
+
+
+
+ 200
+
+
+
+
+ Assertion.response_code
+ false
+ 8
+
+
+
+
+
+
+ continue
+
+ false
+ 1
+
+ 1
+ 1
+ false
+
+
+ true
+ 1669634319000
+ 1669634319000
+
+
+
+
+
+ vars.put("Token", props.get("Token"));
+ javascript
+ true
+
+
+
+
+ false
+
+ saveConfig
+
+
+ true
+ true
+ true
+
+ true
+ true
+ true
+ true
+ false
+ true
+ true
+ false
+ false
+ true
+ false
+ false
+ false
+ false
+ false
+ 0
+ true
+
+
+
+
+
+
+
+
diff --git a/integration-test/iot-community/src/test/resources/keystores/products/client-truststore.jks b/integration-test/iot-community/src/test/resources/keystores/products/client-truststore.jks
new file mode 100644
index 0000000..71adf40
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/products/client-truststore.jks differ
diff --git a/integration-test/iot-community/src/test/resources/keystores/products/client-truststores.jks b/integration-test/iot-community/src/test/resources/keystores/products/client-truststores.jks
new file mode 100644
index 0000000..c477431
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/products/client-truststores.jks differ
diff --git a/integration-test/iot-community/src/test/resources/keystores/products/wso2carbon.jks b/integration-test/iot-community/src/test/resources/keystores/products/wso2carbon.jks
new file mode 100644
index 0000000..357b9db
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/products/wso2carbon.jks differ
diff --git a/integration-test/iot-community/src/test/resources/keystores/products/wso2certs.jks b/integration-test/iot-community/src/test/resources/keystores/products/wso2certs.jks
new file mode 100644
index 0000000..c1e9ace
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/products/wso2certs.jks differ
diff --git a/integration-test/iot-community/src/test/resources/keystores/products/wso2mdm.jks b/integration-test/iot-community/src/test/resources/keystores/products/wso2mdm.jks
new file mode 100644
index 0000000..66b68ea
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/products/wso2mdm.jks differ
diff --git a/integration-test/iot-community/src/test/resources/keystores/stratos/client-truststore.jks b/integration-test/iot-community/src/test/resources/keystores/stratos/client-truststore.jks
new file mode 100644
index 0000000..71adf40
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/stratos/client-truststore.jks differ
diff --git a/integration-test/iot-community/src/test/resources/keystores/stratos/client-truststores.jks b/integration-test/iot-community/src/test/resources/keystores/stratos/client-truststores.jks
new file mode 100644
index 0000000..c477431
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/stratos/client-truststores.jks differ
diff --git a/integration-test/iot-community/src/test/resources/keystores/stratos/wso2carbon.jks b/integration-test/iot-community/src/test/resources/keystores/stratos/wso2carbon.jks
new file mode 100644
index 0000000..357b9db
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/stratos/wso2carbon.jks differ
diff --git a/integration-test/iot-community/src/test/resources/keystores/stratos/wso2certs.jks b/integration-test/iot-community/src/test/resources/keystores/stratos/wso2certs.jks
new file mode 100644
index 0000000..c1e9ace
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/stratos/wso2certs.jks differ
diff --git a/integration-test/iot-community/src/test/resources/keystores/stratos/wso2emm.jks b/integration-test/iot-community/src/test/resources/keystores/stratos/wso2emm.jks
new file mode 100644
index 0000000..ae5670b
Binary files /dev/null and b/integration-test/iot-community/src/test/resources/keystores/stratos/wso2emm.jks differ
diff --git a/integration-test/iot-community/src/test/resources/log4j.properties b/integration-test/iot-community/src/test/resources/log4j.properties
new file mode 100644
index 0000000..3044bf0
--- /dev/null
+++ b/integration-test/iot-community/src/test/resources/log4j.properties
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2018 - 2020 Entgra (Pvt) Ltd, Inc - All Rights Reserved.
+#
+# Unauthorised copying/redistribution of this file, via any medium is strictly prohibited.
+#
+# Licensed under the Entgra Commercial License, Version 1.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# https://entgra.io/licenses/entgra-commercial/1.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.
+#
+
+log4j.rootLogger=INFO, console, Default
+
+log4j.logger.org.wso2=INFO
+log4j.logger.org.apache.axis2.deployment.ModuleDeployer=ERROR
+#Automation file apender
+log4j.appender.Default=org.apache.log4j.RollingFileAppender
+log4j.appender.Default.File=logs/automation.log
+log4j.appender.Default.Append=true
+log4j.appender.Default.MaxFileSize=10MB
+log4j.appender.Default.MaxBackupIndex=10
+log4j.appender.Default.layout=org.apache.log4j.PatternLayout
+log4j.appender.Default.layout.ConversionPattern=%d{ISO8601} %-5p [%c] - %m%n
+
+
+#Automation console apender
+log4j.appender.console=org.apache.log4j.ConsoleAppender
+log4j.appender.console.layout=org.apache.log4j.PatternLayout
+log4j.appender.console.layout.ConversionPattern=%-5p [%c] - %m%n
diff --git a/integration-test/iot-community/src/test/resources/testng-server-mgt.xml b/integration-test/iot-community/src/test/resources/testng-server-mgt.xml
new file mode 100644
index 0000000..559bcaa
--- /dev/null
+++ b/integration-test/iot-community/src/test/resources/testng-server-mgt.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/integration-test/iot-community/src/test/resources/testng.xml b/integration-test/iot-community/src/test/resources/testng.xml
new file mode 100644
index 0000000..5cde643
--- /dev/null
+++ b/integration-test/iot-community/src/test/resources/testng.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/integration-test/pom.xml b/integration-test/pom.xml
new file mode 100644
index 0000000..7be384a
--- /dev/null
+++ b/integration-test/pom.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+ io.entgra.product.community
+ product-parent
+ 5.1.3-SNAPSHOT
+ ../pom.xml
+
+
+ 4.0.0
+ integration-test
+ pom
+ Entgra IoT - Integration Tests
+
+
+ iot-community
+
+
+
diff --git a/pom.xml b/pom.xml
index 24a6301..46b6e56 100644
--- a/pom.xml
+++ b/pom.xml
@@ -38,6 +38,7 @@
iot-core
p2-profile
distribution
+ integration-test
@@ -101,6 +102,255 @@
entgra-iot-core
${project.version}
+
+ org.wso2.carbon.automation
+ org.wso2.carbon.automation.engine
+ ${platform.integration.utils.version}
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ test
+
+
+ org.wso2.carbon.automation
+ org.wso2.carbon.automation.test.utils
+ ${test.framework.version}
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ test
+
+
+ org.wso2.carbon.automationutils
+ org.wso2.carbon.integration.common.utils
+ ${test.framework.version}
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ test
+
+
+ org.wso2.carbon.automation
+ org.wso2.carbon.automation.extensions
+ ${platform.integration.utils.version}
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ org.apache.ftpserver
+ ftpserver-core
+
+
+ org.apache.activemq
+ activemq-all
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-logging-juli
+
+
+ org.springframework.ws.wso2
+ spring.framework
+
+
+ org.apache.cxf
+ cxf-rt-frontend-jaxrs
+
+
+ org.seleniumhq.selenium
+ selenium-java
+
+
+ com.opera
+ operadriver
+
+
+ com.saucelabs.selenium
+ sauce-ondemand-driver
+
+
+ com.saucelabs.selenium
+ selenium-client-factory
+
+
+ org.apache.axis2.wso2
+ axis2
+
+
+ org.apache.ws.commons.axiom.wso2
+ axiom
+
+
+ net.lingala.zip4j
+ zip4j
+
+
+ test
+
+
+ org.wso2.carbon.automationutils
+ org.wso2.carbon.integration.common.extensions
+ ${automation.utils.version}
+ test
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ org.apache.ftpserver
+ ftpserver-core
+
+
+ org.apache.activemq
+ activemq-all
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-logging-juli
+
+
+ org.springframework.ws.wso2
+ spring.framework
+
+
+ org.apache.cxf
+ cxf-rt-frontend-jaxrs
+
+
+ org.seleniumhq.selenium
+ selenium-java
+
+
+ com.opera
+ operadriver
+
+
+ org.apache.jmeter
+ ApacheJMeter_core
+
+
+ com.saucelabs.selenium
+ sauce-ondemand-driver
+
+
+ com.saucelabs.selenium
+ selenium-client-factory
+
+
+ org.apache.axis2.wso2
+ axis2
+
+
+ org.apache.ws.commons.axiom.wso2
+ axiom
+
+
+ net.lingala.zip4j
+ zip4j
+
+
+
+
+ org.wso2.carbon.automationutils
+ org.wso2.carbon.integration.common.admin.client
+ ${automation.utils.version}
+ test
+
+
+ org.slf4j
+ slf4j-api
+
+
+ org.apache.httpcomponents
+ httpclient
+
+
+ org.apache.httpcomponents
+ httpcore
+
+
+ org.wso2.carbon.commons
+ org.wso2.carbon.user.mgt.stub
+
+
+ org.wso2.carbon.commons
+ org.wso2.carbon.ndatasource.stub
+
+
+ org.wso2.carbon.commons
+ org.wso2.carbon.authenticator.stub
+
+
+ org.wso2.carbon
+ org.wso2.carbon.utils
+
+
+
+
+ com.google.code.gson
+ gson
+ ${google.gson.version}
+
@@ -123,6 +373,11 @@
6.0.13-SNAPSHOT
+
+ 2.8.5
+ 4.4.3
+ 4.4.2
+ 4.4.2