diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java index eb1d952491..d5d5244694 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/api/DeviceManagementService.java @@ -1825,6 +1825,141 @@ public interface DeviceManagementService { @Size(max = 45) String id); + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/{type}/{id}/getstatushistory") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Get Device status history", + notes = "Get a list of status history associated with the device type and id", + tags = "Device Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:view") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully fetched the status history of matching devices.", + response = List.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "ETag", + description = "Entity Tag of the response resource.\n" + + "Used by caches, or in conditional requests."), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. Empty body because the client already has the latest version" + + " of the requested resource.\n"), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid request or validation error.", + response = ErrorResponse.class), + @ApiResponse( + code = 404, + message = "Not Found. \n A device with the specified device type and id was not found.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n " + + "Server error occurred while retrieving the device details.", + response = ErrorResponse.class) + }) + Response getDeviceStatusHistory( + @ApiParam( + name = "type", + value = "The device type, such as ios, android, or windows.", + required = true) + @PathParam("type") + @Size(max = 45) + String type, + @ApiParam( + name = "id", + value = "Device ID.", + required = true) + @PathParam("id") + @Size(max = 45) + String id); + @GET + @Produces(MediaType.APPLICATION_JSON) + @Path("/{type}/{id}/getenrolmentstatushistory") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "GET", + value = "Get Device Current Enrolment status history", + notes = "Get a list of status history associated with the device type and id for the current enrolment", + tags = "Device Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:view") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully fetched the status history of matching devices.", + response = List.class, + responseHeaders = { + @ResponseHeader( + name = "Content-Type", + description = "The content type of the body"), + @ResponseHeader( + name = "ETag", + description = "Entity Tag of the response resource.\n" + + "Used by caches, or in conditional requests."), + @ResponseHeader( + name = "Last-Modified", + description = "Date and time the resource was last modified.\n" + + "Used by caches, or in conditional requests."), + }), + @ApiResponse( + code = 304, + message = "Not Modified. Empty body because the client already has the latest version" + + " of the requested resource.\n"), + @ApiResponse( + code = 400, + message = "Bad Request. \n Invalid request or validation error.", + response = ErrorResponse.class), + @ApiResponse( + code = 404, + message = "Not Found. \n A device with the specified device type and id was not found.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n " + + "Server error occurred while retrieving the device details.", + response = ErrorResponse.class) + }) + Response getCurrentEnrolmentDeviceStatusHistory( + @ApiParam( + name = "type", + value = "The device type, such as ios, android, or windows.", + required = true) + @PathParam("type") + @Size(max = 45) + String type, + @ApiParam( + name = "id", + value = "Device ID.", + required = true) + @PathParam("id") + @Size(max = 45) + String id); + @PUT @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java index 804029a13c..ca9df6cb88 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/DeviceManagementServiceImpl.java @@ -85,6 +85,7 @@ import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.NonComplianceData; import org.wso2.carbon.device.mgt.common.policy.mgt.monitor.PolicyComplianceException; import org.wso2.carbon.device.mgt.common.search.PropertyMap; import org.wso2.carbon.device.mgt.common.search.SearchContext; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; import org.wso2.carbon.device.mgt.core.app.mgt.ApplicationManagementProviderService; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; @@ -1141,6 +1142,70 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { } } + /** + * List device status history + * + * @param type Device type + * @param id Device id + * @return {@link Response} object + */ + @GET + @Path("/{type}/{id}/getstatushistory") + public Response getDeviceStatusHistory(@PathParam("type") @Size(max = 45) String type, + @PathParam("id") @Size(max = 45) String id) { + //TODO check authorization for this + RequestValidationUtil.validateDeviceIdentifier(type, id); + DeviceManagementProviderService deviceManagementProviderService = + DeviceMgtAPIUtils.getDeviceManagementService(); + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(id, type); + Device persistedDevice = deviceManagementProviderService.getDevice(deviceIdentifier, false); + if (persistedDevice == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + List deviceStatusHistory = deviceManagementProviderService.getDeviceStatusHistory(persistedDevice); + return Response.status(Response.Status.OK).entity(deviceStatusHistory).build(); + } catch (DeviceManagementException e) { + String msg = "Error occurred while retreiving device status history for device of type : " + type + " and " + + "device id : " + id; + log.error(msg); + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); + } + } + + /** + * List device status history for the current enrolment + * + * @param type Device type + * @param id Device id + * @return {@link Response} object + */ + @GET + @Path("/{type}/{id}/getenrolmentstatushistory") + public Response getCurrentEnrolmentDeviceStatusHistory(@PathParam("type") @Size(max = 45) String type, + @PathParam("id") @Size(max = 45) String id) { + //TODO check authorization for this or current enrolment should be based on for the enrolment associated with the user + RequestValidationUtil.validateDeviceIdentifier(type, id); + DeviceManagementProviderService deviceManagementProviderService = + DeviceMgtAPIUtils.getDeviceManagementService(); + try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(id, type); + Device persistedDevice = deviceManagementProviderService.getDevice(deviceIdentifier, false); + if (persistedDevice == null) { + return Response.status(Response.Status.NOT_FOUND).build(); + } + List deviceStatusHistory = deviceManagementProviderService.getDeviceCurrentEnrolmentStatusHistory(persistedDevice); + return Response.status(Response.Status.OK).entity(deviceStatusHistory).build(); + } catch (DeviceManagementException e) { + String msg = "Error occurred while retreiving device status history for device of type : " + type + " and " + + "device id : " + id; + log.error(msg); + return Response.status(Response.Status.BAD_REQUEST).entity( + new ErrorResponse.ErrorResponseBuilder().setMessage(msg).build()).build(); + } + } + @POST @Path("/{type}/operations") public Response addOperation(@PathParam("type") String type, @Valid OperationRequest operationRequest) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/type/mgt/DeviceStatus.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/type/mgt/DeviceStatus.java new file mode 100644 index 0000000000..a91c4947b1 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/type/mgt/DeviceStatus.java @@ -0,0 +1,61 @@ +package org.wso2.carbon.device.mgt.common.type.mgt; + +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; + +import java.util.Date; + +public class DeviceStatus { + private EnrolmentInfo.Status status; + private Date updateTime; + private int enrolmentId; + private int deviceId; + private String changedBy; + + public DeviceStatus(int enrolmentId, int deviceId, EnrolmentInfo.Status status, Date updateTime, String changedBy) { + this.status = status; + this.updateTime = updateTime; + this.enrolmentId = enrolmentId; + this.deviceId = deviceId; + this.changedBy = changedBy; + } + + public String getChangedBy() { + return changedBy; + } + + public void setChangedBy(String changedBy) { + this.changedBy = changedBy; + } + + public EnrolmentInfo.Status getStatus() { + return status; + } + + public void setStatus(EnrolmentInfo.Status status) { + this.status = status; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public int getEnrolmentId() { + return enrolmentId; + } + + public void setEnrolmentId(int enrolmentId) { + this.enrolmentId = enrolmentId; + } + + public int getDeviceId() { + return deviceId; + } + + public void setDeviceId(int deviceId) { + this.deviceId = deviceId; + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java index c5c778ca23..348f256016 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceManagementDAOFactory.java @@ -26,11 +26,8 @@ import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementExcepti import org.wso2.carbon.device.mgt.common.exceptions.UnsupportedDatabaseEngineException; import org.wso2.carbon.device.mgt.core.config.datasource.DataSourceConfig; import org.wso2.carbon.device.mgt.core.config.datasource.JNDILookupDefinition; -import org.wso2.carbon.device.mgt.core.dao.impl.ApplicationDAOImpl; -import org.wso2.carbon.device.mgt.core.dao.impl.DeviceTypeDAOImpl; -import org.wso2.carbon.device.mgt.core.dao.impl.EnrollmentDAOImpl; +import org.wso2.carbon.device.mgt.core.dao.impl.*; import org.wso2.carbon.device.mgt.core.dao.impl.event.GenericEventConfigDAOImpl; -import org.wso2.carbon.device.mgt.core.dao.impl.GeofenceDAOImpl; import org.wso2.carbon.device.mgt.core.dao.impl.device.GenericDeviceDAOImpl; import org.wso2.carbon.device.mgt.core.dao.impl.device.OracleDeviceDAOImpl; import org.wso2.carbon.device.mgt.core.dao.impl.device.PostgreSQLDeviceDAOImpl; @@ -127,7 +124,9 @@ public class DeviceManagementDAOFactory { public static EnrollmentDAO getEnrollmentDAO() { return new EnrollmentDAOImpl(); } - + public static DeviceStatusDAO getDeviceStatusDAO() { + return new DeviceStatusDAOImpl(); + } public static ApplicationDAO getApplicationDAO() { if (databaseEngine != null) { switch (databaseEngine) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusDAO.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusDAO.java new file mode 100644 index 0000000000..6d9cb18e58 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusDAO.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.device.mgt.core.dao; + +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; + +import java.util.Date; +import java.util.List; + +public interface DeviceStatusDAO { + +// boolean updateStatus(int deviceId, Status status, int tenantId) throws DeviceManagementDAOException; + +// boolean updateStatus(int enrolmentId, Status status) throws DeviceManagementDAOException; + + List getStatus(int deviceId, int tenantId) throws DeviceManagementDAOException; + + List getStatus(int deviceId, int tenantId, Date fromDate, Date toDate) throws DeviceManagementDAOException; + + List getStatus(int enrolmentId) throws DeviceManagementDAOException; + + List getStatus(int enrolmentId, Date fromDate, Date toDate) throws DeviceManagementDAOException; + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/DeviceStatusDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/DeviceStatusDAOImpl.java new file mode 100644 index 0000000000..0305f83114 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/DeviceStatusDAOImpl.java @@ -0,0 +1,89 @@ +package org.wso2.carbon.device.mgt.core.dao.impl; + +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.core.dao.DeviceStatusDAO; +import org.wso2.carbon.device.mgt.core.dao.EnrollmentDAO; +import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; + +import java.sql.*; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class DeviceStatusDAOImpl implements DeviceStatusDAO { + + private List getStatus(int id, Date fromDate, Date toDate, boolean isDeviceId) throws DeviceManagementDAOException { + List result = new ArrayList<>(); + Connection conn; + PreparedStatement stmt = null; + ResultSet rs = null; + EnrolmentInfo.Status status = null; + try { + conn = this.getConnection(); + // either we list all status values for the device using the device id or only get status values for the given enrolment id + String idType = isDeviceId ? "DEVICE_ID" : "ENROLMENT_ID"; + String sql = "SELECT ENROLMENT_ID, DEVICE_ID, UPDATE_TIME, STATUS, CHANGED_BY FROM DM_DEVICE_STATUS WHERE " + idType + " = ?"; + // filter the data based on a date range if specified + if (fromDate != null){ + sql += " AND UPDATE_TIME >= ?"; + } + if (toDate != null){ + sql += " AND UPDATE_TIME <= ?"; + } + + stmt = conn.prepareStatement(sql); + + int i = 1; + stmt.setInt(i++, id); + if (fromDate != null){ + Timestamp fromTime = new Timestamp(fromDate.getTime()); + stmt.setTimestamp(i++, fromTime); + } + if (toDate != null){ + Timestamp toTime = new Timestamp(toDate.getTime()); + stmt.setTimestamp(i++, toTime); + } + rs = stmt.executeQuery(); + + while (rs.next()) { + DeviceStatus ds = new DeviceStatus(rs.getInt("ENROLMENT_ID"), rs.getInt("DEVICE_ID"), + EnrolmentInfo.Status.valueOf(rs.getString("STATUS")), + new Date(rs.getTimestamp("UPDATE_TIME").getTime()), rs.getString("CHANGED_BY")); + result.add(ds); + } + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment", e); + } finally { + DeviceManagementDAOUtil.cleanupResources(stmt, rs); + } + return result; + } + + @Override + public List getStatus(int enrolmentId, Date fromDate, Date toDate) throws DeviceManagementDAOException { + return getStatus(enrolmentId, fromDate, toDate, false); + } + + @Override + public List getStatus(int deviceId, int tenantId) throws DeviceManagementDAOException { + return getStatus(deviceId, tenantId, null, null); + } + + @Override + public List getStatus(int deviceId, int tenantId, Date fromDate, Date toDate) throws DeviceManagementDAOException { + return getStatus(deviceId, fromDate, toDate, true); + } + + @Override + public List getStatus(int enrolmentId) throws DeviceManagementDAOException { + return getStatus(enrolmentId, null, null); + } + + + private Connection getConnection() throws SQLException { + return DeviceManagementDAOFactory.getConnection(); + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java index 186b3b4dd6..df727ebe90 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/dao/impl/EnrollmentDAOImpl.java @@ -18,10 +18,12 @@ */ package org.wso2.carbon.device.mgt.core.dao.impl; +import org.wso2.carbon.context.PrivilegedCarbonContext; import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; +import org.wso2.carbon.device.mgt.core.dao.DeviceStatusDAO; import org.wso2.carbon.device.mgt.core.dao.EnrollmentDAO; import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; @@ -39,7 +41,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { @Override public EnrolmentInfo addEnrollment(int deviceId, EnrolmentInfo enrolmentInfo, - int tenantId) throws DeviceManagementDAOException { + int tenantId) throws DeviceManagementDAOException { Connection conn; PreparedStatement stmt = null; ResultSet rs = null; @@ -64,10 +66,12 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { enrolmentInfo.setId(enrolmentId); enrolmentInfo.setDateOfEnrolment(enrollmentTime.getTime()); enrolmentInfo.setDateOfLastUpdate(enrollmentTime.getTime()); + addDeviceStatus(enrolmentId, enrolmentInfo.getStatus()); return enrolmentInfo; } return null; } catch (SQLException e) { + e.printStackTrace(); throw new DeviceManagementDAOException("Error occurred while adding enrolment configuration", e); } finally { DeviceManagementDAOUtil.cleanupResources(stmt, rs); @@ -89,7 +93,11 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { stmt.setTimestamp(3, new Timestamp(new Date().getTime())); stmt.setInt(4, enrolmentInfo.getId()); stmt.setInt(5, tenantId); - return stmt.executeUpdate(); + int updatedCount = stmt.executeUpdate(); + if (updatedCount == 1){ + addDeviceStatus(enrolmentInfo.getId(), enrolmentInfo.getStatus()); + } + return updatedCount; } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while updating enrolment configuration", e); } finally { @@ -124,6 +132,9 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { } if (updateStatus > 0) { status = true; + for (EnrolmentInfo enrolmentInfo : enrolmentInfos) { + addDeviceStatus(enrolmentInfo); + } } } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while updating enrolment status of given device-list.", e); @@ -194,6 +205,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { if(getCountOfDevicesOfOwner(currentOwner, tenantId) > 0){ try { conn = this.getConnection(); + // TODO add DATE_OF_LAST_UPDATE String sql = "UPDATE DM_ENROLMENT SET STATUS = ? WHERE OWNER = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, status.toString()); @@ -209,20 +221,30 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { } else { return false; } + // TODO: Needs device Id since having owner id doesn't necessary make it unique? + //getDeviceStatusDAO().updateStatus(deviceId, status); } @Override public boolean setStatus(int enrolmentID, EnrolmentInfo.Status status, int tenantId) throws DeviceManagementDAOException { Connection conn; PreparedStatement stmt = null; + Timestamp updateTime = new Timestamp(new Date().getTime()); try { conn = this.getConnection(); - String sql = "UPDATE DM_ENROLMENT SET STATUS = ? WHERE ID = ? AND TENANT_ID = ?"; + String sql = "UPDATE DM_ENROLMENT SET STATUS = ?, DATE_OF_LAST_UPDATE = ? WHERE ID = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setString(1, status.toString()); - stmt.setInt(2, enrolmentID); - stmt.setInt(3, tenantId); - stmt.executeUpdate(); + stmt.setTimestamp(2, updateTime); + stmt.setInt(3, enrolmentID); + stmt.setInt(4, tenantId); + int updatedRowCount = stmt.executeUpdate(); + if (updatedRowCount != 1){ + throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment: "+ + updatedRowCount + " rows were updated instead of one row!!!"); + } + // save the device status history + addDeviceStatus(enrolmentID, status); } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment", e); } finally { @@ -231,6 +253,68 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { return true; } + private boolean addDeviceStatus(EnrolmentInfo config) throws DeviceManagementDAOException { + return addDeviceStatus(config.getId(), config.getStatus()); + } + + private boolean addDeviceStatus(int enrolmentId, EnrolmentInfo.Status status) throws DeviceManagementDAOException { + Connection conn; + String changedBy = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); + PreparedStatement stmt = null; + try { + conn = this.getConnection(); + // get the device id and last udpated status from the device status table + String sql = "SELECT DEVICE_ID, STATUS FROM DM_DEVICE_STATUS WHERE ENROLMENT_ID = ? ORDER BY UPDATE_TIME DESC LIMIT 1"; + stmt = conn.prepareStatement(sql); + stmt.setInt(1, enrolmentId); + ResultSet rs = stmt.executeQuery(); + int deviceId = -1; + EnrolmentInfo.Status previousStatus = null; + if (rs.next()) { + // if there is a record corresponding to the enrolment we save the status and the device id + previousStatus = EnrolmentInfo.Status.valueOf(rs.getString("STATUS")); + deviceId = rs.getInt("DEVICE_ID"); + } + DeviceManagementDAOUtil.cleanupResources(stmt, null); + // if there was no record for the enrolment or the previous status is not the same as the current status + // we'll add a record + if (previousStatus == null || previousStatus != status){ + if (deviceId == -1) { + // we need the device id in order to add a new record, therefore we get it from the enrolment table + sql = "SELECT DEVICE_ID FROM DM_ENROLMENT WHERE ID = ?"; + stmt = conn.prepareStatement(sql); + stmt.setInt(1, enrolmentId); + rs = stmt.executeQuery(); + if (rs.next()) { + deviceId = rs.getInt("DEVICE_ID"); + } else { + // if there were no records corresponding to the enrolment id this is a problem. i.e. enrolment + // id is invalid + throw new DeviceManagementDAOException("Error occurred while setting the status of device enrolment: no record for enrolment id " + enrolmentId); + } + DeviceManagementDAOUtil.cleanupResources(stmt, null); + } + + sql = "INSERT INTO DM_DEVICE_STATUS (ENROLMENT_ID, DEVICE_ID, STATUS, UPDATE_TIME, CHANGED_BY) VALUES(?, ?, ?, ?, ?)"; + stmt = conn.prepareStatement(sql); + // TODO: add changed_by + Timestamp updateTime = new Timestamp(new Date().getTime()); + stmt.setInt(1, enrolmentId); + stmt.setInt(2, deviceId); + stmt.setString(3, status.toString()); + stmt.setTimestamp(4, updateTime); + stmt.setString(5, changedBy); + stmt.execute(); + } else { + // no need to update status since the last recorded status is the same as the current status + } + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error occurred while setting the status of device", e); + } finally { + DeviceManagementDAOUtil.cleanupResources(stmt, null); + } + return true; + } @Override public EnrolmentInfo.Status getStatus(int deviceId, String currentOwner, int tenantId) throws DeviceManagementDAOException { @@ -323,7 +407,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { try { conn = this.getConnection(); String sql = "SELECT ID, DEVICE_ID, OWNER, OWNERSHIP, STATUS, IS_TRANSFERRED, DATE_OF_ENROLMENT, " + - "DATE_OF_LAST_UPDATE, TENANT_ID FROM DM_ENROLMENT WHERE DEVICE_ID = ? AND OWNER = ? AND TENANT_ID = ?"; + "DATE_OF_LAST_UPDATE, TENANT_ID FROM DM_ENROLMENT WHERE DEVICE_ID = ? AND OWNER = ? AND TENANT_ID = ?"; stmt = conn.prepareStatement(sql); stmt.setInt(1, deviceId); stmt.setString(2, user); @@ -336,7 +420,7 @@ public class EnrollmentDAOImpl implements EnrollmentDAO { return enrolmentInfos; } catch (SQLException e) { throw new DeviceManagementDAOException("Error occurred while retrieving the enrolments " + - "information of user '" + user + "' upon device '" + deviceId + "'", e); + "information of user '" + user + "' upon device '" + deviceId + "'", e); } finally { DeviceManagementDAOUtil.cleanupResources(stmt, rs); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java index 6265384aad..a8f249359b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderService.java @@ -72,6 +72,7 @@ import org.wso2.carbon.device.mgt.common.policy.mgt.PolicyMonitoringManager; import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationExecutionFailedException; import org.wso2.carbon.device.mgt.common.push.notification.NotificationStrategy; import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dto.DeviceType; import org.wso2.carbon.device.mgt.core.dto.DeviceTypeVersion; @@ -690,6 +691,14 @@ public interface DeviceManagementProviderService { boolean setStatus(String currentOwner, EnrolmentInfo.Status status) throws DeviceManagementException; + List getDeviceStatusHistory(Device device) throws DeviceManagementException; + + List getDeviceStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException; + + List getDeviceCurrentEnrolmentStatusHistory(Device device) throws DeviceManagementException; + + List getDeviceCurrentEnrolmentStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException; + void notifyOperationToDevices(Operation operation, List deviceIds) throws DeviceManagementException; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java index 77b7ac0e08..bb8c5d91e4 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceImpl.java @@ -108,6 +108,7 @@ import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationExecu import org.wso2.carbon.device.mgt.common.pull.notification.PullNotificationSubscriber; import org.wso2.carbon.device.mgt.common.push.notification.NotificationStrategy; import org.wso2.carbon.device.mgt.common.spi.DeviceManagementService; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; import org.wso2.carbon.device.mgt.common.type.mgt.DeviceTypePlatformDetails; import org.wso2.carbon.device.mgt.common.type.mgt.DeviceTypePlatformVersion; import org.wso2.carbon.device.mgt.core.DeviceManagementConstants; @@ -116,12 +117,7 @@ import org.wso2.carbon.device.mgt.core.cache.DeviceCacheKey; import org.wso2.carbon.device.mgt.core.cache.impl.DeviceCacheManagerImpl; import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; import org.wso2.carbon.device.mgt.core.config.DeviceManagementConfig; -import org.wso2.carbon.device.mgt.core.dao.ApplicationDAO; -import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; -import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; -import org.wso2.carbon.device.mgt.core.dao.DeviceTypeDAO; -import org.wso2.carbon.device.mgt.core.dao.EnrollmentDAO; +import org.wso2.carbon.device.mgt.core.dao.*; import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceDetailsMgtException; import org.wso2.carbon.device.mgt.core.device.details.mgt.DeviceInformationManager; @@ -176,6 +172,7 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv private final DeviceTypeDAO deviceTypeDAO; private final EnrollmentDAO enrollmentDAO; private final ApplicationDAO applicationDAO; + private final DeviceStatusDAO deviceStatusDAO; public DeviceManagementProviderServiceImpl() { this.pluginRepository = new DeviceManagementPluginRepository(); @@ -183,6 +180,7 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv this.applicationDAO = DeviceManagementDAOFactory.getApplicationDAO(); this.deviceTypeDAO = DeviceManagementDAOFactory.getDeviceTypeDAO(); this.enrollmentDAO = DeviceManagementDAOFactory.getEnrollmentDAO(); + this.deviceStatusDAO = DeviceManagementDAOFactory.getDeviceStatusDAO(); /* Registering a listener to retrieve events when some device management service plugin is installed after * the component is done getting initialized */ @@ -1770,6 +1768,64 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv DeviceManagementDAOFactory.closeConnection(); } } + @Override + public List getDeviceStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException{ + if (log.isDebugEnabled()) { + log.debug("get status history of device: " + device.getDeviceIdentifier()); + } + try { + int tenantId = this.getTenantId(); + return deviceStatusDAO.getStatus(device.getId(), tenantId, fromDate, toDate); + } catch (DeviceManagementDAOException e) { + DeviceManagementDAOFactory.rollbackTransaction(); + String msg = "Error occurred while retrieving status history"; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } catch (Exception e) { + String msg = "Error occurred in retrieving status history for device :" + device.getDeviceIdentifier(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + } + + @Override + public List getDeviceCurrentEnrolmentStatusHistory(Device device, Date fromDate, Date toDate) throws DeviceManagementException{ + if (log.isDebugEnabled()) { + log.debug("get status history of device: " + device.getDeviceIdentifier()); + } + try { + int tenantId = this.getTenantId(); + EnrolmentInfo enrolmentInfo = device.getEnrolmentInfo(); + if (enrolmentInfo == null) { + enrolmentInfo = enrollmentDAO.getEnrollment(device.getId(), tenantId); + if (enrolmentInfo == null) { + String msg = "Error occurred in getting enrollment for device :" + device.getDeviceIdentifier(); + log.error(msg); + throw new DeviceManagementException(msg); + } + } + return deviceStatusDAO.getStatus(enrolmentInfo.getId(), fromDate, toDate); + } catch (DeviceManagementDAOException e) { + DeviceManagementDAOFactory.rollbackTransaction(); + String msg = "Error occurred while retrieving status history"; + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } catch (Exception e) { + String msg = "Error occurred in retrieving status history for current enrolment of device : " + device.getDeviceIdentifier(); + log.error(msg, e); + throw new DeviceManagementException(msg, e); + } + } + + @Override + public List getDeviceStatusHistory(Device device) throws DeviceManagementException{ + return getDeviceStatusHistory(device, null, null); + } + + @Override + public List getDeviceCurrentEnrolmentStatusHistory(Device device) throws DeviceManagementException{ + return getDeviceCurrentEnrolmentStatusHistory(device, null, null); + } @Override public boolean setStatus(String currentOwner, EnrolmentInfo.Status status) throws DeviceManagementException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java index d991f92957..2fc9e4122e 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/common/BaseDeviceManagementTest.java @@ -158,6 +158,7 @@ public abstract class BaseDeviceManagementTest { PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantDomain(MultitenantConstants .SUPER_TENANT_DOMAIN_NAME); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(MultitenantConstants.SUPER_TENANT_ID); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername("admin"); } protected DataSourceConfig readDataSourceConfig(String configLocation) throws DeviceManagementException { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusPersistenceTests.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusPersistenceTests.java new file mode 100644 index 0000000000..51724f27cf --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/DeviceStatusPersistenceTests.java @@ -0,0 +1,220 @@ +/* + * Copyright (c) 2015, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + * + */ +package org.wso2.carbon.device.mgt.core.dao; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.testng.Assert; +import org.testng.annotations.Test; +import org.testng.annotations.BeforeClass; +import org.wso2.carbon.device.mgt.common.Device; +import org.wso2.carbon.device.mgt.common.EnrolmentInfo; +import org.wso2.carbon.device.mgt.common.type.mgt.DeviceStatus; +import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest; +import org.wso2.carbon.device.mgt.core.common.TestDataHolder; + +import java.sql.SQLException; +import java.util.*; +import java.util.List; +import java.util.stream.Stream; + +import static org.wso2.carbon.device.mgt.common.EnrolmentInfo.Status.*; + +public class DeviceStatusPersistenceTests extends BaseDeviceManagementTest { + + private static final Log log = LogFactory.getLog(DeviceStatusPersistenceTests.class); + private EnrollmentDAO enrollmentDAO = DeviceManagementDAOFactory.getEnrollmentDAO(); + private DeviceStatusDAO deviceStatusDAO = DeviceManagementDAOFactory.getDeviceStatusDAO(); + + /** + * Validate that the list of statuses received match the statuses + * @param device + * @param receivedStatus + * @param statuses + */ + private void validateDeviceStatus(Device device, List receivedStatus, EnrolmentInfo.Status[] statuses){ + Assert.assertEquals(receivedStatus.size(), statuses.length); + for(int i = 0; i < statuses.length; i++) { + Assert.assertEquals(receivedStatus.get(i).getDeviceId(), device.getId()); + Assert.assertEquals(receivedStatus.get(i).getStatus(), statuses[i]); + } + } + + /** + * Validate the list of statuses corresponds to different enrolments and their statuses + * @param device + * @param receivedStatus + * @param statuses + */ + private void validateDeviceStatus(Device device, List receivedStatus, EnrolmentInfo.Status[][] statuses){ + Map> statusMap = new HashMap<>(); + for (DeviceStatus deviceStatus: receivedStatus) { + Assert.assertEquals(deviceStatus.getDeviceId(), device.getId()); + if (!statusMap.containsKey(deviceStatus.getEnrolmentId())){ + statusMap.put(deviceStatus.getEnrolmentId(), new ArrayList<>()); + } + statusMap.get(deviceStatus.getEnrolmentId()).add(deviceStatus); + } + Assert.assertEquals(statusMap.size(), statuses.length); + Integer[] keys = (new TreeSet<>(statusMap.keySet())).toArray(new Integer[]{}); + for(int i = 0; i < keys.length; i++){ + validateDeviceStatus(device, statusMap.get(keys[i]), statuses[i]); + } + } + + @Test + public void testSingleDeviceOneEnrolmentOneStatus(){ + try { + this.initDataSource(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device); + + EnrolmentInfo.Status[] statuses = {ACTIVE}; + int enrolmentId = createNewEnrolmentAddStatuses(device, "admin", statuses); + +// DeviceManagementDAOFactory.openConnection(); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId), statuses); + validateDeviceStatus(device, deviceStatusDAO.getStatus(device.getId(), TestDataHolder.SUPER_TENANT_ID), + new EnrolmentInfo.Status[][]{statuses}); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing datasource", e); +// } finally{ +// DeviceManagementDAOFactory.closeConnection(); + } + } + + @Test + public void testSingleDeviceOneEnrolmentMultipleStatus(){ + try { + this.initDataSource(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device); + EnrolmentInfo.Status[] statuses = {ACTIVE, ASSIGNED, CONFIGURED, READY_TO_CONNECT}; + int enrolmentId = createNewEnrolmentAddStatuses(device, "admin", statuses); +// DeviceManagementDAOFactory.openConnection(); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId),statuses); + validateDeviceStatus(device, deviceStatusDAO.getStatus(device.getId(), TestDataHolder.SUPER_TENANT_ID), + new EnrolmentInfo.Status[][]{statuses}); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing datasource", e); +// } finally{ +// DeviceManagementDAOFactory.closeConnection(); + } + } + + @Test + public void testSingleDeviceMultipleEnrolmentMultipleStatus(){ + try { + this.initDataSource(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + addDevice(device); + + EnrolmentInfo.Status[] statuses1 = {ACTIVE, ASSIGNED, CONFIGURED, READY_TO_CONNECT}; + int enrolmentId1 = createNewEnrolmentAddStatuses(device, "admin1", statuses1); + + EnrolmentInfo.Status[] statuses2 = {CREATED, SUSPENDED, BLOCKED, DEFECTIVE, REMOVED, WARRANTY_REPLACED, BLOCKED}; + int enrolmentId2 = createNewEnrolmentAddStatuses(device, "admin2", statuses2); + + EnrolmentInfo.Status[] statuses3 = {READY_TO_CONNECT, ASSIGNED}; + addStatus(enrolmentId1, statuses3); + + EnrolmentInfo.Status[] statuses1_3 = Stream.concat(Arrays.stream(statuses1), Arrays.stream(statuses3)).toArray(EnrolmentInfo.Status[]::new); + + // introducing a delay so that data is committed to the database before we try to retrieve for validation +// Thread.sleep(5000); + +// DeviceManagementDAOFactory.openConnection(); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId1), statuses1_3); + validateDeviceStatus(device, deviceStatusDAO.getStatus(enrolmentId2), statuses2); + + validateDeviceStatus(device, deviceStatusDAO.getStatus(device.getId(), TestDataHolder.SUPER_TENANT_ID), + new EnrolmentInfo.Status[][]{statuses1_3, statuses2}); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while getting enrolment status", e); + } catch (Exception e) { + log.error("Error occurred while initializing data source", e); +// } finally{ +// DeviceManagementDAOFactory.closeConnection(); + } + } + private int addDevice(Device device) throws DeviceManagementDAOException { + try { + DeviceManagementDAOFactory.openConnection(); + DeviceDAO deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); + int deviceId = deviceDAO.addDevice(TestDataHolder.initialTestDeviceType.getId(), device, TestDataHolder.SUPER_TENANT_ID); + device.setId(deviceId); + return deviceId; + } catch (SQLException e) { + throw new DeviceManagementDAOException("Error while adding device", e); + }finally{ + DeviceManagementDAOFactory.closeConnection(); + } + + } + private int addEnrolment(Device device, String owner, int tenantId, EnrolmentInfo.Status initialStatus) { + int deviceId = device.getId(); + EnrolmentInfo source = new EnrolmentInfo(owner, EnrolmentInfo.OwnerShip.BYOD, initialStatus); + try { + DeviceManagementDAOFactory.openConnection(); + EnrolmentInfo config = enrollmentDAO.addEnrollment(deviceId, source, tenantId); + device.setEnrolmentInfo(config); + return config.getId(); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while adding enrollment", e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } + return -1; + } + + private int createNewEnrolmentAddStatuses(Device device, String user, EnrolmentInfo.Status[] statuses) { + int enrolmentId = this.addEnrolment(device, user, TestDataHolder.SUPER_TENANT_ID, statuses[0]); + Assert.assertNotEquals(enrolmentId, -1); + addStatus(enrolmentId, Arrays.copyOfRange(statuses, 1, statuses.length)); + return enrolmentId; + } + + private void addStatus(int enrolmentId, EnrolmentInfo.Status[] statuses) { + for(int i = 0; i < statuses.length; i++) { + Assert.assertTrue(addStatus(enrolmentId, statuses[i], TestDataHolder.SUPER_TENANT_ID)); + } + } + + private boolean addStatus(int enrolmentId, EnrolmentInfo.Status status, int tenentId){ + try { + DeviceManagementDAOFactory.openConnection(); + return enrollmentDAO.setStatus(enrolmentId, status, tenentId); + } catch (DeviceManagementDAOException | SQLException e) { + log.error("Error occurred while setting status", e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } + return false; + } + + @BeforeClass + @Override + public void init() throws Exception { + + } +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java index 92647cea28..9497324798 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/dao/EnrolmentPersistenceTests.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.LogFactory; import org.testng.Assert; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; +import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest; import org.wso2.carbon.device.mgt.core.common.TestDataHolder; @@ -36,7 +37,7 @@ public class EnrolmentPersistenceTests extends BaseDeviceManagementTest { @Test public void testAddEnrolment() { - int deviceId = TestDataHolder.initialTestDevice.getId(); + int deviceId = -1; String owner = "admin"; /* Initializing source enrolment configuration bean to be tested */ @@ -47,6 +48,10 @@ public class EnrolmentPersistenceTests extends BaseDeviceManagementTest { /* Adding dummy enrolment configuration to the device management metadata store */ try { DeviceManagementDAOFactory.openConnection(); + Device device = TestDataHolder.generateDummyDeviceData(TestDataHolder.TEST_DEVICE_TYPE); + DeviceDAO deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); + deviceId = deviceDAO.addDevice(TestDataHolder.initialTestDeviceType.getId(), device, TestDataHolder.SUPER_TENANT_ID); + device.setId(deviceId); enrollmentDAO.addEnrollment(deviceId, source, TestDataHolder.SUPER_TENANT_ID); } catch (DeviceManagementDAOException | SQLException e) { log.error("Error occurred while adding enrollment", e); @@ -54,16 +59,18 @@ public class EnrolmentPersistenceTests extends BaseDeviceManagementTest { DeviceManagementDAOFactory.closeConnection(); } /* Retrieving the enrolment associated with the given deviceId and owner */ - EnrolmentInfo target = null; - try { - target = this.getEnrolmentConfig(deviceId, owner, TestDataHolder.SUPER_TENANT_ID); - } catch (DeviceManagementDAOException e) { - String msg = "Error occurred while retrieving application info"; - log.error(msg, e); - Assert.fail(msg, e); - } + if (deviceId != -1) { + EnrolmentInfo target = null; + try { + target = this.getEnrolmentConfig(deviceId, owner, TestDataHolder.SUPER_TENANT_ID); + } catch (DeviceManagementDAOException e) { + String msg = "Error occurred while retrieving application info"; + log.error(msg, e); + Assert.fail(msg, e); + } - Assert.assertEquals(target, source, "Enrolment configuration added is not as same as what's retrieved"); + Assert.assertEquals(target, source, "Enrolment configuration added is not as same as what's retrieved"); + } } private EnrolmentInfo getEnrolmentConfig(int deviceId, String currentOwner, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java index 80183bbcff..443c0f8499 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/java/org/wso2/carbon/device/mgt/core/service/DeviceManagementProviderServiceTest.java @@ -74,6 +74,8 @@ import org.wso2.carbon.device.mgt.core.authorization.DeviceAccessAuthorizationSe import org.wso2.carbon.device.mgt.core.common.BaseDeviceManagementTest; import org.wso2.carbon.device.mgt.core.common.TestDataHolder; import org.wso2.carbon.device.mgt.core.config.DeviceConfigurationManager; +import org.wso2.carbon.device.mgt.core.dao.DeviceDAO; +import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException; import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOFactory; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsDAO; import org.wso2.carbon.device.mgt.core.device.details.mgt.dao.DeviceDetailsMgtDAOException; @@ -885,6 +887,28 @@ public class DeviceManagementProviderServiceTest extends BaseDeviceManagementTes public void testUpdateDevicesStatusWithDeviceID() throws DeviceManagementException { if (!isMock()) { Device device = TestDataHolder.generateDummyDeviceData("abc"); + try { + int tenantId = TestDataHolder.SUPER_TENANT_ID; + DeviceManagementDAOFactory.beginTransaction(); + DeviceDAO deviceDAO = DeviceManagementDAOFactory.getDeviceDAO(); + int deviceId = deviceDAO.addDevice(TestDataHolder.initialTestDeviceType.getId(), device, tenantId); + device.setId(deviceId); + int enrolmentId = deviceDAO.addEnrollment(device, tenantId); + device.getEnrolmentInfo().setId(enrolmentId); + DeviceManagementDAOFactory.commitTransaction(); + } catch (DeviceManagementDAOException e) { + DeviceManagementDAOFactory.rollbackTransaction(); + String msg = "Error occurred while adding '" + device.getType() + "' device with the identifier '" + + device.getDeviceIdentifier() + "'"; + log.error(msg, e); + Assert.fail(msg, e); + } catch (TransactionManagementException e) { + String msg = "Error occurred while initiating transaction"; + log.error(msg, e); + Assert.fail(msg, e); + } finally { + DeviceManagementDAOFactory.closeConnection(); + } boolean status = deviceMgtService.setStatus(device, EnrolmentInfo.Status.ACTIVE); Assert.assertTrue(status); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql index cc69537e90..ea5bd1dd33 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/sql/h2.sql @@ -99,7 +99,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT uk_dm_device_enrolment UNIQUE (DEVICE_ID, OWNER, OWNERSHIP, TENANT_ID) ); - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, ENROLMENT_ID INTEGER NOT NULL, diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml index 889a6238b7..dc0dd4d939 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/test/resources/testng.xml @@ -33,6 +33,8 @@ + + diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql index ef684a6685..d99e112c0c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql +++ b/components/device-mgt/org.wso2.carbon.device.mgt.extensions/src/test/resources/sql-files/h2.sql @@ -120,7 +120,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT uk_dm_device_enrolment UNIQUE (DEVICE_ID, OWNER, OWNERSHIP, TENANT_ID) ); - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, ENROLMENT_ID INTEGER NOT NULL, diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java index 450313aac0..b790106c68 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/java/org/wso2/carbon/policy/mgt/core/BasePolicyManagementDAOTest.java @@ -162,6 +162,8 @@ public abstract class BasePolicyManagementDAOTest { .SUPER_TENANT_DOMAIN_NAME); PrivilegedCarbonContext.getThreadLocalCarbonContext().setTenantId(MultitenantConstants.SUPER_TENANT_ID); + PrivilegedCarbonContext.getThreadLocalCarbonContext().setUsername("admin"); + } private DataSource getDataSource(DataSourceConfig config) { diff --git a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql index 4c59adb953..0c5a71aa73 100644 --- a/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql +++ b/components/policy-mgt/org.wso2.carbon.policy.mgt.core/src/test/resources/sql/CreateH2TestDB.sql @@ -107,7 +107,20 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CONSTRAINT fk_dm_device_enrolment FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); - +DROP TABLE IF EXISTS DM_DEVICE_STATUS; +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); DROP TABLE IF EXISTS DM_ENROLMENT_OP_MAPPING; CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql index ed9ce41c7d..6372b87e16 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/h2.sql @@ -108,7 +108,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, CONSTRAINT uk_dm_device_enrolment UNIQUE (DEVICE_ID, OWNER, OWNERSHIP, TENANT_ID) ); - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT fk_dm_device_status_device FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT fk_dm_device_status_enrolment FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE TABLE IF NOT EXISTS DM_ENROLMENT_OP_MAPPING ( ID INTEGER AUTO_INCREMENT NOT NULL, ENROLMENT_ID INTEGER NOT NULL, diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql index f0cbcf2458..bd301d6641 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mssql.sql @@ -120,6 +120,20 @@ CREATE TABLE DM_OPERATION ( PRIMARY KEY (ID) ); +IF NOT EXISTS (SELECT * FROM SYS.OBJECTS WHERE OBJECT_ID = OBJECT_ID(N'[DBO].[DM_DEVICE_STATUS]') AND TYPE IN (N'U')) +CREATE TABLE DM_DEVICE_STATUS ( + ID INTEGER IDENTITY(1,1) NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME DATETIME2 DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); 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 ( diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql index e11919b0a0..b191a177b7 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/mysql.sql @@ -122,7 +122,19 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CONSTRAINT FK_DM_DEVICE_ENROLMENT FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION )ENGINE = InnoDB; - +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER AUTO_INCREMENT NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +)ENGINE = InnoDB; CREATE INDEX IDX_ENROLMENT_FK_DEVICE_ID ON DM_ENROLMENT(DEVICE_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID ON DM_ENROLMENT(DEVICE_ID, TENANT_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID_STATUS ON DM_ENROLMENT(DEVICE_ID, TENANT_ID, STATUS); diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql index 00adba3500..878d609543 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/oracle.sql @@ -197,6 +197,7 @@ WHEN (NEW.ID IS NULL) / + CREATE TABLE DM_ENROLMENT ( ID NUMBER(10) NOT NULL, DEVICE_ID NUMBER(10) NOT NULL, @@ -212,6 +213,18 @@ CREATE TABLE DM_ENROLMENT ( DM_DEVICE (ID) ) / +CREATE TABLE DM_DEVICE_STATUS ( + ID NUMBER(10) NOT NULL, + ENROLMENT_ID NUMBER(10) NOT NULL, + DEVICE_ID NUMBER(10) NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP(0) DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES DM_ENROLMENT (ID) +); +/ -- Generate ID using sequence and trigger CREATE SEQUENCE DM_ENROLMENT_seq START WITH 1 INCREMENT BY 1 NOCACHE / diff --git a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql index d2537af3a8..a9f08b45f8 100644 --- a/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql +++ b/features/device-mgt/org.wso2.carbon.device.mgt.basics.feature/src/main/resources/dbscripts/cdm/postgresql.sql @@ -102,6 +102,8 @@ CREATE TABLE IF NOT EXISTS DM_OPERATION ( PRIMARY KEY (ID) ); + + CREATE SEQUENCE DM_ENROLMENT_seq; CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( @@ -118,7 +120,21 @@ CREATE TABLE IF NOT EXISTS DM_ENROLMENT ( CONSTRAINT FK_DM_DEVICE_ENROLMENT FOREIGN KEY (DEVICE_ID) REFERENCES DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION ); +CREATE SEQUENCE DM_DEVICE_STATUS_seq; +CREATE TABLE IF NOT EXISTS DM_DEVICE_STATUS ( + ID INTEGER DEFAULT NEXTVAL ('DM_DEVICE_STATUS_seq') NOT NULL, + ENROLMENT_ID INTEGER NOT NULL, + DEVICE_ID INTEGER NOT NULL, + STATUS VARCHAR(50) DEFAULT NULL, + UPDATE_TIME TIMESTAMP(0) DEFAULT NULL, + CHANGED_BY VARCHAR(255) NOT NULL, + PRIMARY KEY (ID), + CONSTRAINT FK_DM_DEVICE_STATUS_DEVICE FOREIGN KEY (DEVICE_ID) REFERENCES + DM_DEVICE (ID) ON DELETE NO ACTION ON UPDATE NO ACTION, + CONSTRAINT FK_DM_DEVICE_STATUS_ENROLMENT FOREIGN KEY (ENROLMENT_ID) REFERENCES + DM_ENROLMENT (ID) ON DELETE NO ACTION ON UPDATE NO ACTION +); CREATE INDEX IDX_ENROLMENT_FK_DEVICE_ID ON DM_ENROLMENT(DEVICE_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID ON DM_ENROLMENT(DEVICE_ID, TENANT_ID); CREATE INDEX IDX_ENROLMENT_DEVICE_ID_TENANT_ID_STATUS ON DM_ENROLMENT(DEVICE_ID, TENANT_ID, STATUS);