Merge branch 'master' of ssh://repository.entgra.net:222/community/device-mgt-core

pull/341/head
commit 4417e9c814

@ -187,6 +187,7 @@
io.entgra.device.mgt.core.apimgt.webapp.publisher.lifecycle.util, io.entgra.device.mgt.core.apimgt.webapp.publisher.lifecycle.util,
org.wso2.carbon.base;version="1.0", org.wso2.carbon.base;version="1.0",
org.wso2.carbon.context;version="4.6", org.wso2.carbon.context;version="4.6",
org.wso2.carbon;version="4.6",
org.wso2.carbon.core;version="4.6", org.wso2.carbon.core;version="4.6",
org.wso2.carbon.core.util;version="4.6", org.wso2.carbon.core.util;version="4.6",
org.wso2.carbon.registry.core.service;version="1.0", org.wso2.carbon.registry.core.service;version="1.0",
@ -195,7 +196,6 @@
org.wso2.carbon.user.core.tenant;version="4.6", org.wso2.carbon.user.core.tenant;version="4.6",
org.wso2.carbon.utils;version="4.6", org.wso2.carbon.utils;version="4.6",
org.wso2.carbon.utils.multitenancy;version="4.6", org.wso2.carbon.utils.multitenancy;version="4.6",
org.wso2.carbon.apimgt.impl.definitions,
org.apache.commons.lang, org.apache.commons.lang,
org.json org.json
</Import-Package> </Import-Package>

@ -38,23 +38,24 @@ import io.entgra.device.mgt.core.apimgt.webapp.publisher.config.WebappPublisherC
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiScope; import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiScope;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiUriTemplate; import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiUriTemplate;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.exception.APIManagerPublisherException; import io.entgra.device.mgt.core.apimgt.webapp.publisher.exception.APIManagerPublisherException;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.internal.APIPublisherDataHolder;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.json.JSONArray; import org.json.JSONArray;
import org.json.JSONObject; import org.json.JSONObject;
import org.wso2.carbon.CarbonConstants;
import org.wso2.carbon.apimgt.api.APIManagementException; import org.wso2.carbon.apimgt.api.APIManagementException;
import org.wso2.carbon.apimgt.api.APIProvider; import org.wso2.carbon.apimgt.api.APIProvider;
import org.wso2.carbon.apimgt.api.model.APIIdentifier; import org.wso2.carbon.apimgt.api.model.APIIdentifier;
import org.wso2.carbon.apimgt.impl.APIConstants; import org.wso2.carbon.apimgt.impl.APIConstants;
import org.wso2.carbon.apimgt.impl.APIManagerFactory; import org.wso2.carbon.apimgt.impl.APIManagerFactory;
import org.wso2.carbon.apimgt.impl.utils.APIUtil; import org.wso2.carbon.apimgt.impl.utils.APIUtil;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.config.WebappPublisherConfig;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiScope;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.dto.ApiUriTemplate;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.exception.APIManagerPublisherException;
import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.user.api.AuthorizationManager;
import org.wso2.carbon.user.api.Permission;
import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.tenant.Tenant; import org.wso2.carbon.user.core.tenant.Tenant;
import org.wso2.carbon.user.core.tenant.TenantSearchResult; import org.wso2.carbon.user.core.tenant.TenantSearchResult;
@ -454,6 +455,7 @@ public class APIPublisherServiceImpl implements APIPublisherService {
log.error(errorMsg, e); log.error(errorMsg, e);
throw new APIManagerPublisherException(e); throw new APIManagerPublisherException(e);
} }
UserStoreManager userStoreManager;
try { try {
for (String tenantDomain : tenants) { for (String tenantDomain : tenants) {
@ -466,20 +468,40 @@ public class APIPublisherServiceImpl implements APIPublisherService {
String fileName = String fileName =
CarbonUtils.getCarbonConfigDirPath() + File.separator + "etc" CarbonUtils.getCarbonConfigDirPath() + File.separator + "etc"
+ File.separator + tenantDomain + ".csv"; + File.separator + tenantDomain + ".csv";
try {
userStoreManager = APIPublisherDataHolder.getInstance().getUserStoreManager();
} catch (UserStoreException e) {
log.error("Unable to retrieve user store manager for tenant: " + tenantDomain);
return;
}
if (Files.exists(Paths.get(fileName))) { if (Files.exists(Paths.get(fileName))) {
BufferedReader br = new BufferedReader(new FileReader(fileName)); BufferedReader br = new BufferedReader(new FileReader(fileName));
int lineNumber = 0; int lineNumber = 0;
Map<Integer, String> roles = new HashMap<>(); Map<Integer, String> roles = new HashMap<>();
String line = ""; Map<String, List<String>> rolePermissions = new HashMap<>();
String line;
String splitBy = ","; String splitBy = ",";
while ((line = br.readLine()) != null) //returns a Boolean value while ((line = br.readLine()) != null) { //returns a Boolean value
{
lineNumber++; lineNumber++;
String[] scopeMapping = line.split(splitBy); // use comma as separator String[] scopeMapping = line.split(splitBy); // use comma as separator
String role;
if (lineNumber == 1) { // skip titles if (lineNumber == 1) { // skip titles
for (int i = 0; i < scopeMapping.length; i++) { for (int i = 4; i < scopeMapping.length; i++) {
if (i > 3) { role = scopeMapping[i];
roles.put(i, scopeMapping[i]); // add roles to the map roles.put(i, role); // add roles to the map
if (!"admin".equals(role)) {
try {
if (!userStoreManager.isExistingRole(role)) {
try {
addRole(role);
} catch (UserStoreException e) {
log.error("Error occurred when adding new role: " + role, e);
}
}
} catch (UserStoreException e) {
log.error("Error occurred when checking the existence of role: " + role, e);
}
rolePermissions.put(role, new ArrayList<>());
} }
} }
continue; continue;
@ -494,11 +516,15 @@ public class APIPublisherServiceImpl implements APIPublisherService {
scopeMapping[2] != null ? StringUtils.trim(scopeMapping[2]) : StringUtils.EMPTY); scopeMapping[2] != null ? StringUtils.trim(scopeMapping[2]) : StringUtils.EMPTY);
// scope.setPermissions( // scope.setPermissions(
// scopeMapping[3] != null ? StringUtils.trim(scopeMapping[3]) : StringUtils.EMPTY); // scopeMapping[3] != null ? StringUtils.trim(scopeMapping[3]) : StringUtils.EMPTY);
String permission = scopeMapping[3] != null ? StringUtils.trim(scopeMapping[3]) : StringUtils.EMPTY;
String roleString = ""; String roleString = "";
for (int i = 4; i < scopeMapping.length; i++) { for (int i = 4; i < scopeMapping.length; i++) {
if (scopeMapping[i] != null && StringUtils.trim(scopeMapping[i]).equals("Yes")) { if (scopeMapping[i] != null && StringUtils.trim(scopeMapping[i]).equals("Yes")) {
roleString = roleString + "," + roles.get(i); roleString = roleString + "," + roles.get(i);
if (rolePermissions.containsKey(roles.get(i)) && StringUtils.isNotEmpty(permission)) {
rolePermissions.get(roles.get(i)).add(permission);
}
} }
} }
if (roleString.length() > 1) { if (roleString.length() > 1) {
@ -513,8 +539,15 @@ public class APIPublisherServiceImpl implements APIPublisherService {
if (scopeObj.getString("name").equals(scopeMapping[2] != null ? if (scopeObj.getString("name").equals(scopeMapping[2] != null ?
StringUtils.trim(scopeMapping[2]) : StringUtils.EMPTY)) { StringUtils.trim(scopeMapping[2]) : StringUtils.EMPTY)) {
scope.setId(scopeObj.getString("id")); scope.setId(scopeObj.getString("id"));
// Including already existing roles
JSONArray existingRolesArray = (JSONArray) scopeObj.get("bindings");
for (int j = 0; j < existingRolesArray.length(); j++) {
roleString = roleString + "," + existingRolesArray.get(j);
} }
} }
}
scope.setRoles(roleString);
if (publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo, scope.getKey())) { if (publisherRESTAPIServices.isSharedScopeNameExists(apiApplicationKey, accessTokenInfo, scope.getKey())) {
publisherRESTAPIServices.updateSharedScope(apiApplicationKey, accessTokenInfo, scope); publisherRESTAPIServices.updateSharedScope(apiApplicationKey, accessTokenInfo, scope);
@ -525,6 +558,13 @@ public class APIPublisherServiceImpl implements APIPublisherService {
} }
} }
} }
for (String role : rolePermissions.keySet()) {
try {
updatePermissions(role, rolePermissions.get(role));
} catch (UserStoreException e) {
log.error("Error occurred when adding permissions to role: " + role, e);
}
}
} }
} catch (IOException | DirectoryIteratorException ex) { } catch (IOException | DirectoryIteratorException ex) {
log.error("failed to read scopes from file.", ex); log.error("failed to read scopes from file.", ex);
@ -553,6 +593,28 @@ public class APIPublisherServiceImpl implements APIPublisherService {
} }
} }
private void updatePermissions(String role, List<String> permissions) throws UserStoreException {
AuthorizationManager authorizationManager = APIPublisherDataHolder.getInstance().getUserRealm()
.getAuthorizationManager();
if (log.isDebugEnabled()) {
log.debug("Updating the role '" + role + "'");
}
if (permissions != null && !permissions.isEmpty()) {
authorizationManager.clearRoleAuthorization(role);
for (String permission : permissions) {
authorizationManager.authorizeRole(role, permission, CarbonConstants.UI_PERMISSION_ACTION);
}
}
}
private void addRole(String role) throws UserStoreException {
UserStoreManager userStoreManager = APIPublisherDataHolder.getInstance().getUserStoreManager();
if (log.isDebugEnabled()) {
log.debug("Persisting the role " + role + " in the underlying user store");
}
userStoreManager.addRole(role, new String[]{"admin"}, new Permission[0]);
}
private APIInfo getAPI(APIConfig config, boolean includeScopes) { private APIInfo getAPI(APIConfig config, boolean includeScopes) {
APIInfo apiInfo = new APIInfo(); APIInfo apiInfo = new APIInfo();

@ -19,7 +19,12 @@ package io.entgra.device.mgt.core.apimgt.webapp.publisher.internal;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIConfig; import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIConfig;
import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIPublisherService; import io.entgra.device.mgt.core.apimgt.webapp.publisher.APIPublisherService;
import org.wso2.carbon.context.CarbonContext;
import org.wso2.carbon.context.PrivilegedCarbonContext;
import org.wso2.carbon.registry.core.service.RegistryService; import org.wso2.carbon.registry.core.service.RegistryService;
import org.wso2.carbon.user.api.UserRealm;
import org.wso2.carbon.user.api.UserStoreException;
import org.wso2.carbon.user.api.UserStoreManager;
import org.wso2.carbon.user.core.service.RealmService; import org.wso2.carbon.user.core.service.RealmService;
import org.wso2.carbon.user.core.tenant.TenantManager; import org.wso2.carbon.user.core.tenant.TenantManager;
import org.wso2.carbon.utils.ConfigurationContextService; import org.wso2.carbon.utils.ConfigurationContextService;
@ -79,6 +84,25 @@ public class APIPublisherDataHolder {
realmService.getTenantManager() : null); realmService.getTenantManager() : null);
} }
public UserStoreManager getUserStoreManager() throws UserStoreException {
if (realmService == null) {
String msg = "Realm service has not initialized.";
throw new IllegalStateException(msg);
}
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
return realmService.getTenantUserRealm(tenantId).getUserStoreManager();
}
public UserRealm getUserRealm() throws UserStoreException {
UserRealm realm;
if (realmService == null) {
throw new IllegalStateException("Realm service not initialized");
}
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
realm = realmService.getTenantUserRealm(tenantId);
return realm;
}
private void setTenantManager(TenantManager tenantManager) { private void setTenantManager(TenantManager tenantManager) {
this.tenantManager = tenantManager; this.tenantManager = tenantManager;
} }

