Merge branch 'pr/temp/feature/subscription/status/change' into 'master'

API to change application subscription status

See merge request entgra/carbon-device-mgt!868
feature/traccar-sync
Pahansith Gunathilake 3 years ago
commit 236d356a26

@ -23,6 +23,7 @@ import java.sql.Timestamp;
public class DeviceSubscriptionData {
private int subId;
private String action;
private long actionTriggeredTimestamp;
private String actionTriggeredBy;
@ -82,4 +83,12 @@ public class DeviceSubscriptionData {
public String getCurrentInstalledVersion() { return currentInstalledVersion; }
public void setCurrentInstalledVersion(String currentInstalledVersion) { this.currentInstalledVersion = currentInstalledVersion; }
public int getSubId() {
return subId;
}
public void setSubId(int subId) {
this.subId = subId;
}
}

@ -0,0 +1,32 @@
package io.entgra.application.mgt.common;
public class OperationStatusBean {
private int operationId;
private String status;
private String operationCode;
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;
}
public String getOperationCode() {
return operationCode;
}
public void setOperationCode(String operationCode) {
this.operationCode = operationCode;
}
}

@ -327,7 +327,9 @@ public interface ApplicationManager {
*/
boolean checkSubDeviceIdsForOperations(int operationId, int deviceId) throws ApplicationManagementException;
void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException;
void updateSubStatus(int deviceId, List<Integer> operationId, String status) throws ApplicationManagementException;
void updateSubsStatus(int deviceId, int operationId, String status) throws ApplicationManagementException;
/**

@ -34,6 +34,16 @@ import java.util.Properties;
*/
public interface SubscriptionManager {
/**
* Use to update status of a subscription
*
* @param deviceId Id of the device
* @param subId subscription id
* @param status status to be changed
*/
void updateSubscriptionStatus(int deviceId, int subId, String status)
throws SubscriptionManagementException;
/**
* Performs bulk subscription operation for a given application and a subscriber list.
* @param applicationUUID UUID of the application to subscribe/unsubscribe

@ -101,6 +101,10 @@ public interface SubscriptionDAO {
List<Integer> getDeviceSubIds(List<Integer> deviceIds, int applicationReleaseId, int tenantId)
throws ApplicationManagementDAOException;
int getDeviceIdForSubId(int subId, int tenantId) throws ApplicationManagementDAOException;
List<Integer> getOperationIdsForSubId(int subId, int tenantId) throws ApplicationManagementDAOException;
List<Integer> getDeviceSubIdsForOperation(int operationId, int deviceID, int tenantId)
throws ApplicationManagementDAOException;

@ -691,6 +691,68 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
}
}
@Override
public int getDeviceIdForSubId(int subId, int tenantId) throws ApplicationManagementDAOException {
try {
Connection conn = this.getDBConnection();
String sql = "SELECT DM_DEVICE_ID "
+ "FROM AP_DEVICE_SUBSCRIPTION "
+ "WHERE ID = ? AND "
+ "TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, subId);
stmt.setInt(2, tenantId);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return rs.getInt("DM_DEVICE_ID");
}
}
return -1;
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to get app operation ids for given "
+ "subscription id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when processing SQL to get operation ids for given subscription id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public List<Integer> getOperationIdsForSubId(int subId, int tenantId) throws ApplicationManagementDAOException {
try {
Connection conn = this.getDBConnection();
List<Integer> operationIds = new ArrayList<>();
String sql = "SELECT OPERATION_ID "
+ "FROM AP_APP_SUB_OP_MAPPING "
+ "WHERE AP_DEVICE_SUBSCRIPTION_ID = ? AND "
+ "TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, subId);
stmt.setInt(2, tenantId);
try (ResultSet rs = stmt.executeQuery()) {
while (rs.next()) {
operationIds.add(rs.getInt("OPERATION_ID"));
}
}
return operationIds;
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to get app operation ids for given "
+ "subscription id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when processing SQL to get operation ids for given subscription id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public List<Integer> getDeviceSubIdsForOperation(int operationId, int deviceId, int tenantId)
throws ApplicationManagementDAOException {

@ -3668,7 +3668,40 @@ ApplicationManagerImpl implements ApplicationManager {
}
@Override
public void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException {
public void updateSubStatus(int deviceId, List<Integer> operationIds, String status) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
try {
ConnectionManagerUtil.beginDBTransaction();
for (int operationId : operationIds) {
List<Integer> deviceSubIds = subscriptionDAO.getDeviceSubIdsForOperation(operationId, deviceId, tenantId);
if (!subscriptionDAO.updateDeviceSubStatus(deviceId, deviceSubIds, status, tenantId)){
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Didn't update an any app subscription of device for operation Id: " + operationId;
log.error(msg);
throw new ApplicationManagementException(msg);
}
}
ConnectionManagerUtil.commitDBTransaction();
} catch (ApplicationManagementDAOException e) {
String msg = "Error occured while updating app subscription status of the device.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while obersving the database connection to update aoo subscription status of "
+ "device.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (TransactionManagementException e) {
String msg = "Error occurred while executing database transaction";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
@Override
public void updateSubsStatus(int deviceId, int operationId, String status) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId();
try {
ConnectionManagerUtil.beginDBTransaction();

@ -20,6 +20,7 @@ package io.entgra.application.mgt.core.impl;
import com.google.gson.Gson;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.apache.commons.lang.StringUtils;
@ -1189,18 +1190,62 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
}
}
@Override
public void updateSubscriptionStatus(int deviceId, int subId, String status)
throws SubscriptionManagementException {
try {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
List<Integer> operationIds = getOperationIdsForSubId(subId, tenantId);
APIUtil.getApplicationManager().updateSubStatus(deviceId, operationIds, status);
} catch (DBConnectionException e) {
String msg = "Error occurred while observing the database connection to get operation Ids for " + subId;
log.error(msg, e);
throw new SubscriptionManagementException(msg, e);
} catch (ApplicationManagementException e) {
String msg = "Error occurred while updating subscription status with the id: "
+ subId;
log.error(msg, e);
throw new SubscriptionManagementException(msg, e);
}
}
private List<Integer> getOperationIdsForSubId(int subId, int tenantId) throws SubscriptionManagementException {
try {
ConnectionManagerUtil.openDBConnection();
return subscriptionDAO.getOperationIdsForSubId(subId, tenantId);
} catch (ApplicationManagementDAOException e) {
String msg = "Error occurred while getting operation Ids for subId" + subId;
log.error(msg, e);
throw new SubscriptionManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while observing the database connection to get operation Ids for " + subId;
log.error(msg, e);
throw new SubscriptionManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
private int invokeIOTCoreAPI(HttpMethodBase request) throws UserStoreException, APIManagerException, IOException {
HttpClient httpClient;
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
ApiApplicationKey apiApplicationKey = OAuthUtils.getClientCredentials(tenantDomain);
String username =
PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getRealmConfiguration()
.getAdminUserName() + Constants.ApplicationInstall.AT + tenantDomain;
AccessTokenInfo tokenInfo = OAuthUtils.getOAuthCredentials(apiApplicationKey, username);
request.addRequestHeader(Constants.ApplicationInstall.AUTHORIZATION,
Constants.ApplicationInstall.AUTHORIZATION_HEADER_VALUE + tokenInfo.getAccessToken());
httpClient = new HttpClient();
httpClient.executeMethod(request);
return request.getStatusCode();
}
public int installEnrollmentApplications(ApplicationPolicyDTO applicationPolicyDTO)
throws ApplicationManagementException {
HttpClient httpClient;
PostMethod request;
try {
String tenantDomain = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantDomain();
ApiApplicationKey apiApplicationKey = OAuthUtils.getClientCredentials(tenantDomain);
String username =
PrivilegedCarbonContext.getThreadLocalCarbonContext().getUserRealm().getRealmConfiguration()
.getAdminUserName() + Constants.ApplicationInstall.AT + tenantDomain;
AccessTokenInfo tokenInfo = OAuthUtils.getOAuthCredentials(apiApplicationKey, username);
String requestUrl = Constants.ApplicationInstall.ENROLLMENT_APP_INSTALL_PROTOCOL + System
.getProperty(Constants.ApplicationInstall.IOT_CORE_HOST) + Constants.ApplicationInstall.COLON
+ System.getProperty(Constants.ApplicationInstall.IOT_CORE_PORT)
@ -1210,14 +1255,9 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
StringRequestEntity requestEntity = new StringRequestEntity(payload, MediaType.APPLICATION_JSON,
Constants.ApplicationInstall.ENCODING);
httpClient = new HttpClient();
request = new PostMethod(requestUrl);
request.addRequestHeader(Constants.ApplicationInstall.AUTHORIZATION,
Constants.ApplicationInstall.AUTHORIZATION_HEADER_VALUE + tokenInfo.getAccessToken());
request.setRequestEntity(requestEntity);
httpClient.executeMethod(request);
return request.getStatusCode();
return invokeIOTCoreAPI(request);
} catch (UserStoreException e) {
String msg = "Error while accessing user store for user with Android device.";
log.error(msg, e);
@ -1240,6 +1280,13 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
}
}
private String getIOTCoreBaseUrl() {
return Constants.HTTPS_PROTOCOL + Constants.SCHEME_SEPARATOR + System
.getProperty(Constants.IOT_CORE_HOST) + Constants.COLON
+ System.getProperty(Constants.IOT_CORE_HTTPS_PORT);
}
@Override
public PaginationResult getAppInstalledDevices(PaginationRequest request, String appUUID)
throws ApplicationManagementException {
@ -1430,6 +1477,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
}
deviceSubscriptionData.setActionType(subscription.getActionTriggeredFrom());
deviceSubscriptionData.setStatus(subscription.getStatus());
deviceSubscriptionData.setSubId(subscription.getId());
deviceSubscriptionDataList.add(deviceSubscriptionData);
break;
}

@ -44,7 +44,11 @@ public class Constants {
public static final String IOT_CORE_HTTPS_PORT = "iot.core.https.port";
public static final String HTTPS_PROTOCOL = "https";
public static final String HTTP_PROTOCOL = "http";
public static final String SCHEME_SEPARATOR = "://";
public static final String OPERATION_STATUS_UPDATE_API_BASE = "/api/device-mgt/v1.0/devices";
public static final String OPERATION_STATUS_UPDATE_API_URI = "/operation";
public static final String COLON = ":";
public static final String FORWARD_SLASH = "/";
public static final String ANY = "ANY";
public static final String DEFAULT_PCK_NAME = "default.app.com";

@ -0,0 +1,43 @@
package io.entgra.application.mgt.store.api.beans;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
/**
* This is used to map the status of subscription.
*/
@ApiModel(
value = "SubscriptionStatusBean",
description = "This class carries all information related map statuses of the subscription."
)
public class SubscriptionStatusBean {
@ApiModelProperty(
name = "sub id",
value = "Subscription Id.",
required = true
)
private int subId;
@ApiModelProperty(
name = "status",
value = "Status of the subscription.",
required = true
)
private String status;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public int getSubId() {
return subId;
}
public void setSubId(int subId) {
this.subId = subId;
}
}

@ -17,6 +17,7 @@
*/
package io.entgra.application.mgt.store.api.services.admin;
import io.entgra.application.mgt.store.api.beans.SubscriptionStatusBean;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
@ -34,6 +35,7 @@ import io.entgra.application.mgt.common.ErrorResponse;
import javax.validation.constraints.Size;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
@ -69,6 +71,13 @@ import java.util.List;
key = "perm:admin:app:subscription:view",
roles = {"Internal/devicemgt-admin"},
permissions = {"/app-mgt/store/admin/subscription/view"}
),
@Scope(
name = "View Application Subscriptions",
description = "View Application Subscriptions.",
key = "perm:admin:app:subscription:modify",
roles = {"Internal/devicemgt-admin"},
permissions = {"/app-mgt/store/admin/subscription/modify"}
)
}
)
@ -79,6 +88,51 @@ public interface SubscriptionManagementAdminAPI {
String SCOPE = "scope";
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Path("/device/{deviceId}/status")
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON,
httpMethod = "PUT",
value = "Update subscription status",
notes = "This will update the subscription status that belongs to the given device id",
tags = "Subscription Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:admin:app:subscription:modify")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully updated subscription status.",
response = List.class,
responseContainer = "List"),
@ApiResponse(
code = 404,
message = "Not Found. \n No Application found which has application release of UUID.",
response = ErrorResponse.class),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred while updating subscription status",
response = ErrorResponse.class)
})
Response updateSubscription(
@ApiParam(
name = "deviceId",
value = "Id of the device",
required = true)
@PathParam("deviceId") int deviceId,
@ApiParam(
name = "subscription status change bean",
value = "this bean contains the information related to status change",
required = true)
SubscriptionStatusBean subscriptionStatusBean
);
@GET
@Path("/{uuid}")
@Produces(MediaType.APPLICATION_JSON)

