Merge pull request 'Add DAOs for asset management' (#162) from osh.silva/device-mgt-core:asset-dao-10179 into vpp-v2

Reviewed-on: community/device-mgt-core#162
vpp
Inosh Perara 1 year ago
commit 949fbef41b

@ -0,0 +1,77 @@
/*
* Copyright (c) 2018 - 2023, Entgra (Pvt) Ltd. (http://www.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 io.entgra.device.mgt.core.application.mgt.common;
public class DepConfig {
private String agentPackageName;
private String consumerKey;
private String consumerSecret;
private String accessToken;
private String accessSecret;
private String accessTokenExpiry;
public String getAgentPackageName() {
return agentPackageName;
}
public void setAgentPackageName(String agentPackageName) {
this.agentPackageName = agentPackageName;
}
public String getConsumerKey() {
return consumerKey;
}
public void setConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
}
public String getConsumerSecret() {
return consumerSecret;
}
public void setConsumerSecret(String consumerSecret) {
this.consumerSecret = consumerSecret;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public String getAccessSecret() {
return accessSecret;
}
public void setAccessSecret(String accessSecret) {
this.accessSecret = accessSecret;
}
public String getAccessTokenExpiry() {
return accessTokenExpiry;
}
public void setAccessTokenExpiry(String accessTokenExpiry) {
this.accessTokenExpiry = accessTokenExpiry;
}
}

@ -38,7 +38,7 @@ public interface VPPApplicationManager {
void syncAssets(int nextPageIndex) throws ApplicationManagementException;
VppAssetDTO getAssetByAppId(String appId) throws ApplicationManagementException;
VppAssetDTO getAssetByAppId(int appId) throws ApplicationManagementException;
ProxyResponse callVPPBackend(String url, String payload, String accessToken, String method) throws IOException;

@ -18,6 +18,7 @@
package io.entgra.device.mgt.core.application.mgt.core.dao;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppAssetDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppUserDTO;
import io.entgra.device.mgt.core.application.mgt.core.exception.ApplicationManagementDAOException;
@ -29,4 +30,10 @@ public interface VppApplicationDAO {
VppUserDTO updateVppUser(VppUserDTO userDTO, int tenantId) throws ApplicationManagementDAOException;
VppUserDTO getUserByDMUsername(String emmUsername, int tenantId) throws ApplicationManagementDAOException;
VppAssetDTO getAssetByAppId(int appId, int tenantId) throws ApplicationManagementDAOException;
int addAsset(VppAssetDTO vppAssetDTO, int tenantId) throws ApplicationManagementDAOException;
VppAssetDTO updateAsset(VppAssetDTO vppAssetDTO, int tenantId) throws ApplicationManagementDAOException;
}

@ -18,6 +18,7 @@
package io.entgra.device.mgt.core.application.mgt.core.dao.impl.vpp;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppAssetDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppUserDTO;
import io.entgra.device.mgt.core.application.mgt.common.exception.DBConnectionException;
import io.entgra.device.mgt.core.application.mgt.core.dao.VppApplicationDAO;
@ -29,6 +30,7 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.sql.*;
import java.util.List;
public class GenericVppApplicationDAOImpl extends AbstractDAOImpl implements VppApplicationDAO {
private static final Log log = LogFactory.getLog(GenericVppApplicationDAOImpl.class);
@ -83,6 +85,7 @@ public class GenericVppApplicationDAOImpl extends AbstractDAOImpl implements Vp
}
}
@Override
public VppUserDTO updateVppUser(VppUserDTO userDTO, int tenantId)
throws ApplicationManagementDAOException {
@ -130,6 +133,7 @@ public class GenericVppApplicationDAOImpl extends AbstractDAOImpl implements Vp
}
}
@Override
public VppUserDTO getUserByDMUsername(String emmUsername, int tenantId)
throws ApplicationManagementDAOException {
String sql = "SELECT "
@ -168,4 +172,156 @@ public class GenericVppApplicationDAOImpl extends AbstractDAOImpl implements Vp
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public VppAssetDTO getAssetByAppId(int appId, int tenantId)
throws ApplicationManagementDAOException {
String sql = "SELECT "
+ "ID, "
+ "APP_ID, "
+ "TENANT_ID, "
+ "CREATED_TIME, "
+ "LAST_UPDATED_TIME, "
+ "ADAM_ID, "
+ "ASSIGNED_COUNT, "
+ "DEVICE_ASSIGNABLE, "
+ "PRICING_PARAMS, "
+ "PRODUCT_TYPE, "
+ "RETIRED_COUNT, "
+ "REVOCABLE "
// + "SUPPORTED_PLATFORMS "
+ "FROM AP_ASSETS "
+ "WHERE APP_ID = ? AND TENANT_ID = ?";
try {
Connection conn = this.getDBConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, appId);
stmt.setInt(2, tenantId);
try (ResultSet rs = stmt.executeQuery()) {
return DAOUtil.loadAsset(rs);
}
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining database connection when retrieving asset data of app id "+ appId;
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when processing SQL to retrieve asset by app id.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (UnexpectedServerErrorException e) {
String msg = "Found more than one app for app id: " + appId;
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public int addAsset(VppAssetDTO vppAssetDTO, int tenantId)
throws ApplicationManagementDAOException {
int assetId = -1;
String sql = "INSERT INTO "
+ "AP_ASSETS("
+ "APP_ID, "
+ "TENANT_ID, "
+ "CREATED_TIME,"
+ "LAST_UPDATED_TIME,"
+ "ADAM_ID,"
+ "ASSIGNED_COUNT,"
+ "DEVICE_ASSIGNABLE,"
+ "PRICING_PARAMS,"
+ "PRODUCT_TYPE,"
+ "RETIRED_COUNT,"
+ "REVOCABLE) "
// + "SUPPORTED_PLATFORMS) "
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
try {
Connection conn = this.getDBConnection();
try (PreparedStatement stmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) {
long currentTime = System.currentTimeMillis();
stmt.setInt(1, vppAssetDTO.getAppId());
stmt.setInt(2, tenantId);
stmt.setLong(3, currentTime);
stmt.setLong(4, currentTime);
stmt.setString(5, vppAssetDTO.getAdamId());
stmt.setString(6, vppAssetDTO.getAssignedCount());
stmt.setString(7, vppAssetDTO.getDeviceAssignable());
stmt.setString(8, vppAssetDTO.getPricingParam());
stmt.setString(9, vppAssetDTO.getProductType());
stmt.setString(10, vppAssetDTO.getRetiredCount());
stmt.setString(11, vppAssetDTO.getRevocable());
// List<String> platformList = vppAssetDTO.getSupportedPlatforms();
// String platformString = String.join(",", platformList);
// stmt.setString(12, platformString);
stmt.executeUpdate();
try (ResultSet rs = stmt.getGeneratedKeys()) {
if (rs.next()) {
assetId = rs.getInt(1);
}
}
return assetId;
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining database connection when adding the asset.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when processing SQL to add the asset.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
@Override
public VppAssetDTO updateAsset(VppAssetDTO vppAssetDTO, int tenantId)
throws ApplicationManagementDAOException {
String sql = "UPDATE "
+ "AP_ASSETS "
+ "SET "
+ "APP_ID = ?,"
+ "LAST_UPDATED_TIME = ?, "
+ "ADAM_ID = ?, "
+ "ASSIGNED_COUNT = ?, "
+ "DEVICE_ASSIGNABLE = ?, "
+ "PRICING_PARAMS = ?, "
+ "PRODUCT_TYPE = ?, "
+ "RETIRED_COUNT = ?, "
+ "REVOCABLE = ? "
// + "SUPPORTED_PLATFORMS = ? "
+ "WHERE ID = ? AND TENANT_ID = ?";
try {
Connection conn = this.getDBConnection();
long updatedTime = System.currentTimeMillis();
try (PreparedStatement stmt = conn.prepareStatement(sql)) {
stmt.setInt(1, vppAssetDTO.getAppId());
stmt.setLong(2, updatedTime);
stmt.setString(3, vppAssetDTO.getAdamId());
stmt.setString(4, vppAssetDTO.getAssignedCount());
stmt.setString(5, vppAssetDTO.getDeviceAssignable());
stmt.setString(6, vppAssetDTO.getPricingParam());
stmt.setString(7, vppAssetDTO.getProductType());
stmt.setString(8, vppAssetDTO.getRetiredCount());
stmt.setString(9, vppAssetDTO.getRevocable());
// List<String> platformList = vppAssetDTO.getSupportedPlatforms();
// String platformString = String.join(",", platformList);
// stmt.setString(10, platformString);
stmt.setInt(10, vppAssetDTO.getId());
stmt.setLong(11, tenantId);
stmt.executeUpdate();
if (stmt.executeUpdate() == 1) {
return vppAssetDTO;
}
return null;
}
} catch (DBConnectionException e) {
String msg = "Error occurred while obtaining database connection when updating the vpp user";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
} catch (SQLException e) {
String msg = "Error occurred when processing SQL to updating the vpp user.";
log.error(msg, e);
throw new ApplicationManagementDAOException(msg, e);
}
}
}

@ -170,11 +170,12 @@ public class SubscriptionManagerImpl implements SubscriptionManager {
applicationSubscriptionInfo) throws ApplicationManagementException {
try {
// Only for iOS devices
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
if (DeviceTypes.IOS.toString().equalsIgnoreCase(APIUtil.getDeviceTypeData(applicationDTO
.getDeviceTypeId()).getName())) {
// TODO: replace getAssetByAppId with the correct one in DAO
// Check if the app trying to subscribe is a VPP asset.
VppAssetDTO storedAsset = vppApplicationDAO.getAssetByAppId(applicationDTO.getId());
VppAssetDTO storedAsset = vppApplicationDAO.getAssetByAppId(applicationDTO.getId(), tenantId);
if (storedAsset != null) { // This is a VPP asset
List<VppUserDTO> users = new ArrayList<>();
List<Device> devices = applicationSubscriptionInfo.getDevices();// get

@ -21,12 +21,14 @@ package io.entgra.device.mgt.core.application.mgt.core.impl;
import com.google.gson.Gson;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import io.entgra.device.mgt.core.application.mgt.common.DepConfig;
import io.entgra.device.mgt.core.application.mgt.common.dto.ItuneAppDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.ProxyResponse;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppAssetDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppAssociationDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppItuneAssetDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppItuneUserDTO;
import io.entgra.device.mgt.core.application.mgt.common.response.Application;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppUserDTO;
import io.entgra.device.mgt.core.application.mgt.common.exception.ApplicationManagementException;
import io.entgra.device.mgt.core.application.mgt.common.exception.DBConnectionException;
@ -48,6 +50,11 @@ import io.entgra.device.mgt.core.application.mgt.core.util.ApplicationManagement
import io.entgra.device.mgt.core.application.mgt.core.util.ConnectionManagerUtil;
import io.entgra.device.mgt.core.application.mgt.core.util.Constants;
import io.entgra.device.mgt.core.application.mgt.core.util.VppHttpUtil;
import io.entgra.device.mgt.core.device.mgt.common.exceptions.MetadataManagementException;
import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.Metadata;
import io.entgra.device.mgt.core.device.mgt.common.metadata.mgt.MetadataManagementService;
import io.entgra.device.mgt.core.device.mgt.core.DeviceManagementConstants;
import io.entgra.device.mgt.core.device.mgt.core.internal.DeviceManagementDataHolder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.http.HttpStatus;
@ -250,6 +257,7 @@ public class VppApplicationManagerImpl implements VPPApplicationManager {
@Override
public void syncAssets(int nextPageIndex) throws ApplicationManagementException {
ProxyResponse proxyResponse = null;
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
try {
String url = ASSETS;
if (nextPageIndex > 0) { // Not the first page
@ -272,17 +280,66 @@ public class VppApplicationManagerImpl implements VPPApplicationManager {
for (VppAssetDTO vppAssetDTO : vppItuneAssetResponse.getAssets()) {
ItuneAppDTO ituneAppDTO = lookupAsset(vppAssetDTO.getAdamId());
ApplicationManagementUtil.persistApp(ituneAppDTO);
List<Application> applications = ApplicationManagementUtil.getAppDetails(vppAssetDTO.getAdamId());
for (Application application :applications) {
VppAssetDTO vppAssetDTOs = getAssetByAppId(application.getId());
if (vppAssetDTOs == null) {
vppAssetDTOs = new VppAssetDTO();
vppAssetDTOs.setAppId(application.getId());
try {
ConnectionManagerUtil.beginDBTransaction();
if (vppApplicationDAO.addAsset(vppAssetDTOs, tenantId) != -1) {
ConnectionManagerUtil.commitDBTransaction();
}
ConnectionManagerUtil.rollbackDBTransaction();
} catch (ApplicationManagementDAOException e) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Error occurred while adding the Asset.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (TransactionManagementException e) {
String msg = "Error occurred while executing database transaction for adding Asset.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while retrieving the database connection for adding Asset.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
} else {
vppAssetDTOs.setAppId(application.getId());
try {
ConnectionManagerUtil.beginDBTransaction();
if (vppApplicationDAO.updateAsset(vppAssetDTOs, tenantId) == null) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Unable to update the asset: " +vppAssetDTOs.getAdamId();
log.error(msg);
throw new ApplicationManagementException(msg);
}
ConnectionManagerUtil.commitDBTransaction();
} catch (ApplicationManagementDAOException e) {
ConnectionManagerUtil.rollbackDBTransaction();
String msg = "Error occurred while updating the Asset.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (TransactionManagementException e) {
String msg = "Error occurred while executing database transaction for Asset update.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (DBConnectionException e) {
String msg = "Error occurred while retrieving the database connection for Asset update.";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
}
}
// TODO: Store/update vppItuneAssetResponse.getAssets() in the DB
// TODO: END of DAO access
}
if (vppItuneAssetResponse.getCurrentPageIndex() == (vppItuneAssetResponse
.getTotalPages() - 1)) {
return;
@ -295,8 +352,6 @@ public class VppApplicationManagerImpl implements VPPApplicationManager {
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
}
}
private ItuneAppDTO lookupAsset(String packageName) throws ApplicationManagementException {
@ -359,10 +414,22 @@ public class VppApplicationManagerImpl implements VPPApplicationManager {
}
public VppAssetDTO getAssetByAppId(String appId) throws ApplicationManagementException {
// App ID is the app id of the App management database
VppAssetDTO assetDTO = null; // TODO: load from the DB
return assetDTO;
public VppAssetDTO getAssetByAppId(int appId) throws ApplicationManagementException {
int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true);
try {
ConnectionManagerUtil.openDBConnection();
return vppApplicationDAO.getAssetByAppId(appId, tenantId);
} catch (DBConnectionException e) {
String msg = "DB Connection error occurs while getting asset related to app with app id " + appId + ".";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} catch (ApplicationManagementDAOException e) {
String msg = "Error occurred while getting asset data related to app with app id " + appId + ".";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
} finally {
ConnectionManagerUtil.closeDBConnection();
}
}
@Override
@ -373,6 +440,28 @@ public class VppApplicationManagerImpl implements VPPApplicationManager {
return VppHttpUtil.execute(url, payload, accessToken, method);
}
public String getVppToken() throws ApplicationManagementException {
String token = "";
MetadataManagementService meta = DeviceManagementDataHolder
.getInstance().getMetadataManagementService();
Metadata metadata = null;
try {
metadata = meta.retrieveMetadata("DEP_META_KEY");
if (metadata != null) {
Gson g = new Gson();
DepConfig depConfigs = g.fromJson(metadata.getMetaValue(), DepConfig.class);
token = depConfigs.getAccessToken();
return token;
}
}catch (MetadataManagementException e) {
String msg = "Error when retrieving metadata of vpp feature";
log.error(msg, e);
throw new ApplicationManagementException(msg, e);
}
return token;
}
public boolean addAssociation(VppAssetDTO asset, List<VppUserDTO> vppUsers) throws
ApplicationManagementException {
@ -418,5 +507,4 @@ public class VppApplicationManagerImpl implements VPPApplicationManager {
return false;
}
}

@ -527,4 +527,11 @@ public class ApplicationManagementUtil {
}
}
}
public static List<Application> getAppDetails(String adamId) throws ApplicationManagementException {
ApplicationManager applicationManager = APIUtil.getApplicationManager();
List<String> packageNamesOfApps = new ArrayList<>();
packageNamesOfApps.add(adamId);
return applicationManager.getApplications(packageNamesOfApps);
}
}

@ -19,20 +19,14 @@ package io.entgra.device.mgt.core.application.mgt.core.util;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import io.entgra.device.mgt.core.application.mgt.common.dto.IdentityServerDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.*;
import io.entgra.device.mgt.core.application.mgt.core.exception.UnexpectedServerErrorException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import io.entgra.device.mgt.core.application.mgt.common.ExecutionStatus;
import io.entgra.device.mgt.core.application.mgt.common.SubscriptionType;
import io.entgra.device.mgt.core.application.mgt.common.dto.ApplicationDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.ApplicationReleaseDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.DeviceSubscriptionDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.ReviewDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.ScheduledSubscriptionDTO;
import io.entgra.device.mgt.core.application.mgt.common.dto.VppUserDTO;
import io.entgra.device.mgt.core.device.mgt.common.DeviceIdentifier;
import java.sql.PreparedStatement;
@ -382,6 +376,52 @@ public class DAOUtil {
return vppUserDTOS;
}
public static VppAssetDTO loadAsset(ResultSet rs) throws SQLException, UnexpectedServerErrorException {
List<VppAssetDTO> vppAssetDTOS = loadAssets(rs);
if (vppAssetDTOS.isEmpty()) {
return null;
}
if (vppAssetDTOS.size() > 1) {
String msg = "Internal server error. Found more than one asset for given app id.";
log.error(msg);
throw new UnexpectedServerErrorException(msg);
}
return vppAssetDTOS.get(0);
}
public static List<VppAssetDTO> loadAssets (ResultSet rs) throws SQLException {
List<VppAssetDTO> vppAssetDTOS = new ArrayList<>();
while (rs.next()) {
VppAssetDTO vppAssetDTO = new VppAssetDTO();
vppAssetDTO.setId(rs.getInt("ID"));
vppAssetDTO.setAppId(rs.getInt("APP_ID"));
vppAssetDTO.setTenantId(rs.getInt("TENANT_ID"));
if (rs.getLong("CREATED_TIME") != 0) {
vppAssetDTO.setCreatedTime(new Date(rs.getLong(("CREATED_TIME")) * 1000).toString());
}
if (rs.getLong("LAST_UPDATED_TIME") != 0) {
vppAssetDTO.setLastUpdatedTime(new Date(rs.getLong(("LAST_UPDATED_TIME")) * 1000).toString());
}
vppAssetDTO.setAdamId(rs.getString("ADAM_ID"));
vppAssetDTO.setAssignedCount(rs.getString("ASSIGNED_COUNT"));
vppAssetDTO.setDeviceAssignable(rs.getString("DEVICE_ASSIGNABLE"));
vppAssetDTO.setPricingParam(rs.getString("PRICING_PARAMS"));
vppAssetDTO.setProductType(rs.getString("PRODUCT_TYPE"));
vppAssetDTO.setRetiredCount(rs.getString("RETIRED_COUNT"));
vppAssetDTO.setRevocable(rs.getString("REVOCABLE"));
// String jsonString = rs.getString("SUPPORTED_PLATFORMS");
// ObjectMapper objectMapper = new ObjectMapper();
// try {
// List<String> platformList = objectMapper.readValue(jsonString, new TypeReference<List<String>>() {});
// vppAssetDTO.setSupportedPlatforms(platformList);
// } catch (IOException e) {
// e.printStackTrace();
// }
vppAssetDTOS.add(vppAssetDTO);
}
return vppAssetDTOS;
}
/**
* Cleans up the statement and resultset after executing the query
*

@ -29,6 +29,7 @@ public final class DeviceManagementConstants {
public static final String GEOFENCE_CACHE = "GEOFENCE_CACHE";
public static final String BILLING_CACHE = "BILLING_CACHE";
public static final String META_KEY = "PER_DEVICE_COST";
public static final String DEP_META_KEY = "DEP_CONFIG";
public static final String ACTIVE_STATUS = "ACTIVE";
public static final String ENROLLMENT_NOTIFICATION_API_ENDPOINT = "/api/device-mgt/enrollment-notification";
public static final String URL_SEPERATOR = "/";

Loading…
Cancel
Save