@ -466,6 +466,11 @@ public interface ActivityInfoProviderService {
value = "Operation Code to filter" value = "Operation Code to filter"
) )
@QueryParam("operationCode") String operationCode, @QueryParam("operationCode") String operationCode,
@ApiParam(
name = "operationId",
value = "Operation Id to filter"
)
@QueryParam("operationId") int operationId,
@ApiParam( @ApiParam(
name = "deviceType", name = "deviceType",
value = "Device Type to filter" value = "Device Type to filter"

@ -65,6 +65,7 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.util.List; import java.util.List;
import java.util.Map;
/** /**
* Device related REST-API. This can be used to manipulated device related details. * Device related REST-API. This can be used to manipulated device related details.
@ -283,6 +284,12 @@ public interface DeviceManagementService {
required = false) required = false)
@QueryParam("serialNumber") @QueryParam("serialNumber")
String serialNumber, String serialNumber,
@ApiParam(
name = "customProperty",
value = "CustomProperty from device as a JSON encoded string.",
required = false)
@QueryParam("customProperty")
String customProperty,
@ApiParam( @ApiParam(
name = "status", name = "status",
value = "Provide the device status details, such as active or inactive.", value = "Provide the device status details, such as active or inactive.",

@ -253,6 +253,7 @@ public class ActivityProviderServiceImpl implements ActivityInfoProviderService
@QueryParam("since") String since, @QueryParam("since") String since,
@QueryParam("initiatedBy") String initiatedBy, @QueryParam("initiatedBy") String initiatedBy,
@QueryParam("operationCode") String operationCode, @QueryParam("operationCode") String operationCode,
@QueryParam("operationId") int operationId,
@QueryParam("deviceType") String deviceType, @QueryParam("deviceType") String deviceType,
@QueryParam("deviceId") List<String> deviceIds, @QueryParam("deviceId") List<String> deviceIds,
@QueryParam("type") String type, @QueryParam("type") String type,
@ -321,6 +322,9 @@ public class ActivityProviderServiceImpl implements ActivityInfoProviderService
if (operationCode != null && !operationCode.isEmpty()) { if (operationCode != null && !operationCode.isEmpty()) {
activityPaginationRequest.setOperationCode(operationCode); activityPaginationRequest.setOperationCode(operationCode);
} }
if (operationId > 0) {
activityPaginationRequest.setOperationId(operationId);
}
if (deviceType != null && !deviceType.isEmpty()) { if (deviceType != null && !deviceType.isEmpty()) {
activityPaginationRequest.setDeviceType(deviceType); activityPaginationRequest.setDeviceType(deviceType);
} }

@ -18,6 +18,7 @@
package io.entgra.device.mgt.core.device.mgt.api.jaxrs.service.impl; package io.entgra.device.mgt.core.device.mgt.api.jaxrs.service.impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.gson.Gson; import com.google.gson.Gson;
import io.entgra.device.mgt.core.application.mgt.common.ApplicationInstallResponse; import io.entgra.device.mgt.core.application.mgt.common.ApplicationInstallResponse;
import io.entgra.device.mgt.core.application.mgt.common.SubscriptionType; import io.entgra.device.mgt.core.application.mgt.common.SubscriptionType;
@ -29,7 +30,6 @@ import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.json.JSONObject;
import io.entgra.device.mgt.core.apimgt.keymgt.extension.DCRResponse; import io.entgra.device.mgt.core.apimgt.keymgt.extension.DCRResponse;
import io.entgra.device.mgt.core.apimgt.keymgt.extension.TokenRequest; import io.entgra.device.mgt.core.apimgt.keymgt.extension.TokenRequest;
import io.entgra.device.mgt.core.apimgt.keymgt.extension.TokenResponse; import io.entgra.device.mgt.core.apimgt.keymgt.extension.TokenResponse;
@ -64,7 +64,6 @@ import io.entgra.device.mgt.core.device.mgt.common.type.mgt.DeviceStatus;
import io.entgra.device.mgt.core.device.mgt.core.app.mgt.ApplicationManagementProviderService; import io.entgra.device.mgt.core.device.mgt.core.app.mgt.ApplicationManagementProviderService;
import io.entgra.device.mgt.core.device.mgt.core.config.DeviceConfigurationManager; import io.entgra.device.mgt.core.device.mgt.core.config.DeviceConfigurationManager;
import io.entgra.device.mgt.core.device.mgt.core.config.DeviceManagementConfig; import io.entgra.device.mgt.core.device.mgt.core.config.DeviceManagementConfig;
import io.entgra.device.mgt.core.device.mgt.core.dao.TrackerManagementDAOException;
import io.entgra.device.mgt.core.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; import io.entgra.device.mgt.core.device.mgt.core.device.details.mgt.DeviceDetailsMgtException;
import io.entgra.device.mgt.core.device.mgt.core.device.details.mgt.DeviceInformationManager; import io.entgra.device.mgt.core.device.mgt.core.device.details.mgt.DeviceInformationManager;
import io.entgra.device.mgt.core.device.mgt.core.dto.DeviceType; import io.entgra.device.mgt.core.device.mgt.core.dto.DeviceType;
@ -75,10 +74,7 @@ import io.entgra.device.mgt.core.device.mgt.core.search.mgt.SearchManagerService
import io.entgra.device.mgt.core.device.mgt.core.search.mgt.SearchMgtException; import io.entgra.device.mgt.core.device.mgt.core.search.mgt.SearchMgtException;
import io.entgra.device.mgt.core.device.mgt.core.service.DeviceManagementProviderService; import io.entgra.device.mgt.core.device.mgt.core.service.DeviceManagementProviderService;
import io.entgra.device.mgt.core.device.mgt.core.service.GroupManagementProviderService; import io.entgra.device.mgt.core.device.mgt.core.service.GroupManagementProviderService;
import io.entgra.device.mgt.core.device.mgt.core.traccar.api.service.DeviceAPIClientService;
import io.entgra.device.mgt.core.device.mgt.core.traccar.common.TraccarHandlerConstants;
import io.entgra.device.mgt.core.device.mgt.core.util.DeviceManagerUtil; import io.entgra.device.mgt.core.device.mgt.core.util.DeviceManagerUtil;
import io.entgra.device.mgt.core.device.mgt.core.util.HttpReportingUtil;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.*; import io.entgra.device.mgt.core.device.mgt.api.jaxrs.beans.*;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.service.api.DeviceManagementService; import io.entgra.device.mgt.core.device.mgt.api.jaxrs.service.api.DeviceManagementService;
import io.entgra.device.mgt.core.device.mgt.api.jaxrs.service.impl.util.InputValidationException; import io.entgra.device.mgt.core.device.mgt.api.jaxrs.service.impl.util.InputValidationException;
@ -98,6 +94,7 @@ import javax.validation.Valid;
import javax.validation.constraints.Size; import javax.validation.constraints.Size;
import javax.ws.rs.*; import javax.ws.rs.*;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.io.IOException;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList; import java.util.ArrayList;
@ -105,6 +102,7 @@ import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Properties; import java.util.Properties;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.Map;
@Path("/devices") @Path("/devices")
public class DeviceManagementServiceImpl implements DeviceManagementService { public class DeviceManagementServiceImpl implements DeviceManagementService {
@ -142,6 +140,7 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
@QueryParam("role") String role, @QueryParam("role") String role,
@QueryParam("ownership") String ownership, @QueryParam("ownership") String ownership,
@QueryParam("serialNumber") String serialNumber, @QueryParam("serialNumber") String serialNumber,
@QueryParam("customProperty") String customProperty,
@QueryParam("status") List<String> status, @QueryParam("status") List<String> status,
@QueryParam("groupId") int groupId, @QueryParam("groupId") int groupId,
@QueryParam("since") String since, @QueryParam("since") String since,
@ -155,6 +154,7 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} }
// RequestValidationUtil.validateSelectionCriteria(type, user, roleName, ownership, status); // RequestValidationUtil.validateSelectionCriteria(type, user, roleName, ownership, status);
final ObjectMapper objectMapper = new ObjectMapper();
RequestValidationUtil.validatePaginationParameters(offset, limit); RequestValidationUtil.validatePaginationParameters(offset, limit);
DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService(); DeviceManagementProviderService dms = DeviceMgtAPIUtils.getDeviceManagementService();
DeviceAccessAuthorizationService deviceAccessAuthorizationService = DeviceAccessAuthorizationService deviceAccessAuthorizationService =
@ -166,6 +166,22 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
if (name != null && !name.isEmpty()) { if (name != null && !name.isEmpty()) {
request.setDeviceName(name); request.setDeviceName(name);
} }
if (customProperty != null && !customProperty.isEmpty()) {
try {
Map<String, String> customProperties = objectMapper.readValue(customProperty, Map.class);
// Extract and set custom properties
for (Map.Entry<String, String> entry : customProperties.entrySet()) {
String propertyName = entry.getKey();
String propertyValue = entry.getValue();
// Add custom property to the paginationRequest object
request.addCustomProperty(propertyName, propertyValue);
}
} catch (IOException e) {
String msg = "Error occurred while converting custom property string to a Java Map";
log.error(msg);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
if (type != null && !type.isEmpty()) { if (type != null && !type.isEmpty()) {
request.setDeviceType(type); request.setDeviceType(type);
} }

@ -157,7 +157,7 @@ public class DeviceManagementServiceImplTest {
.toReturn(this.deviceAccessAuthorizationService); .toReturn(this.deviceAccessAuthorizationService);
Response response = this.deviceManagementService Response response = this.deviceManagementService
.getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 1, null, null, false, null, null, DEFAULT_STATUS_LIST, 1, null, null, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
} }
@ -177,22 +177,22 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 1, null, null, false, null,null, DEFAULT_STATUS_LIST, 1, null, null, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService response = this.deviceManagementService
.getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, null, DEFAULT_OWNERSHIP, .getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, null, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 1, null, null, false, null, null, DEFAULT_STATUS_LIST, 1, null, null, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService response = this.deviceManagementService
.getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, null, null, null, DEFAULT_OWNERSHIP, .getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, null, null, null, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 1, null, null, false, null, null, DEFAULT_STATUS_LIST, 1, null, null, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService response = this.deviceManagementService
.getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, null, null, null, DEFAULT_OWNERSHIP, .getDevices(TEST_DEVICE_NAME, TEST_DEVICE_TYPE, null, null, null, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 1, null, null, true, null, null, DEFAULT_STATUS_LIST, 1, null, null, true,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
} }
@ -306,7 +306,7 @@ public class DeviceManagementServiceImplTest {
.toReturn(this.deviceManagementProviderService); .toReturn(this.deviceManagementProviderService);
Mockito.when(deviceAccessAuthorizationService.isDeviceAdminUser()).thenReturn(true); Mockito.when(deviceAccessAuthorizationService.isDeviceAdminUser()).thenReturn(true);
deviceManagementService.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, deviceManagementService.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null,
DEFAULT_ROLE, DEFAULT_OWNERSHIP, null, DEFAULT_STATUS_LIST, 1, DEFAULT_ROLE, DEFAULT_OWNERSHIP, null,null, DEFAULT_STATUS_LIST, 1,
null, null, false, 10, 5); null, null, false, 10, 5);
} }
@ -326,11 +326,11 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP
, null, DEFAULT_STATUS_LIST, 1, null, null, false, 10, 5); , null, null, DEFAULT_STATUS_LIST, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, null, DEFAULT_USERNAME, DEFAULT_ROLE, DEFAULT_OWNERSHIP .getDevices(null, TEST_DEVICE_TYPE, null, DEFAULT_USERNAME, DEFAULT_ROLE, DEFAULT_OWNERSHIP
, null, DEFAULT_STATUS_LIST, 1, null, null, false, 10, 5); , null, null, DEFAULT_STATUS_LIST, 1, null, null, false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
} }
@ -352,7 +352,7 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, "newuser", null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, "newuser", null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 0, null, null, false, null, null, DEFAULT_STATUS_LIST, 0, null, null, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.UNAUTHORIZED.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.UNAUTHORIZED.getStatusCode());
Mockito.reset(this.deviceAccessAuthorizationService); Mockito.reset(this.deviceAccessAuthorizationService);
@ -374,17 +374,17 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 0, null, ifModifiedSince, false, null, null, DEFAULT_STATUS_LIST, 0, null, ifModifiedSince, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.NOT_MODIFIED.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.NOT_MODIFIED.getStatusCode());
response = this.deviceManagementService response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 0, null, ifModifiedSince, true, null, null, DEFAULT_STATUS_LIST, 0, null, ifModifiedSince, true,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.NOT_MODIFIED.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.NOT_MODIFIED.getStatusCode());
response = this.deviceManagementService response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 0, null, "ErrorModifiedSince", null, null, DEFAULT_STATUS_LIST, 0, null, "ErrorModifiedSince",
false, 10, 5); false, 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
} }
@ -405,17 +405,17 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 0, since, null, false, null, null,DEFAULT_STATUS_LIST, 0, since, null, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 0, since, null, true, null, null,DEFAULT_STATUS_LIST, 0, since, null, true,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode());
response = this.deviceManagementService response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 0, "ErrorSince", null, false, null, null,DEFAULT_STATUS_LIST, 0, "ErrorSince", null, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.BAD_REQUEST.getStatusCode());
} }
@ -438,7 +438,7 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 1, null, null, false, null, null, DEFAULT_STATUS_LIST, 1, null, null, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
Mockito.reset(this.deviceManagementProviderService); Mockito.reset(this.deviceManagementProviderService);
@ -461,7 +461,7 @@ public class DeviceManagementServiceImplTest {
Response response = this.deviceManagementService Response response = this.deviceManagementService
.getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP, .getDevices(null, TEST_DEVICE_TYPE, DEFAULT_USERNAME, null, DEFAULT_ROLE, DEFAULT_OWNERSHIP,
null, DEFAULT_STATUS_LIST, 1, null, null, false, null, null, DEFAULT_STATUS_LIST, 1, null, null, false,
10, 5); 10, 5);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode()); Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode());
Mockito.reset(this.deviceAccessAuthorizationService); Mockito.reset(this.deviceAccessAuthorizationService);

@ -32,6 +32,7 @@ public class ActivityPaginationRequest {
private String deviceType; private String deviceType;
private List<String> deviceIds; private List<String> deviceIds;
private String operationCode; private String operationCode;
private int operationId;
private String initiatedBy; private String initiatedBy;
private long since; private long since;
private Operation.Type type; private Operation.Type type;
@ -132,4 +133,12 @@ public class ActivityPaginationRequest {
public void setEndTimestamp(long endTimestamp) { public void setEndTimestamp(long endTimestamp) {
this.endTimestamp = endTimestamp; this.endTimestamp = endTimestamp;
} }
public int getOperationId() {
return operationId;
}
public void setOperationId(int operationId) {
this.operationId = operationId;
}
} }

@ -41,6 +41,7 @@ public class PaginationRequest {
private Date since; private Date since;
private String filter; private String filter;
private String serialNumber; private String serialNumber;
private Map<String, String> customProperty = new HashMap<>();
private Map<String, Object> property = new HashMap<>(); private Map<String, Object> property = new HashMap<>();
private List<String> statusList = new ArrayList<>(); private List<String> statusList = new ArrayList<>();
private OperationLogFilters operationLogFilters = new OperationLogFilters(); private OperationLogFilters operationLogFilters = new OperationLogFilters();
@ -115,6 +116,18 @@ public class PaginationRequest {
return ownership; return ownership;
} }
public Map<String, String> getCustomProperty() {
return customProperty;
}
public void setCustomProperty(Map<String, String> customProperty) {
this.customProperty = customProperty;
}
public void addCustomProperty(String key, String value) {
customProperty.put(key, value);
}
public void setOwnership(String ownership) { public void setOwnership(String ownership) {
this.ownership = ownership; this.ownership = ownership;
} }

@ -49,6 +49,14 @@ public class Activity {
@JsonProperty("code") @JsonProperty("code")
private String code; private String code;
@ApiModelProperty(
name = "operationId",
value = "Operation Id",
required = false,
example = "10")
@JsonProperty("operationId")
private int operationId;
@ApiModelProperty( @ApiModelProperty(
name = "type", name = "type",
value = "Activity type", value = "Activity type",
@ -122,6 +130,14 @@ public class Activity {
this.code = code; this.code = code;
} }
public int getOperationId() {
return operationId;
}
public void setOperationId(int operationId) {
this.operationId = operationId;
}
public Type getType() { public Type getType() {
return type; return type;
} }

@ -41,6 +41,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.Map;
/** /**
* This class holds the generic implementation of DeviceDAO which can be used to support ANSI db syntax. * This class holds the generic implementation of DeviceDAO which can be used to support ANSI db syntax.
@ -92,14 +93,40 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
"d.DEVICE_IDENTIFICATION, " + "d.DEVICE_IDENTIFICATION, " +
"t.NAME AS DEVICE_TYPE "; "t.NAME AS DEVICE_TYPE ";
//Filter by serial number or any Custom Property in DM_DEVICE_INFO
if (serial != null || !request.getCustomProperty().isEmpty()) {
sql = sql +
"FROM DM_DEVICE d " +
"INNER JOIN DM_DEVICE_TYPE t ON d.DEVICE_TYPE_ID = t.ID " +
"WHERE ";
if (serial != null) { if (serial != null) {
sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t, DM_DEVICE_INFO i " + sql += "EXISTS (" +
"WHERE DEVICE_TYPE_ID = t.ID " + "SELECT VALUE_FIELD " +
"AND d.ID= i.DEVICE_ID " + "FROM DM_DEVICE_INFO di " +
"AND i.KEY_FIELD = 'serial' " + "WHERE di.DEVICE_ID = d.ID " +
"AND i.VALUE_FIELD LIKE ? " + "AND di.KEY_FIELD = 'serial' " +
"AND d.TENANT_ID = ? "; "AND di.VALUE_FIELD LIKE ? ) ";
isSerialProvided = true; isSerialProvided = true;
}
if (!request.getCustomProperty().isEmpty()) {
if (serial != null) {
sql += "AND ";
}
boolean firstCondition = true;
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
if (!firstCondition) {
sql += "AND ";
}
sql += "EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di " +
"WHERE di.DEVICE_ID = d.ID " +
"AND di.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di.VALUE_FIELD LIKE ? ) ";
firstCondition = false;
}
}
sql += "AND d.TENANT_ID = ? ";
} else { } else {
sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ? "; sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ? ";
} }
@ -143,6 +170,11 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isSerialProvided) { if (isSerialProvided) {
stmt.setString(paramIdx++, "%" + serial + "%"); stmt.setString(paramIdx++, "%" + serial + "%");
} }
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
stmt.setString(paramIdx++, "%" + entry.getValue() + "%");
}
}
stmt.setInt(paramIdx++, tenantId); stmt.setInt(paramIdx++, tenantId);
if (isSinceProvided) { if (isSinceProvided) {
stmt.setTimestamp(paramIdx++, new Timestamp(since.getTime())); stmt.setTimestamp(paramIdx++, new Timestamp(since.getTime()));
@ -624,6 +656,8 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isStatusProvided = false; boolean isStatusProvided = false;
Date since = request.getSince(); Date since = request.getSince();
boolean isSinceProvided = false; boolean isSinceProvided = false;
String serial = request.getSerialNumber();
boolean isSerialProvided = false;
try { try {
Connection conn = getConnection(); Connection conn = getConnection();
@ -692,6 +726,28 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql += buildStatusQuery(statusList); sql += buildStatusQuery(statusList);
isStatusProvided = true; isStatusProvided = true;
} }
//Filter Group with serial number or any Custom Property in DM_DEVICE_INFO
if (serial != null || !request.getCustomProperty().isEmpty()) {
if (serial != null) {
sql += "AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di " +
"WHERE di.DEVICE_ID = d1.DEVICE_ID " +
"AND di.KEY_FIELD = 'serial' " +
"AND di.VALUE_FIELD LIKE ?) ";
isSerialProvided = true;
}
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
sql += "AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di2 " +
"WHERE di2.DEVICE_ID = d1.DEVICE_ID " +
"AND di2.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di2.VALUE_FIELD LIKE ?)";
}
}
}
sql = sql + " LIMIT ?,?"; sql = sql + " LIMIT ?,?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) { try (PreparedStatement stmt = conn.prepareStatement(sql)) {
@ -721,6 +777,14 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
stmt.setString(paramIdx++, status); stmt.setString(paramIdx++, status);
} }
} }
if (isSerialProvided) {
stmt.setString(paramIdx++, "%" + serial + "%");
}
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
stmt.setString(paramIdx++, "%" + entry.getValue() + "%");
}
}
stmt.setInt(paramIdx++, request.getStartIndex()); stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount()); stmt.setInt(paramIdx, request.getRowCount());
@ -1250,6 +1314,17 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
query += buildStatusQuery(status); query += buildStatusQuery(status);
isStatusProvided = true; isStatusProvided = true;
} }
// Loop through custom properties and add conditions
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
query += " AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di2 " +
"WHERE di2.DEVICE_ID = DM_DEVICE.ID " +
"AND di2.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di2.VALUE_FIELD LIKE ?)";
}
}
query = query + " LIMIT ?,?"; query = query + " LIMIT ?,?";
@ -1277,6 +1352,12 @@ public class GenericDeviceDAOImpl extends AbstractDeviceDAOImpl {
ps.setString(index++, deviceStatus); ps.setString(index++, deviceStatus);
} }
} }
// Set custom property values in the loop
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
ps.setString(index++, "%" + entry.getValue() + "%");
}
}
ps.setInt(index++, offsetValue); ps.setInt(index++, offsetValue);
ps.setInt(index, limitValue); ps.setInt(index, limitValue);

@ -42,6 +42,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.Map;
/** /**
* This class holds the generic implementation of DeviceDAO which can be used to support ANSI db syntax. * This class holds the generic implementation of DeviceDAO which can be used to support ANSI db syntax.
@ -93,14 +94,40 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
"d.DEVICE_IDENTIFICATION, " + "d.DEVICE_IDENTIFICATION, " +
"t.NAME AS DEVICE_TYPE "; "t.NAME AS DEVICE_TYPE ";
//Filter by serial number or any Custom Property in DM_DEVICE_INFO
if (serial != null || !request.getCustomProperty().isEmpty()) {
sql = sql +
"FROM DM_DEVICE d " +
"INNER JOIN DM_DEVICE_TYPE t ON d.DEVICE_TYPE_ID = t.ID " +
"WHERE ";
if (serial != null) { if (serial != null) {
sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t, DM_DEVICE_INFO i " + sql += "EXISTS (" +
"WHERE DEVICE_TYPE_ID = t.ID " + "SELECT VALUE_FIELD " +
"AND d.ID= i.DEVICE_ID " + "FROM DM_DEVICE_INFO di " +
"AND i.KEY_FIELD = 'serial' " + "WHERE di.DEVICE_ID = d.ID " +
"AND i.VALUE_FIELD LIKE ? " + "AND di.KEY_FIELD = 'serial' " +
"AND d.TENANT_ID = ? "; "AND di.VALUE_FIELD LIKE ? ) ";
isSerialProvided = true; isSerialProvided = true;
}
if (!request.getCustomProperty().isEmpty()) {
if (serial != null) {
sql += "AND ";
}
boolean firstCondition = true;
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
if (!firstCondition) {
sql += "AND ";
}
sql += "EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di " +
"WHERE di.DEVICE_ID = d.ID " +
"AND di.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di.VALUE_FIELD LIKE ? ) ";
firstCondition = false;
}
}
sql += "AND d.TENANT_ID = ? ";
} else { } else {
sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ? "; sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ? ";
} }
@ -144,6 +171,11 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isSerialProvided) { if (isSerialProvided) {
stmt.setString(paramIdx++, "%" + serial + "%"); stmt.setString(paramIdx++, "%" + serial + "%");
} }
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
stmt.setString(paramIdx++, "%" + entry.getValue() + "%");
}
}
stmt.setInt(paramIdx++, tenantId); stmt.setInt(paramIdx++, tenantId);
if (isSinceProvided) { if (isSinceProvided) {
stmt.setTimestamp(paramIdx++, new Timestamp(since.getTime())); stmt.setTimestamp(paramIdx++, new Timestamp(since.getTime()));
@ -454,6 +486,8 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isStatusProvided = false; boolean isStatusProvided = false;
Date since = request.getSince(); Date since = request.getSince();
boolean isSinceProvided = false; boolean isSinceProvided = false;
String serial = request.getSerialNumber();
boolean isSerialProvided = false;
try { try {
conn = getConnection(); conn = getConnection();
@ -522,6 +556,28 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql += buildStatusQuery(statusList); sql += buildStatusQuery(statusList);
isStatusProvided = true; isStatusProvided = true;
} }
//Filter Group with serial number or any Custom Property in DM_DEVICE_INFO
if (serial != null || !request.getCustomProperty().isEmpty()) {
if (serial != null) {
sql += "AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di " +
"WHERE di.DEVICE_ID = d1.DEVICE_ID " +
"AND di.KEY_FIELD = 'serial' " +
"AND di.VALUE_FIELD LIKE ?) ";
isSerialProvided = true;
}
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
sql += "AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di2 " +
"WHERE di2.DEVICE_ID = d1.DEVICE_ID " +
"AND di2.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di2.VALUE_FIELD LIKE ?)";
}
}
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
try (PreparedStatement stmt = conn.prepareStatement(sql)) { try (PreparedStatement stmt = conn.prepareStatement(sql)) {
@ -551,6 +607,14 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
stmt.setString(paramIdx++, status); stmt.setString(paramIdx++, status);
} }
} }
if (isSerialProvided) {
stmt.setString(paramIdx++, "%" + serial + "%");
}
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
stmt.setString(paramIdx++, "%" + entry.getValue() + "%");
}
}
stmt.setInt(paramIdx++, request.getStartIndex()); stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount()); stmt.setInt(paramIdx, request.getRowCount());
@ -1035,6 +1099,17 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
query += buildStatusQuery(status); query += buildStatusQuery(status);
isStatusProvided = true; isStatusProvided = true;
} }
// Loop through custom properties and add conditions
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
query += " AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di2 " +
"WHERE di2.DEVICE_ID = DM_DEVICE.ID " +
"AND di2.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di2.VALUE_FIELD LIKE ?)";
}
}
query = query + " ORDER BY DM_DEVICE.ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; query = query + " ORDER BY DM_DEVICE.ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
@ -1059,6 +1134,12 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
ps.setString(index++, deviceStatus); ps.setString(index++, deviceStatus);
} }
} }
// Set custom property values in the loop
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
ps.setString(index++, "%" + entry.getValue() + "%");
}
}
ps.setInt(index++, offsetValue); ps.setInt(index++, offsetValue);
ps.setInt(index, limitValue); ps.setInt(index, limitValue);

@ -41,6 +41,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.Map;
/** /**
* This class holds the generic implementation of DeviceDAO which can be used to support ANSI db syntax. * This class holds the generic implementation of DeviceDAO which can be used to support ANSI db syntax.
@ -92,14 +93,40 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
"d.DEVICE_IDENTIFICATION, " + "d.DEVICE_IDENTIFICATION, " +
"t.NAME AS DEVICE_TYPE "; "t.NAME AS DEVICE_TYPE ";
//Filter by serial number or any Custom Property in DM_DEVICE_INFO
if (serial != null || !request.getCustomProperty().isEmpty()) {
sql = sql +
"FROM DM_DEVICE d " +
"INNER JOIN DM_DEVICE_TYPE t ON d.DEVICE_TYPE_ID = t.ID " +
"WHERE ";
if (serial != null) { if (serial != null) {
sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t, DM_DEVICE_INFO i " + sql += "EXISTS (" +
"WHERE DEVICE_TYPE_ID = t.ID " + "SELECT VALUE_FIELD " +
"AND d.ID= i.DEVICE_ID " + "FROM DM_DEVICE_INFO di " +
"AND i.KEY_FIELD = 'serial' " + "WHERE di.DEVICE_ID = d.ID " +
"AND i.VALUE_FIELD LIKE ? " + "AND di.KEY_FIELD = 'serial' " +
"AND d.TENANT_ID = ? "; "AND di.VALUE_FIELD LIKE ? ) ";
isSerialProvided = true; isSerialProvided = true;
}
if (!request.getCustomProperty().isEmpty()) {
if (serial != null) {
sql += "AND ";
}
boolean firstCondition = true;
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
if (!firstCondition) {
sql += "AND ";
}
sql += "EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di " +
"WHERE di.DEVICE_ID = d.ID " +
"AND di.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di.VALUE_FIELD LIKE ? ) ";
firstCondition = false;
}
}
sql += "AND d.TENANT_ID = ? ";
} else { } else {
sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ? "; sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ? ";
} }
@ -138,6 +165,11 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isSerialProvided) { if (isSerialProvided) {
stmt.setString(paramIdx++, "%" + serial + "%"); stmt.setString(paramIdx++, "%" + serial + "%");
} }
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
stmt.setString(paramIdx++, "%" + entry.getValue() + "%");
}
}
stmt.setInt(paramIdx++, tenantId); stmt.setInt(paramIdx++, tenantId);
if (isDeviceTypeProvided) { if (isDeviceTypeProvided) {
stmt.setString(paramIdx++, deviceType); stmt.setString(paramIdx++, deviceType);
@ -435,6 +467,8 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isStatusProvided = false; boolean isStatusProvided = false;
Date since = request.getSince(); Date since = request.getSince();
boolean isSinceProvided = false; boolean isSinceProvided = false;
String serial = request.getSerialNumber();
boolean isSerialProvided = false;
try { try {
conn = getConnection(); conn = getConnection();
@ -502,6 +536,28 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql += buildStatusQuery(statusList); sql += buildStatusQuery(statusList);
isStatusProvided = true; isStatusProvided = true;
} }
//Filter Group with serial number or any Custom Property in DM_DEVICE_INFO
if (serial != null || !request.getCustomProperty().isEmpty()) {
if (serial != null) {
sql += "AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di " +
"WHERE di.DEVICE_ID = d1.DEVICE_ID " +
"AND di.KEY_FIELD = 'serial' " +
"AND di.VALUE_FIELD LIKE ?) ";
isSerialProvided = true;
}
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
sql += "AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di2 " +
"WHERE di2.DEVICE_ID = d1.DEVICE_ID " +
"AND di2.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di2.VALUE_FIELD LIKE ?)";
}
}
}
sql = sql + " LIMIT ? OFFSET ?"; sql = sql + " LIMIT ? OFFSET ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) { try (PreparedStatement stmt = conn.prepareStatement(sql)) {
@ -531,6 +587,14 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
stmt.setString(paramIdx++, status); stmt.setString(paramIdx++, status);
} }
} }
if (isSerialProvided) {
stmt.setString(paramIdx++, "%" + serial + "%");
}
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
stmt.setString(paramIdx++, "%" + entry.getValue() + "%");
}
}
stmt.setInt(paramIdx++, request.getRowCount()); stmt.setInt(paramIdx++, request.getRowCount());
stmt.setInt(paramIdx, request.getStartIndex()); stmt.setInt(paramIdx, request.getStartIndex());
@ -1014,6 +1078,17 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
query += buildStatusQuery(status); query += buildStatusQuery(status);
isStatusProvided = true; isStatusProvided = true;
} }
// Loop through custom properties and add conditions
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
query += " AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di2 " +
"WHERE di2.DEVICE_ID = DM_DEVICE.ID " +
"AND di2.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di2.VALUE_FIELD LIKE ?)";
}
}
query = query + " LIMIT ? OFFSET ?"; query = query + " LIMIT ? OFFSET ?";
@ -1038,6 +1113,12 @@ public class PostgreSQLDeviceDAOImpl extends AbstractDeviceDAOImpl {
ps.setString(index++, deviceStatus); ps.setString(index++, deviceStatus);
} }
} }
// Set custom property values in the loop
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
ps.setString(index++, "%" + entry.getValue() + "%");
}
}
ps.setInt(index++, offsetValue); ps.setInt(index++, offsetValue);
ps.setInt(index, limitValue); ps.setInt(index, limitValue);

@ -43,6 +43,7 @@ import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.StringJoiner; import java.util.StringJoiner;
import java.util.Map;
/** /**
* This class holds the generic implementation of DeviceDAO which can be used to support ANSI db syntax. * This class holds the generic implementation of DeviceDAO which can be used to support ANSI db syntax.
@ -94,14 +95,40 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
"d.DEVICE_IDENTIFICATION, " + "d.DEVICE_IDENTIFICATION, " +
"t.NAME AS DEVICE_TYPE "; "t.NAME AS DEVICE_TYPE ";
//Filter by serial number or any Custom Property in DM_DEVICE_INFO
if (serial != null || !request.getCustomProperty().isEmpty()) {
sql = sql +
"FROM DM_DEVICE d " +
"INNER JOIN DM_DEVICE_TYPE t ON d.DEVICE_TYPE_ID = t.ID " +
"WHERE ";
if (serial != null) { if (serial != null) {
sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t, DM_DEVICE_INFO i " + sql += "EXISTS (" +
"WHERE DEVICE_TYPE_ID = t.ID " + "SELECT VALUE_FIELD " +
"AND d.ID= i.DEVICE_ID " + "FROM DM_DEVICE_INFO di " +
"AND i.KEY_FIELD = 'serial' " + "WHERE di.DEVICE_ID = d.ID " +
"AND i.VALUE_FIELD LIKE ? " + "AND di.KEY_FIELD = 'serial' " +
"AND d.TENANT_ID = ? "; "AND di.VALUE_FIELD LIKE ? ) ";
isSerialProvided = true; isSerialProvided = true;
}
if (!request.getCustomProperty().isEmpty()) {
if (serial != null) {
sql += "AND ";
}
boolean firstCondition = true;
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
if (!firstCondition) {
sql += "AND ";
}
sql += "EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di " +
"WHERE di.DEVICE_ID = d.ID " +
"AND di.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di.VALUE_FIELD LIKE ? ) ";
firstCondition = false;
}
}
sql += "AND d.TENANT_ID = ? ";
} else { } else {
sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ? "; sql = sql + "FROM DM_DEVICE d, DM_DEVICE_TYPE t WHERE DEVICE_TYPE_ID = t.ID AND d.TENANT_ID = ? ";
} }
@ -145,6 +172,11 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
if (isSerialProvided) { if (isSerialProvided) {
stmt.setString(paramIdx++, "%" + serial + "%"); stmt.setString(paramIdx++, "%" + serial + "%");
} }
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
stmt.setString(paramIdx++, "%" + entry.getValue() + "%");
}
}
stmt.setInt(paramIdx++, tenantId); stmt.setInt(paramIdx++, tenantId);
if (isSinceProvided) { if (isSinceProvided) {
stmt.setTimestamp(paramIdx++, new Timestamp(since.getTime())); stmt.setTimestamp(paramIdx++, new Timestamp(since.getTime()));
@ -455,6 +487,8 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
boolean isStatusProvided = false; boolean isStatusProvided = false;
Date since = request.getSince(); Date since = request.getSince();
boolean isSinceProvided = false; boolean isSinceProvided = false;
String serial = request.getSerialNumber();
boolean isSerialProvided = false;
try { try {
conn = getConnection(); conn = getConnection();
@ -522,6 +556,28 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
sql += buildStatusQuery(statusList); sql += buildStatusQuery(statusList);
isStatusProvided = true; isStatusProvided = true;
} }
//Filter Group with serial number or any Custom Property in DM_DEVICE_INFO
if (serial != null || !request.getCustomProperty().isEmpty()) {
if (serial != null) {
sql += "AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di " +
"WHERE di.DEVICE_ID = d1.DEVICE_ID " +
"AND di.KEY_FIELD = 'serial' " +
"AND di.VALUE_FIELD LIKE ?) ";
isSerialProvided = true;
}
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
sql += "AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di2 " +
"WHERE di2.DEVICE_ID = d1.DEVICE_ID " +
"AND di2.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di2.VALUE_FIELD LIKE ?)";
}
}
}
sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; sql = sql + " ORDER BY ENROLMENT_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
try (PreparedStatement stmt = conn.prepareStatement(sql)) { try (PreparedStatement stmt = conn.prepareStatement(sql)) {
@ -551,6 +607,14 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
stmt.setString(paramIdx++, status); stmt.setString(paramIdx++, status);
} }
} }
if (isSerialProvided) {
stmt.setString(paramIdx++, "%" + serial + "%");
}
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
stmt.setString(paramIdx++, "%" + entry.getValue() + "%");
}
}
stmt.setInt(paramIdx++, request.getStartIndex()); stmt.setInt(paramIdx++, request.getStartIndex());
stmt.setInt(paramIdx, request.getRowCount()); stmt.setInt(paramIdx, request.getRowCount());
@ -883,6 +947,17 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
query += buildStatusQuery(status); query += buildStatusQuery(status);
isStatusProvided = true; isStatusProvided = true;
} }
// Loop through custom properties and add conditions
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
query += " AND EXISTS (" +
"SELECT VALUE_FIELD " +
"FROM DM_DEVICE_INFO di2 " +
"WHERE di2.DEVICE_ID = DM_DEVICE.ID " +
"AND di2.KEY_FIELD = '" + entry.getKey() + "' " +
"AND di2.VALUE_FIELD LIKE ?)";
}
}
query = query + " ORDER BY DM_DEVICE.ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY"; query = query + " ORDER BY DM_DEVICE.ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
@ -907,6 +982,12 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
ps.setString(index++, deviceStatus); ps.setString(index++, deviceStatus);
} }
} }
// Set custom property values in the loop
if (!request.getCustomProperty().isEmpty()) {
for (Map.Entry<String, String> entry : request.getCustomProperty().entrySet()) {
ps.setString(index++, "%" + entry.getValue() + "%");
}
}
ps.setInt(index++, offsetValue); ps.setInt(index++, offsetValue);
ps.setInt(index, limitValue); ps.setInt(index, limitValue);

@ -2220,19 +2220,28 @@ public class GenericOperationDAOImpl implements OperationDAO {
" DM_ENROLMENT_OP_MAPPING eom " + " DM_ENROLMENT_OP_MAPPING eom " +
"LEFT JOIN " + "LEFT JOIN " +
" DM_DEVICE_OPERATION_RESPONSE opr ON opr.EN_OP_MAP_ID = eom.ID " + " DM_DEVICE_OPERATION_RESPONSE opr ON opr.EN_OP_MAP_ID = eom.ID " +
"INNER JOIN " + "INNER JOIN ");
" (SELECT DISTINCT OPERATION_ID FROM DM_ENROLMENT_OP_MAPPING WHERE TENANT_ID = ? ");
if (activityPaginationRequest.getDeviceType() != null ||
(activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) ||
activityPaginationRequest.getSince() != 0 ||
activityPaginationRequest.getStatus() != null) {
sql.append("(SELECT DISTINCT OPERATION_ID FROM DM_ENROLMENT_OP_MAPPING eom WHERE TENANT_ID = ? ");
if (activityPaginationRequest.getDeviceType() != null) { if (activityPaginationRequest.getDeviceType() != null) {
sql.append("AND DEVICE_TYPE = ? "); sql.append("AND DEVICE_TYPE = ? ");
} }
if (activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) { if (activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) {
sql.append("AND DEVICE_IDENTIFICATION IN ("); sql.append("AND eom.DEVICE_IDENTIFICATION IN (");
for (int i = 0; i < activityPaginationRequest.getDeviceIds().size() - 1; i++) { for (int i = 0; i < activityPaginationRequest.getDeviceIds().size() - 1; i++) {
sql.append("?, "); sql.append("?, ");
} }
sql.append("?) "); sql.append("?) ");
} }
if (activityPaginationRequest.getOperationId() > 0) {
sql.append("AND OPERATION_ID = ? ");
}
if (activityPaginationRequest.getOperationCode() != null) { if (activityPaginationRequest.getOperationCode() != null) {
sql.append("AND OPERATION_CODE = ? "); sql.append("AND OPERATION_CODE = ? ");
} }
@ -2255,6 +2264,26 @@ public class GenericOperationDAOImpl implements OperationDAO {
sql.append("ORDER BY OPERATION_ID ASC limit ? , ? ) eom_ordered " + sql.append("ORDER BY OPERATION_ID ASC limit ? , ? ) eom_ordered " +
"ON eom_ordered.OPERATION_ID = eom.OPERATION_ID WHERE eom.TENANT_ID = ? "); "ON eom_ordered.OPERATION_ID = eom.OPERATION_ID WHERE eom.TENANT_ID = ? ");
} else {
sql.append("(SELECT ID AS OPERATION_ID FROM DM_OPERATION WHERE TENANT_ID = ? ");
if (activityPaginationRequest.getStartTimestamp() > 0 && activityPaginationRequest.getEndTimestamp() > 0) {
isTimeDurationFilteringProvided = true;
sql.append("AND CREATED_TIMESTAMP BETWEEN ? AND ? ");
}
if (activityPaginationRequest.getOperationId() > 0) {
sql.append("AND ID = ? ");
}
if (activityPaginationRequest.getOperationCode() != null) {
sql.append("AND OPERATION_CODE = ? ");
}
if (activityPaginationRequest.getInitiatedBy() != null) {
sql.append("AND INITIATED_BY = ? ");
}
sql.append("ORDER BY ID ASC ) dm_ordered " +
"ON dm_ordered.OPERATION_ID = eom.OPERATION_ID WHERE eom.TENANT_ID = ? ");
}
if (activityPaginationRequest.getDeviceType() != null) { if (activityPaginationRequest.getDeviceType() != null) {
sql.append("AND eom.DEVICE_TYPE = ? "); sql.append("AND eom.DEVICE_TYPE = ? ");
@ -2269,6 +2298,9 @@ public class GenericOperationDAOImpl implements OperationDAO {
if (activityPaginationRequest.getOperationCode() != null) { if (activityPaginationRequest.getOperationCode() != null) {
sql.append("AND eom.OPERATION_CODE = ? "); sql.append("AND eom.OPERATION_CODE = ? ");
} }
if (activityPaginationRequest.getOperationId() > 0) {
sql.append("AND eom.OPERATION_ID = ? ");
}
if (activityPaginationRequest.getInitiatedBy() != null) { if (activityPaginationRequest.getInitiatedBy() != null) {
sql.append("AND eom.INITIATED_BY = ? "); sql.append("AND eom.INITIATED_BY = ? ");
} }
@ -2290,6 +2322,11 @@ public class GenericOperationDAOImpl implements OperationDAO {
int index = 1; int index = 1;
try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) { try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) {
stmt.setInt(index++, tenantId); stmt.setInt(index++, tenantId);
if (activityPaginationRequest.getDeviceType() != null ||
(activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) ||
activityPaginationRequest.getSince() != 0 ||
activityPaginationRequest.getStatus() != null) {
if (activityPaginationRequest.getDeviceType() != null) { if (activityPaginationRequest.getDeviceType() != null) {
stmt.setString(index++, activityPaginationRequest.getDeviceType()); stmt.setString(index++, activityPaginationRequest.getDeviceType());
} }
@ -2301,6 +2338,9 @@ public class GenericOperationDAOImpl implements OperationDAO {
if (activityPaginationRequest.getOperationCode() != null) { if (activityPaginationRequest.getOperationCode() != null) {
stmt.setString(index++, activityPaginationRequest.getOperationCode()); stmt.setString(index++, activityPaginationRequest.getOperationCode());
} }
if (activityPaginationRequest.getOperationId() > 0) {
stmt.setInt(index++, activityPaginationRequest.getOperationId());
}
if (activityPaginationRequest.getInitiatedBy() != null) { if (activityPaginationRequest.getInitiatedBy() != null) {
stmt.setString(index++, activityPaginationRequest.getInitiatedBy()); stmt.setString(index++, activityPaginationRequest.getInitiatedBy());
} }
@ -2330,9 +2370,38 @@ public class GenericOperationDAOImpl implements OperationDAO {
stmt.setString(index++, deviceId); stmt.setString(index++, deviceId);
} }
} }
} else {
if (isTimeDurationFilteringProvided) {
stmt.setLong(index++, activityPaginationRequest.getStartTimestamp());
stmt.setLong(index++, activityPaginationRequest.getEndTimestamp());
}
if (activityPaginationRequest.getOperationCode() != null) { if (activityPaginationRequest.getOperationCode() != null) {
stmt.setString(index++, activityPaginationRequest.getOperationCode()); stmt.setString(index++, activityPaginationRequest.getOperationCode());
} }
if (activityPaginationRequest.getOperationId() > 0) {
stmt.setInt(index++, activityPaginationRequest.getOperationId());
}
if (activityPaginationRequest.getInitiatedBy() != null) {
stmt.setString(index++, activityPaginationRequest.getInitiatedBy());
}
stmt.setInt(index++, tenantId);
if (activityPaginationRequest.getDeviceType() != null) {
stmt.setString(index++, activityPaginationRequest.getDeviceType());
}
if (activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) {
for (String deviceId : activityPaginationRequest.getDeviceIds()) {
stmt.setString(index++, deviceId);
}
}
}
if (activityPaginationRequest.getOperationCode() != null) {
stmt.setString(index++, activityPaginationRequest.getOperationCode());
}
if (activityPaginationRequest.getOperationId() > 0) {
stmt.setInt(index++, activityPaginationRequest.getOperationId());
}
if (activityPaginationRequest.getInitiatedBy() != null) { if (activityPaginationRequest.getInitiatedBy() != null) {
stmt.setString(index++, activityPaginationRequest.getInitiatedBy()); stmt.setString(index++, activityPaginationRequest.getInitiatedBy());
} }
@ -2374,7 +2443,14 @@ public class GenericOperationDAOImpl implements OperationDAO {
boolean isTimeDurationFilteringProvided = false; boolean isTimeDurationFilteringProvided = false;
Connection conn = OperationManagementDAOFactory.getConnection(); Connection conn = OperationManagementDAOFactory.getConnection();
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
StringBuilder sql = new StringBuilder("SELECT count(DISTINCT OPERATION_ID) AS ACTIVITY_COUNT " + StringBuilder sql = new StringBuilder();
if (activityPaginationRequest.getDeviceType() != null ||
(activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) ||
activityPaginationRequest.getSince() != 0 ||
activityPaginationRequest.getStatus() != null) {
sql.append("SELECT count(DISTINCT OPERATION_ID) AS ACTIVITY_COUNT " +
"FROM DM_ENROLMENT_OP_MAPPING WHERE TENANT_ID = ? "); "FROM DM_ENROLMENT_OP_MAPPING WHERE TENANT_ID = ? ");
if (activityPaginationRequest.getDeviceType() != null) { if (activityPaginationRequest.getDeviceType() != null) {
@ -2390,6 +2466,9 @@ public class GenericOperationDAOImpl implements OperationDAO {
if (activityPaginationRequest.getOperationCode() != null) { if (activityPaginationRequest.getOperationCode() != null) {
sql.append("AND OPERATION_CODE = ? "); sql.append("AND OPERATION_CODE = ? ");
} }
if (activityPaginationRequest.getOperationId() > 0) {
sql.append("AND OPERATION_ID = ? ");
}
if (activityPaginationRequest.getInitiatedBy() != null) { if (activityPaginationRequest.getInitiatedBy() != null) {
sql.append("AND INITIATED_BY = ? "); sql.append("AND INITIATED_BY = ? ");
} }
@ -2402,6 +2481,20 @@ public class GenericOperationDAOImpl implements OperationDAO {
if (activityPaginationRequest.getStatus() != null) { if (activityPaginationRequest.getStatus() != null) {
sql.append("AND STATUS = ? "); sql.append("AND STATUS = ? ");
} }
} else {
sql.append("SELECT count(ID) AS ACTIVITY_COUNT FROM DM_OPERATION WHERE TENANT_ID = ? ");
if (activityPaginationRequest.getOperationCode() != null) {
sql.append("AND OPERATION_CODE = ? ");
}
if (activityPaginationRequest.getOperationId() > 0) {
sql.append("AND ID = ? ");
}
if (activityPaginationRequest.getInitiatedBy() != null) {
sql.append("AND INITIATED_BY = ? ");
}
}
if (activityPaginationRequest.getStartTimestamp() > 0 && activityPaginationRequest.getEndTimestamp() > 0) { if (activityPaginationRequest.getStartTimestamp() > 0 && activityPaginationRequest.getEndTimestamp() > 0) {
isTimeDurationFilteringProvided = true; isTimeDurationFilteringProvided = true;
sql.append("AND CREATED_TIMESTAMP BETWEEN ? AND ? "); sql.append("AND CREATED_TIMESTAMP BETWEEN ? AND ? ");
@ -2410,6 +2503,11 @@ public class GenericOperationDAOImpl implements OperationDAO {
int index = 1; int index = 1;
try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) { try (PreparedStatement stmt = conn.prepareStatement(sql.toString())) {
stmt.setInt(index++, tenantId); stmt.setInt(index++, tenantId);
if (activityPaginationRequest.getDeviceType() != null ||
(activityPaginationRequest.getDeviceIds() != null && !activityPaginationRequest.getDeviceIds().isEmpty()) ||
activityPaginationRequest.getSince() != 0 ||
activityPaginationRequest.getStatus() != null) {
if (activityPaginationRequest.getDeviceType() != null) { if (activityPaginationRequest.getDeviceType() != null) {
stmt.setString(index++, activityPaginationRequest.getDeviceType()); stmt.setString(index++, activityPaginationRequest.getDeviceType());
} }
@ -2421,6 +2519,9 @@ public class GenericOperationDAOImpl implements OperationDAO {
if (activityPaginationRequest.getOperationCode() != null) { if (activityPaginationRequest.getOperationCode() != null) {
stmt.setString(index++, activityPaginationRequest.getOperationCode()); stmt.setString(index++, activityPaginationRequest.getOperationCode());
} }
if (activityPaginationRequest.getOperationId() > 0) {
stmt.setInt(index++, activityPaginationRequest.getOperationId());
}
if (activityPaginationRequest.getInitiatedBy() != null) { if (activityPaginationRequest.getInitiatedBy() != null) {
stmt.setString(index++, activityPaginationRequest.getInitiatedBy()); stmt.setString(index++, activityPaginationRequest.getInitiatedBy());
} }
@ -2433,6 +2534,17 @@ public class GenericOperationDAOImpl implements OperationDAO {
if (activityPaginationRequest.getStatus() != null) { if (activityPaginationRequest.getStatus() != null) {
stmt.setString(index++, activityPaginationRequest.getStatus().name()); stmt.setString(index++, activityPaginationRequest.getStatus().name());
} }
} else {
if (activityPaginationRequest.getOperationCode() != null) {
stmt.setString(index++, activityPaginationRequest.getOperationCode());
}
if (activityPaginationRequest.getOperationId() > 0) {
stmt.setInt(index++, activityPaginationRequest.getOperationId());
}
if (activityPaginationRequest.getInitiatedBy() != null) {
stmt.setString(index++, activityPaginationRequest.getInitiatedBy());
}
}
if (isTimeDurationFilteringProvided) { if (isTimeDurationFilteringProvided) {
stmt.setLong(index++, activityPaginationRequest.getStartTimestamp()); stmt.setLong(index++, activityPaginationRequest.getStartTimestamp());
stmt.setLong(index, activityPaginationRequest.getEndTimestamp()); stmt.setLong(index, activityPaginationRequest.getEndTimestamp());

@ -248,6 +248,7 @@ public class OperationDAOUtil {
activity.setCreatedTimeStamp( activity.setCreatedTimeStamp(
new java.util.Date(rs.getLong(("CREATED_TIMESTAMP")) * 1000).toString()); new java.util.Date(rs.getLong(("CREATED_TIMESTAMP")) * 1000).toString());
activity.setCode(rs.getString("OPERATION_CODE")); activity.setCode(rs.getString("OPERATION_CODE"));
activity.setOperationId(rs.getInt("OPERATION_ID"));
activity.setInitiatedBy(rs.getString("INITIATED_BY")); activity.setInitiatedBy(rs.getString("INITIATED_BY"));
DeviceIdentifier deviceIdentifier = new DeviceIdentifier(); DeviceIdentifier deviceIdentifier = new DeviceIdentifier();

@ -92,6 +92,7 @@ CREATE TABLE IF NOT EXISTS DM_OPERATION (
INITIATED_BY VARCHAR(100) NULL, INITIATED_BY VARCHAR(100) NULL,
OPERATION_DETAILS BLOB DEFAULT NULL, OPERATION_DETAILS BLOB DEFAULT NULL,
ENABLED BOOLEAN NOT NULL DEFAULT FALSE, ENABLED BOOLEAN NOT NULL DEFAULT FALSE,
TENANT_ID INT NOT NULL,
PRIMARY KEY (ID) PRIMARY KEY (ID)
); );

@ -126,9 +126,17 @@ CREATE TABLE DM_OPERATION (
INITIATED_BY VARCHAR(100) NULL, INITIATED_BY VARCHAR(100) NULL,
OPERATION_DETAILS VARBINARY(MAX) DEFAULT NULL, OPERATION_DETAILS VARBINARY(MAX) DEFAULT NULL,
ENABLED BIT NOT NULL DEFAULT 0, ENABLED BIT NOT NULL DEFAULT 0,
TENANT_ID INTEGER NOT NULL,
PRIMARY KEY (ID) PRIMARY KEY (ID)
); );
IF NOT EXISTS (SELECT * FROM SYS.INDEXES WHERE NAME = 'IDX_OP_CREATED' AND OBJECT_ID = OBJECT_ID('DM_OPERATION'))
CREATE INDEX IDX_OP_CREATED ON DM_OPERATION(CREATED_TIMESTAMP);
IF NOT EXISTS (SELECT * FROM SYS.INDEXES WHERE NAME = 'IDX_OP_CODE' AND OBJECT_ID = OBJECT_ID('DM_OPERATION'))
CREATE INDEX IDX_OP_CODE ON DM_OPERATION(OPERATION_CODE);
IF NOT EXISTS (SELECT * FROM SYS.INDEXES WHERE NAME = 'IDX_OP_INITIATED_BY' AND OBJECT_ID = OBJECT_ID('DM_OPERATION'))
CREATE INDEX IDX_OP_INITIATED_BY ON DM_OPERATION(INITIATED_BY);
IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_ENROLMENT]') AND TYPE IN (N'U')) IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_ENROLMENT]') AND TYPE IN (N'U'))
CREATE TABLE DM_ENROLMENT ( CREATE TABLE DM_ENROLMENT (
ID INTEGER IDENTITY(1,1) NOT NULL, ID INTEGER IDENTITY(1,1) NOT NULL,
@ -196,6 +204,8 @@ IF NOT EXISTS (SELECT * FROM SYS.INDEXES WHERE NAME = 'IDX_EN_OP_MAPPING_OP_ID'
CREATE INDEX IDX_EN_OP_MAPPING_OP_ID ON DM_ENROLMENT_OP_MAPPING(OPERATION_ID); CREATE INDEX IDX_EN_OP_MAPPING_OP_ID ON DM_ENROLMENT_OP_MAPPING(OPERATION_ID);
IF NOT EXISTS (SELECT * FROM SYS.INDEXES WHERE NAME = 'IDX_EN_OP_MAPPING_EN_ID_STATUS' AND OBJECT_ID = OBJECT_ID('DM_ENROLMENT_OP_MAPPING')) IF NOT EXISTS (SELECT * FROM SYS.INDEXES WHERE NAME = 'IDX_EN_OP_MAPPING_EN_ID_STATUS' AND OBJECT_ID = OBJECT_ID('DM_ENROLMENT_OP_MAPPING'))
CREATE INDEX IDX_EN_OP_MAPPING_EN_ID_STATUS ON DM_ENROLMENT_OP_MAPPING(ENROLMENT_ID, STATUS); CREATE INDEX IDX_EN_OP_MAPPING_EN_ID_STATUS ON DM_ENROLMENT_OP_MAPPING(ENROLMENT_ID, STATUS);
IF NOT EXISTS (SELECT * FROM SYS.INDEXES WHERE NAME = 'IDX_ENROLMENT_OP_MAPPING_CREATED_TS' AND OBJECT_ID = OBJECT_ID('DM_ENROLMENT_OP_MAPPING'))
CREATE INDEX IDX_ENROLMENT_OP_MAPPING_CREATED_TS ON DM_ENROLMENT_OP_MAPPING(CREATED_TIMESTAMP);
IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_DEVICE_OPERATION_RESPONSE]') AND TYPE IN (N'U')) IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_DEVICE_OPERATION_RESPONSE]') AND TYPE IN (N'U'))
CREATE TABLE DM_DEVICE_OPERATION_RESPONSE ( CREATE TABLE DM_DEVICE_OPERATION_RESPONSE (

@ -107,9 +107,14 @@ CREATE TABLE IF NOT EXISTS DM_OPERATION (
INITIATED_BY VARCHAR(100) NULL, INITIATED_BY VARCHAR(100) NULL,
OPERATION_DETAILS BLOB DEFAULT NULL, OPERATION_DETAILS BLOB DEFAULT NULL,
ENABLED BOOLEAN NOT NULL DEFAULT FALSE, ENABLED BOOLEAN NOT NULL DEFAULT FALSE,
TENANT_ID INT NOT NULL,
PRIMARY KEY (ID) PRIMARY KEY (ID)
)ENGINE = InnoDB; )ENGINE = InnoDB;
CREATE INDEX IDX_OP_CREATED ON DM_OPERATION (CREATED_TIMESTAMP ASC);
CREATE INDEX IDX_OP_CODE ON DM_OPERATION (OPERATION_CODE ASC);
CREATE INDEX IDX_OP_INITIATED_BY ON DM_OPERATION (INITIATED_BY ASC);
CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CREATE TABLE IF NOT EXISTS DM_ENROLMENT (
ID INTEGER AUTO_INCREMENT NOT NULL, ID INTEGER AUTO_INCREMENT NOT NULL,
DEVICE_ID INTEGER NOT NULL, DEVICE_ID INTEGER NOT NULL,
@ -170,6 +175,7 @@ CREATE INDEX IDX_ENROLMENT_OP_MAPPING ON DM_ENROLMENT_OP_MAPPING (UPDATED_TIMEST
CREATE INDEX IDX_EN_OP_MAPPING_EN_ID ON DM_ENROLMENT_OP_MAPPING(ENROLMENT_ID); CREATE INDEX IDX_EN_OP_MAPPING_EN_ID ON DM_ENROLMENT_OP_MAPPING(ENROLMENT_ID);
CREATE INDEX IDX_EN_OP_MAPPING_OP_ID ON DM_ENROLMENT_OP_MAPPING(OPERATION_ID); CREATE INDEX IDX_EN_OP_MAPPING_OP_ID ON DM_ENROLMENT_OP_MAPPING(OPERATION_ID);
CREATE INDEX IDX_EN_OP_MAPPING_EN_ID_STATUS ON DM_ENROLMENT_OP_MAPPING(ENROLMENT_ID, STATUS); CREATE INDEX IDX_EN_OP_MAPPING_EN_ID_STATUS ON DM_ENROLMENT_OP_MAPPING(ENROLMENT_ID, STATUS);
CREATE INDEX IDX_ENROLMENT_OP_MAPPING_CREATED_TS ON DM_ENROLMENT_OP_MAPPING (CREATED_TIMESTAMP ASC);
CREATE TABLE IF NOT EXISTS DM_DEVICE_OPERATION_RESPONSE CREATE TABLE IF NOT EXISTS DM_DEVICE_OPERATION_RESPONSE
( (

@ -181,6 +181,7 @@ CREATE TABLE DM_OPERATION (
INITIATED_BY VARCHAR2(100) NULL, INITIATED_BY VARCHAR2(100) NULL,
ENABLED NUMBER(10) DEFAULT 0 NOT NULL, ENABLED NUMBER(10) DEFAULT 0 NOT NULL,
OPERATION_DETAILS BLOB DEFAULT NULL, OPERATION_DETAILS BLOB DEFAULT NULL,
TENANT_ID INTEGER NOT NULL,
CONSTRAINT PK_DM_OPERATION PRIMARY KEY (ID) CONSTRAINT PK_DM_OPERATION PRIMARY KEY (ID)
) )
/ /

@ -100,10 +100,13 @@ CREATE TABLE IF NOT EXISTS DM_OPERATION (
INITIATED_BY VARCHAR(100) NULL, INITIATED_BY VARCHAR(100) NULL,
OPERATION_DETAILS BYTEA DEFAULT NULL, OPERATION_DETAILS BYTEA DEFAULT NULL,
ENABLED BOOLEAN NOT NULL DEFAULT FALSE, ENABLED BOOLEAN NOT NULL DEFAULT FALSE,
TENANT_ID INTEGER NOT NULL,
PRIMARY KEY (ID) PRIMARY KEY (ID)
); );
CREATE INDEX IDX_OP_CREATED ON DM_OPERATION (CREATED_TIMESTAMP);
CREATE INDEX IDX_OP_CODE ON DM_OPERATION (OPERATION_CODE);
CREATE INDEX IDX_OP_INITIATED_BY ON DM_OPERATION (INITIATED_BY);
CREATE SEQUENCE DM_ENROLMENT_seq; CREATE SEQUENCE DM_ENROLMENT_seq;
@ -161,6 +164,7 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING (
CREATE INDEX fk_dm_device_operation_mapping_operation ON DM_ENROLMENT_OP_MAPPING (OPERATION_ID); CREATE INDEX fk_dm_device_operation_mapping_operation ON DM_ENROLMENT_OP_MAPPING (OPERATION_ID);
CREATE INDEX IDX_DM_ENROLMENT_OP_MAPPING ON DM_ENROLMENT_OP_MAPPING (ENROLMENT_ID,OPERATION_ID); CREATE INDEX IDX_DM_ENROLMENT_OP_MAPPING ON DM_ENROLMENT_OP_MAPPING (ENROLMENT_ID,OPERATION_ID);
CREATE INDEX ID_DM_ENROLMENT_OP_MAPPING_UPDATED_TIMESTAMP ON DM_ENROLMENT_OP_MAPPING (UPDATED_TIMESTAMP); CREATE INDEX ID_DM_ENROLMENT_OP_MAPPING_UPDATED_TIMESTAMP ON DM_ENROLMENT_OP_MAPPING (UPDATED_TIMESTAMP);
CREATE INDEX IDX_ENROLMENT_OP_MAPPING_CREATED_TS ON DM_ENROLMENT_OP_MAPPING (CREATED_TIMESTAMP);
ALTER TABLE DM_ENROLMENT_OP_MAPPING ALTER TABLE DM_ENROLMENT_OP_MAPPING
ADD OPERATION_CODE VARCHAR(50) NOT NULL, ADD OPERATION_CODE VARCHAR(50) NOT NULL,

Loading…
Cancel
Save