Merge remote-tracking branch 'support/support-4.1.15' into support-4.1.15

4.x.x
Charitha Goonetilleke 3 years ago
commit bc4e76eed7

@ -25,3 +25,4 @@ deploy:
- mvn $MAVEN_CLI_OPTS deploy -Dmaven.test.skip=true
only:
- master@entgra/carbon-device-mgt
- kernel-4.6.x@entgra/carbon-device-mgt

@ -24,8 +24,10 @@ import org.wso2.carbon.device.application.mgt.common.exception.SubscriptionManag
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.mgt.common.app.mgt.App;
import java.util.List;
import java.util.Properties;
/**
* This interface manages all the operations related with ApplicationDTO Subscription.
@ -33,22 +35,22 @@ import java.util.List;
public interface SubscriptionManager {
/**
* Performs bulk subscription operation for a given application and a subscriber list.
*
* @param applicationUUID UUID of the application to subscribe/unsubscribe
* @param applicationUUID UUID of the application to subscribe/unsubscribe
* @param params list of subscribers. This list can be of either
* {@link org.wso2.carbon.device.mgt.common.DeviceIdentifier} if {@param subType} is equal
* {@link DeviceIdentifier} if {@param subType} is equal
* to DEVICE or
* {@link String} if {@param subType} is USER, ROLE or GROUP
* @param subType subscription type. E.g. <code>DEVICE, USER, ROLE, GROUP</code> {@see {
* @param action subscription action. E.g. <code>INSTALL/UNINSTALL</code> {@see {
* @param <T> generic type of the method.
* @return {@link ApplicationInstallResponse}
* @throws ApplicationManagementException if error occurs when subscribing to the given application
* @link org.wso2.carbon.device.application.mgt.common.SubscriptionType}}
* @link org.wso2.carbon.device.application.mgt.common.SubAction}}
* @param action subscription action. E.g. <code>INSTALL/UNINSTALL</code> {@see {
* @param <T> generic type of the method.
* @return {@link ApplicationInstallResponse}
* @throws ApplicationManagementException if error occurs when subscribing to the given application
* @link org.wso2.carbon.device.application.mgt.common.SubscriptionType}}
* @link org.wso2.carbon.device.application.mgt.common.SubAction}}
* @param properties
*/
<T> ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List<T> params, String subType,
String action) throws ApplicationManagementException;
String action, Properties properties) throws ApplicationManagementException;
/**
* Create an entry related to the scheduled task in the database.
@ -121,7 +123,7 @@ public interface SubscriptionManager {
* @throws ApplicationManagementException if error occurred while installing given applications into the given
* device
*/
void installAppsForDevice(DeviceIdentifier deviceIdentifier, List<String> releaseUUID)
void installAppsForDevice(DeviceIdentifier deviceIdentifier, List<App> apps)
throws ApplicationManagementException;
/***
@ -161,4 +163,16 @@ public interface SubscriptionManager {
*/
PaginationResult getAppSubscriptionDetails(PaginationRequest request, String appUUID, String actionStatus, String action)
throws ApplicationManagementException;
/***
* This method is responsible to provide application subscription devices data for given application release UUID.
* @param request PaginationRequest object holding the data for pagination
* @param appUUID UUID of the application release.
* @param subType subscription type of the application(eg: GROUP, USER, ...)
* @param subTypeName subscription type name of the application (Name of the group, Name of the user, ...).
* @return {@link PaginationResult} pagination result of the category details.
* @throws {@link ApplicationManagementException} Exception of the application management
*/
PaginationResult getAppInstalledSubscribeDevices(PaginationRequest request, String appUUID, String subType,
String subTypeName) throws ApplicationManagementException;
}

@ -228,4 +228,16 @@ public interface SubscriptionDAO {
throws ApplicationManagementDAOException;
int getSubscribedGroupCount(int appReleaseId, int tenantId) throws ApplicationManagementDAOException;
/**
* This method is used to get the details of subscribed groups
*
* @param tenantId id of the current tenant
* @param appReleaseId id of the application release..
* @param subtype application subscribed type.
* @return subscribedDevices - list of app subscribed devices under the subtype.
* @throws {@link ApplicationManagementDAOException} if connections establishment fails.
*/
List<Integer> getAppSubscribedDevicesForGroups(int appReleaseId, String subtype, int tenantId)
throws ApplicationManagementDAOException;
}

