diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/beans/OperationStatusBean.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/beans/OperationStatusBean.java new file mode 100644 index 00000000000..302712e5849 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/beans/OperationStatusBean.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2020, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. 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.jaxrs.beans; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +/** + * This is used to map the status of an operation. + */ +@ApiModel( + value = "OperationStatusBean", + description = "This class carries all information related map statuses of the operations." +) +public class OperationStatusBean { + + @ApiModelProperty( + name = "operationId", + value = "Operation Id.", + required = true + ) + int operationId; + + @ApiModelProperty( + name = "status", + value = "Status of the Operation.", + required = true + ) + private String status; + + public int getOperationId() { + return operationId; + } + + public void setOperationId(int operationId) { + this.operationId = operationId; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } +} 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 085e1fe9147..15747cb21c3 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 @@ -64,6 +64,7 @@ import org.wso2.carbon.device.mgt.jaxrs.beans.ApplicationList; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList; import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse; import org.wso2.carbon.device.mgt.jaxrs.beans.OperationRequest; +import org.wso2.carbon.device.mgt.jaxrs.beans.OperationStatusBean; import org.wso2.carbon.device.mgt.jaxrs.util.Constants; import javax.validation.Valid; @@ -2135,4 +2136,56 @@ public interface DeviceManagementService { required = true) @PathParam("packageName") String packageName); + + @PUT + @Path("/{deviceType}/{id}/operation") + @ApiOperation( + produces = MediaType.APPLICATION_JSON, + httpMethod = "PUT", + value = "Update status of a given opeation", + notes = "Updates the status of a given operation of a given device in Entgra IoT Server.", + tags = "Device Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = Constants.SCOPE, value = "perm:devices:operations") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 200, + message = "OK. \n Successfully updated the operation status.", + 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 = 400, + message = "Bad Request. \n Invalid request or validation error.", + response = ErrorResponse.class), + @ApiResponse( + code = 500, + message = "Error occurred while updating operation status.", + response = ErrorResponse.class) + }) + Response updateOperationStatus( + @ApiParam( + name = "device-type", + value = "The device type, such as ios, android, or windows.") + @PathParam("deviceType") String deviceType, + @ApiParam( + name = "id", + value = "The device identifier") + @PathParam("id") String deviceId, + OperationStatusBean operationStatusBean); } 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 936192b8dad..5f748b32c2b 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 @@ -65,6 +65,7 @@ import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceTypeNotFoundException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidConfigurationException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException; +import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException; import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup; import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException; import org.wso2.carbon.device.mgt.common.operation.mgt.Activity; @@ -80,20 +81,22 @@ import org.wso2.carbon.device.mgt.common.search.SearchContext; 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; -import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder; +import org.wso2.carbon.device.mgt.core.dto.DeviceType; import org.wso2.carbon.device.mgt.core.operation.mgt.CommandOperation; import org.wso2.carbon.device.mgt.core.operation.mgt.ConfigOperation; import org.wso2.carbon.device.mgt.core.operation.mgt.ProfileOperation; import org.wso2.carbon.device.mgt.core.search.mgt.SearchManagerService; import org.wso2.carbon.device.mgt.core.search.mgt.SearchMgtException; import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; +import org.wso2.carbon.device.mgt.core.util.DeviceManagerUtil; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceCompliance; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceList; import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse; import org.wso2.carbon.device.mgt.jaxrs.beans.OperationList; import org.wso2.carbon.device.mgt.jaxrs.beans.OperationRequest; -import org.wso2.carbon.device.mgt.jaxrs.beans.ComplianceDeviceList; import org.wso2.carbon.device.mgt.jaxrs.beans.ApplicationList; +import org.wso2.carbon.device.mgt.jaxrs.beans.OperationStatusBean; +import org.wso2.carbon.device.mgt.jaxrs.beans.ComplianceDeviceList; import org.wso2.carbon.device.mgt.jaxrs.service.api.DeviceManagementService; import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.InputValidationException; import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil; @@ -1225,4 +1228,50 @@ public class DeviceManagementServiceImpl implements DeviceManagementService { return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } } + + @PUT + @Path("/{deviceType}/{id}/operation") + @Override + public Response updateOperationStatus( + @PathParam("deviceType") String deviceType, + @PathParam("id") String deviceId, + OperationStatusBean operationStatusBean) { + if (log.isDebugEnabled()) { + log.debug("Requesting device information from " + deviceId); + } + if (operationStatusBean == null) { + String errorMessage = "Request does not contain the required payload."; + log.error(errorMessage); + return Response.status(Response.Status.BAD_REQUEST).entity(errorMessage).build(); + } + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(deviceId, deviceType); + int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId(); + try { + Device device = DeviceMgtAPIUtils.getDeviceManagementService() + .getDevice(deviceIdentifier, false); + DeviceType deviceTypeObj = DeviceManagerUtil.getDeviceType( + deviceType, tenantId); + if (deviceTypeObj == null) { + String msg = "Error, device of type: " + deviceType + " does not exist"; + log.error(msg); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } + Operation operation = DeviceMgtAPIUtils.validateOperationStatusBean(operationStatusBean); + operation.setId(operationStatusBean.getOperationId()); + DeviceMgtAPIUtils.getDeviceManagementService().updateOperation(device, operation); + return Response.status(Response.Status.OK).entity("OperationStatus updated successfully.").build(); + } catch (DeviceManagementException e) { + String msg = "Error occurred when fetching device " + deviceIdentifier.toString(); + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } catch (OperationManagementException e) { + String msg = "Error occurred when updating operation of device " + deviceIdentifier; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } catch (BadRequestException e) { + String msg = "Error occured due to invalid request"; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java index e8efa58d19d..36fd22cca49 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/Constants.java @@ -60,6 +60,15 @@ public class Constants { public static final String WINDOWS = "windows"; + public final class OperationStatus { + private OperationStatus () { throw new AssertionError(); } + public static final String COMPLETED = "completed"; + public static final String ERROR = "error"; + public static final String IN_PROGRESS = "in_progress"; + public static final String PENDING = "pending"; + public static final String NOTNOW = "notnow"; + public static final String REPEATED = "repeated"; + } public final class ErrorMessages { private ErrorMessages () { throw new AssertionError(); } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java index 6aec717dd99..9be33cd3977 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/util/DeviceMgtAPIUtils.java @@ -38,6 +38,7 @@ import org.wso2.carbon.core.util.Utils; import org.wso2.carbon.device.mgt.analytics.data.publisher.service.EventsPublisherService; import org.wso2.carbon.device.mgt.common.Device; import org.wso2.carbon.device.mgt.common.DeviceIdentifier; +import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.EnrolmentInfo; import org.wso2.carbon.device.mgt.common.authorization.DeviceAccessAuthorizationService; @@ -46,6 +47,7 @@ import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfiguration import org.wso2.carbon.device.mgt.common.configuration.mgt.PlatformConfigurationManagementService; import org.wso2.carbon.device.mgt.common.geo.service.GeoLocationProviderService; import org.wso2.carbon.device.mgt.common.notification.mgt.NotificationManagementService; +import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; import org.wso2.carbon.device.mgt.common.permission.mgt.PermissionManagerService; import org.wso2.carbon.device.mgt.common.report.mgt.ReportManagementService; import org.wso2.carbon.device.mgt.common.spi.DeviceTypeGeneratorService; @@ -58,6 +60,7 @@ import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService; import org.wso2.carbon.device.mgt.jaxrs.beans.DeviceTypeVersionWrapper; import org.wso2.carbon.device.mgt.jaxrs.beans.ErrorResponse; +import org.wso2.carbon.device.mgt.jaxrs.beans.OperationStatusBean; import org.wso2.carbon.device.mgt.jaxrs.beans.analytics.EventAttributeList; import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.InputValidationException; import org.wso2.carbon.event.processor.stub.EventProcessorAdminServiceStub; @@ -816,4 +819,48 @@ public class DeviceMgtAPIUtils { } } } + + /** + * This method validates the status of the operation + * + * @param operationStatusBean {@link OperationStatusBean} object + * @return {@link Operation} Returns Operation object with status set. + * @throws {@link BadRequestException} If invalid status received + */ + public static Operation validateOperationStatusBean(OperationStatusBean operationStatusBean) + throws BadRequestException { + Operation operation = new Operation(); + if (operationStatusBean.getStatus() != null) { + switch (operationStatusBean.getStatus().toLowerCase()) { + case Constants.OperationStatus.COMPLETED: + operation.setStatus(Operation.Status.COMPLETED); + break; + case Constants.OperationStatus.ERROR: + operation.setStatus(Operation.Status.ERROR); + break; + case Constants.OperationStatus.IN_PROGRESS: + operation.setStatus(Operation.Status.IN_PROGRESS); + break; + case Constants.OperationStatus.PENDING: + operation.setStatus(Operation.Status.PENDING); + break; + case Constants.OperationStatus.NOTNOW: + operation.setStatus(Operation.Status.NOTNOW); + break; + case Constants.OperationStatus.REPEATED: + operation.setStatus(Operation.Status.REPEATED); + break; + default: + String msg = "Invalid operation status. Valid operations: " + + "[IN_PROGRESS, PENDING, COMPLETED, ERROR, REPEATED, NOTNOW]"; + log.error(msg); + throw new BadRequestException(msg); + } + } else { + String msg = "Payload does not contain status value"; + log.error(msg); + throw new BadRequestException(msg); + } + return operation; + } } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java index 3b9a24385fd..3edfb201618 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/operation/mgt/OperationManager.java @@ -122,4 +122,14 @@ public interface OperationManager { */ NotificationStrategy getNotificationStrategy(); -} \ No newline at end of file + /** + * Check if an operation exists for a given device identifier and operation id + * + * @param deviceId Device identifier of the device + * @param operationId Id of the operation + * @return true if operation already exists, else false + * @throws {@link OperationManagementException} + */ + boolean isOperationExist(DeviceIdentifier deviceId, int operationId) throws OperationManagementException; + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java index cb7c7a84f34..f1cdd695a9c 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/operation/mgt/OperationManagerImpl.java @@ -1209,4 +1209,51 @@ public class OperationManagerImpl implements OperationManager { } return operations; } + + @Override + public boolean isOperationExist(DeviceIdentifier deviceId, int operationId) + throws OperationManagementException { + if (log.isDebugEnabled()) { + log.debug("Operation Id: " + operationId + " Device Type: " + deviceId.getType() + " Device Identifier: " + + deviceId.getId()); + } + if (!isActionAuthorized(deviceId)) { + String msg = "User '" + getUser() + "' is not authorized to access the '" + + deviceId.getType() + "' device, which carries the identifier '" + + deviceId.getId() + "'"; + log.error(msg); + throw new OperationManagementException(msg); + } + EnrolmentInfo enrolmentInfo = this.getActiveEnrolmentInfo(deviceId); + if (enrolmentInfo == null) { + String msg = "Device not found for given device identifier: " + + deviceId.getId() + " type: " + deviceId.getType(); + log.error(msg); + throw new OperationManagementException(msg); + } + + try { + OperationManagementDAOFactory.openConnection(); + org.wso2.carbon.device.mgt.core.dto.operation.mgt.Operation deviceSpecificOperation = operationDAO. + getOperationByDeviceAndId(enrolmentInfo.getId(), + operationId); + if (deviceSpecificOperation == null) { + return false; + } else { + return true; + } + } catch (OperationManagementDAOException e) { + String msg = "Error occurred while checking if operation with operation id " + + operationId +" exist for " + deviceId.getType() + "' device '" + deviceId.getId() + "'"; + log.error(msg, e); + throw new OperationManagementException(msg, e); + } catch (SQLException e) { + String msg = "Error occurred while opening connection to the data source"; + log.error(msg, e); + throw new OperationManagementException(msg, + e); + } finally { + OperationManagementDAOFactory.closeConnection(); + } + } } 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 7c806b7ac5f..a2f42c4e371 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 @@ -903,4 +903,14 @@ public interface DeviceManagementProviderService { List getAppVersions(String packageName) throws ApplicationManagementException; int getFunctioningDevicesInSystem() throws DeviceManagementException; + + /** + * Check if an operation exists for a given device identifier and operation id + * + * @param deviceId Device identifier of the device + * @param operationId Id of the operation + * @return true if operation already exists, else false + * @throws {@link OperationManagementException} + */ + boolean isOperationExist(DeviceIdentifier deviceId, int operationId) throws OperationManagementException; } 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 1c5f3ec73a0..26dae9327e6 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 @@ -92,6 +92,7 @@ import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException; import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException; import org.wso2.carbon.device.mgt.common.exceptions.UnauthorizedDeviceAccessException; import org.wso2.carbon.device.mgt.common.exceptions.UserNotFoundException; +import org.wso2.carbon.device.mgt.common.exceptions.BadRequestException; import org.wso2.carbon.device.mgt.common.app.mgt.Application; import org.wso2.carbon.device.mgt.common.configuration.mgt.AmbiguousConfigurationException; import org.wso2.carbon.device.mgt.common.configuration.mgt.ConfigurationEntry; @@ -1916,9 +1917,17 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv "Device not found for device id:" + device.getDeviceIdentifier() + " " + "type:" + device.getType()); } - pluginRepository.getOperationManager(device.getType(), this.getTenantId()) - .updateOperation(enrolmentInfo.getId(), operation); try { + DeviceIdentifier deviceIdentifier = new DeviceIdentifier(device.getDeviceIdentifier(), device.getType()); + if (!pluginRepository.getOperationManager(device.getType(), this.getTenantId()) + .isOperationExist(deviceIdentifier, operation.getId())) { + String msg = "Operation with operation id: " + operation.getId() + + " does not exist."; + log.error(msg); + throw new BadRequestException(msg); + } + pluginRepository.getOperationManager(device.getType(), this.getTenantId()) + .updateOperation(enrolmentInfo.getId(), operation); if (DeviceManagerUtil.isPublishOperationResponseEnabled()) { List permittedOperations = DeviceManagerUtil.getEnabledOperationsForResponsePublish(); if (permittedOperations.contains(operation.getCode()) @@ -1945,6 +1954,10 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv String msg = "Error occurred while publishing event."; log.error(msg, e); throw new OperationManagementException(msg, e); + } catch (BadRequestException e) { + String msg = "Error occured due to invalid request"; + log.error(msg, e); + throw new OperationManagementException(msg, e); } } @@ -4169,4 +4182,10 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv DeviceManagementDAOFactory.closeConnection(); } } + + @Override + public boolean isOperationExist(DeviceIdentifier deviceId, int operationId) throws OperationManagementException { + return pluginRepository.getOperationManager(deviceId.getType(), this.getTenantId()) + .isOperationExist(deviceId, operationId); + } }