diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/DeviceTypes.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/DeviceTypes.java index 465b67cafd..7fa8a02062 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/DeviceTypes.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/DeviceTypes.java @@ -17,5 +17,5 @@ package org.wso2.carbon.device.application.mgt.common; public enum DeviceTypes { - ANDROID, IOS + ANDROID, IOS, WINDOWS } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/Application.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/Application.java index 778f7fc08d..0cd8a72783 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/Application.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/Application.java @@ -31,6 +31,10 @@ public class Application { required = true) private String name; + @ApiModelProperty(name = "installerName", + value = "Application Installer Name") + private String installerName; + @ApiModelProperty(name = "description", value = "Description of the application", required = true) @@ -173,4 +177,8 @@ public class Application { public boolean isAndroidEnterpriseApp() { return isAndroidEnterpriseApp; } public void setAndroidEnterpriseApp(boolean androidEnterpriseApp) { isAndroidEnterpriseApp = androidEnterpriseApp; } + + public String getInstallerName() { return installerName; } + + public void setInstallerName(String installerName) { this.installerName = installerName; } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/ApplicationRelease.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/ApplicationRelease.java index 36ed3e8648..25009bae1f 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/ApplicationRelease.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/response/ApplicationRelease.java @@ -87,6 +87,10 @@ public class ApplicationRelease { value = "Application Rating") private double rating; + @ApiModelProperty(name = "packageName", + value = "package name of the application") + private String packageName; + public String getReleaseType() { return releaseType; } @@ -162,4 +166,8 @@ public class ApplicationRelease { public List getScreenshots() { return screenshots; } public void setScreenshots(List screenshots) { this.screenshots = screenshots; } + + public String getPackageName() { return packageName; } + + public void setPackageName(String packageName) { this.packageName = packageName; } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java index ab031305f8..73f78478e7 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationManager.java @@ -281,6 +281,16 @@ public interface ApplicationManager { String getInstallableLifecycleState() throws ApplicationManagementException; + /** + * Check if there are subscription devices for operations + * + * @param operationId Id of the operation + * @param deviceId deviceId of the relevant device + * @return boolean value either true or false according to the situation + * @throws ApplicationManagementException + */ + boolean checkSubDeviceIdsForOperations(int operationId, int deviceId) throws ApplicationManagementException; + void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/EntAppReleaseWrapper.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/EntAppReleaseWrapper.java index ad79d9e7f3..c3c7ddb84d 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/EntAppReleaseWrapper.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/EntAppReleaseWrapper.java @@ -60,6 +60,14 @@ public class EntAppReleaseWrapper { @NotNull private String supportedOsVersions; + @ApiModelProperty(name = "version", + value = "Version number of the applications installer specifically for windows") + private String version; + + @ApiModelProperty(name = "packageName", + value = "PackageName of the application installer specifically for windows") + private String packageName; + public String getReleaseType() { return releaseType; } @@ -99,4 +107,20 @@ public class EntAppReleaseWrapper { public String getSupportedOsVersions() { return supportedOsVersions; } public void setSupportedOsVersions(String supportedOsVersions) { this.supportedOsVersions = supportedOsVersions; } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java index 1059fe4580..845c4626d5 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/GenericApplicationDAOImpl.java @@ -173,14 +173,14 @@ public class GenericApplicationDAOImpl extends AbstractDAOImpl implements Applic if (deviceTypeId != -1) { sql += "AND AP_APP.DEVICE_TYPE_ID = ? "; } - sql += "GROUP BY AP_APP.ID "; + sql += "GROUP BY AP_APP.ID ORDER BY AP_APP.ID "; if (StringUtils.isNotEmpty(filter.getSortBy())) { - sql += "ORDER BY ID " + filter.getSortBy() +" "; + sql += filter.getSortBy() +" "; } if (filter.getLimit() != -1) { sql += "LIMIT ? OFFSET ? "; } - sql += ") AS app_data ON app_data.ID = AP_APP.ID WHERE AP_APP.TENANT_ID = ?"; + sql += ") AS app_data ON app_data.ID = AP_APP.ID WHERE AP_APP.TENANT_ID = ? ORDER BY AP_APP.ID"; try { Connection conn = this.getDBConnection(); try (PreparedStatement stmt = conn.prepareStatement(sql)) { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java index b570c10fab..c57ef8a07d 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/OracleApplicationDAOImpl.java @@ -118,15 +118,14 @@ public class OracleApplicationDAOImpl extends GenericApplicationDAOImpl { if (deviceTypeId != -1) { sql += "AND AP_APP.DEVICE_TYPE_ID = ? "; } - sql += "GROUP BY AP_APP.ID "; + sql += "GROUP BY AP_APP.ID ORDER BY AP_APP.ID "; if (StringUtils.isNotEmpty(filter.getSortBy())) { - sql += "ORDER BY ID " + filter.getSortBy() + " "; + sql += filter.getSortBy() +" "; } if (filter.getLimit() != -1) { sql += "OFFSET ? ROWS FETCH NEXT ? ROWS ONLY "; } - sql += ") AS app_data ON app_data.ID = AP_APP.ID " + - "WHERE AP_APP.TENANT_ID = ?"; + sql += ") AS app_data ON app_data.ID = AP_APP.ID WHERE AP_APP.TENANT_ID = ? ORDER BY AP_APP.ID"; try { Connection conn = this.getDBConnection(); try (PreparedStatement stmt = conn.prepareStatement(sql)) { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java index 8825b20579..ee7c78f8ba 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/dao/impl/application/SQLServerApplicationDAOImpl.java @@ -117,15 +117,14 @@ public class SQLServerApplicationDAOImpl extends GenericApplicationDAOImpl { if (deviceTypeId != -1) { sql += "AND AP_APP.DEVICE_TYPE_ID = ? "; } - sql += "GROUP BY AP_APP.ID "; + sql += "GROUP BY AP_APP.ID ORDER BY AP_APP.ID "; if (StringUtils.isNotEmpty(filter.getSortBy())) { - sql += "ORDER BY ID " + filter.getSortBy() + " "; + sql += filter.getSortBy() +" "; } if (filter.getLimit() != -1) { sql += "ORDER BY ID OFFSET ? ROWS FETCH NEXT ? ROWS ONLY "; } - sql += ") AS app_data ON app_data.ID = AP_APP.ID " + - "WHERE AP_APP.TENANT_ID = ?"; + sql += ") AS app_data ON app_data.ID = AP_APP.ID WHERE AP_APP.TENANT_ID = ? ORDER BY AP_APP.ID"; try { Connection conn = this.getDBConnection(); try (PreparedStatement stmt = conn.prepareStatement(sql)) { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java index 1404c0c912..67b9143dc4 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationManagerImpl.java @@ -141,6 +141,7 @@ public class ApplicationManagerImpl implements ApplicationManager { } int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); ApplicationDTO applicationDTO = APIUtil.convertToAppDTO(applicationWrapper); + //uploading application artifacts ApplicationReleaseDTO applicationReleaseDTO = uploadEntAppReleaseArtifacts( applicationDTO.getApplicationReleaseDTOs().get(0), applicationArtifact, @@ -338,24 +339,36 @@ public class ApplicationManagerImpl implements ApplicationManager { byte[] content = IOUtils.toByteArray(applicationArtifact.getInstallerStream()); applicationReleaseDTO.setInstallerName(applicationArtifact.getInstallerName()); try (ByteArrayInputStream binary = new ByteArrayInputStream(content)) { - ApplicationInstaller applicationInstaller = applicationStorageManager - .getAppInstallerData(binary, deviceType); - String packagename = applicationInstaller.getPackageName(); + if (!DeviceTypes.WINDOWS.toString().equalsIgnoreCase(deviceType)) { + ApplicationInstaller applicationInstaller = applicationStorageManager + .getAppInstallerData(binary, deviceType); + applicationReleaseDTO.setVersion(applicationInstaller.getVersion()); + applicationReleaseDTO.setPackageName(applicationInstaller.getPackageName()); + } else { + String windowsInstallerName = applicationArtifact.getInstallerName(); + String extension = windowsInstallerName.substring(windowsInstallerName.lastIndexOf(".") + 1); + if (!extension.equalsIgnoreCase(Constants.MSI) && + !extension.equalsIgnoreCase(Constants.APPX)) { + String msg = "Application Type doesn't match with supporting application types of " + + deviceType + "platform which are APPX and MSI"; + log.error(msg); + throw new BadRequestException(msg); + } + } + String packageName = applicationReleaseDTO.getPackageName(); try { ConnectionManagerUtil.openDBConnection(); if (!isNewRelease && applicationReleaseDAO - .isActiveReleaseExisitForPackageName(packagename, tenantId, + .isActiveReleaseExisitForPackageName(packageName, tenantId, lifecycleStateManager.getEndState())) { - String msg = "Application release is already exist for the package name: " + packagename - + ". Either you can delete all application releases for package " + packagename + " or " + String msg = "Application release is already exist for the package name: " + packageName + + ". Either you can delete all application releases for package " + packageName + " or " + "you can add this app release as an new application release, under the existing " + "application."; log.error(msg); throw new ApplicationManagementException(msg); } - applicationReleaseDTO.setVersion(applicationInstaller.getVersion()); - applicationReleaseDTO.setPackageName(packagename); String md5OfApp = StorageManagementUtil.getMD5(new ByteArrayInputStream(content)); if (md5OfApp == null) { String msg = "Error occurred while md5sum value retrieving process: application UUID " @@ -1012,6 +1025,7 @@ public class ApplicationManagerImpl implements ApplicationManager { log.error(msg); throw new BadRequestException(msg); } + ApplicationReleaseDTO applicationReleaseDTO = uploadEntAppReleaseArtifacts( APIUtil.releaseWrapperToReleaseDTO(entAppReleaseWrapper), applicationArtifact, deviceType.getName(), tenantId, true); @@ -1909,13 +1923,13 @@ public class ApplicationManagerImpl implements ApplicationManager { int deviceTypeId; if (!deviceTypeName.equals(Constants.ALL)) { DeviceType deviceType = deviceManagementProviderService.getDeviceType(deviceTypeName); - deviceTypeId = deviceType.getId(); if (deviceType == null) { String msg = "Device type doesn't exist. Hence check the application name existence with valid " + "device type name."; log.error(msg); throw new BadRequestException(msg); } + deviceTypeId = deviceType.getId(); } else { //For web-clips device type = 'ALL' deviceTypeId = 0; @@ -2662,6 +2676,7 @@ public class ApplicationManagerImpl implements ApplicationManager { try { ConnectionManagerUtil.beginDBTransaction(); ApplicationDTO applicationDTO = this.applicationDAO.getAppWithRelatedRelease(releaseUuid, tenantId); + DeviceType deviceTypeObj = APIUtil.getDeviceTypeData(applicationDTO.getDeviceTypeId()); AtomicReference applicationReleaseDTO = new AtomicReference<>( applicationDTO.getApplicationReleaseDTOs().get(0)); validateAppReleaseUpdating(entAppReleaseWrapper, applicationDTO, applicationArtifact, @@ -2681,9 +2696,18 @@ public class ApplicationManagerImpl implements ApplicationManager { applicationReleaseDTO.get().setMetaData(entAppReleaseWrapper.getMetaData()); } + //If the application device type is WINDOWS, it is allowed to modify version number and package name. + if (DeviceTypes.WINDOWS.toString().equalsIgnoreCase(deviceTypeObj.getName())) { + if (!StringUtils.isEmpty(entAppReleaseWrapper.getVersion())) { + applicationReleaseDTO.get().setVersion(entAppReleaseWrapper.getVersion()); + } + if (!StringUtils.isEmpty(entAppReleaseWrapper.getPackageName())) { + applicationReleaseDTO.get().setPackageName(entAppReleaseWrapper.getPackageName()); + } + } + if (!StringUtils.isEmpty(applicationArtifact.getInstallerName()) && applicationArtifact.getInstallerStream() != null) { - DeviceType deviceTypeObj = APIUtil.getDeviceTypeData(applicationDTO.getDeviceTypeId()); applicationReleaseDTO .set(updateEntAppReleaseArtifact(deviceTypeObj.getName(), applicationReleaseDTO.get(), applicationArtifact)); @@ -3326,6 +3350,15 @@ public class ApplicationManagerImpl implements ApplicationManager { log.error(msg); throw new BadRequestException(msg); } + //Validating the version number and the packageName of the Windows new applications releases + if (DeviceTypes.WINDOWS.toString().equalsIgnoreCase(deviceType)) { + if (entAppReleaseWrapper.get().getVersion() == null || entAppReleaseWrapper.get().getPackageName() == null) { + String msg = "Application Version number or/and PackageName..both are required only when the app type is " + + deviceType + " platform type"; + log.error(msg); + throw new BadRequestException(msg); + } + } } else if (param instanceof WebAppReleaseWrapper) { WebAppReleaseWrapper webAppReleaseWrapper = (WebAppReleaseWrapper) param; UrlValidator urlValidator = new UrlValidator(); @@ -3414,18 +3447,31 @@ public class ApplicationManagerImpl implements ApplicationManager { } } + @Override + public boolean checkSubDeviceIdsForOperations(int operationId, int deviceId) throws ApplicationManagementException { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); + try { + ConnectionManagerUtil.openDBConnection(); + List deviceSubIds = subscriptionDAO.getDeviceSubIdsForOperation(operationId, deviceId, tenantId); + if (deviceSubIds.isEmpty()) { + return false; + } + } catch (ApplicationManagementDAOException e) { + String msg = "Error occurred while getting the device sub ids for the operations"; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + return true; + } + @Override public void updateSubsStatus (int deviceId, int operationId, String status) throws ApplicationManagementException { int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(); try { ConnectionManagerUtil.beginDBTransaction(); List deviceSubIds = subscriptionDAO.getDeviceSubIdsForOperation(operationId, deviceId, tenantId); - if (deviceSubIds.isEmpty()){ - ConnectionManagerUtil.rollbackDBTransaction(); - String msg = "Couldn't find device subscription for operation id " + operationId; - log.error(msg); - throw new ApplicationManagementException(msg); - } if (!subscriptionDAO.updateDeviceSubStatus(deviceId, deviceSubIds, status, tenantId)){ ConnectionManagerUtil.rollbackDBTransaction(); String msg = "Didn't update an any app subscription of device for operation Id: " + operationId; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java index 158b67ff10..248b0d272c 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/SubscriptionManagerImpl.java @@ -82,6 +82,7 @@ import org.wso2.carbon.device.mgt.core.service.DeviceManagementProviderService; import org.wso2.carbon.device.mgt.core.service.GroupManagementProviderService; import org.wso2.carbon.device.mgt.core.util.MDMAndroidOperationUtil; import org.wso2.carbon.device.mgt.core.util.MDMIOSOperationUtil; +import org.wso2.carbon.device.mgt.core.util.MDMWindowsOperationUtil; import org.wso2.carbon.identity.jwt.client.extension.dto.AccessTokenInfo; import org.wso2.carbon.user.api.UserStoreException; import org.wso2.carbon.device.mgt.common.PaginationResult; @@ -108,7 +109,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { private LifecycleStateManager lifecycleStateManager; public SubscriptionManagerImpl() { - lifecycleStateManager = DataHolder.getInstance().getLifecycleStateManager(); + this.lifecycleStateManager = DataHolder.getInstance().getLifecycleStateManager(); this.subscriptionDAO = ApplicationManagementDAOFactory.getSubscriptionDAO(); this.applicationDAO = ApplicationManagementDAOFactory.getApplicationDAO(); } @@ -549,7 +550,7 @@ public class SubscriptionManagerImpl implements SubscriptionManager { boolean isValidSubType = Arrays.stream(SubscriptionType.values()) .anyMatch(sub -> sub.name().equalsIgnoreCase(subType)); if (!isValidSubType) { - String msg = "Found invalid subscription type " + subType+ " to install application release" ; + String msg = "Found invalid subscription type " + subType+ " to subscribe application release" ; log.error(msg); throw new BadRequestException(msg); } @@ -579,8 +580,10 @@ public class SubscriptionManagerImpl implements SubscriptionManager { ApplicationDTO applicationDTO, String subType, List subscribers, String action) throws ApplicationManagementException { + //Get app subscribing info of each device SubscribingDeviceIdHolder subscribingDeviceIdHolder = getSubscribingDeviceIdHolder(devices, applicationDTO.getApplicationReleaseDTOs().get(0).getId()); + List activityList = new ArrayList<>(); List deviceIdentifiers = new ArrayList<>(); List ignoredDeviceIdentifiers = new ArrayList<>(); @@ -1020,6 +1023,18 @@ public class SubscriptionManagerImpl implements SubscriptionManager { log.error(msg); throw new ApplicationManagementException(msg); } + } else if (DeviceTypes.WINDOWS.toString().equalsIgnoreCase(deviceType)) { + app.setType(mobileAppType); + app.setIdentifier(application.getPackageName()); + app.setMetaData(application.getApplicationReleases().get(0).getMetaData()); + app.setName(application.getInstallerName()); + if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) { + return MDMWindowsOperationUtil.createInstallAppOperation(app); + } else { + String msg = "Invalid Action is found. Action: " + action; + log.error(msg); + throw new ApplicationManagementException(msg); + } } else { String msg = "Invalid device type is found. Device Type: " + deviceType; log.error(msg); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/APIUtil.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/APIUtil.java index 12d28191b7..a2375f1d55 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/APIUtil.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/APIUtil.java @@ -304,6 +304,10 @@ public class APIUtil { applicationReleaseDTO.setIsSharedWithAllTenants(entAppReleaseWrapper.getIsSharedWithAllTenants()); applicationReleaseDTO.setMetaData(entAppReleaseWrapper.getMetaData()); applicationReleaseDTO.setSupportedOsVersions(entAppReleaseWrapper.getSupportedOsVersions()); + //Setting version number value specifically for windows type and in an instance of android and ios it will be null + applicationReleaseDTO.setVersion(entAppReleaseWrapper.getVersion()); + //Setting package name value specifically for windows type and in an instance of android and ios it will be null + applicationReleaseDTO.setPackageName(entAppReleaseWrapper.getPackageName()); } else if (param instanceof WebAppReleaseWrapper){ WebAppReleaseWrapper webAppReleaseWrapper = (WebAppReleaseWrapper) param; applicationReleaseDTO.setDescription(webAppReleaseWrapper.getDescription()); @@ -358,6 +362,7 @@ public class APIUtil { application.setTags(applicationDTO.getTags()); application.setUnrestrictedRoles(applicationDTO.getUnrestrictedRoles()); application.setRating(applicationDTO.getAppRating()); + application.setInstallerName(applicationDTO.getApplicationReleaseDTOs().get(0).getInstallerName()); List applicationReleases = new ArrayList<>(); if (ApplicationType.PUBLIC.toString().equals(applicationDTO.getType()) && application.getCategories() .contains("GooglePlaySyncedApp")) { @@ -384,6 +389,7 @@ public class APIUtil { applicationRelease.setDescription(applicationReleaseDTO.getDescription()); applicationRelease.setVersion(applicationReleaseDTO.getVersion()); + applicationRelease.setPackageName(applicationReleaseDTO.getPackageName()); applicationRelease.setUuid(applicationReleaseDTO.getUuid()); applicationRelease.setReleaseType(applicationReleaseDTO.getReleaseType()); applicationRelease.setPrice(applicationReleaseDTO.getPrice()); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java index 11ca234e1a..c18224f417 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/util/Constants.java @@ -65,6 +65,10 @@ public class Constants { public static final String SUBSCRIBED = "SUBSCRIBED"; public static final String UNSUBSCRIBED = "UNSUBSCRIBED"; + //App type constants related to window device type + public static final String MSI = "MSI"; + public static final String APPX = "APPX"; + private static final Map AGENT_DATA = new HashMap<>(); static { AGENT_DATA.put("android", "android-agent.apk"); diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.api/src/main/java/org/wso2/carbon/device/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.api/src/main/java/org/wso2/carbon/device/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java index a7dc95940f..74f4b4ee75 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.api/src/main/java/org/wso2/carbon/device/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.api/src/main/java/org/wso2/carbon/device/application/mgt/publisher/api/services/impl/ApplicationManagementPublisherAPIImpl.java @@ -334,7 +334,7 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem log.error(msg, e); return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } catch (ApplicationManagementException e) { - String msg = "Error occurred while creating a costom application"; + String msg = "Error occurred while creating a custom application"; log.error(msg, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } catch (RequestValidatingException e) { @@ -373,6 +373,10 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem log.error("ApplicationDTO Creation Failed"); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); } + } catch (BadRequestException e) { + String msg = "Found incompatible payload with enterprise app release creating request."; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } catch (ApplicationManagementException e) { String msg = "Error occurred while creating the application"; log.error(msg, e); @@ -716,7 +720,7 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem .changeLifecycleState(applicationUuid, lifecycleChanger); return Response.status(Response.Status.CREATED).entity(applicationRelease).build(); } catch (BadRequestException e) { - String msg = "Request payload contains invalid data, hence veryfy the request payload."; + String msg = "Request payload contains invalid data, hence verify the request payload."; log.error(msg, e); return Response.status(Response.Status.BAD_REQUEST).build(); } catch (ForbiddenException e) { @@ -1005,10 +1009,10 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem } if (attachmentList != null && !attachmentList.isEmpty()) { - Map scrrenshotData = new TreeMap<>(); + Map screenshotData = new TreeMap<>(); for (Attachment sc : attachmentList) { dataHandler = sc.getDataHandler(); - String screenshotrFileName = dataHandler.getName(); + String screenshotFileName = dataHandler.getName(); InputStream screenshotStream = dataHandler.getInputStream(); if (screenshotStream == null) { String msg = @@ -1017,16 +1021,16 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem log.error(msg); throw new BadRequestException(msg); } - if (screenshotrFileName == null) { + if (screenshotFileName == null) { String msg = "Screenshot file name retrieving is failed for one screenshot. Hence can't proceed. " + "Please verify the screenshots."; log.error(msg); throw new BadRequestException(msg); } - scrrenshotData.put(screenshotrFileName, screenshotStream); + screenshotData.put(screenshotFileName, screenshotStream); } - applicationArtifact.setScreenshots(scrrenshotData); + applicationArtifact.setScreenshots(screenshotData); } return applicationArtifact; } catch (IOException e) { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/public/conf/config.json b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/public/conf/config.json index de1726ce39..9264aac6d7 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/public/conf/config.json +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/public/conf/config.json @@ -85,6 +85,12 @@ } }, "deviceTypes": { - "mobileTypes": ["android", "ios"] + "mobileTypes": ["android", "ios", "windows"] + }, + "windowsDeviceType": { + "appType": ["msi", "appx"] + }, + "windowsAppxMsiKeyValueForMetaData": { + "metaKeyArray": ["Package_Url", "Dependency_Package_Url", "Certificate_Hash", "Encoded_Cert_Content", "Package_Family_Name", "Product_Id", "Content_Uri", "File_Hash"] } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppDetailsForm/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppDetailsForm/index.js index 4219e5fcd6..c1bfc1f5c0 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppDetailsForm/index.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppDetailsForm/index.js @@ -46,6 +46,7 @@ class NewAppDetailsForm extends React.Component { categories: [], tags: [], deviceTypes: [], + selectedValue: null, fetching: false, roleSearchValue: [], unrestrictedRoles: [], @@ -312,12 +313,33 @@ class NewAppDetailsForm extends React.Component { }); }; + // Event handler for selecting the device type + handleSelect = event => { + this.setState({ + selectedValue: event, + }); + if (this.props.selectedValueHandler) { + this.props.selectedValueHandler(event); + } + }; + + // Event handler for selecting the windows app type + handleSelectForAppType = event => { + if (this.props.selectedAppTypeHandler) { + this.props.selectedAppTypeHandler(event); + } + }; + render() { + const config = this.props.context; + // Windows installation app types + const appTypes = config.windowsDeviceType.appType; const { formConfig } = this.props; const { categories, tags, deviceTypes, + selectedValue, fetching, unrestrictedRoles, } = this.state; @@ -358,6 +380,7 @@ class NewAppDetailsForm extends React.Component { )} + {/* App Type only shown for windows device types for enterprise apps */} + {selectedValue === 'windows' && + this.props.formConfig.installationType === 'ENTERPRISE' && ( + + {getFieldDecorator('appType', { + rules: [ + { + required: true, + message: 'Please select app type', + }, + ], + })( + , + )} + + )} + {/* description*/} {getFieldDecorator('description', { diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppUploadForm/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppUploadForm/index.js index 32368cb26a..eb918dc423 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppUploadForm/index.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/scenes/Home/scenes/AddNewApp/components/AddNewAppForm/components/NewAppUploadForm/index.js @@ -33,6 +33,7 @@ import { } from 'antd'; import '@babel/polyfill'; import Authorized from '../../../../../../../../components/Authorized/Authorized'; +import { withConfigContext } from '../../../../../../../../components/ConfigContext'; const { Text } = Typography; @@ -59,6 +60,12 @@ function getBase64(file) { }); } +// function for access the full name of the binary file using the installation path +function extractBinaryFileName(installationPath) { + let UploadedBinaryName = installationPath.split('/'); + return UploadedBinaryName[UploadedBinaryName.length - 1]; +} + class NewAppUploadForm extends React.Component { constructor(props) { super(props); @@ -77,6 +84,7 @@ class NewAppUploadForm extends React.Component { osVersionsHelperText: '', osVersionsValidateStatus: 'validating', metaData: [], + appType: null, }; this.lowerOsVersion = null; this.upperOsVersion = null; @@ -93,6 +101,8 @@ class NewAppUploadForm extends React.Component { e.preventDefault(); const { formConfig } = this.props; const { specificElements } = formConfig; + let windowsAppTypeMetaArray = []; + let metaValue = []; this.props.form.validateFields((err, values) => { if (!err) { @@ -107,6 +117,19 @@ class NewAppUploadForm extends React.Component { releaseType, } = values; + /** + * To save the metaData value that receive from + * metaData UI In an windows app type creation + */ + if ( + ((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows') || + this.props.deviceType === 'windows') && + this.state.metaData.length !== 0 + ) { + metaValue = [...this.state.metaData]; + } + // add release data const release = { description: releaseDescription, @@ -116,6 +139,90 @@ class NewAppUploadForm extends React.Component { releaseType: releaseType, }; + const data = new FormData(); + const config = this.props.context; + // Accessing the Meta Key value for windows device type from the config.json file + const metaKeyValues = + config.windowsAppxMsiKeyValueForMetaData.metaKeyArray; + + /* + Setting up the app type specific values to the + metaData state field and Setting up the version + and the packageName for windows type + */ + if ( + (this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows') || + this.props.deviceType === 'windows' + ) { + // Setting up the version and packageName + release.version = values.version; + release.packageName = values.packageName; + // setting the metaData value for appx type + if ( + this.props.selectedAppType === 'appx' || + this.state.appType === 'appx' + ) { + windowsAppTypeMetaArray = [ + { + key: metaKeyValues[0], + value: values.packageUrl, + }, + { + key: metaKeyValues[1], + value: values.dependencyPackageUrl, + }, + { + key: metaKeyValues[2], + value: values.certificateHash, + }, + { + key: metaKeyValues[3], + value: values.encodedCertContent, + }, + { + key: metaKeyValues[4], + value: values.packageName, + }, + ]; + windowsAppTypeMetaArray = [ + ...windowsAppTypeMetaArray, + ...metaValue, + ]; + } else if ( + this.props.selectedAppType === 'msi' || + this.state.appType === 'msi' + ) { + windowsAppTypeMetaArray = [ + { + key: metaKeyValues[5], + value: values.productId, + }, + { + key: metaKeyValues[6], + value: values.contentUri, + }, + { + key: metaKeyValues[7], + value: values.fileHash, + }, + ]; + windowsAppTypeMetaArray = [ + ...windowsAppTypeMetaArray, + ...metaValue, + ]; + } + this.setState( + { + metaData: windowsAppTypeMetaArray, + }, + () => { + release.metaData = JSON.stringify(this.state.metaData); + this.props.onSuccessReleaseData({ data, release }); + }, + ); + } + if (specificElements.hasOwnProperty('version')) { release.version = values.version; } @@ -126,7 +233,6 @@ class NewAppUploadForm extends React.Component { release.packageName = values.packageName; } - const data = new FormData(); let isFormValid = true; // flag to check if this form is valid if ( @@ -187,7 +293,16 @@ class NewAppUploadForm extends React.Component { if (specificElements.hasOwnProperty('binaryFile')) { data.append('binaryFile', binaryFile[0].originFileObj); } - this.props.onSuccessReleaseData({ data, release }); + // Condition to check is it not an Enterprise windows app creation or release + if ( + !( + this.props.selectedValue === 'windows' && + this.props.formConfig.installationType === 'ENTERPRISE' + ) && + this.props.deviceType !== 'windows' + ) { + this.props.onSuccessReleaseData({ data, release }); + } } } }); @@ -203,13 +318,44 @@ class NewAppUploadForm extends React.Component { icons: fileList, }); }; + handleBinaryFileChange = ({ fileList }) => { + let validity = true; + // To set the app type of windows by using the binary file in an new app release + if (this.props.formConfig.isNewRelease && fileList.length !== 0) { + let firstUploadedBinaryFileName = extractBinaryFileName( + this.props.uploadedInstalltionAppType, + ); + let FirstFileExtension = firstUploadedBinaryFileName.substr( + firstUploadedBinaryFileName.lastIndexOf('.') + 1, + ); + let LastFileExtension = fileList[0].name.substr( + fileList[0].name.lastIndexOf('.') + 1, + ); + if (FirstFileExtension !== LastFileExtension) { + validity = false; + } else if (LastFileExtension === 'msi' || LastFileExtension === 'appx') { + this.setState({ + appType: LastFileExtension, + }); + } + } + if (fileList.length === 1) { this.setState({ binaryFileHelperText: '', }); } - this.setState({ binaryFiles: fileList }); + + if (validity) { + this.setState({ + binaryFiles: fileList, + }); + } else { + this.setState({ + binaryFileHelperText: 'Upload Correct Binary File extension', + }); + } }; handleScreenshotChange = ({ fileList }) => { @@ -266,6 +412,7 @@ class NewAppUploadForm extends React.Component { render() { const { formConfig, supportedOsVersions } = this.props; const { getFieldDecorator } = this.props.form; + const config = this.props.context; const { icons, screenshots, @@ -399,7 +546,12 @@ class NewAppUploadForm extends React.Component { - {formConfig.specificElements.hasOwnProperty('packageName') && ( + + {/* Package Name field for windows device type and other specific scene using it */} + {(formConfig.specificElements.hasOwnProperty('packageName') || + (this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows') || + this.props.deviceType === 'windows') && ( {getFieldDecorator('packageName', { rules: [ @@ -425,7 +577,11 @@ class NewAppUploadForm extends React.Component { )} - {formConfig.specificElements.hasOwnProperty('version') && ( + {/* Version field for windows device type and other specific scene using it */} + {(formConfig.specificElements.hasOwnProperty('version') || + (this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows') || + this.props.deviceType === 'windows') && ( {getFieldDecorator('version', { rules: [ @@ -438,6 +594,127 @@ class NewAppUploadForm extends React.Component { )} + {/* Windows Appx App Type Fields */} + {/* For Windows appx app type only -> Package Url */} + {((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows' && + this.props.selectedAppType === 'appx') || + this.state.appType === 'appx') && ( + + {getFieldDecorator('packageUrl', { + rules: [ + { + required: true, + message: 'Please input the package url', + }, + ], + })()} + + )} + + {/* For Windows appx app type only -> Dependency Package Url */} + {((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows' && + this.props.selectedAppType === 'appx') || + this.state.appType === 'appx') && ( + + {getFieldDecorator('dependencyPackageUrl', {})( + , + )} + + )} + + {/* For Windows appx app type only -> Certificate Hash */} + {((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows' && + this.props.selectedAppType === 'appx') || + this.state.appType === 'appx') && ( + + {getFieldDecorator('certificateHash', { + rules: [ + { + required: true, + message: 'Please input the certificate hash', + }, + ], + })()} + + )} + + {/* For Windows appx app type only -> Encoded Certificate Content */} + {((this.props.formConfig.installationType === 'ENTERPRISE' && + this.props.selectedValue === 'windows' && + this.props.selectedAppType === 'appx') || + this.state.appType === 'appx') && ( + + {getFieldDecorator('encodedCertContent', { + rules: [ + { + required: true, + message: 'Give the encoded cert content', + }, + ], + })( +