@ -1271,4 +1271,45 @@ public class GenericSubscriptionDAOImpl extends AbstractDAOImpl implements Subsc
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public List<Integer> getAppSubscribedDevicesForGroups(int appReleaseId, String subType, int tenantId)
throws ApplicationManagementDAOException {
if (log.isDebugEnabled()) {
log.debug("Request received in DAO Layer to get already subscribed devices for " +
"given app release id.");
}
// retrieve all device list by action triggered type and app release id
try {
Connection conn = this.getDBConnection();
List<Integer> subscribedGroupDevices = new ArrayList<>();
String sql = "SELECT "
+ "AP_DEVICE_SUBSCRIPTION.DM_DEVICE_ID AS DEVICES "
+ "FROM AP_DEVICE_SUBSCRIPTION "
+ "WHERE "
+ "AP_APP_RELEASE_ID = ? AND ACTION_TRIGGERED_FROM=? AND "
+ "UNSUBSCRIBED=FALSE AND TENANT_ID = ?";
try (PreparedStatement ps = conn.prepareStatement(sql)) {
ps.setInt(1, appReleaseId);
ps.setString(2, subType.toLowerCase());;
ps.setInt(3, tenantId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
subscribedGroupDevices.add(rs.getInt("DEVICES"));
}
}
return subscribedGroupDevices;
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining the DB connection to get already " +
"subscribed groups for given app release id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "SQL Error occurred while getting subscribed devices for given " +
"app release id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
}

@ -121,7 +121,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
@Override
public <T> ApplicationInstallResponse performBulkAppOperation(String applicationUUID, List<T> params,
String subType, String action) throws ApplicationManagementException {
String subType, String action, Properties properties) throws ApplicationManagementException {
if (log.isDebugEnabled()) {
log.debug("Install application release which has UUID " + applicationUUID + " to " + params.size()
+ " users.");
@ -134,7 +134,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
params);
ApplicationInstallResponse applicationInstallResponse = performActionOnDevices(
applicationSubscriptionInfo.getAppSupportingDeviceTypeName(), applicationSubscriptionInfo.getDevices(),
applicationDTO, subType, applicationSubscriptionInfo.getSubscribers(), action);
applicationDTO, subType, applicationSubscriptionInfo.getSubscribers(), action, properties);
applicationInstallResponse.setErrorDeviceIdentifiers(applicationSubscriptionInfo.getErrorDeviceIdentifiers());
return applicationInstallResponse;
@ -347,7 +347,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
}
@Override
public void installAppsForDevice(DeviceIdentifier deviceIdentifier, List<String> releaseUUIDs)
public void installAppsForDevice(DeviceIdentifier deviceIdentifier, List<App> apps)
throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
@ -370,7 +370,8 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
List<DeviceIdentifier> appInstallingDevices = new ArrayList<>();
for (String releaseUUID : releaseUUIDs) {
for (App app : apps) {
String releaseUUID = app.getId();
try {
ConnectionManagerUtil.openDBConnection();
ApplicationDTO applicationDTO = this.applicationDAO.getAppWithRelatedRelease(releaseUUID, tenantId);
@ -409,7 +410,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
if (!appInstallingDevices.isEmpty()) {
performBulkAppOperation(releaseUUID, appInstallingDevices, SubscriptionType.DEVICE.toString(),
SubAction.INSTALL.toString());
SubAction.INSTALL.toString(), app.getProperties());
}
}
}
@ -621,7 +622,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
* data.
*/
private ApplicationInstallResponse performActionOnDevices(String deviceType, List<Device> devices,
ApplicationDTO applicationDTO, String subType, List<String> subscribers, String action)
ApplicationDTO applicationDTO, String subType, List<String> subscribers, String action, Properties properties)
throws ApplicationManagementException {
//Get app subscribing info of each device
@ -667,11 +668,11 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
}
for (Map.Entry<String, List<DeviceIdentifier>> entry : deviceIdentifierMap.entrySet()) {
Activity activity = addAppOperationOnDevices(applicationDTO, new ArrayList<>(entry.getValue()),
entry.getKey(), action);
entry.getKey(), action, properties);
activityList.add(activity);
}
} else {
Activity activity = addAppOperationOnDevices(applicationDTO, deviceIdentifiers, deviceType, action);
Activity activity = addAppOperationOnDevices(applicationDTO, deviceIdentifiers, deviceType, action, properties);
activityList.add(activity);
}
@ -982,13 +983,13 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
* @throws ApplicationManagementException if found an invalid device.
*/
private Activity addAppOperationOnDevices(ApplicationDTO applicationDTO,
List<DeviceIdentifier> deviceIdentifierList, String deviceType, String action)
List<DeviceIdentifier> deviceIdentifierList, String deviceType, String action, Properties properties)
throws ApplicationManagementException {
DeviceManagementProviderService deviceManagementProviderService = HelperUtil
.getDeviceManagementProviderService();
try {
Application application = APIUtil.appDtoToAppResponse(applicationDTO);
Operation operation = generateOperationPayloadByDeviceType(deviceType, application, action);
Operation operation = generateOperationPayloadByDeviceType(deviceType, application, action, properties);
return deviceManagementProviderService.addOperation(deviceType, operation, deviceIdentifierList);
} catch (OperationManagementException e) {
String msg = "Error occurred while adding the application install operation to devices";
@ -1010,7 +1011,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
* @throws ApplicationManagementException if unknown application type is found to generate operation payload or
* invalid action is found to generate operation payload.
*/
private Operation generateOperationPayloadByDeviceType(String deviceType, Application application, String action)
private Operation generateOperationPayloadByDeviceType(String deviceType, Application application, String action, Properties properties)
throws ApplicationManagementException {
try {
if (ApplicationType.CUSTOM.toString().equalsIgnoreCase(application.getType())) {
@ -1046,6 +1047,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
app.setLocation(application.getApplicationReleases().get(0).getInstallerPath());
app.setIdentifier(application.getPackageName());
app.setName(application.getName());
app.setProperties(properties);
if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) {
return MDMAndroidOperationUtil.createInstallAppOperation(app);
} else {
@ -1067,7 +1069,6 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
app.setType(mobileAppType);
app.setLocation(plistDownloadEndpoint);
app.setIconImage(application.getApplicationReleases().get(0).getIconPath());
Properties properties = new Properties();
properties.put(MDMAppConstants.IOSConstants.IS_PREVENT_BACKUP, true);
properties.put(MDMAppConstants.IOSConstants.IS_REMOVE_APP, true);
properties.put(MDMAppConstants.IOSConstants.I_TUNES_ID, application.getPackageName());
@ -1427,4 +1428,50 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
ConnectionManagerUtil.closeDBConnection();
}
}
@Override
public PaginationResult getAppInstalledSubscribeDevices(PaginationRequest request, String appUUID, String subType,
String subTypeName) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
DeviceManagementProviderService deviceManagementProviderService = HelperUtil
.getDeviceManagementProviderService();
try {
ConnectionManagerUtil.openDBConnection();
ApplicationDTO applicationDTO = this.applicationDAO.getAppWithRelatedRelease(appUUID, tenantId);
int applicationReleaseId = applicationDTO.getApplicationReleaseDTOs().get(0).getId();
List<Integer> subscriptionDeviceList = new ArrayList<>();
//todo update the API for other subscription types
if (SubscriptionType.GROUP.toString().equalsIgnoreCase(subType)) {
subscriptionDeviceList = subscriptionDAO
.getAppSubscribedDevicesForGroups(applicationReleaseId, subType, tenantId);
} else {
String msg = "Found invalid sub type: " + subType;
log.error(msg);
throw new NotFoundException(msg);
}
if (subscriptionDeviceList.isEmpty()) {
PaginationResult paginationResult = new PaginationResult();
paginationResult.setData(subscriptionDeviceList);
paginationResult.setRecordsFiltered(0);
paginationResult.setRecordsTotal(0);
return paginationResult;
}
return deviceManagementProviderService.getDevicesDetails(request, subscriptionDeviceList, subTypeName);
} catch (DeviceManagementException e) {
String msg = "service error occurred while getting device data from the device management service.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (ApplicationManagementDAOException e) {
String msg = "Error occurred when get application release devices data for application release UUID: "
+ appUUID;
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "DB Connection error occurred while getting category details that given application id";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
}

@ -37,6 +37,7 @@ import org.wso2.carbon.device.mgt.core.task.impl.RandomlyAssignedScheduleTask;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -45,6 +46,7 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask {
private static final String TASK_NAME = "SCHEDULE_APP_SUBSCRIPTION";
private SubscriptionManager subscriptionManager;
private String payload;
private String subscribers;
private String subscriptionType;
private String application;
@ -57,6 +59,7 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask {
@Override
public void setProperties(Map<String, String> map) {
this.subscribers = map.get(Constants.SUBSCRIBERS);
this.payload = map.get(Constants.PAYLOAD);
this.subscriptionType = map.get(Constants.SUB_TYPE);
this.application = map.get(Constants.APP_UUID);
this.action = map.get(Constants.ACTION);
@ -89,8 +92,9 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask {
new TypeToken<List<DeviceIdentifier>>() {
}.getType());
try {
Properties properties = new Gson().fromJson(payload, Properties.class);
subscriptionManager.performBulkAppOperation(this.application, deviceIdentifiers,
this.subscriptionType, this.action);
this.subscriptionType, this.action, properties);
subscriptionDTO.setStatus(ExecutionStatus.EXECUTED);
} catch (ApplicationManagementException e) {
log.error(
@ -102,8 +106,9 @@ public class ScheduledAppSubscriptionTask extends RandomlyAssignedScheduleTask {
List<String> subscriberList = Pattern.compile(",").splitAsStream(this.subscribers).collect(
Collectors.toList());
try {
Properties properties = new Gson().fromJson(payload, Properties.class);
subscriptionManager.performBulkAppOperation(this.application, subscriberList,
this.subscriptionType, this.action);
this.subscriptionType, this.action, properties);
subscriptionDTO.setStatus(ExecutionStatus.EXECUTED);
} catch (ApplicationManagementException e) {
log.error(

@ -46,6 +46,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.stream.Collectors;
public class ScheduledAppSubscriptionTaskManager {
@ -73,7 +74,7 @@ public class ScheduledAppSubscriptionTaskManager {
* @throws ApplicationOperationTaskException if error occurred while scheduling the subscription
*/
public void scheduleAppSubscriptionTask(String applicationUUID, List<?> subscribers,
SubscriptionType subscriptionType, SubAction action, long timestamp)
SubscriptionType subscriptionType, SubAction action, long timestamp, Properties properties)
throws ApplicationOperationTaskException {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date(timestamp * 1000));
@ -106,7 +107,6 @@ public class ScheduledAppSubscriptionTaskManager {
taskProperties.put(Constants.APP_UUID, applicationUUID);
taskProperties.put(Constants.TENANT_DOMAIN, carbonContext.getTenantDomain(true));
taskProperties.put(Constants.SUBSCRIBER, carbonContext.getUsername());
String subscribersString;
if (SubscriptionType.DEVICE.equals(subscriptionType)) {
subscribersString = new Gson().toJson(subscribers);
@ -115,6 +115,10 @@ public class ScheduledAppSubscriptionTaskManager {
subscribersString = subscribers.stream().map(String.class::cast).collect(Collectors.joining(","));
taskProperties.put(Constants.SUBSCRIBERS, subscribersString);
}
if(properties != null) {
String payload = new Gson().toJson(properties);
taskProperties.put(Constants.PAYLOAD, payload);
}
if (log.isDebugEnabled()) {
log.debug("Scheduling a task to " + action.toString() + " application: " + applicationUUID +
" to/from the following " + subscriptionType.toString() + "S [" + subscribersString + "] at: "

@ -58,6 +58,7 @@ public class Constants {
public static final String SUB_TYPE = "SUBSCRIPTION_TYPE";
public static final String ACTION = "ACTION";
public static final String APP_UUID = "APP_UUID";
public static final String APP_PROPERTIES = "APP_PROPERTIES";
public static final String SUBSCRIBER = "SUBSCRIBER";
public static final String TENANT_DOMAIN = "TENANT_DOMAIN";
public static final String TENANT_ID = "__TENANT_ID_PROP__";

@ -130,7 +130,12 @@ public interface SubscriptionManagementAPI {
name = "timestamp",
value = "Timestamp of scheduled install/uninstall operation"
)
@QueryParam("timestamp") long timestamp
@QueryParam("timestamp") long timestamp,
@ApiParam(
name = "block-uninstall",
value = "App removal status of the install operation"
)
@QueryParam("block-uninstall") Boolean isUninstallBlocked
);
@POST
@ -183,7 +188,12 @@ public interface SubscriptionManagementAPI {
name = "timestamp",
value = "Timestamp of scheduled install/uninstall operation"
)
@QueryParam("timestamp") long timestamp
@QueryParam("timestamp") long timestamp,
@ApiParam(
name = "block-uninstall",
value = "App removal status of the install operation"
)
@QueryParam("block-uninstall") Boolean isUninstallBlocked
);
@POST
@ -446,4 +456,101 @@ public interface SubscriptionManagementAPI {
defaultValue = "5")
@QueryParam("limit") int limit
);
@GET
@Path("/{uuid}/{subType}/{subTypeName}/devices")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@ApiOperation(
consumes = MediaType.APPLICATION_JSON,
produces = MediaType.APPLICATION_JSON,
httpMethod = "GET",
value = "Get device details in categories that have a given application install",
notes = "This will get the category's device details that have a given application install, if exists",
tags = "Subscription Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = SCOPE, value = "perm:app:subscription:uninstall")
})
}
)
@ApiResponses(
value = {
@ApiResponse(
code = 200,
message = "OK. \n Successfully retrieved device details.",
response = List.class,
responseContainer = "List"),
@ApiResponse(
code = 404,
message = "Not Found. \n No Devices found which has application " +
"release of UUID.",
response = ErrorResponse.class),
@ApiResponse(
code = 400,
message = "Bad Request. \n Found invalid payload with the request.",
response = List.class),
@ApiResponse(
code = 403,
message = "Forbidden. \n Don't have permission to get the details.",
response = List.class),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Error occurred while getting data",
response = ErrorResponse.class)
})
Response getAppInstalledDevicesOnCategories(
@ApiParam(
name="uuid",
value="uuid of the application release.",
required = true)
@PathParam("uuid") String uuid,
@ApiParam(
name="subType",
value="Subscription type of the application release.",
required = true)
@PathParam("subType") String subType,
@ApiParam(
name="subTypeName",
value="Subscription type name of the application release.",
required = true)
@PathParam("subTypeName") String subTypeName,
@ApiParam(
name = "offset",
value = "The starting pagination index for the complete list of qualified items.",
defaultValue = "0")
@QueryParam("offset") int offset,
@ApiParam(
name = "limit",
value = "Provide how many device details you require from the starting " +
"pagination index/offset.",
defaultValue = "5")
@QueryParam("limit") int limit,
@ApiParam(
name = "name",
value = "The device name. For example, Nexus devices can have names, such as shamu, bullhead or angler.",
required = false)
@Size(max = 45)
String name,
@ApiParam(
name = "user",
value = "The username of the owner of the device.",
required = false)
@QueryParam("user")
String user,
@ApiParam(
name = "ownership",
allowableValues = "BYOD, COPE",
value = "Provide the ownership status of the device. The following values can be assigned:\n" +
"- BYOD: Bring Your Own Device\n" +
"- COPE: Corporate-Owned, Personally-Enabled",
required = false)
@QueryParam("ownership")
@Size(max = 45)
String ownership,
@ApiParam(
name = "status",
value = "Provide the device status details, such as active or inactive.")
@QueryParam("status") List<String> status
);
}

@ -34,8 +34,6 @@ import org.wso2.carbon.device.application.mgt.common.BasicUserInfoList;
import org.wso2.carbon.device.application.mgt.common.RoleList;
import org.wso2.carbon.device.application.mgt.common.DeviceGroupList;
import org.wso2.carbon.device.application.mgt.store.api.services.impl.util.RequestValidationUtil;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult;
import org.wso2.carbon.device.application.mgt.core.exception.BadRequestException;
import org.wso2.carbon.device.application.mgt.core.exception.ForbiddenException;
import org.wso2.carbon.device.application.mgt.core.exception.NotFoundException;
@ -44,6 +42,9 @@ import org.wso2.carbon.device.application.mgt.core.util.APIUtil;
import org.wso2.carbon.device.application.mgt.store.api.services.SubscriptionManagementAPI;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.DeviceIdentifier;
import org.wso2.carbon.device.mgt.common.MDMAppConstants;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationResult;
import javax.validation.Valid;
import javax.ws.rs.Path;
@ -55,9 +56,8 @@ import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.core.Response;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Properties;
/**
* Implementation of Subscription Management related APIs.
@ -75,16 +75,22 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{
@PathParam("uuid") String uuid,
@PathParam("action") String action,
@Valid List<DeviceIdentifier> deviceIdentifiers,
@QueryParam("timestamp") long timestamp) {
@QueryParam("timestamp") long timestamp,
@QueryParam("block-uninstall") Boolean isUninstallBlocked
) {
Properties properties = new Properties();
if(isUninstallBlocked != null) {
properties.put(MDMAppConstants.AndroidConstants.IS_BLOCK_UNINSTALL, isUninstallBlocked);
}
try {
if (0 == timestamp) {
SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager();
ApplicationInstallResponse response = subscriptionManager
.performBulkAppOperation(uuid, deviceIdentifiers, SubscriptionType.DEVICE.toString(), action);
.performBulkAppOperation(uuid, deviceIdentifiers, SubscriptionType.DEVICE.toString(), action, properties);
return Response.status(Response.Status.OK).entity(response).build();
} else {
return scheduleApplicationOperationTask(uuid, deviceIdentifiers, SubscriptionType.DEVICE,
SubAction.valueOf(action.toUpperCase()), timestamp);
SubAction.valueOf(action.toUpperCase()), timestamp, properties);
}
} catch (NotFoundException e) {
String msg = "Couldn't found an application release for UUI: " + uuid;
@ -116,17 +122,23 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{
@PathParam("subType") String subType,
@PathParam("action") String action,
@Valid List<String> subscribers,
@QueryParam("timestamp") long timestamp) {
@QueryParam("timestamp") long timestamp,
@QueryParam("block-uninstall") Boolean isUninstallBlocked
) {
Properties properties = new Properties();
if(isUninstallBlocked != null) {
properties.put(MDMAppConstants.AndroidConstants.IS_BLOCK_UNINSTALL, isUninstallBlocked);
}
try {
if (0 == timestamp) {
SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager();
ApplicationInstallResponse response = subscriptionManager
.performBulkAppOperation(uuid, subscribers, subType, action);
.performBulkAppOperation(uuid, subscribers, subType, action, properties);
return Response.status(Response.Status.OK).entity(response).build();
} else {
return scheduleApplicationOperationTask(uuid, subscribers,
SubscriptionType.valueOf(subType.toUpperCase()), SubAction.valueOf(action.toUpperCase()),
timestamp);
timestamp, properties);
}
} catch (NotFoundException e) {
String msg = "Couldn't found an application release for UUID: " + uuid + ". Hence, verify the payload";
@ -170,7 +182,7 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{
return Response.status(Response.Status.OK).entity(msg).build();
} else {
return scheduleApplicationOperationTask(uuid, deviceIdentifiers, SubscriptionType.DEVICE,
SubAction.valueOf(SubAction.INSTALL.toString().toUpperCase()), timestamp);
SubAction.valueOf(SubAction.INSTALL.toString().toUpperCase()), timestamp, null);
}
} catch (NotFoundException e) {
String msg = "Couldn't found an application release for UUI: " + uuid + " to perform ent app installation "
@ -216,7 +228,7 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{
} else {
return scheduleApplicationOperationTask(uuid, subscribers,
SubscriptionType.valueOf(subType.toUpperCase()),
SubAction.valueOf(SubAction.INSTALL.toString().toUpperCase()), timestamp);
SubAction.valueOf(SubAction.INSTALL.toString().toUpperCase()), timestamp, null);
}
} catch (NotFoundException e) {
String msg = "Couldn't found an application release for UUID: " + uuid + ". Hence, verify the payload";
@ -255,11 +267,11 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{
* @return {@link Response} of the operation
*/
private Response scheduleApplicationOperationTask(String applicationUUID, List<?> subscribers,
SubscriptionType subType, SubAction subAction, long timestamp) {
SubscriptionType subType, SubAction subAction, long timestamp, Properties payload) {
try {
ScheduledAppSubscriptionTaskManager subscriptionTaskManager = new ScheduledAppSubscriptionTaskManager();
subscriptionTaskManager.scheduleAppSubscriptionTask(applicationUUID, subscribers, subType, subAction,
timestamp);
timestamp, payload);
} catch (ApplicationOperationTaskException e) {
String msg = "Error occurred while scheduling the application install operation";
log.error(msg, e);
@ -330,7 +342,7 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{
return Response.status(Response.Status.FORBIDDEN).entity(msg).build();
} catch (ApplicationManagementException e) {
String msg = "Error occurred while getting application with the application release uuid: "
+ uuid;
+ uuid;
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
@ -385,17 +397,86 @@ public class SubscriptionManagementAPIImpl implements SubscriptionManagementAPI{
return Response.status(Response.Status.NOT_FOUND).entity(msg).build();
} catch (BadRequestException e) {
String msg = "Found invalid payload for getting application which has UUID: " + uuid
+ ". Hence verify the payload";
+ ". Hence verify the payload";
log.error(msg, e);
return Response.status(Response.Status.BAD_REQUEST).entity(msg).build();
} catch (ForbiddenException e) {
String msg = "Application release is not in the installable state."
+ "Hence you are not permitted to get the devices details.";
+ "Hence you are not permitted to get the devices details.";
log.error(msg, e);
return Response.status(Response.Status.FORBIDDEN).entity(msg).build();
} catch (ApplicationManagementException e) {
String msg = "Error occurred while getting application with the application " +
"release uuid: " + uuid;
"release uuid: " + uuid;
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}
}
@GET
@Consumes("application/json")
@Produces("application/json")
@Path("/{uuid}/{subType}/{subTypeName}/devices")
public Response getAppInstalledDevicesOnCategories(
@PathParam("uuid") String uuid,
@PathParam("subType") String subType,
@PathParam("subTypeName") String subTypeName,
@DefaultValue("0")
@QueryParam("offset") int offset,
@DefaultValue("5")
@QueryParam("limit") int limit,
@QueryParam("name") String name,
@QueryParam("user") String user,
@QueryParam("ownership") String ownership,
@QueryParam("status") List<String> status) {
try {
SubscriptionManager subscriptionManager = APIUtil.getSubscriptionManager();
PaginationRequest request = new PaginationRequest(offset, limit);
if (StringUtils.isNotBlank(name)) {
request.setDeviceName(name);
}
if (StringUtils.isNotBlank(user)) {
request.setOwner(user);
}
if (StringUtils.isNotBlank(ownership)) {
RequestValidationUtil.validateOwnershipType(ownership);
request.setOwnership(ownership);
}
if (status != null && !status.isEmpty()) {
boolean isStatusEmpty = true;
for (String statusString : status) {
if (StringUtils.isNotBlank(statusString)) {
isStatusEmpty = false;
break;
}
}
if (!isStatusEmpty) {
RequestValidationUtil.validateStatus(status);
request.setStatusList(status);
}
}
//todo need to update the API for other subscription types
if (SubscriptionType.GROUP.toString().equalsIgnoreCase(subType)) {
PaginationResult subscribedCategoryDetails = subscriptionManager
.getAppInstalledSubscribeDevices(request, uuid, subType, subTypeName);
DeviceList devices = new DeviceList();
devices.setList((List<Device>) subscribedCategoryDetails.getData());
devices.setCount(subscribedCategoryDetails.getRecordsTotal());
return Response.status(Response.Status.OK).entity(devices).build();
} else {
String msg = "Found invalid sub type: " + subType;
log.error(msg);
return Response.status(Response.Status.NOT_FOUND).entity(msg).build();
}
} catch (NotFoundException e) {
String msg = "Application with application release UUID: " + uuid + " is not found";
log.error(msg, e);
return Response.status(Response.Status.NOT_FOUND).entity(msg).build();
} catch (ApplicationManagementException e) {
String msg = "Error occurred while getting application with the application " +
"release uuid: " + uuid;
log.error(msg, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build();
}

@ -15,6 +15,22 @@
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.service.api;
@ -45,6 +61,7 @@ import org.wso2.carbon.device.mgt.jaxrs.util.Constants;
import javax.validation.Valid;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
@ -243,6 +260,87 @@ public interface GroupManagementService {
@QueryParam("requireGroupProps")
boolean requireGroupProps);
@GET
@Path("/hierarchy")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = HTTPConstants.HEADER_GET,
value = "Getting the List of Hierarchical Groups",
notes = "Returns all groups enrolled with the system hierarchically.",
tags = "Device Group Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:groups:groups")
})
}
)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK. \n Successfully fetched the list of device hierarchical groups.",
response = DeviceGroupList.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 has been modified the last time.\n" +
"Used by caches, or in conditional requests."),
}),
@ApiResponse(
code = 304,
message = "Not Modified. \n Empty body because the client has already the latest version of " +
"the requested resource."),
@ApiResponse(
code = 406,
message = "Not Acceptable.\n The requested media type is not supported."),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Server error occurred while fetching the groups list.",
response = ErrorResponse.class)
})
Response getGroupsWithHierarchy(
@ApiParam(
name = "name",
value = "Name of the group.")
@QueryParam("name")
String name,
@ApiParam(
name = "owner",
value = "Owner of the group.")
@QueryParam("owner")
String owner,
@ApiParam(
name = "requireGroupProps",
value = "Request group properties to include in the response",
defaultValue = "false")
@QueryParam("requireGroupProps")
boolean requireGroupProps,
@ApiParam(
name = "depth",
value = "Depth of the group hierarchy.")
@DefaultValue("3")
@QueryParam("depth")
int depth,
@ApiParam(
name = "offset",
value = "The starting pagination index for the complete list of qualified items.",
defaultValue = "0")
@DefaultValue("0")
@QueryParam("offset")
int offset,
@ApiParam(
name = "limit",
value = "Provide how many records require from the starting pagination index/offset.",
defaultValue = "5")
@DefaultValue("5")
@QueryParam("limit")
int limit);
@Path("/count")
@GET
@ApiOperation(
@ -412,7 +510,14 @@ public interface GroupManagementService {
value = "Request group properties to include in the response",
defaultValue = "false")
@QueryParam("requireGroupProps")
boolean requireGroupProps);
boolean requireGroupProps,
@ApiParam(
name = "depth",
value = "Depth of the group hierarchy.",
defaultValue = "1")
@DefaultValue("1")
@QueryParam("depth")
int depth);
@Path("/name/{groupName}")
@GET
@ -471,7 +576,14 @@ public interface GroupManagementService {
value = "Request group properties to include in the response",
defaultValue = "false")
@QueryParam("requireGroupProps")
boolean requireGroupProps);
boolean requireGroupProps,
@ApiParam(
name = "depth",
value = "Depth of the group hierarchy.",
defaultValue = "1")
@DefaultValue("1")
@QueryParam("depth")
int depth);
@Path("/id/{groupId}")
@PUT
@ -580,7 +692,12 @@ public interface GroupManagementService {
name = "groupId",
value = "ID of the group to be deleted.",
required = true)
@PathParam("groupId") int groupId);
@PathParam("groupId") int groupId,
@ApiParam(
name = "isDeleteChildren",
value = "Is the children groups needs to be deleted.",
required = true)
@QueryParam("isDeleteChildren") boolean isDeleteChildren);
@Path("/id/{groupId}/share")
@POST