@ -18,6 +18,8 @@
package io.entgra.application.mgt.store.api.services.impl.admin;
import io.entgra.application.mgt.common.exception.SubscriptionManagementException;
import io.entgra.application.mgt.store.api.beans.SubscriptionStatusBean;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
@ -34,10 +36,12 @@ import org.wso2.carbon.device.mgt.common.PaginationResult;
import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.List;
@ -50,6 +54,30 @@ public class SubscriptionManagementAdminAPIImpl implements SubscriptionManagemen
private static final Log log = LogFactory.getLog(SubscriptionManagementAdminAPIImpl.class);
@Override
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@Path("/device/{deviceId}/status")
public Response updateSubscription(
@PathParam("deviceId") int deviceId,
SubscriptionStatusBean subscriptionStatusBean
) {
try {
RequestValidationUtil.validateSubscriptionStatus(subscriptionStatusBean.getStatus());
SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager();
subscriptionManager.updateSubscriptionStatus(deviceId, subscriptionStatusBean.getSubId(),
subscriptionStatusBean.getStatus());
return Response.status(Response.Status.OK).entity("Subscription status updated successfully").build();
} catch (BadRequestException e) {
return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
} catch (SubscriptionManagementException e) {
String msg = "Error occurred while changing subscription status of the subscription with the id "
+ subscriptionStatusBean.getSubId();
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
@GET
@Consumes("application/json")
@Produces("application/json")

@ -18,12 +18,16 @@
*/
package io.entgra.application.mgt.store.api.services.impl.util;
import org.apache.commons.lang3.EnumUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import io.entgra.application.mgt.core.exception.BadRequestException;
import io.entgra.application.mgt.store.api.util.Constants;
import org.wso2.carbon.device.mgt.common.operation.mgt.ActivityStatus;
import java.util.List;
import java.util.StringJoiner;
public class RequestValidationUtil {
@ -113,4 +117,18 @@ public class RequestValidationUtil {
throw new BadRequestException(msg);
}
}
/**
* Checks if user requested subscription status is valid.
*
*/
public static void validateSubscriptionStatus(String status) throws BadRequestException{
if (!EnumUtils.isValidEnum(ActivityStatus.Status.class, status)) {
List<ActivityStatus.Status> validStatuses = EnumUtils.getEnumList(ActivityStatus.Status.class);
String validStatusesString = StringUtils.join(validStatuses, " | ");
String msg = "Invalid status type: " + status + ". \nValid status types are " + validStatusesString;
log.error(msg);
throw new BadRequestException(msg);
}
}
}

@ -49,6 +49,7 @@
<Scope>perm:admin:app:publisher:update</Scope>
<Scope>perm:admin:app:review:update</Scope>
<Scope>perm:admin:app:subscription:view</Scope>
<Scope>perm:admin:app:subscription:modify</Scope>
<Scope>perm:device-types:types</Scope>
<Scope>perm:enterprise:modify</Scope>
<Scope>perm:enterprise:view</Scope>

@ -67,6 +67,7 @@
<Scope>perm:admin:app:publisher:update</Scope>
<Scope>perm:admin:app:review:update</Scope>
<Scope>perm:admin:app:subscription:view</Scope>
<Scope>perm:admin:app:subscription:modify</Scope>
<Scope>perm:device-types:types</Scope>
<Scope>perm:enterprise:modify</Scope>
<Scope>perm:enterprise:view</Scope>

Loading…
Cancel
Save