@ -15,6 +15,22 @@
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.service.api.admin;
@ -163,6 +179,90 @@ public interface GroupManagementAdminService {
@QueryParam("requireGroupProps")
boolean requireGroupProps);
@GET
@Path("hierarchy")
@ApiOperation(
produces = MediaType.APPLICATION_JSON,
httpMethod = HTTPConstants.HEADER_GET,
value = "Getting the List of Hierarchical Groups",
notes = "Returns all groups enrolled with the system hierarchically.",
tags = "Device Group Management",
extensions = {
@Extension(properties = {
@ExtensionProperty(name = Constants.SCOPE, value = "perm:admin-groups:view")
})
}
)
@ApiResponses(value = {
@ApiResponse(code = 200, message = "OK. \n Successfully fetched the list of device groups hierarchically.",
response = DeviceGroupList.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 has been modified the last time.\n" +
"Used by caches, or in conditional requests."),
}),
@ApiResponse(
code = 304,
message = "Not Modified. \n Empty body because the client has already the latest version of " +
"the requested resource."),
@ApiResponse(
code = 406,
message = "Not Acceptable.\n The requested media type is not supported."),
@ApiResponse(
code = 500,
message = "Internal Server Error. \n Server error occurred while fetching the groups list.",
response = ErrorResponse.class)
})
Response getGroupsWithHierarchy(@ApiParam(
name = "name",
value = "Name of the group.")
@QueryParam("name")
String name,
@ApiParam(
name = "owner",
value = "Owner of the group.")
@QueryParam("owner")
String owner,
@ApiParam(
name = "status",
value = "status of group to be retrieve.")
@QueryParam("status")
String status,
@ApiParam(
name = "requireGroupProps",
value = "Request group properties to include in the response",
defaultValue = "false")
@QueryParam("requireGroupProps")
boolean requireGroupProps,
@ApiParam(
name = "depth",
value = "Depth of the group hierarchy.")
@DefaultValue("3")
@QueryParam("depth")
int depth,
@ApiParam(
name = "offset",
value = "The starting pagination index for the complete list of qualified items.",
defaultValue = "0")
@DefaultValue("0")
@QueryParam("offset")
int offset,
@ApiParam(
name = "limit",
value = "Provide how many records require from the starting pagination index/offset.",
defaultValue = "5")
@DefaultValue("5")
@QueryParam("limit")
int limit);
@Path("/count")
@GET
@ApiOperation(

@ -136,6 +136,7 @@ import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.ArrayList;
import java.util.Properties;
@Path("/devices")
public class DeviceManagementServiceImpl implements DeviceManagementService {
@ -915,7 +916,7 @@ public class DeviceManagementServiceImpl implements DeviceManagementService {
if (UUID != null) {
ApplicationInstallResponse response = subscriptionManager
.performBulkAppOperation(UUID, deviceIdentifiers, SubscriptionType.DEVICE.toString(),
"uninstall");
"uninstall", new Properties());
return Response.status(Response.Status.OK).entity(response).build();
//if the applications not installed via entgra store
} else {

@ -15,6 +15,22 @@
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.service.impl;
@ -36,7 +52,6 @@ import org.wso2.carbon.device.mgt.common.group.mgt.GroupAlreadyExistException;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupNotExistException;
import org.wso2.carbon.device.mgt.common.group.mgt.RoleDoesNotExistException;
import org.wso2.carbon.device.mgt.common.policy.mgt.Policy;
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.DeviceGroupList;
@ -47,10 +62,12 @@ import org.wso2.carbon.device.mgt.jaxrs.service.api.GroupManagementService;
import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil;
import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils;
import org.wso2.carbon.policy.mgt.common.PolicyAdministratorPoint;
import org.wso2.carbon.policy.mgt.common.PolicyEvaluationException;
import org.wso2.carbon.policy.mgt.common.PolicyEvaluationPoint;
import org.wso2.carbon.policy.mgt.common.PolicyManagementException;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
@ -89,11 +106,41 @@ public class GroupManagementServiceImpl implements GroupManagementService {
}
}
@GET
@Path("/hierarchy")
@Override
public Response getGroupsWithHierarchy(
@QueryParam("name") String name,
@QueryParam("owner") String owner,
@QueryParam("requireGroupProps") boolean requireGroupProps,
@DefaultValue("3") @QueryParam("depth") int depth,
@DefaultValue("0") @QueryParam("offset") int offset,
@DefaultValue("5") @QueryParam("limit") int limit) {
try {
RequestValidationUtil.validatePaginationParameters(offset, limit);
String currentUser = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
GroupPaginationRequest request = new GroupPaginationRequest(offset, limit);
request.setGroupName(name);
request.setOwner(owner);
request.setDepth(depth);
PaginationResult deviceGroupsResult = DeviceMgtAPIUtils.getGroupManagementProviderService()
.getGroupsWithHierarchy(currentUser, request, requireGroupProps);
DeviceGroupList deviceGroupList = new DeviceGroupList();
deviceGroupList.setList(deviceGroupsResult.getData());
deviceGroupList.setCount(deviceGroupsResult.getRecordsTotal());
return Response.status(Response.Status.OK).entity(deviceGroupList).build();
} catch (GroupManagementException e) {
String error = "Error occurred while retrieving groups with hierarchy.";
log.error(error, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(error).build();
}
}
@Override
public Response getGroupCount() {
try {
String currentUser = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername();
int count = DeviceMgtAPIUtils.getGroupManagementProviderService().getGroupCount(currentUser);
int count = DeviceMgtAPIUtils.getGroupManagementProviderService().getGroupCount(currentUser, null);
return Response.status(Response.Status.OK).entity(count).build();
} catch (GroupManagementException e) {
String msg = "Error occurred while retrieving group count.";
@ -125,10 +172,10 @@ public class GroupManagementServiceImpl implements GroupManagementService {
}
@Override
public Response getGroup(int groupId, boolean requireGroupProps) {
public Response getGroup(int groupId, boolean requireGroupProps, int depth) {
try {
GroupManagementProviderService service = DeviceMgtAPIUtils.getGroupManagementProviderService();
DeviceGroup deviceGroup = service.getGroup(groupId, requireGroupProps);
DeviceGroup deviceGroup = service.getGroup(groupId, requireGroupProps, depth);
if (deviceGroup != null) {
return Response.status(Response.Status.OK).entity(deviceGroup).build();
} else {
@ -142,7 +189,7 @@ public class GroupManagementServiceImpl implements GroupManagementService {
}
@Override
public Response getGroup(String groupName, boolean requireGroupProps) {
public Response getGroup(String groupName, boolean requireGroupProps, int depth) {
try {
GroupManagementProviderService service = DeviceMgtAPIUtils.getGroupManagementProviderService();
DeviceGroup deviceGroup = service.getGroup(groupName, requireGroupProps);
@ -178,9 +225,9 @@ public class GroupManagementServiceImpl implements GroupManagementService {
}
@Override
public Response deleteGroup(int groupId) {
public Response deleteGroup(int groupId, boolean isDeleteChildren) {
try {
if (DeviceMgtAPIUtils.getGroupManagementProviderService().deleteGroup(groupId)) {
if (DeviceMgtAPIUtils.getGroupManagementProviderService().deleteGroup(groupId, isDeleteChildren)) {
return Response.status(Response.Status.OK).build();
} else {
return Response.status(Response.Status.NOT_FOUND).entity("Group not found.").build();

@ -15,6 +15,22 @@
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.service.impl.admin;
@ -31,6 +47,10 @@ import org.wso2.carbon.device.mgt.jaxrs.service.api.admin.GroupManagementAdminSe
import org.wso2.carbon.device.mgt.jaxrs.service.impl.util.RequestValidationUtil;
import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
@ -74,6 +94,37 @@ public class GroupManagementAdminServiceImpl implements GroupManagementAdminServ
}
}
@GET
@Path("/hierarchy")
@Override
public Response getGroupsWithHierarchy(
@QueryParam("name") String name,
@QueryParam("owner") String owner,
@QueryParam("status") String status,
@QueryParam("requireGroupProps") boolean requireGroupProps,
@DefaultValue("3") @QueryParam("depth") int depth,
@DefaultValue("0") @QueryParam("offset") int offset,
@DefaultValue("5") @QueryParam("limit") int limit) {
try {
RequestValidationUtil.validatePaginationParameters(offset, limit);
GroupPaginationRequest request = new GroupPaginationRequest(offset, limit);
request.setGroupName(name);
request.setOwner(owner);
request.setStatus(status);
request.setDepth(depth);
PaginationResult deviceGroupsResult = DeviceMgtAPIUtils.getGroupManagementProviderService()
.getGroupsWithHierarchy(null, request, requireGroupProps);
DeviceGroupList deviceGroupList = new DeviceGroupList();
deviceGroupList.setList(deviceGroupsResult.getData());
deviceGroupList.setCount(deviceGroupsResult.getRecordsTotal());
return Response.status(Response.Status.OK).entity(deviceGroupList).build();
} catch (GroupManagementException e) {
String error = "Error occurred while retrieving groups with hierarchy.";
log.error(error, e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(error).build();
}
}
@Override
public Response getGroupCount(String status) {
try {

@ -15,11 +15,26 @@
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.service.impl;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
@ -130,13 +145,14 @@ public class GroupManagementServiceImplTest {
.toReturn(groupManagementProviderService);
PowerMockito.stub(PowerMockito.method(PrivilegedCarbonContext.class, "getThreadLocalCarbonContext"))
.toReturn(context);
Mockito.doReturn(2).when(groupManagementProviderService).getGroupCount(Mockito.anyString());
Mockito.doReturn(2).when(groupManagementProviderService)
.getGroupCount(Mockito.anyString(), Mockito.anyString());
Response response = groupManagementService.getGroupCount();
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(),
"GetGroupCount request failed with valid parameters");
Mockito.reset(groupManagementProviderService);
Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService)
.getGroupCount(Mockito.anyString());
.getGroupCount(Mockito.anyString(), Mockito.anyString());
response = groupManagementService.getGroupCount();
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
"GetGroupCount request succeeded with in-valid parameters");
@ -174,16 +190,16 @@ public class GroupManagementServiceImplTest {
public void testGetGroup() throws GroupManagementException {
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getGroupManagementProviderService"))
.toReturn(groupManagementProviderService);
Mockito.doReturn(new DeviceGroup()).when(groupManagementProviderService).getGroup(1, false);
Mockito.doReturn(null).when(groupManagementProviderService).getGroup(2, false);
Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService).getGroup(3, false);
Response response = groupManagementService.getGroup(1, false);
Mockito.doReturn(new DeviceGroup()).when(groupManagementProviderService).getGroup(1, false, 1);
Mockito.doReturn(null).when(groupManagementProviderService).getGroup(2, false, 1);
Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService).getGroup(3, false, 1);
Response response = groupManagementService.getGroup(1, false, 1);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(),
"getGroup request failed for a request with valid parameters");
response = groupManagementService.getGroup(2, false);
response = groupManagementService.getGroup(2, false, 1);
Assert.assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode(),
"getGroup request returned a group for a non-existing group");
response = groupManagementService.getGroup(3, false);
response = groupManagementService.getGroup(3, false, 1);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
"getGroup request returned a group for a in-valid request");
}
@ -216,16 +232,16 @@ public class GroupManagementServiceImplTest {
public void testDeleteGroup() throws GroupManagementException {
PowerMockito.stub(PowerMockito.method(DeviceMgtAPIUtils.class, "getGroupManagementProviderService"))
.toReturn(groupManagementProviderService);
Mockito.doReturn(true).when(groupManagementProviderService).deleteGroup(1);
Mockito.doReturn(false).when(groupManagementProviderService).deleteGroup(2);
Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService).deleteGroup(3);
Response response = groupManagementService.deleteGroup(1);
Mockito.doReturn(true).when(groupManagementProviderService).deleteGroup(1, false);
Mockito.doReturn(false).when(groupManagementProviderService).deleteGroup(2, false);
Mockito.doThrow(new GroupManagementException()).when(groupManagementProviderService).deleteGroup(3, false);
Response response = groupManagementService.deleteGroup(1, false);
Assert.assertEquals(response.getStatus(), Response.Status.OK.getStatusCode(),
"delete group request failed for a request with valid parameters");
response = groupManagementService.deleteGroup(2);
response = groupManagementService.deleteGroup(2, false);
Assert.assertEquals(response.getStatus(), Response.Status.NOT_FOUND.getStatusCode(),
"Non-existing group was successfully deleted");
response = groupManagementService.deleteGroup(3);
response = groupManagementService.deleteGroup(3, false);
Assert.assertEquals(response.getStatus(), Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
"Deletion succeeded with an erroneous condition.");
}

@ -14,6 +14,23 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.common;
@ -28,6 +45,8 @@ public class GroupPaginationRequest {
private String owner;
private String groupName;
private String status;
private String parentPath;
private int depth;
public GroupPaginationRequest(int start, int rowCount) {
this.startIndex = start;
@ -74,6 +93,22 @@ public class GroupPaginationRequest {
this.groupName = groupName;
}
public String getParentPath() {
return parentPath;
}
public void setParentPath(String parentPath) {
this.parentPath = parentPath;
}
public int getDepth() {
return depth;
}
public void setDepth(int depth) {
this.depth = depth;
}
@Override
public String toString() {
return "Group Name '" + this.groupName + "' num of rows: " + this.rowCount + " start index: " + this.startIndex

@ -45,6 +45,7 @@ public class MDMAppConstants {
private AndroidConstants() {
throw new AssertionError();
}
public static final String IS_BLOCK_UNINSTALL = "isBlockUninstall";
public static final String OPCODE_INSTALL_APPLICATION = "INSTALL_APPLICATION";
public static final String OPCODE_UNINSTALL_APPLICATION = "UNINSTALL_APPLICATION";
public static final String UNMANAGED_APP_UNINSTALL= "UNMANAGED_APP_UNINSTALL";

@ -21,6 +21,7 @@ package org.wso2.carbon.device.mgt.common.app.mgt.android;
import com.google.gson.Gson;
import java.io.Serializable;
import java.util.Properties;
/**
* This class represents the Appstore AuthenticationImpl information.
@ -29,6 +30,7 @@ public class AppStoreApplication implements Serializable {
private String type;
private String appIdentifier;
private Properties properties;
public String getType() {
return type;
@ -51,4 +53,11 @@ public class AppStoreApplication implements Serializable {
return gson.toJson(this);
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}

@ -21,6 +21,7 @@ package org.wso2.carbon.device.mgt.common.app.mgt.android;
import com.google.gson.Gson;
import java.io.Serializable;
import java.util.Properties;
/**
* This class represents the Enterprise AuthenticationImpl information.
@ -30,6 +31,7 @@ public class EnterpriseApplication implements Serializable {
private String type;
private String url;
private String appIdentifier;
private Properties properties;
public String getAppIdentifier() {
return appIdentifier;
@ -60,4 +62,11 @@ public class EnterpriseApplication implements Serializable {
return gson.toJson(this);
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}

@ -21,6 +21,7 @@ package org.wso2.carbon.device.mgt.common.app.mgt.android;
import com.google.gson.Gson;
import java.io.Serializable;
import java.util.Properties;
/**
* This class represents the Web AuthenticationImpl information.
@ -30,6 +31,7 @@ public class WebApplication implements Serializable {
private String name;
private String url;
private String type;
private Properties properties;
public String getName() {
return name;
@ -60,4 +62,11 @@ public class WebApplication implements Serializable {
return gson.toJson(this);
}
public Properties getProperties() {
return properties;
}
public void setProperties(Properties properties) {
this.properties = properties;
}
}

@ -14,6 +14,23 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.common.group.mgt;
@ -21,6 +38,7 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
@ -47,6 +65,15 @@ public class DeviceGroup implements Serializable {
@ApiModelProperty(name = "status", value = "The status of group that needs updating/retrieval.")
private String status;
@ApiModelProperty(name = "parentGroupId", value = "Group ID of parent group")
private int parentGroupId;
@ApiModelProperty(name = "parentPath", value = "Path of parent group")
private String parentPath;
@ApiModelProperty(name = "childrenGroups", value = "Children groups")
private List<DeviceGroup> childrenGroups;
public String getStatus() {
return status;
}
@ -103,4 +130,27 @@ public class DeviceGroup implements Serializable {
this.groupProperties = groupProperties;
}
public int getParentGroupId() {
return parentGroupId;
}
public void setParentGroupId(int parentGroupId) {
this.parentGroupId = parentGroupId;
}
public String getParentPath() {
return parentPath;
}
public void setParentPath(String parentPath) {
this.parentPath = parentPath;
}
public List<DeviceGroup> getChildrenGroups() {
return childrenGroups;
}
public void setChildrenGroups(List<DeviceGroup> childrenGroups) {
this.childrenGroups = childrenGroups;
}
}

@ -84,4 +84,8 @@ public class DeviceGroupConstants {
public static final String[] DEFAULT_VIEW_EVENTS_PERMISSIONS =
{"/permission/device-mgt/user/groups/device_events"};
}
public static final class HierarchicalGroup {
public static final String SEPERATOR = "/";
}
}

@ -758,4 +758,27 @@ public interface DeviceDAO {
String version) throws DeviceManagementDAOException;
int getFunctioningDevicesInSystem() throws DeviceManagementDAOException;
/**
* This method is used to get the details of devices when give deviceIDs list and group name.
* @param deviceIds device ids of the devices.
* @param tenantId Id of the current tenant.
* @param request paginated request object.
* @param groupName group name.
* @return devices - device details list
* @throws DeviceManagementDAOException if connections establishment fails.
*/
List<Device> getGroupedDevicesDetails(PaginationRequest request, List<Integer> deviceIds, String groupName,
int tenantId) throws DeviceManagementDAOException;
/**
* @param deviceIds device ids of the devices.
* @param tenantId tenant id
* @param request paginated request object.
* @param groupName group name.
* @return number of device count under the group name.
* @throws DeviceManagementDAOException if error occurred while processing the SQL statement.
*/
int getGroupedDevicesCount(PaginationRequest request, List<Integer> deviceIds, String groupName, int tenantId)
throws DeviceManagementDAOException;
}

@ -14,6 +14,23 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.dao;
@ -74,6 +91,15 @@ public interface GroupDAO {
*/
void deleteAllGroupProperties(int groupId, int tenantId) throws GroupManagementDAOException;
/**
* Remove properties of device groups.
*
* @param groupIds to be deleted.
* @param tenantId of the group.
* @throws GroupManagementDAOException on error during deletion of group properties of groups
*/
void deleteAllGroupsProperties(List<Integer> groupIds, int tenantId) throws GroupManagementDAOException;
/**
* Retrives all properties stored against a group.
*
@ -95,6 +121,15 @@ public interface GroupDAO {
void updateGroup(DeviceGroup deviceGroup, int groupId, int tenantId)
throws GroupManagementDAOException;
/**
* Update existing Device Groups.
*
* @param deviceGroups groups to update.
* @param tenantId of the group.
* @throws GroupManagementDAOException on error during updating of groups
*/
void updateGroups(List<DeviceGroup> deviceGroups, int tenantId) throws GroupManagementDAOException;
/**
* Delete an existing Device Group.
*
@ -104,6 +139,24 @@ public interface GroupDAO {
*/
void deleteGroup(int groupId, int tenantId) throws GroupManagementDAOException;
/**
* Delete mappings of Device Groups.
*
* @param groupIds of Device Groups.
* @param tenantId of the group.
* @throws GroupManagementDAOException on error during deletion of mappings of groups
*/
void deleteGroupsMapping(List<Integer> groupIds, int tenantId) throws GroupManagementDAOException;
/**
* Delete existing Device Groups.
*
* @param groupIds of Device Groups.
* @param tenantId of the group.
* @throws GroupManagementDAOException on error during deletion of groups
*/
void deleteGroups(List<Integer> groupIds, int tenantId) throws GroupManagementDAOException;
/**
* Get device group by id.
*
@ -114,6 +167,25 @@ public interface GroupDAO {
*/
DeviceGroup getGroup(int groupId, int tenantId) throws GroupManagementDAOException;
/**
* Get children groups by parent path.
*
* @param parentPath of parent group.
* @param tenantId of the group.
* @return {@link List<DeviceGroup>} list of children device groups
* @throws GroupManagementDAOException on error during retrieval of children groups
*/
List<DeviceGroup> getChildrenGroups(String parentPath, int tenantId) throws GroupManagementDAOException;
/**
* Get root groups.
*
* @param tenantId of the group.
* @return {@link List<DeviceGroup>} list of root device groups
* @throws GroupManagementDAOException on error during retrieval of root groups
*/
List<DeviceGroup> getRootGroups(int tenantId) throws GroupManagementDAOException;
/**
* Get the groups of device with device id provided
* @param deviceId
@ -306,10 +378,11 @@ public interface GroupDAO {
*
* @param roles of the group.
* @param tenantId of user's tenant.
* @param parentPath of the group.
* @return count of device groups.
* @throws GroupManagementDAOException
*/
int getGroupsCount(String[] roles, int tenantId) throws GroupManagementDAOException;
int getGroupsCount(String[] roles, int tenantId, String parentPath) throws GroupManagementDAOException;
/**
* Get all device groups which owned by user.
@ -336,10 +409,11 @@ public interface GroupDAO {
*
* @param username of the owner.
* @param tenantId of user's tenant.
* @param parentPath of the group.
* @return count of device groups.
* @throws GroupManagementDAOException
*/
int getOwnGroupsCount(String username, int tenantId) throws GroupManagementDAOException;
int getOwnGroupsCount(String username, int tenantId, String parentPath) throws GroupManagementDAOException;
/**
* Get device Ids of devices which are assigned to groups.

@ -3022,4 +3022,154 @@ public abstract class AbstractDeviceDAOImpl implements DeviceDAO {
throw new DeviceManagementDAOException(msg, e);
}
}
@Override
public List<Device> getGroupedDevicesDetails(PaginationRequest request, List<Integer> deviceIds, String groupName,
int tenantId) throws DeviceManagementDAOException {
int limitValue = request.getRowCount();
int offsetValue = request.getStartIndex();
List<String> status = request.getStatusList();
String name = request.getDeviceName();
String user = request.getOwner();
String ownership = request.getOwnership();
try {
List<Device> devices = new ArrayList<>();
if (deviceIds.isEmpty()) {
return devices;
}
Connection conn = this.getConnection();
int index = 1;
StringJoiner joiner = new StringJoiner(",",
"SELECT "
+ "DM_DEVICE.ID AS DEVICE_ID, "
+ "DM_DEVICE.NAME AS DEVICE_NAME, "
+ "DM_DEVICE.DESCRIPTION AS DESCRIPTION, "
+ "DM_DEVICE.DEVICE_TYPE_ID, "
+ "DM_DEVICE.DEVICE_IDENTIFICATION AS DEVICE_IDENTIFICATION, "
+ "e.ID AS ENROLMENT_ID, "
+ "e.OWNER, "
+ "e.OWNERSHIP, "
+ "e.DATE_OF_ENROLMENT, "
+ "e.DATE_OF_LAST_UPDATE, "
+ "e.STATUS, "
+ "e.IS_TRANSFERRED, "
+ "device_types.NAME AS DEVICE_TYPE "
+ "FROM DM_DEVICE_GROUP_MAP "
+ "INNER JOIN DM_DEVICE ON "
+ "DM_DEVICE_GROUP_MAP.DEVICE_ID = DM_DEVICE.ID "
+ "INNER JOIN DM_GROUP ON "
+ "DM_DEVICE_GROUP_MAP.GROUP_ID = DM_GROUP.ID "
+ "INNER JOIN DM_ENROLMENT e ON "
+ "DM_DEVICE.ID = e.DEVICE_ID AND "
+ "DM_DEVICE.TENANT_ID = e.TENANT_ID "
+ "INNER JOIN (SELECT ID, NAME FROM DM_DEVICE_TYPE) AS device_types ON "
+ "device_types.ID = DM_DEVICE.DEVICE_TYPE_ID "
+ "WHERE DM_DEVICE.ID IN (",
") AND DM_DEVICE.TENANT_ID = ?");
deviceIds.stream().map(ignored -> "?").forEach(joiner::add);
String query = joiner.toString();
if (StringUtils.isNotBlank(groupName)) {
query += " AND DM_GROUP.GROUP_NAME = ?";
}
if (StringUtils.isNotBlank(name)) {
query += " AND DM_DEVICE.NAME LIKE ?";
}
if (StringUtils.isNotBlank(user)) {
query += " AND e.OWNER = ?";
}
if (StringUtils.isNotBlank(ownership)) {
query += " AND e.OWNERSHIP = ?";
}
if (status != null && !status.isEmpty()) {
query += buildStatusQuery(status);
}
query += "LIMIT ? OFFSET ?";
try (PreparedStatement ps = conn.prepareStatement(query)) {
for (Integer deviceId : deviceIds) {
ps.setInt(index++, deviceId);
}
ps.setInt(index++, tenantId);
if (StringUtils.isNotBlank(groupName)) {
ps.setString(index++, groupName);
}
if (StringUtils.isNotBlank(name)) {
ps.setString(index++, name);
}
if (StringUtils.isNotBlank(user)) {
ps.setString(index++, user);
}
if (StringUtils.isNotBlank(ownership)) {
ps.setString(index++, ownership);
}
if (status != null && !status.isEmpty()) {
for (String deviceStatus : status) {
ps.setString(index++, deviceStatus);
}
}
ps.setInt(index++, limitValue);
ps.setInt(index, offsetValue);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
devices.add(DeviceManagementDAOUtil.loadDevice(rs));
}
return devices;
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving information of all registered devices " +
"according to device ids and the limit area.";
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
@Override
public int getGroupedDevicesCount(PaginationRequest request, List<Integer> deviceIds, String groupName,
int tenantId) throws DeviceManagementDAOException {
try {
Connection conn = this.getConnection();
if (deviceIds.isEmpty()) {
return 0;
}
int index = 1;
StringJoiner joiner = new StringJoiner(",",
"SELECT "
+ "COUNT(DM_DEVICE_GROUP_MAP.DEVICE_ID) AS DEVICE_COUNT "
+ "FROM DM_DEVICE_GROUP_MAP "
+ "INNER JOIN DM_GROUP ON "
+ "DM_DEVICE_GROUP_MAP.GROUP_ID = DM_GROUP.ID "
+ "WHERE DM_DEVICE_GROUP_MAP.DEVICE_ID IN (",
") AND DM_GROUP.TENANT_ID = ?");
deviceIds.stream().map(ignored -> "?").forEach(joiner::add);
String query = joiner.toString();
if (StringUtils.isNotBlank(groupName)) {
query += " AND DM_GROUP.GROUP_NAME = ?";
}
try (PreparedStatement ps = conn.prepareStatement(query)) {
for (Integer deviceId : deviceIds) {
ps.setInt(index++, deviceId);
}
ps.setInt(index++, tenantId);
if (StringUtils.isNotBlank(groupName)) {
ps.setString(index, groupName);
}
try (ResultSet rs = ps.executeQuery()) {
if (rs.next()) {
return rs.getInt("DEVICE_COUNT");
}
return 0;
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving information of all registered devices " +
"according to device ids and the limit area.";
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
}

@ -14,18 +14,34 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.dao.impl;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.solr.common.StringUtils;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.GroupPaginationRequest;
import org.wso2.carbon.device.mgt.common.PaginationRequest;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
import org.wso2.carbon.device.mgt.core.dao.DeviceManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.GroupDAO;
import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOFactory;
@ -49,6 +65,127 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
private static final Log log = LogFactory.getLog(AbstractGroupDAOImpl.class);
@Override
public List<DeviceGroup> getGroups(GroupPaginationRequest request, int tenantId)
throws GroupManagementDAOException {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE TENANT_ID = ?";
if (StringUtils.isNotBlank(request.getGroupName())) {
sql += " AND UPPER(GROUP_NAME) LIKE ?";
}
if (StringUtils.isNotBlank(request.getOwner())) {
sql += " AND UPPER(OWNER) LIKE ?";
}
if (StringUtils.isNotBlank(request.getStatus())) {
sql += " AND STATUS = ?";
}
if (StringUtils.isNotBlank(request.getParentPath())) {
sql += " AND PARENT_PATH LIKE ?";
}
if (request.getRowCount() != 0) {
sql += " LIMIT ? OFFSET ?";
}
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
int paramIndex = 1;
stmt.setInt(paramIndex++, tenantId);
if (StringUtils.isNotBlank(request.getGroupName())) {
stmt.setString(paramIndex++, request.getGroupName() + "%");
}
if (StringUtils.isNotBlank(request.getOwner())) {
stmt.setString(paramIndex++, request.getOwner() + "%");
}
if (StringUtils.isNotBlank(request.getStatus())) {
stmt.setString(paramIndex++, request.getStatus().toUpperCase());
}
if (StringUtils.isNotBlank(request.getParentPath())) {
stmt.setString(paramIndex++, request.getParentPath());
}
if (request.getRowCount() != 0) {
stmt.setInt(paramIndex++, request.getRowCount());
stmt.setInt(paramIndex, request.getStartIndex());
}
List<DeviceGroup> deviceGroupList = new ArrayList<>();
try (ResultSet resultSet = stmt.executeQuery()) {
while (resultSet.next()) {
deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet));
}
}
return deviceGroupList;
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving groups in tenant: " + tenantId;
log.error(msg);
throw new GroupManagementDAOException(msg, e);
}
}
@Override
public List<DeviceGroup> getGroups(GroupPaginationRequest request, List<Integer> deviceGroupIds,
int tenantId) throws GroupManagementDAOException {
int deviceGroupIdsCount = deviceGroupIds.size();
if (deviceGroupIdsCount == 0) {
return new ArrayList<>();
}
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP WHERE TENANT_ID = ?";
if (StringUtils.isNotBlank(request.getGroupName())) {
sql += " AND GROUP_NAME LIKE ?";
}
if (StringUtils.isNotBlank(request.getOwner())) {
sql += " AND OWNER LIKE ?";
}
if (StringUtils.isNotBlank(request.getParentPath())) {
sql += " AND PARENT_PATH LIKE ?";
}
sql += " AND ID IN (";
for (int i = 0; i < deviceGroupIdsCount; i++) {
sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?";
}
sql += ")";
if (request.getRowCount() != 0) {
sql += " LIMIT ? OFFSET ?";
}
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
int paramIndex = 1;
stmt.setInt(paramIndex++, tenantId);
if (StringUtils.isNotBlank(request.getGroupName())) {
stmt.setString(paramIndex++, request.getGroupName() + "%");
}
if (StringUtils.isNotBlank(request.getOwner())) {
stmt.setString(paramIndex++, request.getOwner() + "%");
}
if (StringUtils.isNotBlank(request.getParentPath())) {
stmt.setString(paramIndex++, request.getParentPath());
}
for (Integer deviceGroupId : deviceGroupIds) {
stmt.setInt(paramIndex++, deviceGroupId);
}
if (request.getRowCount() != 0) {
stmt.setInt(paramIndex++, request.getRowCount());
stmt.setInt(paramIndex, request.getStartIndex());
}
List<DeviceGroup> deviceGroupList = new ArrayList<>();
try (ResultSet resultSet = stmt.executeQuery()) {
while (resultSet.next()) {
deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet));
}
}
return deviceGroupList;
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving groups of groups IDs " + deviceGroupIds.toString()
+ " in tenant: " + tenantId;
log.error(msg);
throw new GroupManagementDAOException(msg, e);
}
}
@Override
public int addGroup(DeviceGroup deviceGroup, int tenantId) throws GroupManagementDAOException {
PreparedStatement stmt = null;
@ -59,9 +196,11 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql;
if (deviceGroup.getStatus() == null || deviceGroup.getStatus().isEmpty()) {
sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID) VALUES (?, ?, ?, ?)";
sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, PARENT_PATH) "
+ "VALUES (?, ?, ?, ?, ?)";
} else {
sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, STATUS) VALUES (?, ?, ?, ?, ?)";
sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, PARENT_PATH, STATUS) "
+ "VALUES (?, ?, ?, ?, ?, ?)";
hasStatus = true;
}
stmt = conn.prepareStatement(sql, new String[]{"ID"});
@ -69,8 +208,9 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
stmt.setString(2, deviceGroup.getName());
stmt.setString(3, deviceGroup.getOwner());
stmt.setInt(4, tenantId);
stmt.setString(5, deviceGroup.getParentPath());
if (hasStatus) {
stmt.setString(5, deviceGroup.getStatus());
stmt.setString(6, deviceGroup.getStatus());
}
stmt.executeUpdate();
@ -151,10 +291,12 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql =
"UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ? WHERE ID = ? AND TENANT_ID = ?";
"UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ?, PARENT_PATH = ? WHERE ID = ? "
+ "AND TENANT_ID = ?";
if (deviceGroup.getStatus() != null && !deviceGroup.getStatus().isEmpty()) {
sql = "UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ?, STATUS = ? WHERE ID = ? AND TENANT_ID = ?";
sql = "UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ?, PARENT_PATH = ?, STATUS = ? "
+ "WHERE ID = ? AND TENANT_ID = ?";
hasStatus = true;
}
stmt = conn.prepareStatement(sql);
@ -162,11 +304,12 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
stmt.setString(paramIndex++, deviceGroup.getDescription());
stmt.setString(paramIndex++, deviceGroup.getName());
stmt.setString(paramIndex++, deviceGroup.getOwner());
stmt.setString(paramIndex++, deviceGroup.getParentPath());
if (hasStatus) {
stmt.setString(paramIndex++, deviceGroup.getStatus());
}
stmt.setInt(paramIndex++, groupId);
stmt.setInt(paramIndex++, tenantId);
stmt.setInt(paramIndex, tenantId);
stmt.executeUpdate();
} catch (SQLException e) {
throw new GroupManagementDAOException("Error occurred while updating deviceGroup '" +
@ -176,6 +319,32 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
}
}
@Override
public void updateGroups(List<DeviceGroup> deviceGroups, int tenantId) throws GroupManagementDAOException {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "UPDATE DM_GROUP SET DESCRIPTION = ?, GROUP_NAME = ?, OWNER = ?, STATUS = ?, "
+ "PARENT_PATH = ? WHERE ID = ? AND TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)){
for (DeviceGroup deviceGroup : deviceGroups) {
stmt.setString(1, deviceGroup.getDescription());
stmt.setString(2, deviceGroup.getName());
stmt.setString(3, deviceGroup.getOwner());
stmt.setString(4, deviceGroup.getStatus());
stmt.setString(5, deviceGroup.getParentPath());
stmt.setInt(6, deviceGroup.getGroupId());
stmt.setInt(7, tenantId);
stmt.addBatch();
}
stmt.executeBatch();
}
} catch (SQLException e) {
String msg = "Error occurred while updating groups as batch";
log.error(msg);
throw new GroupManagementDAOException(msg, e);
}
}
@Override
public void deleteGroup(int groupId, int tenantId) throws GroupManagementDAOException {
Connection conn;
@ -217,6 +386,64 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
}
}
@Override
public void deleteGroupsMapping(List<Integer> groupIds, int tenantId) throws GroupManagementDAOException {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "DELETE FROM DM_ROLE_GROUP_MAP WHERE GROUP_ID = ? AND TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
for (int groupId : groupIds) {
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
stmt.addBatch();
}
stmt.executeBatch();
}
sql = "DELETE FROM DM_DEVICE_GROUP_MAP WHERE GROUP_ID = ? AND TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
for (int groupId : groupIds) {
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
stmt.addBatch();
}
stmt.executeBatch();
}
sql = "DELETE FROM DM_DEVICE_GROUP_POLICY WHERE DEVICE_GROUP_ID = ? AND TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
for (int groupId : groupIds) {
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
stmt.addBatch();
}
stmt.executeBatch();
}
} catch (SQLException e) {
String msg = "Error occurred while removing mappings of groups as batches";
log.error(msg);
throw new GroupManagementDAOException(msg, e);
}
}
@Override
public void deleteGroups(List<Integer> groupIds, int tenantId) throws GroupManagementDAOException {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "DELETE FROM DM_GROUP WHERE ID = ? AND TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
for (int groupId : groupIds) {
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
stmt.addBatch();
}
stmt.executeBatch();
}
} catch (SQLException e) {
String msg = "Error occurred while deleting groups as batches";
log.error(msg);
throw new GroupManagementDAOException(msg, e);
}
}
public void deleteAllGroupProperties(int groupId, int tenantId)
throws GroupManagementDAOException {
PreparedStatement stmt = null;
@ -235,6 +462,25 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
}
}
public void deleteAllGroupsProperties(List<Integer> groupIds, int tenantId) throws GroupManagementDAOException {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "DELETE FROM GROUP_PROPERTIES WHERE GROUP_ID = ? AND TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
for (Integer groupId : groupIds) {
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
stmt.addBatch();
}
stmt.executeUpdate();
}
} catch (SQLException e) {
String msg = "Error occurred while deleting properties of groups as batches";
log.error(msg);
throw new GroupManagementDAOException(msg, e);
}
}
public Map<String, String> getAllGroupProperties(int groupId, int tenantId)
throws GroupManagementDAOException {
PreparedStatement stmt = null;
@ -266,7 +512,8 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
ResultSet resultSet = null;
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE ID = ? AND TENANT_ID = ?";
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP WHERE ID = ? "
+ "AND TENANT_ID = ?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, groupId);
stmt.setInt(2, tenantId);
@ -284,6 +531,56 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
}
}
@Override
public List<DeviceGroup> getChildrenGroups(String parentPath, int tenantId) throws GroupManagementDAOException {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE PARENT_PATH LIKE ? AND TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, parentPath + "%");
stmt.setInt(2, tenantId);
List<DeviceGroup> deviceGroupList = new ArrayList<>();
try (ResultSet resultSet = stmt.executeQuery()) {
while (resultSet.next()) {
deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet));
}
}
return deviceGroupList;
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving children group having parent path '" + parentPath
+ "' in tenant: " + tenantId;
log.error(msg);
throw new GroupManagementDAOException(msg, e);
}
}
@Override
public List<DeviceGroup> getRootGroups(int tenantId) throws GroupManagementDAOException {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE PARENT_PATH LIKE ? AND TENANT_ID = ?";
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setString(1, "/");
stmt.setInt(2, tenantId);
List<DeviceGroup> deviceGroupList = new ArrayList<>();
try (ResultSet resultSet = stmt.executeQuery()) {
deviceGroupList = new ArrayList<>();
while (resultSet.next()) {
deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet));
}
}
return deviceGroupList;
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving root groups in tenant: " + tenantId;
log.error(msg);
throw new GroupManagementDAOException(msg, e);
}
}
@Override
public List<DeviceGroup> getGroups(int deviceId, int tenantId) throws GroupManagementDAOException {
PreparedStatement stmt = null;
@ -291,7 +588,7 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
List<DeviceGroup> deviceGroupBuilders = new ArrayList<>();
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT G.ID, G.GROUP_NAME, G.DESCRIPTION, G.OWNER, G.STATUS FROM DM_GROUP G " +
String sql = "SELECT G.ID, G.GROUP_NAME, G.DESCRIPTION, G.OWNER, G.STATUS, G.PARENT_PATH FROM DM_GROUP G " +
"INNER JOIN DM_DEVICE_GROUP_MAP GM ON G.ID = GM.GROUP_ID " +
"WHERE GM.DEVICE_ID = ? AND GM.TENANT_ID = ?";
stmt = conn.prepareStatement(sql);
@ -316,7 +613,8 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
List<DeviceGroup> deviceGroupList = null;
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?";
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE TENANT_ID = ?";
stmt = conn.prepareStatement(sql);
stmt.setInt(1, tenantId);
resultSet = stmt.executeQuery();
@ -389,6 +687,9 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
sql += " AND STATUS = ?";
hasStatus = true;
}
if (StringUtils.isNotBlank(request.getParentPath())) {
sql += " AND PARENT_PATH = ?";
}
int paramIndex = 1;
stmt = conn.prepareStatement(sql);
@ -397,10 +698,13 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
stmt.setString(paramIndex++, groupName + "%");
}
if (hasOwner) {
stmt.setString(paramIndex, owner + "%");
stmt.setString(paramIndex++, owner + "%");
}
if (hasStatus) {
stmt.setString(paramIndex, request.getStatus());
stmt.setString(paramIndex++, request.getStatus());
}
if (StringUtils.isNotBlank(request.getParentPath())) {
stmt.setString(paramIndex, request.getParentPath());
}
resultSet = stmt.executeQuery();
if (resultSet.next()) {
@ -422,7 +726,8 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql =
"SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE GROUP_NAME = ? AND TENANT_ID = ?";
"SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE GROUP_NAME = ? AND TENANT_ID = ?";
stmt = conn.prepareStatement(sql);
stmt.setString(1, groupName);
stmt.setInt(2, tenantId);
@ -592,7 +897,7 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
List<DeviceGroup> deviceGroupList = null;
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP g, " +
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP g, " +
"(SELECT GROUP_ID FROM DM_ROLE_GROUP_MAP WHERE ROLE IN (";
int index = 0;
@ -658,7 +963,7 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
}
@Override
public int getGroupsCount(String[] roles, int tenantId) throws GroupManagementDAOException {
public int getGroupsCount(String[] roles, int tenantId, String parentPath) throws GroupManagementDAOException {
int rolesCount = roles.length;
if (rolesCount == 0) {
return 0;
@ -672,14 +977,20 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
for (int i = 0; i < rolesCount; i++) {
sql += (rolesCount - 1 != i) ? "?," : "?";
}
sql += ")) gr WHERE g.ID = gr.GROUP_ID AND TENANT_ID = ? GROUP BY g.ID";
sql += ")) gr WHERE g.ID = gr.GROUP_ID AND TENANT_ID = ? ";
if (StringUtils.isNotBlank(parentPath)) {
sql += " AND g.PARENT_PATH = ? ";
}
sql += "GROUP BY g.ID";
stmt = conn.prepareStatement(sql);
int index = 0;
while (index++ < rolesCount) {
stmt.setString(index, roles[index - 1]);
}
stmt.setInt(index, tenantId);
stmt.setInt(index++, tenantId);
if (StringUtils.isNotBlank(parentPath)) {
stmt.setString(index, parentPath);
}
resultSet = stmt.executeQuery();
if (resultSet.next()) {
return resultSet.getInt("GROUP_COUNT");
@ -700,7 +1011,8 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
List<DeviceGroup> deviceGroupList = null;
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE OWNER = ? AND TENANT_ID = ?";
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE OWNER = ? AND TENANT_ID = ?";
stmt = conn.prepareStatement(sql);
stmt.setString(1, username);
stmt.setInt(2, tenantId);
@ -744,15 +1056,21 @@ public abstract class AbstractGroupDAOImpl implements GroupDAO {
}
@Override
public int getOwnGroupsCount(String username, int tenantId) throws GroupManagementDAOException {
public int getOwnGroupsCount(String username, int tenantId, String parentPath) throws GroupManagementDAOException {
PreparedStatement stmt = null;
ResultSet resultSet = null;
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT COUNT(ID) AS GROUP_COUNT FROM DM_GROUP WHERE OWNER = ? AND TENANT_ID = ?";
if (StringUtils.isNotBlank(parentPath)) {
sql += " AND PARENT_PATH = ?";
}
stmt = conn.prepareStatement(sql);
stmt.setString(1, username);
stmt.setInt(2, tenantId);
if (StringUtils.isNotBlank(parentPath)) {
stmt.setString(3, parentPath);
}
resultSet = stmt.executeQuery();
if (resultSet.next()) {
return resultSet.getInt("GROUP_COUNT");

@ -1194,6 +1194,79 @@ public class OracleDeviceDAOImpl extends AbstractDeviceDAOImpl {
}
}
@Override
public List<Device> getGroupedDevicesDetails(PaginationRequest request, List<Integer> deviceIds, String groupName,
int tenantId) throws DeviceManagementDAOException {
int limitValue = request.getRowCount();
int offsetValue = request.getStartIndex();
try {
List<Device> devices = new ArrayList<>();
if (deviceIds.isEmpty()) {
return devices;
}
Connection conn = this.getConnection();
int index = 1;
StringJoiner joiner = new StringJoiner(",",
"SELECT "
+ "DM_DEVICE.ID AS DEVICE_ID, "
+ "DM_DEVICE.NAME AS DEVICE_NAME, "
+ "DM_DEVICE.DESCRIPTION AS DESCRIPTION, "
+ "DM_DEVICE.DEVICE_TYPE_ID, "
+ "DM_DEVICE.DEVICE_IDENTIFICATION AS DEVICE_IDENTIFICATION, "
+ "e.ID AS ENROLMENT_ID, "
+ "e.OWNER, "
+ "e.OWNERSHIP, "
+ "e.DATE_OF_ENROLMENT, "
+ "e.DATE_OF_LAST_UPDATE, "
+ "e.STATUS, "
+ "e.IS_TRANSFERRED, "
+ "device_types.NAME AS DEVICE_TYPE "
+ "FROM DM_DEVICE_GROUP_MAP "
+ "INNER JOIN DM_DEVICE ON "
+ "DM_DEVICE_GROUP_MAP.DEVICE_ID = DM_DEVICE.ID "
+ "INNER JOIN DM_GROUP ON "
+ "DM_DEVICE_GROUP_MAP.GROUP_ID = DM_GROUP.ID "
+ "INNER JOIN DM_ENROLMENT e ON "
+ "DM_DEVICE.ID = e.DEVICE_ID AND "
+ "DM_DEVICE.TENANT_ID = e.TENANT_ID "
+ "INNER JOIN (SELECT ID, NAME FROM DM_DEVICE_TYPE) AS device_types ON "
+ "device_types.ID = DM_DEVICE.DEVICE_TYPE_ID "
+ "WHERE DM_DEVICE.ID IN (",
") AND DM_DEVICE.TENANT_ID = ?");
deviceIds.stream().map(ignored -> "?").forEach(joiner::add);
String query = joiner.toString();
if (StringUtils.isNotBlank(groupName)) {
query += " AND DM_GROUP.GROUP_NAME = ?";
}
query += " ORDER BY DEVICE_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
try (PreparedStatement ps = conn.prepareStatement(query)) {
for (Integer deviceId : deviceIds) {
ps.setInt(index++, deviceId);
}
ps.setInt(index++, tenantId);
if (StringUtils.isNotBlank(groupName)) {
ps.setString(index++, groupName);
}
ps.setInt(index++, offsetValue);
ps.setInt(index, limitValue);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
devices.add(DeviceManagementDAOUtil.loadDevice(rs));
}
return devices;
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving information of all registered devices " +
"according to device ids and the limit area.";
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
private Connection getConnection() throws SQLException {
return DeviceManagementDAOFactory.getConnection();
}

@ -18,6 +18,7 @@
package org.wso2.carbon.device.mgt.core.dao.impl.device;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.device.mgt.common.Count;
@ -1132,6 +1133,79 @@ public class SQLServerDeviceDAOImpl extends AbstractDeviceDAOImpl {
}
}
@Override
public List<Device> getGroupedDevicesDetails(PaginationRequest request, List<Integer> deviceIds, String groupName,
int tenantId) throws DeviceManagementDAOException {
int limitValue = request.getRowCount();
int offsetValue = request.getStartIndex();
try {
List<Device> devices = new ArrayList<>();
if (deviceIds.isEmpty()) {
return devices;
}
Connection conn = this.getConnection();
int index = 1;
StringJoiner joiner = new StringJoiner(",",
"SELECT "
+ "DM_DEVICE.ID AS DEVICE_ID, "
+ "DM_DEVICE.NAME AS DEVICE_NAME, "
+ "DM_DEVICE.DESCRIPTION AS DESCRIPTION, "
+ "DM_DEVICE.DEVICE_TYPE_ID, "
+ "DM_DEVICE.DEVICE_IDENTIFICATION AS DEVICE_IDENTIFICATION, "
+ "e.ID AS ENROLMENT_ID, "
+ "e.OWNER, "
+ "e.OWNERSHIP, "
+ "e.DATE_OF_ENROLMENT, "
+ "e.DATE_OF_LAST_UPDATE, "
+ "e.STATUS, "
+ "e.IS_TRANSFERRED, "
+ "device_types.NAME AS DEVICE_TYPE "
+ "FROM DM_DEVICE_GROUP_MAP "
+ "INNER JOIN DM_DEVICE ON "
+ "DM_DEVICE_GROUP_MAP.DEVICE_ID = DM_DEVICE.ID "
+ "INNER JOIN DM_GROUP ON "
+ "DM_DEVICE_GROUP_MAP.GROUP_ID = DM_GROUP.ID "
+ "INNER JOIN DM_ENROLMENT e ON "
+ "DM_DEVICE.ID = e.DEVICE_ID AND "
+ "DM_DEVICE.TENANT_ID = e.TENANT_ID "
+ "INNER JOIN (SELECT ID, NAME FROM DM_DEVICE_TYPE) AS device_types ON "
+ "device_types.ID = DM_DEVICE.DEVICE_TYPE_ID "
+ "WHERE DM_DEVICE.ID IN (",
") AND DM_DEVICE.TENANT_ID = ?");
deviceIds.stream().map(ignored -> "?").forEach(joiner::add);
String query = joiner.toString();
if (StringUtils.isNotBlank(groupName)) {
query += " AND DM_GROUP.GROUP_NAME = ?";
}
query += " ORDER BY DEVICE_ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
try (PreparedStatement ps = conn.prepareStatement(query)) {
for (Integer deviceId : deviceIds) {
ps.setInt(index++, deviceId);
}
ps.setInt(index++, tenantId);
if (StringUtils.isNotBlank(groupName)) {
ps.setString(index++, groupName);
}
ps.setInt(index++, offsetValue);
ps.setInt(index, limitValue);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
devices.add(DeviceManagementDAOUtil.loadDevice(rs));
}
return devices;
}
}
} catch (SQLException e) {
String msg = "Error occurred while retrieving information of all registered devices " +
"according to device ids and the limit area.";
log.error(msg, e);
throw new DeviceManagementDAOException(msg, e);
}
}
//TODO: Override for MSSQL
/*
@Override

@ -14,18 +14,32 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.dao.impl.group;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.GroupPaginationRequest;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.dao.impl.AbstractGroupDAOImpl;
import org.wso2.carbon.device.mgt.core.dao.util.DeviceManagementDAOUtil;
import org.wso2.carbon.device.mgt.core.dao.util.GroupManagementDAOUtil;
import java.sql.Connection;
import java.sql.PreparedStatement;
@ -38,134 +52,6 @@ import java.util.List;
* This class represents implementation of GroupDAO
*/
public class GenericGroupDAOImpl extends AbstractGroupDAOImpl {
@Override
public List<DeviceGroup> getGroups(GroupPaginationRequest request, int tenantId)
throws GroupManagementDAOException {
PreparedStatement stmt = null;
ResultSet resultSet = null;
List<DeviceGroup> deviceGroupList = null;
String groupName = request.getGroupName();
boolean hasGroupName = false;
String owner = request.getOwner();
String status = request.getStatus();
boolean hasOwner = false;
boolean hasStatus = false;
boolean hasLimit = request.getRowCount() != 0;
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?";
if (groupName != null && !groupName.isEmpty()) {
sql += " AND UPPER(GROUP_NAME) LIKE ?";
hasGroupName = true;
}
if (owner != null && !owner.isEmpty()) {
sql += " AND UPPER(OWNER) LIKE ?";
hasOwner = true;
}
if (status != null && !status.isEmpty()) {
sql += " AND STATUS = ?";
hasStatus = true;
}
if (hasLimit) {
sql += " LIMIT ?, ?";
}
int paramIndex = 1;
stmt = conn.prepareStatement(sql);
stmt.setInt(paramIndex++, tenantId);
if (hasGroupName) {
stmt.setString(paramIndex++, groupName + "%");
}
if (hasOwner) {
stmt.setString(paramIndex++, owner + "%");
}
if (hasStatus) {
stmt.setString(paramIndex++, status.toUpperCase());
}
if (hasLimit) {
stmt.setInt(paramIndex++, request.getStartIndex());
stmt.setInt(paramIndex, request.getRowCount());
}
resultSet = stmt.executeQuery();
deviceGroupList = new ArrayList<>();
while (resultSet.next()) {
deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet));
}
} catch (SQLException e) {
throw new GroupManagementDAOException("Error occurred while listing all groups in tenant: " + tenantId, e);
} finally {
GroupManagementDAOUtil.cleanupResources(stmt, resultSet);
}
return deviceGroupList;
}
@Override
public List<DeviceGroup> getGroups(GroupPaginationRequest request, List<Integer> deviceGroupIds,
int tenantId) throws GroupManagementDAOException {
int deviceGroupIdsCount = deviceGroupIds.size();
if (deviceGroupIdsCount == 0) {
return new ArrayList<>();
}
PreparedStatement stmt = null;
ResultSet resultSet = null;
List<DeviceGroup> deviceGroupList = null;
String groupName = request.getGroupName();
boolean hasGroupName = false;
String owner = request.getOwner();
boolean hasOwner = false;
boolean hasLimit = request.getRowCount() != 0;
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?";
if (groupName != null && !groupName.isEmpty()) {
sql += " AND GROUP_NAME LIKE ?";
hasGroupName = true;
}
if (owner != null && !owner.isEmpty()) {
sql += " AND OWNER LIKE ?";
hasOwner = true;
}
sql += " AND ID IN (";
for (int i = 0; i < deviceGroupIdsCount; i++) {
sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?";
}
sql += ")";
if (hasLimit) {
sql += " LIMIT ?, ?";
}
int paramIndex = 1;
stmt = conn.prepareStatement(sql);
stmt.setInt(paramIndex++, tenantId);
if (hasGroupName) {
stmt.setString(paramIndex++, groupName + "%");
}
if (hasOwner) {
stmt.setString(paramIndex++, owner + "%");
}
for (Integer deviceGroupId : deviceGroupIds) {
stmt.setInt(paramIndex++, deviceGroupId);
}
if (hasLimit) {
stmt.setInt(paramIndex++, request.getStartIndex());
stmt.setInt(paramIndex, request.getRowCount());
}
resultSet = stmt.executeQuery();
deviceGroupList = new ArrayList<>();
while (resultSet.next()) {
deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet));
}
} catch (SQLException e) {
throw new GroupManagementDAOException("Error occurred while listing all groups in tenant: " + tenantId, e);
} finally {
GroupManagementDAOUtil.cleanupResources(stmt, resultSet);
}
return deviceGroupList;
}
@Override
public List<Device> getDevices(int groupId, int startIndex, int rowCount, int tenantId)

@ -14,10 +14,28 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.dao.impl.group;
import org.apache.commons.lang.StringUtils;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.GroupPaginationRequest;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
@ -56,7 +74,8 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?";
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE TENANT_ID = ?";
if (groupName != null && !groupName.isEmpty()) {
sql += " AND GROUP_NAME LIKE ?";
hasGroupName = true;
@ -69,6 +88,9 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl {
sql += " AND STATUS = ?";
hasStatus = true;
}
if (StringUtils.isNotBlank(request.getParentPath())) {
sql += " AND PARENT_PATH LIKE ?";
}
if (hasLimit) {
sql += " OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
}
@ -85,6 +107,9 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl {
if (hasStatus) {
stmt.setString(paramIndex++, status.toUpperCase());
}
if (StringUtils.isNotBlank(request.getParentPath())) {
stmt.setString(paramIndex++, request.getParentPath());
}
if (hasLimit) {
stmt.setInt(paramIndex++, request.getStartIndex());
stmt.setInt(paramIndex, request.getRowCount());
@ -121,7 +146,8 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?";
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE TENANT_ID = ?";
if (groupName != null && !groupName.isEmpty()) {
sql += " AND GROUP_NAME LIKE ?";
hasGroupName = true;
@ -130,6 +156,9 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl {
sql += " AND OWNER LIKE ?";
hasOwner = true;
}
if (StringUtils.isNotBlank(request.getParentPath())) {
sql += " AND PARENT_PATH LIKE ?";
}
sql += " AND ID IN (";
for (int i = 0; i < deviceGroupIdsCount; i++) {
sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?";
@ -148,6 +177,9 @@ public class OracleGroupDAOImpl extends AbstractGroupDAOImpl {
if (hasOwner) {
stmt.setString(paramIndex++, owner + "%");
}
if (StringUtils.isNotBlank(request.getParentPath())) {
stmt.setString(paramIndex++, request.getParentPath());
}
for (Integer deviceGroupId : deviceGroupIds) {
stmt.setInt(paramIndex++, deviceGroupId);
}

@ -14,13 +14,29 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.dao.impl.group;
import org.apache.solr.common.StringUtils;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.GroupPaginationRequest;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOFactory;
@ -50,10 +66,10 @@ public class PostgreSQLGroupDAOImpl extends AbstractGroupDAOImpl {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql;
if(StringUtils.isEmpty(deviceGroup.getStatus())) {
sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID) " +
sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, PARENT_PATH) " +
"VALUES (?, ?, ?, ?) RETURNING ID";
} else {
sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, STATUS) " +
sql = "INSERT INTO DM_GROUP(DESCRIPTION, GROUP_NAME, OWNER, TENANT_ID, PARENT_PATH, STATUS) " +
"VALUES (?, ?, ?, ?, ?) RETURNING ID";
hasStatus = true;
}
@ -62,8 +78,9 @@ public class PostgreSQLGroupDAOImpl extends AbstractGroupDAOImpl {
stmt.setString(2, deviceGroup.getName());
stmt.setString(3, deviceGroup.getOwner());
stmt.setInt(4, tenantId);
stmt.setString(5, deviceGroup.getParentPath());
if(hasStatus) {
stmt.setString(5, deviceGroup.getStatus());
stmt.setString(6, deviceGroup.getStatus());
}
stmt.execute();
rs = stmt.getGeneratedKeys();
@ -79,135 +96,6 @@ public class PostgreSQLGroupDAOImpl extends AbstractGroupDAOImpl {
}
}
@Override
public List<DeviceGroup> getGroups(GroupPaginationRequest request, int tenantId)
throws GroupManagementDAOException {
PreparedStatement stmt = null;
ResultSet resultSet = null;
List<DeviceGroup> deviceGroupList = null;
String groupName = request.getGroupName();
boolean hasGroupName = false;
String owner = request.getOwner();
String status = request.getStatus();
boolean hasOwner = false;
boolean hasStatus = false;
boolean hasLimit = request.getRowCount() != 0;
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?";
if (groupName != null && !groupName.isEmpty()) {
sql += " AND GROUP_NAME LIKE ?";
hasGroupName = true;
}
if (owner != null && !owner.isEmpty()) {
sql += " AND OWNER LIKE ?";
hasOwner = true;
}
if (status != null && !status.isEmpty()) {
sql += " AND STATUS = ?";
hasStatus = true;
}
if (hasLimit) {
sql += " LIMIT ? OFFSET ?";
}
int paramIndex = 1;
stmt = conn.prepareStatement(sql);
stmt.setInt(paramIndex++, tenantId);
if (hasGroupName) {
stmt.setString(paramIndex++, groupName + "%");
}
if (hasOwner) {
stmt.setString(paramIndex++, owner + "%");
}
if (hasStatus) {
stmt.setString(paramIndex++, status.toUpperCase());
}
if (hasLimit) {
stmt.setInt(paramIndex++, request.getRowCount());
stmt.setInt(paramIndex, request.getStartIndex());
}
resultSet = stmt.executeQuery();
deviceGroupList = new ArrayList<>();
while (resultSet.next()) {
deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet));
}
} catch (SQLException e) {
throw new GroupManagementDAOException("Error occurred while listing all groups in tenant: " + tenantId, e);
} finally {
GroupManagementDAOUtil.cleanupResources(stmt, resultSet);
}
return deviceGroupList;
}
@Override
public List<DeviceGroup> getGroups(GroupPaginationRequest request, List<Integer> deviceGroupIds,
int tenantId) throws GroupManagementDAOException {
int deviceGroupIdsCount = deviceGroupIds.size();
if (deviceGroupIdsCount == 0) {
return new ArrayList<>();
}
PreparedStatement stmt = null;
ResultSet resultSet = null;
List<DeviceGroup> deviceGroupList = null;
String groupName = request.getGroupName();
boolean hasGroupName = false;
String owner = request.getOwner();
boolean hasOwner = false;
boolean hasLimit = request.getRowCount() != 0;
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?";
if (groupName != null && !groupName.isEmpty()) {
sql += " AND GROUP_NAME LIKE ?";
hasGroupName = true;
}
if (owner != null && !owner.isEmpty()) {
sql += " AND OWNER LIKE ?";
hasOwner = true;
}
sql += " AND ID IN (";
for (int i = 0; i < deviceGroupIdsCount; i++) {
sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?";
}
sql += ")";
if (hasLimit) {
sql += " LIMIT ? OFFSET ?";
}
int paramIndex = 1;
stmt = conn.prepareStatement(sql);
stmt.setInt(paramIndex++, tenantId);
if (hasGroupName) {
stmt.setString(paramIndex++, groupName + "%");
}
if (hasOwner) {
stmt.setString(paramIndex++, owner + "%");
}
for (Integer deviceGroupId : deviceGroupIds) {
stmt.setInt(paramIndex++, deviceGroupId);
}
if (hasLimit) {
stmt.setInt(paramIndex++, request.getRowCount());
stmt.setInt(paramIndex, request.getStartIndex());
}
resultSet = stmt.executeQuery();
deviceGroupList = new ArrayList<>();
while (resultSet.next()) {
deviceGroupList.add(GroupManagementDAOUtil.loadGroup(resultSet));
}
} catch (SQLException e) {
throw new GroupManagementDAOException("Error occurred while listing all groups in tenant: " + tenantId, e);
} finally {
GroupManagementDAOUtil.cleanupResources(stmt, resultSet);
}
return deviceGroupList;
}
@Override
public List<Device> getDevices(int groupId, int startIndex, int rowCount, int tenantId)
throws GroupManagementDAOException {

@ -14,10 +14,28 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.dao.impl.group;
import org.apache.commons.lang.StringUtils;
import org.wso2.carbon.device.mgt.common.Device;
import org.wso2.carbon.device.mgt.common.GroupPaginationRequest;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
@ -56,7 +74,8 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?";
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE TENANT_ID = ?";
if (groupName != null && !groupName.isEmpty()) {
sql += " AND GROUP_NAME LIKE ?";
hasGroupName = true;
@ -69,6 +88,9 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl {
sql += " AND STATUS = ?";
hasStatus = true;
}
if (StringUtils.isNotBlank(request.getParentPath())) {
sql += " AND PARENT_PATH LIKE ?";
}
if (hasLimit) {
sql += " ORDER BY ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY";
}
@ -85,6 +107,9 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl {
if (hasStatus) {
stmt.setString(paramIndex++, status.toUpperCase());
}
if (StringUtils.isNotBlank(request.getParentPath())) {
stmt.setString(paramIndex++, request.getParentPath());
}
if (hasLimit) {
stmt.setInt(paramIndex++, request.getStartIndex());
stmt.setInt(paramIndex, request.getRowCount());
@ -121,7 +146,8 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl {
try {
Connection conn = GroupManagementDAOFactory.getConnection();
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS FROM DM_GROUP WHERE TENANT_ID = ?";
String sql = "SELECT ID, DESCRIPTION, GROUP_NAME, OWNER, STATUS, PARENT_PATH FROM DM_GROUP "
+ "WHERE TENANT_ID = ?";
if (groupName != null && !groupName.isEmpty()) {
sql += " AND GROUP_NAME LIKE ?";
hasGroupName = true;
@ -130,6 +156,9 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl {
sql += " AND OWNER LIKE ?";
hasOwner = true;
}
if (StringUtils.isNotBlank(request.getParentPath())) {
sql += " AND PARENT_PATH LIKE ?";
}
sql += " AND ID IN (";
for (int i = 0; i < deviceGroupIdsCount; i++) {
sql += (deviceGroupIdsCount - 1 != i) ? "?," : "?";
@ -148,6 +177,9 @@ public class SQLServerGroupDAOImpl extends AbstractGroupDAOImpl {
if (hasOwner) {
stmt.setString(paramIndex++, owner + "%");
}
if (StringUtils.isNotBlank(request.getParentPath())) {
stmt.setString(paramIndex++, request.getParentPath());
}
for (Integer deviceGroupId : deviceGroupIds) {
stmt.setInt(paramIndex++, deviceGroupId);
}

@ -14,6 +14,22 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.dao.util;
@ -86,6 +102,7 @@ public final class GroupManagementDAOUtil {
group.setName(resultSet.getString("GROUP_NAME"));
group.setOwner(resultSet.getString("OWNER"));
group.setStatus(resultSet.getString("STATUS"));
group.setParentPath(resultSet.getString("PARENT_PATH"));
return group;
}

@ -315,7 +315,7 @@ public class ReportManagementServiceImpl implements ReportManagementService {
}
//Map date blocks and counts
resultObject.addProperty(
dateFormat.format(endDate) + " - " + dateFormat.format(previousDate), sum);
dateFormat.format(previousDate) + " - " + dateFormat.format(endDate), sum);
endDate = previousDate;
}

@ -992,4 +992,14 @@ public interface DeviceManagementProviderService {
License getLicenseConfig (String deviceTypeName) throws DeviceManagementException;
/**
* This method retrieves a list of devices details.
* @param request paginated request object.
* @param devicesIds devices ids list
* @param groupName name of the group
* @return {@link PaginationResult}
* @throws DeviceManagementException if any service level or DAO level error occurs.
*/
PaginationResult getDevicesDetails(PaginationRequest request, List<Integer> devicesIds, String groupName)
throws DeviceManagementException;
}

@ -4429,4 +4429,39 @@ public class DeviceManagementProviderServiceImpl implements DeviceManagementProv
return deviceManagementService.getLicenseConfig();
}
@Override
public PaginationResult getDevicesDetails(PaginationRequest request, List<Integer> devicesIds,
String groupName) throws DeviceManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
if (log.isDebugEnabled()) {
log.debug("Getting all devices details for device ids: " + devicesIds);
}
PaginationResult paginationResult = new PaginationResult();
List<Device> subscribedDeviceDetails;
try {
DeviceManagementDAOFactory.openConnection();
subscribedDeviceDetails = deviceDAO.getGroupedDevicesDetails(request, devicesIds, groupName, tenantId);
if (subscribedDeviceDetails.isEmpty()) {
paginationResult.setData(new ArrayList<>());
paginationResult.setRecordsFiltered(0);
paginationResult.setRecordsTotal(0);
return paginationResult;
}
int count = deviceDAO.getGroupedDevicesCount(request, devicesIds, groupName, tenantId);
paginationResult.setRecordsFiltered(count);
paginationResult.setRecordsTotal(count);
} catch (DeviceManagementDAOException e) {
String msg = "Error occurred while retrieving device list for device ids " + devicesIds;
log.error(msg, e);
throw new DeviceManagementException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while opening a connection to the data source";
log.error(msg, e);
throw new DeviceManagementException(msg, e);
} finally {
DeviceManagementDAOFactory.closeConnection();
}
paginationResult.setData(populateAllDeviceInfo(subscribedDeviceDetails));
return paginationResult;
}
}

@ -14,6 +14,23 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.service;
@ -60,10 +77,11 @@ public interface GroupManagementProviderService {
* Delete existing device group.
*
* @param groupId to be deleted.
* @param isDeleteChildren to delete the children groups or not.
* @return status of the delete operation.
* @throws GroupManagementException
*/
boolean deleteGroup(int groupId) throws GroupManagementException;
boolean deleteGroup(int groupId, boolean isDeleteChildren) throws GroupManagementException;
/**
* Get the device group provided the device group id.
@ -75,6 +93,17 @@ public interface GroupManagementProviderService {
*/
DeviceGroup getGroup(int groupId, boolean requireGroupProps) throws GroupManagementException;
/**
* Get the device group provided the device group id and depth of children groups.
*
* @param groupId of the group.
* @param requireGroupProps to include group properties.
* @param depth of children groups to retrieve.
* @return {@link DeviceGroup} group with details.
* @throws GroupManagementException on error during retrieval of group
*/
DeviceGroup getGroup(int groupId, boolean requireGroupProps, int depth) throws GroupManagementException;
/**
* Get the device group provided the device group name.
*
@ -85,6 +114,17 @@ public interface GroupManagementProviderService {
*/
DeviceGroup getGroup(String groupName, boolean requireGroupProps) throws GroupManagementException;
/**
* Get the device group provided the device group id and depth of children groups.
*
* @param groupName of the group.
* @param requireGroupProps to include group properties.
* @param depth of children groups to retrieve.
* @return {@link DeviceGroup} group with details.
* @throws GroupManagementException on error during retrieval of group
*/
DeviceGroup getGroup(String groupName, boolean requireGroupProps, int depth) throws GroupManagementException;
/**
* Get all device groups in tenant.
*
@ -127,6 +167,18 @@ public interface GroupManagementProviderService {
PaginationResult getGroups(String username, GroupPaginationRequest paginationRequest, boolean requireGroupProps)
throws GroupManagementException;
/**
* Get device groups with children groups hierarchically which belongs to specified user with pagination.
*
* @param username of the user.
* @param request to filter results
* @param requireGroupProps to include group properties
* @return {@link PaginationResult} paginated groups.
* @throws GroupManagementException on error during retrieval of groups with hierarchy
*/
PaginationResult getGroupsWithHierarchy(String username, GroupPaginationRequest request,
boolean requireGroupProps) throws GroupManagementException;
/**
* Get all device group count in tenant
*
@ -147,10 +199,11 @@ public interface GroupManagementProviderService {
* Get device group count of user
*
* @param username of the user
* @param parentPath of the group
* @return group count
* @throws GroupManagementException
*/
int getGroupCount(String username) throws GroupManagementException;
int getGroupCount(String username, String parentPath) throws GroupManagementException;
/**
* Manage device group sharing with user with list of roles.

@ -14,10 +14,28 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.service;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.wso2.carbon.CarbonConstants;
@ -36,14 +54,12 @@ import org.wso2.carbon.device.mgt.common.group.mgt.GroupAlreadyExistException;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupNotExistException;
import org.wso2.carbon.device.mgt.common.group.mgt.RoleDoesNotExistException;
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.dao.GroupDAO;
import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOException;
import org.wso2.carbon.device.mgt.core.dao.GroupManagementDAOFactory;
import org.wso2.carbon.device.mgt.core.event.config.EventOperationTaskConfiguration;
import org.wso2.carbon.device.mgt.core.event.config.GroupAssignmentEventOperationExecutor;
import org.wso2.carbon.device.mgt.core.geo.task.GeoFenceEventOperationManager;
import org.wso2.carbon.device.mgt.core.internal.DeviceManagementDataHolder;
@ -56,10 +72,11 @@ import org.wso2.carbon.user.api.UserStoreManager;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;
public class GroupManagementProviderServiceImpl implements GroupManagementProviderService {
@ -95,6 +112,20 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
GroupManagementDAOFactory.beginTransaction();
DeviceGroup existingGroup = this.groupDAO.getGroup(deviceGroup.getName(), tenantId);
if (existingGroup == null) {
if (deviceGroup.getParentGroupId() == 0) {
deviceGroup.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR);
} else {
DeviceGroup immediateParentGroup = groupDAO.getGroup(deviceGroup.getParentGroupId(), tenantId);
if (immediateParentGroup == null) {
String msg = "Parent group with group ID '" + deviceGroup.getParentGroupId()
+ "' does not exist. Hence creating of group '" + deviceGroup.getName()
+ "' was not success";
log.error(msg);
throw new GroupManagementException(msg);
}
String parentPath = DeviceManagerUtil.createParentPath(immediateParentGroup);
deviceGroup.setParentPath(parentPath);
}
int updatedGroupID = this.groupDAO.addGroup(deviceGroup, tenantId);
if (deviceGroup.getGroupProperties() != null && deviceGroup.getGroupProperties().size() > 0) {
this.groupDAO.addGroupProperties(deviceGroup, updatedGroupID, tenantId);
@ -146,7 +177,35 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
GroupManagementDAOFactory.beginTransaction();
DeviceGroup existingGroup = this.groupDAO.getGroup(groupId, tenantId);
if (existingGroup != null) {
this.groupDAO.updateGroup(deviceGroup, groupId, tenantId);
List<DeviceGroup> groupsToUpdate = new ArrayList<>();
String immediateParentID = StringUtils.substringAfterLast(existingGroup.getParentPath(), DeviceGroupConstants.HierarchicalGroup.SEPERATOR);
String parentPath = "";
if (deviceGroup.getParentGroupId() == 0) {
deviceGroup.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR);
} else {
DeviceGroup immediateParentGroup = groupDAO.getGroup(deviceGroup.getParentGroupId(), tenantId);
if (immediateParentGroup == null) {
String msg = "Parent group with group ID '" + deviceGroup.getParentGroupId()
+ "' does not exist. Hence updating of group '" + groupId
+ "' was not success";
log.error(msg);
throw new GroupManagementException(msg);
}
parentPath = DeviceManagerUtil.createParentPath(immediateParentGroup);
deviceGroup.setParentPath(parentPath);
}
deviceGroup.setGroupId(groupId);
groupsToUpdate.add(deviceGroup);
if (StringUtils.isNotBlank(immediateParentID)) {
List<DeviceGroup> childrenGroups = groupDAO.getChildrenGroups(
DeviceManagerUtil.createParentPath(existingGroup), tenantId);
for (DeviceGroup childrenGroup : childrenGroups) {
childrenGroup.setParentPath(childrenGroup.getParentPath()
.replace(existingGroup.getParentPath(), parentPath));
groupsToUpdate.add(childrenGroup);
}
}
this.groupDAO.updateGroups(groupsToUpdate, tenantId);
if (deviceGroup.getGroupProperties() != null && deviceGroup.getGroupProperties().size() > 0) {
this.groupDAO.updateGroupProperties(deviceGroup, groupId, tenantId);
}
@ -178,7 +237,7 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
* {@inheritDoc}
*/
@Override
public boolean deleteGroup(int groupId) throws GroupManagementException {
public boolean deleteGroup(int groupId, boolean isDeleteChildren) throws GroupManagementException {
if (log.isDebugEnabled()) {
log.debug("Delete group: " + groupId);
}
@ -189,8 +248,37 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
try {
GroupManagementDAOFactory.beginTransaction();
this.groupDAO.deleteGroup(groupId, tenantId);
this.groupDAO.deleteAllGroupProperties(groupId, tenantId);
List<DeviceGroup> childrenGroups = new ArrayList<>();
List<Integer> groupIdsToDelete = new ArrayList<>();
if (deviceGroup.getChildrenGroups() != null && !deviceGroup.getChildrenGroups().isEmpty()) {
String parentPath = DeviceManagerUtil.createParentPath(deviceGroup);
childrenGroups = groupDAO.getChildrenGroups(parentPath, tenantId);
if (isDeleteChildren) {
groupIdsToDelete = childrenGroups.stream().map(DeviceGroup::getGroupId)
.collect(Collectors.toList());
} else {
for (DeviceGroup childrenGroup : childrenGroups) {
String newParentPath = childrenGroup.getParentPath()
.replace(DeviceGroupConstants.HierarchicalGroup.SEPERATOR + deviceGroup.getGroupId(), "");
if (StringUtils.isEmpty(newParentPath)) {
newParentPath = DeviceGroupConstants.HierarchicalGroup.SEPERATOR;
}
childrenGroup.setParentPath(newParentPath);
}
}
}
if (isDeleteChildren) {
groupIdsToDelete.add(groupId);
groupDAO.deleteGroupsMapping(groupIdsToDelete, tenantId);
groupDAO.deleteGroups(groupIdsToDelete, tenantId);
groupDAO.deleteAllGroupsProperties(groupIdsToDelete, tenantId);
} else {
groupDAO.deleteGroup(groupId, tenantId);
groupDAO.deleteAllGroupProperties(groupId, tenantId);
if (!childrenGroups.isEmpty()) {
groupDAO.updateGroups(childrenGroups, tenantId);
}
}
GroupManagementDAOFactory.commitTransaction();
if (log.isDebugEnabled()) {
log.debug("DeviceGroup " + deviceGroup.getName() + " removed.");
@ -219,6 +307,11 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
*/
@Override
public DeviceGroup getGroup(int groupId, boolean requireGroupProps) throws GroupManagementException {
return getGroup(groupId, requireGroupProps, 1);
}
@Override
public DeviceGroup getGroup(int groupId, boolean requireGroupProps, int depth) throws GroupManagementException {
if (log.isDebugEnabled()) {
log.debug("Get group by id: " + groupId);
}
@ -227,8 +320,13 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
try {
GroupManagementDAOFactory.openConnection();
deviceGroup = this.groupDAO.getGroup(groupId, tenantId);
if (requireGroupProps) {
populateGroupProperties(deviceGroup, tenantId);
if (deviceGroup != null) {
String parentPath = DeviceManagerUtil.createParentPath(deviceGroup);
List<DeviceGroup> childrenGroups = groupDAO.getChildrenGroups(parentPath, tenantId);
createGroupWithChildren(deviceGroup, childrenGroups, requireGroupProps, tenantId, depth, 0);
if (requireGroupProps) {
populateGroupProperties(deviceGroup, tenantId);
}
}
return deviceGroup;
} catch (GroupManagementDAOException e) {
@ -236,11 +334,8 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
log.error(msg, e);
throw new GroupManagementException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while opening a connection to the data source.";
log.error(msg, e);
throw new GroupManagementException(msg, e);
} catch (Exception e) {
String msg = "Error occurred in getGroup for groupId: " + groupId;
String msg = "Error occurred while opening a connection to the data source to retrieve the group '"
+ groupId + "'";
log.error(msg, e);
throw new GroupManagementException(msg, e);
} finally {
@ -253,6 +348,11 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
*/
@Override
public DeviceGroup getGroup(String groupName, boolean requireGroupProps) throws GroupManagementException {
return getGroup(groupName, requireGroupProps, 1);
}
@Override
public DeviceGroup getGroup(String groupName, boolean requireGroupProps, int depth) throws GroupManagementException {
if (groupName == null) {
String msg = "Received empty groupName for getGroup";
log.error(msg);
@ -266,8 +366,13 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
try {
GroupManagementDAOFactory.openConnection();
deviceGroup = this.groupDAO.getGroup(groupName, tenantId);
if (requireGroupProps) {
populateGroupProperties(deviceGroup, tenantId);
if (deviceGroup != null) {
String parentPath = DeviceManagerUtil.createParentPath(deviceGroup);
List<DeviceGroup> childrenGroups = groupDAO.getChildrenGroups(parentPath, tenantId);
createGroupWithChildren(deviceGroup, childrenGroups, requireGroupProps, tenantId, depth , 0);
if (requireGroupProps) {
populateGroupProperties(deviceGroup, tenantId);
}
}
return deviceGroup;
} catch (GroupManagementDAOException e) {
@ -275,11 +380,8 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
log.error(msg, e);
throw new GroupManagementException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while opening a connection to the data source.";
log.error(msg, e);
throw new GroupManagementException(msg, e);
} catch (Exception e) {
String msg = "Error occurred in getGroup with name " + groupName;
String msg = "Error occurred while opening a connection to the data source to retrieve group with name '"
+ groupName + "'";
log.error(msg, e);
throw new GroupManagementException(msg, e);
} finally {
@ -296,12 +398,10 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
try {
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
GroupManagementDAOFactory.openConnection();
deviceGroups = this.groupDAO.getGroups(tenantId);
if (requireGroupProps) {
if (deviceGroups != null && !deviceGroups.isEmpty()) {
for (DeviceGroup group : deviceGroups) {
populateGroupProperties(group, tenantId);
}
deviceGroups = groupDAO.getRootGroups(tenantId);
for (DeviceGroup deviceGroup : deviceGroups) {
if (requireGroupProps) {
populateGroupProperties(deviceGroup, tenantId);
}
}
return deviceGroups;
@ -334,16 +434,15 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
log.debug("Get groups with pagination " + request.toString());
}
request = DeviceManagerUtil.validateGroupListPageSize(request);
List<DeviceGroup> deviceGroups;
List<DeviceGroup> rootGroups;
try {
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
GroupManagementDAOFactory.openConnection();
deviceGroups = this.groupDAO.getGroups(request, tenantId);
if (requireGroupProps) {
if (deviceGroups != null && !deviceGroups.isEmpty()) {
for (DeviceGroup group : deviceGroups) {
populateGroupProperties(group, tenantId);
}
request.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR);
rootGroups = this.groupDAO.getGroups(request, tenantId);
for (DeviceGroup rootGroup : rootGroups) {
if (requireGroupProps) {
populateGroupProperties(rootGroup, tenantId);
}
}
} catch (GroupManagementDAOException e) {
@ -362,11 +461,68 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
GroupManagementDAOFactory.closeConnection();
}
PaginationResult groupResult = new PaginationResult();
groupResult.setData(deviceGroups);
groupResult.setData(rootGroups);
groupResult.setRecordsTotal(getGroupCount(request));
return groupResult;
}
@Override
public PaginationResult getGroupsWithHierarchy(String username, GroupPaginationRequest request,
boolean requireGroupProps) throws GroupManagementException {
if (request == null) {
String msg = "Received incomplete data for retrieve groups with hierarchy";
log.error(msg);
throw new GroupManagementException(msg);
}
if (log.isDebugEnabled()) {
log.debug("Get groups with hierarchy " + request.toString());
}
DeviceManagerUtil.validateGroupListPageSize(request);
List<DeviceGroup> rootGroups;
try {
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
request.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR);
if (StringUtils.isBlank(username)) {
GroupManagementDAOFactory.openConnection();
rootGroups = groupDAO.getGroups(request, tenantId);
} else {
List<Integer> allDeviceGroupIdsOfUser = getGroupIds(username);
GroupManagementDAOFactory.openConnection();
rootGroups = this.groupDAO.getGroups(request, allDeviceGroupIdsOfUser, tenantId);
}
String parentPath;
List<DeviceGroup> childrenGroups;
for (DeviceGroup rootGroup : rootGroups) {
parentPath = DeviceManagerUtil.createParentPath(rootGroup);
childrenGroups = groupDAO.getChildrenGroups(parentPath, tenantId);
createGroupWithChildren(
rootGroup, childrenGroups, requireGroupProps, tenantId, request.getDepth(), 0);
if (requireGroupProps) {
populateGroupProperties(rootGroup, tenantId);
}
}
} catch (GroupManagementDAOException e) {
String msg = "Error occurred while retrieving all groups with hierarchy";
log.error(msg, e);
throw new GroupManagementException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred while opening a connection to the data source to retrieve all groups "
+ "with hierarchy";
log.error(msg, e);
throw new GroupManagementException(msg, e);
} finally {
GroupManagementDAOFactory.closeConnection();
}
PaginationResult groupResult = new PaginationResult();
groupResult.setData(rootGroups);
if (StringUtils.isBlank(username)) {
groupResult.setRecordsTotal(getGroupCount(request));
} else {
groupResult.setRecordsTotal(getGroupCount(username, request.getParentPath()));
}
return groupResult;
}
@Override
public List<DeviceGroup> getGroups(String username, boolean requireGroupProps) throws GroupManagementException {
if (username == null || username.isEmpty()) {
@ -457,16 +613,15 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
}
request = DeviceManagerUtil.validateGroupListPageSize(request);
List<Integer> allDeviceGroupIdsOfUser = getGroupIds(currentUser);
List<DeviceGroup> allMatchingGroups;
List<DeviceGroup> rootGroups;
try {
int tenantId = CarbonContext.getThreadLocalCarbonContext().getTenantId();
GroupManagementDAOFactory.openConnection();
allMatchingGroups = this.groupDAO.getGroups(request, allDeviceGroupIdsOfUser, tenantId);
if (requireGroupProps) {
if (allMatchingGroups != null && !allMatchingGroups.isEmpty()) {
for (DeviceGroup group : allMatchingGroups) {
populateGroupProperties(group, tenantId);
}
request.setParentPath(DeviceGroupConstants.HierarchicalGroup.SEPERATOR);
rootGroups = this.groupDAO.getGroups(request, allDeviceGroupIdsOfUser, tenantId);
for (DeviceGroup rootGroup : rootGroups) {
if (requireGroupProps) {
populateGroupProperties(rootGroup, tenantId);
}
}
} catch (GroupManagementDAOException | SQLException e) {
@ -481,8 +636,8 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
GroupManagementDAOFactory.closeConnection();
}
PaginationResult groupResult = new PaginationResult();
groupResult.setData(allMatchingGroups);
groupResult.setRecordsTotal(getGroupCount(currentUser));
groupResult.setData(rootGroups);
groupResult.setRecordsTotal(getGroupCount(currentUser, request.getParentPath()));
return groupResult;
}
@ -557,7 +712,7 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
* {@inheritDoc}
*/
@Override
public int getGroupCount(String username) throws GroupManagementException {
public int getGroupCount(String username, String parentPath) throws GroupManagementException {
if (username == null || username.isEmpty()) {
String msg = "Received empty user name for getGroupCount";
log.error(msg);
@ -574,8 +729,8 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
.getUserStoreManager();
String[] roleList = userStoreManager.getRoleListOfUser(username);
GroupManagementDAOFactory.openConnection();
count = groupDAO.getOwnGroupsCount(username, tenantId);
count += groupDAO.getGroupsCount(roleList, tenantId);
count = groupDAO.getOwnGroupsCount(username, tenantId, parentPath);
count += groupDAO.getGroupsCount(roleList, tenantId, parentPath);
return count;
} catch (UserStoreException e) {
String msg = "Error occurred while retrieving role list of user '" + username + "'";
@ -1131,4 +1286,40 @@ public class GroupManagementProviderServiceImpl implements GroupManagementProvid
}
}
}
/**
* Recursive method to create group with children based on params to provide hierarchical grouping.
* @param parentGroup to which children group should be set.
* @param childrenGroups which are descendants of parent group.
* @param requireGroupProps to include device properties.
* @param tenantId of the group.
* @param depth of children groups set and when reaches recursive call returns to callee.
* @param counter to track the recursive calls and to stop when reaches the depth.
* @throws GroupManagementDAOException on error during population of group properties.
*/
private void createGroupWithChildren(DeviceGroup parentGroup, List<DeviceGroup> childrenGroups,
boolean requireGroupProps, int tenantId, int depth, int counter) throws GroupManagementDAOException {
if (childrenGroups.isEmpty() || depth == counter) {
return;
}
List<DeviceGroup> immediateChildrenGroups = new ArrayList<>();
Iterator<DeviceGroup> iterator = childrenGroups.iterator();
while (iterator.hasNext()) {
DeviceGroup childGroup = iterator.next();
int immediateParentID = Integer.parseInt(StringUtils.substringAfterLast(
childGroup.getParentPath(), DeviceGroupConstants.HierarchicalGroup.SEPERATOR));
if (immediateParentID == parentGroup.getGroupId()) {
if (requireGroupProps) {
populateGroupProperties(childGroup, tenantId);
}
immediateChildrenGroups.add(childGroup);
iterator.remove();
}
}
parentGroup.setChildrenGroups(immediateChildrenGroups);
counter++;
for (DeviceGroup nextParentGroup : immediateChildrenGroups) {
createGroupWithChildren(nextParentGroup, childrenGroups, requireGroupProps, tenantId, depth, counter);
}
}
}

@ -70,6 +70,7 @@ import org.wso2.carbon.device.mgt.common.exceptions.DeviceNotFoundException;
import org.wso2.carbon.device.mgt.common.exceptions.TransactionManagementException;
import org.wso2.carbon.device.mgt.common.geo.service.GeofenceData;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroup;
import org.wso2.carbon.device.mgt.common.group.mgt.DeviceGroupConstants;
import org.wso2.carbon.device.mgt.common.group.mgt.GroupManagementException;
import org.wso2.carbon.device.mgt.common.exceptions.MetadataManagementException;
import org.wso2.carbon.device.mgt.common.notification.mgt.NotificationManagementException;
@ -1162,4 +1163,18 @@ public final class DeviceManagerUtil {
.getUserStoreManager();
return userStoreManager.getUserClaimValue(username, claimUri, null);
}
/**
* Create the parent path that the children groups can have
* @param deviceGroup parent group
* @return created parent path
*/
public static String createParentPath(DeviceGroup deviceGroup) {
if (DeviceGroupConstants.HierarchicalGroup.SEPERATOR.equals(deviceGroup.getParentPath())) {
return deviceGroup.getParentPath() + deviceGroup.getGroupId();
} else {
return deviceGroup.getParentPath() + DeviceGroupConstants.HierarchicalGroup.SEPERATOR
+ deviceGroup.getGroupId();
}
}
}

@ -52,6 +52,7 @@ public class MDMAndroidOperationUtil {
enterpriseApplication.setType(application.getType().toString());
enterpriseApplication.setUrl(application.getLocation());
enterpriseApplication.setAppIdentifier(application.getIdentifier());
enterpriseApplication.setProperties(application.getProperties());
operation.setPayLoad(enterpriseApplication.toJSON());
break;
case PUBLIC:
@ -59,6 +60,7 @@ public class MDMAndroidOperationUtil {
new AppStoreApplication();
appStoreApplication.setType(application.getType().toString());
appStoreApplication.setAppIdentifier(application.getIdentifier());
appStoreApplication.setProperties(application.getProperties());
operation.setPayLoad(appStoreApplication.toJSON());
break;
case WEBAPP:
@ -67,6 +69,7 @@ public class MDMAndroidOperationUtil {
webApplication.setUrl(application.getLocation());
webApplication.setName(application.getName());
webApplication.setType(application.getType().toString());
webApplication.setProperties(application.getProperties());
operation.setPayLoad(webApplication.toJSON());
break;
default:
@ -93,6 +96,7 @@ public class MDMAndroidOperationUtil {
new EnterpriseApplication();
enterpriseApplication.setType(application.getType().toString());
enterpriseApplication.setAppIdentifier(application.getIdentifier());
enterpriseApplication.setProperties(application.getProperties());
operation.setPayLoad(enterpriseApplication.toJSON());
break;
case PUBLIC:

@ -8,6 +8,23 @@
*
* 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.
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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
@ -103,7 +120,7 @@ public class GroupManagementProviderServiceNegativeTest extends BaseDeviceManage
expectedExceptions = {GroupManagementException.class}, expectedExceptionsMessageRegExp = "Error occurred "
+ "while retrieving group count of user.*")
public void testGetGroupCountWithUserName() throws GroupManagementException {
groupManagementProviderService.getGroupCount("test");
groupManagementProviderService.getGroupCount("test", null);
}
@ -145,7 +162,7 @@ public class GroupManagementProviderServiceNegativeTest extends BaseDeviceManage
expectedExceptions = {GroupManagementException.class}, expectedExceptionsMessageRegExp = "Received empty "
+ "user name for getGroupCount.*")
public void testGetGroupCountWithUserName2() throws GroupManagementException {
groupManagementProviderService.getGroupCount(null);
groupManagementProviderService.getGroupCount(null, null);
}
@Test(description = "This method tests getGroups method under negative circumstances",

@ -14,6 +14,23 @@
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*
* Copyright (c) 2021, Entgra (pvt) Ltd. (https://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.core.service;
@ -119,13 +136,13 @@ public class GroupManagementProviderServiceTest extends BaseDeviceManagementTest
@Test(dependsOnMethods = ("createGroup"))
public void deleteGroup() throws GroupManagementException {
DeviceGroup deviceGroup = groupManagementProviderService.getGroup(TestUtils.createDeviceGroup4().getName(), false);
Assert.assertTrue(groupManagementProviderService.deleteGroup(deviceGroup.getGroupId()));
Assert.assertTrue(groupManagementProviderService.deleteGroup(deviceGroup.getGroupId(), false));
}
@Test(dependsOnMethods = ("createGroup"))
public void deleteGroupNotExists() throws GroupManagementException {
groupManagementProviderService.deleteGroup(8);
groupManagementProviderService.deleteGroup(8, false);
}
@ -190,7 +207,7 @@ public class GroupManagementProviderServiceTest extends BaseDeviceManagementTest
@Test(dependsOnMethods = ("createGroup"))
public void getGroupCountByUsername(String username) throws GroupManagementException {
int x = groupManagementProviderService.getGroupCount(username);
int x = groupManagementProviderService.getGroupCount(username, null);
Assert.assertNotNull(x);
}

@ -14,6 +14,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP (
STATUS VARCHAR(50) DEFAULT NULL,
DESCRIPTION TEXT DEFAULT NULL,
OWNER VARCHAR(45) DEFAULT NULL,
PARENT_PATH VARCHAR(255) DEFAULT NULL,
TENANT_ID INTEGER DEFAULT 0,
PRIMARY KEY (ID)
);
@ -398,7 +399,7 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_INFO (
DEVICE_ID INT NULL,
ENROLMENT_ID INT NOT NULL,
KEY_FIELD VARCHAR(45) NULL,
VALUE_FIELD VARCHAR(1000) NULL,
VALUE_FIELD VARCHAR(1500) NULL,
PRIMARY KEY (ID),
CONSTRAINT DM_DEVICE_INFO_DEVICE
FOREIGN KEY (DEVICE_ID)

@ -400,7 +400,7 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_INFO (
ID INTEGER AUTO_INCREMENT NOT NULL,
DEVICE_ID INT NULL,
KEY_FIELD VARCHAR(45) NULL,
VALUE_FIELD VARCHAR(1000) NULL,
VALUE_FIELD VARCHAR(1500) NULL,
PRIMARY KEY (ID),
CONSTRAINT DM_DEVICE_INFO_DEVICE
FOREIGN KEY (DEVICE_ID)

@ -18,6 +18,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP (
DATE_OF_CREATE BIGINT DEFAULT NULL,
DATE_OF_LAST_UPDATE BIGINT DEFAULT NULL,
OWNER VARCHAR(45) DEFAULT NULL,
PARENT_PATH VARCHAR(255) DEFAULT NULL,
TENANT_ID INTEGER DEFAULT 0,
PRIMARY KEY (ID)
);
@ -452,7 +453,7 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_INFO (
DEVICE_ID INT NULL,
ENROLMENT_ID INT NOT NULL,
KEY_FIELD VARCHAR(45) NULL,
VALUE_FIELD VARCHAR(1000) NULL,
VALUE_FIELD VARCHAR(1500) NULL,
PRIMARY KEY (ID),
CONSTRAINT DM_DEVICE_INFO_DEVICE
FOREIGN KEY (DEVICE_ID)

@ -23,6 +23,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP (
STATUS VARCHAR(50) DEFAULT NULL,
DESCRIPTION TEXT DEFAULT NULL,
OWNER VARCHAR(255) DEFAULT NULL,
PARENT_PATH VARCHAR(255) DEFAULT NULL,
TENANT_ID INTEGER DEFAULT 0,
PRIMARY KEY (ID)
);
@ -424,7 +425,7 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_INFO (
DEVICE_ID INT NULL,
ENROLMENT_ID INT NOT NULL,
KEY_FIELD VARCHAR(45) NULL,
VALUE_FIELD VARCHAR(1000) NULL,
VALUE_FIELD VARCHAR(1500) NULL,
PRIMARY KEY (ID),
CONSTRAINT DM_DEVICE_INFO_DEVICE
FOREIGN KEY (DEVICE_ID)

@ -32,6 +32,7 @@ IF NOT EXISTS(SELECT *
STATUS VARCHAR(50) DEFAULT NULL,
DESCRIPTION VARCHAR(MAX) DEFAULT NULL,
OWNER VARCHAR(255) DEFAULT NULL,
PARENT_PATH VARCHAR(255) DEFAULT NULL,
TENANT_ID INTEGER DEFAULT 0,
PRIMARY KEY (ID)
);
@ -474,7 +475,7 @@ CREATE TABLE DM_DEVICE_INFO (
DEVICE_ID INTEGER NULL,
ENROLMENT_ID INTEGER NOT NULL,
KEY_FIELD VARCHAR(45) NULL,
VALUE_FIELD VARCHAR(1000) NULL,
VALUE_FIELD VARCHAR(1500) NULL,
PRIMARY KEY (ID),
INDEX DM_DEVICE_INFO_DEVICE_idx (DEVICE_ID ASC),
INDEX DM_DEVICE_INFO_DEVICE_ENROLLMENT_idx (ENROLMENT_ID ASC),

@ -27,6 +27,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP (
STATUS VARCHAR(50) DEFAULT NULL,
DESCRIPTION TEXT DEFAULT NULL,
OWNER VARCHAR(255) DEFAULT NULL,
PARENT_PATH VARCHAR(255) DEFAULT NULL,
TENANT_ID INTEGER DEFAULT 0,
PRIMARY KEY (ID)
)
@ -491,7 +492,7 @@ CREATE TABLE IF NOT EXISTS DM_DEVICE_INFO (
DEVICE_ID INT NULL,
ENROLMENT_ID INT NOT NULL,
KEY_FIELD VARCHAR(45) NULL,
VALUE_FIELD VARCHAR(1000) NULL,
VALUE_FIELD VARCHAR(1500) NULL,
PRIMARY KEY (ID),
INDEX DM_DEVICE_INFO_DEVICE_idx (DEVICE_ID ASC),
INDEX DM_DEVICE_INFO_DEVICE_ENROLLMENT_idx (ENROLMENT_ID ASC),

@ -50,6 +50,7 @@ CREATE TABLE DM_GROUP (
GROUP_NAME VARCHAR2(100) DEFAULT NULL,
STATUS VARCHAR2(50) DEFAULT NULL,
OWNER VARCHAR2(255) DEFAULT NULL,
PARENT_PATH VARCHAR2(255) DEFAULT NULL,
TENANT_ID NUMBER(10) DEFAULT 0,
CONSTRAINT PK_DM_GROUP PRIMARY KEY (ID)
)
@ -779,7 +780,7 @@ CREATE TABLE DM_DEVICE_INFO (
DEVICE_ID NUMBER(10) NOT NULL,
ENROLMENT_ID NUMBER(10) NOT NULL,
KEY_FIELD VARCHAR2(45) NULL,
VALUE_FIELD VARCHAR2(1000) NULL,
VALUE_FIELD VARCHAR2(1500) NULL,
PRIMARY KEY (ID),
CONSTRAINT DM_DEVICE_INFO_DEVICE
FOREIGN KEY (DEVICE_ID)

@ -21,6 +21,7 @@ CREATE TABLE IF NOT EXISTS DM_GROUP (
GROUP_NAME VARCHAR(100) DEFAULT NULL,
DESCRIPTION TEXT DEFAULT NULL,
OWNER VARCHAR(45) DEFAULT NULL,
PARENT_PATH VARCHAR(255) DEFAULT NULL,
TENANT_ID INTEGER DEFAULT 0,
PRIMARY KEY (ID)
)

Loading…
Cancel
Save