From cfd0f709ef7add52e1eec8a2fbce8102e63b0616 Mon Sep 17 00:00:00 2001 From: lasanthaDLPDS Date: Sat, 14 Sep 2019 20:42:32 +0530 Subject: [PATCH 1/2] Add new application type Add new application type for IoT use cases. Further, it is named as CUSTOM --- .../mgt/common/ApplicationType.java | 2 +- .../common/services/ApplicationManager.java | 12 +- .../services/ApplicationStorageManager.java | 10 +- .../wrapper/CustomAppReleaseWrapper.java | 125 +++++++ .../mgt/common/wrapper/CustomAppWrapper.java | 123 +++++++ .../mgt/core/impl/ApplicationManagerImpl.java | 338 ++++++++++++++---- .../impl/ApplicationStorageManagerImpl.java | 12 +- .../core/impl/SubscriptionManagerImpl.java | 58 ++- .../application/mgt/core/util/APIUtil.java | 26 ++ .../ApplicationManagementPublisherAPI.java | 149 +++++++- ...ApplicationManagementPublisherAPIImpl.java | 108 +++++- .../src/components/apps/list-apps/Filters.js | 1 + .../apps/release/edit-release/EditRelease.js | 14 + .../react-app/src/index.js | 6 + .../src/pages/dashboard/Dashboard.js | 2 + .../dashboard/add-new-app/AddNewCustomApp.js | 84 +++++ .../mgt/jaxrs/beans/ApplicationWrapper.java | 8 +- ...ApplicationManagementAdminServiceImpl.java | 14 +- .../app/mgt/{MobileApp.java => App.java} | 4 +- .../app/mgt/android/CustomApplication.java | 62 ++++ .../core/util/MDMAndroidOperationUtil.java | 6 +- .../mgt/core/util/MDMIOSOperationUtil.java | 6 +- 22 files changed, 1048 insertions(+), 122 deletions(-) create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/CustomAppReleaseWrapper.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/CustomAppWrapper.java create mode 100644 components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/add-new-app/AddNewCustomApp.js rename components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/{MobileApp.java => App.java} (98%) create mode 100644 components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/CustomApplication.java diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ApplicationType.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ApplicationType.java index 289e36938e..e5ba6a0312 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ApplicationType.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/ApplicationType.java @@ -23,6 +23,6 @@ package org.wso2.carbon.device.application.mgt.common; * Application Types. */ public enum ApplicationType { - ENTERPRISE, PUBLIC, WEB_APP, WEB_CLIP + ENTERPRISE, PUBLIC, WEB_APP, WEB_CLIP, CUSTOM } 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 fc822e6d10..116de80c91 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 @@ -31,6 +31,8 @@ import org.wso2.carbon.device.application.mgt.common.response.Application; import org.wso2.carbon.device.application.mgt.common.response.ApplicationRelease; import org.wso2.carbon.device.application.mgt.common.response.Category; import org.wso2.carbon.device.application.mgt.common.response.Tag; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppReleaseWrapper; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.EntAppReleaseWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.ApplicationUpdateWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.ApplicationWrapper; @@ -63,6 +65,9 @@ public interface ApplicationManager { Application createPublicApp(PublicAppWrapper publicAppWrapper, ApplicationArtifact applicationArtifact) throws ApplicationManagementException; + Application createCustomApp(CustomAppWrapper customAppWrapper, ApplicationArtifact applicationArtifact) + throws ApplicationManagementException; + /** * Updates an already existing application. * @@ -203,6 +208,9 @@ public interface ApplicationManager { ApplicationRelease updateWebAppRelease(String releaseUuid, WebAppReleaseWrapper webAppReleaseWrapper, ApplicationArtifact applicationArtifact) throws ApplicationManagementException; + ApplicationRelease updateCustomAppRelease(String releaseUuid, CustomAppReleaseWrapper customAppReleaseWrapper, + ApplicationArtifact applicationArtifact) throws ApplicationManagementException; + /*** * To validate the application creating request * @@ -211,9 +219,9 @@ public interface ApplicationManager { /*** * - * @throws RequestValidatingException throws if payload does not satisfy requrements. + * @throws ApplicationManagementException throws if payload does not satisfy requirements. */ - void validateReleaseCreatingRequest(T param) throws ApplicationManagementException; + void validateReleaseCreatingRequest(T param, String deviceType) 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/services/ApplicationStorageManager.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationStorageManager.java index fd93372178..24879c0e6d 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationStorageManager.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/services/ApplicationStorageManager.java @@ -47,15 +47,15 @@ public interface ApplicationStorageManager { /** - * To upload release artifacts for an ApplicationDTO. + * To upload release artifacts for an Application. * * @param applicationRelease ApplicationDTO Release Object. - * @param deviceType Compatible device tipe of the application. + * @param deviceType Compatible device type of the application. * @param binaryFile Binary File for the release. - * @throws ResourceManagementException Resource Management Exception. + * @throws ResourceManagementException if IO Exception occured while saving the release artifacts in the server. */ - ApplicationReleaseDTO uploadReleaseArtifact(ApplicationReleaseDTO applicationRelease, String deviceType, - InputStream binaryFile) throws ResourceManagementException; + void uploadReleaseArtifact(ApplicationReleaseDTO applicationRelease, String deviceType, InputStream binaryFile) + throws ResourceManagementException; /** * To upload release artifacts for an ApplicationDTO. diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/CustomAppReleaseWrapper.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/CustomAppReleaseWrapper.java new file mode 100644 index 0000000000..ca9a77e2cd --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/CustomAppReleaseWrapper.java @@ -0,0 +1,125 @@ +/* Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.common.wrapper; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; + +@ApiModel(value = "CustomAppReleaseWrapper", description = "This class holds the details when releasing an Custom app release to application store") +public class CustomAppReleaseWrapper { + + @ApiModelProperty(name = "description", + value = "Description of the application release") + @NotNull + private String description; + + @ApiModelProperty(name = "releaseType", + value = "Release type of the application release", + required = true, + example = "alpha, beta etc") + @NotNull + private String releaseType; + + @ApiModelProperty(name = "price", + value = "Price of the application release", + required = true) + @NotNull + private Double price; + + @ApiModelProperty(name = "isSharedWithAllTenants", + value = "If application release is shared with all tenants it is eqal to 1 otherwise 0", + required = true) + @NotNull + private boolean isSharedWithAllTenants; + + @ApiModelProperty(name = "metaData", + value = "Meta data of the application release", + required = true) + private String metaData; + + @ApiModelProperty(name = "version", + value = "Version of the public app release.", + required = true) + @NotNull + private String version; + + @ApiModelProperty(name = "packageName", + value = "Package name of the public app release.", + required = true) + @NotNull + private String packageName; + + public String getReleaseType() { + return releaseType; + } + + public void setReleaseType(String releaseType) { + this.releaseType = releaseType; + } + + public void setIsSharedWithAllTenants(boolean isSharedWithAllTenants) { + this.isSharedWithAllTenants = isSharedWithAllTenants; + } + + public void setMetaData(String metaData) { + this.metaData = metaData; + } + + public Double getPrice() { + return price; + } + + public void setPrice(Double price) { + this.price = price; + } + + public boolean getIsSharedWithAllTenants() { + return isSharedWithAllTenants; + } + + public String getMetaData() { + return metaData; + } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } + + public boolean isSharedWithAllTenants() { return isSharedWithAllTenants; } + + public void setSharedWithAllTenants(boolean sharedWithAllTenants) { + isSharedWithAllTenants = sharedWithAllTenants; + } + + 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.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/CustomAppWrapper.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/CustomAppWrapper.java new file mode 100644 index 0000000000..ce0108bbd7 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.common/src/main/java/org/wso2/carbon/device/application/mgt/common/wrapper/CustomAppWrapper.java @@ -0,0 +1,123 @@ +/* Copyright (c) 2019, 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 org.wso2.carbon.device.application.mgt.common.wrapper; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +import javax.validation.constraints.NotNull; +import java.util.List; + +@ApiModel(value = "CustomAppWrapper", description = "CustomAppWrapper represents an Application used to install in IoT devices") +public class CustomAppWrapper { + + @ApiModelProperty(name = "name", + value = "Name of the application", + required = true) + @NotNull + private String name; + + @ApiModelProperty(name = "description", + value = "Description of the application", + required = true) + @NotNull + private String description; + + @ApiModelProperty(name = "categories", + value = "CategoryDTO of the application", + required = true, + example = "Educational, Gaming, Travel, Entertainment etc") + @NotNull + private List categories; + + @ApiModelProperty(name = "subMethod", + value = "Subscription type of the application", + required = true, + example = "PAID, FREE") + @NotNull + private String subMethod; + + @ApiModelProperty(name = "paymentCurrency", + value = "Payment currency of the application", + required = true, + example = "$") + private String paymentCurrency; + + @ApiModelProperty(name = "tags", + value = "List of application tags") + @NotNull + private List tags; + + @ApiModelProperty(name = "unrestrictedRoles", + value = "List of roles that users should have to access the application") + @NotNull + private List unrestrictedRoles; + + @ApiModelProperty(name = "deviceType", + value = "Related device type of the application", + required = true, + example = "IoS, Android, Arduino, RaspberryPi etc") + @NotNull + private String deviceType; + + @ApiModelProperty(name = "customAppReleaseWrappers", + value = "List of custom app releases", + required = true) + @NotNull + private List customAppReleaseWrappers; + + public String getName() { + return name; + } + + public void setName(String name) { this.name = name; } + + public List getCategories() { return categories; } + + public void setCategories(List categories) { + this.categories = categories; + } + + public List getTags() { return tags; } + + public void setTags(List tags) { this.tags = tags; } + + public String getSubMethod() { return subMethod; } + + public void setSubMethod(String subMethod) { this.subMethod = subMethod; } + + public String getPaymentCurrency() { return paymentCurrency; } + + public void setPaymentCurrency(String paymentCurrency) { this.paymentCurrency = paymentCurrency; } + + public List getCustomAppReleaseWrappers() { return customAppReleaseWrappers; } + + public void setCustomAppReleaseWrappers(List customAppReleaseWrappers) { + this.customAppReleaseWrappers = customAppReleaseWrappers; } + + public List getUnrestrictedRoles() { return unrestrictedRoles; } + + public void setUnrestrictedRoles(List unrestrictedRoles) { this.unrestrictedRoles = unrestrictedRoles; } + + public String getDeviceType() { return deviceType; } + + public void setDeviceType(String deviceType) { this.deviceType = deviceType; } + + public String getDescription() { return description; } + + public void setDescription(String description) { this.description = description; } +} 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 6ff1f5997c..6561bac37f 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 @@ -56,6 +56,8 @@ import org.wso2.carbon.device.application.mgt.common.response.Category; import org.wso2.carbon.device.application.mgt.common.response.Tag; import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; import org.wso2.carbon.device.application.mgt.common.services.ApplicationStorageManager; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppReleaseWrapper; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.EntAppReleaseWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.ApplicationUpdateWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.ApplicationWrapper; @@ -133,49 +135,31 @@ public class ApplicationManagerImpl implements ApplicationManager { @Override public Application createEntApp(ApplicationWrapper applicationWrapper, ApplicationArtifact applicationArtifact) throws ApplicationManagementException { - - int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); - String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); if (log.isDebugEnabled()) { - log.debug("Application create request is received for the tenant : " + tenantId + " and the user: " - + userName); + log.debug("Ent. Application create request is received. Application name: " + applicationWrapper.getName() + + " Device type: " + applicationWrapper.getDeviceType()); } - ApplicationDTO applicationDTO = APIUtil.convertToAppDTO(applicationWrapper); - ApplicationReleaseDTO applicationReleaseDTO = applicationDTO.getApplicationReleaseDTOs().get(0); - if (!isValidOsVersions(applicationReleaseDTO.getSupportedOsVersions(), applicationWrapper.getDeviceType())) { - String msg = "You are trying to create application which has an application release contains invalid or " - + "unsupported OS versions in the supportedOsVersions section. Hence, please re-evaluate the " - + "request payload."; - log.error(msg); - throw new BadRequestException(msg); - } - //uploading application artifacts - applicationReleaseDTO = uploadEntAppReleaseArtifacts(applicationReleaseDTO, applicationArtifact, + ApplicationReleaseDTO applicationReleaseDTO = uploadEntAppReleaseArtifacts( + applicationDTO.getApplicationReleaseDTOs().get(0), applicationArtifact, applicationWrapper.getDeviceType(), false); applicationDTO.getApplicationReleaseDTOs().clear(); applicationDTO.getApplicationReleaseDTOs().add(applicationReleaseDTO); - return addAppDataIntoDB(applicationDTO, tenantId); + return addAppDataIntoDB(applicationDTO); } @Override public Application createWebClip(WebAppWrapper webAppWrapper, ApplicationArtifact applicationArtifact) throws ApplicationManagementException { - int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); - String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); if (log.isDebugEnabled()) { - log.debug("Web clip create request is received for the tenant : " + tenantId + " From" + " the user : " - + userName); + log.debug("Web clip create request is received. App name: " + webAppWrapper.getName() + " Device type: " + + Constants.ANY); } - ApplicationDTO applicationDTO = APIUtil.convertToAppDTO(webAppWrapper); ApplicationReleaseDTO applicationReleaseDTO = applicationDTO.getApplicationReleaseDTOs().get(0); - String uuid = UUID.randomUUID().toString(); - String md5 = DigestUtils.md5Hex(applicationReleaseDTO.getInstallerName()); - applicationReleaseDTO.setUuid(uuid); - applicationReleaseDTO.setAppHashValue(md5); - + applicationReleaseDTO.setUuid(UUID.randomUUID().toString()); + applicationReleaseDTO.setAppHashValue(DigestUtils.md5Hex(applicationReleaseDTO.getInstallerName())); //uploading application artifacts try { applicationDTO.getApplicationReleaseDTOs().clear(); @@ -185,46 +169,31 @@ public class ApplicationManagerImpl implements ApplicationManager { log.error(msg); throw new ApplicationManagementException(msg, e); } - //insert application data into database - return addAppDataIntoDB(applicationDTO, tenantId); + return addAppDataIntoDB(applicationDTO); } @Override public Application createPublicApp(PublicAppWrapper publicAppWrapper, ApplicationArtifact applicationArtifact) throws ApplicationManagementException { - int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); - String userName = PrivilegedCarbonContext.getThreadLocalCarbonContext().getUsername(); - String publicAppStorePath = ""; if (log.isDebugEnabled()) { - log.debug("Public app creating request is received for the tenant : " + tenantId + " From" + " the user : " - + userName); - } - - if (!isValidOsVersions(publicAppWrapper.getPublicAppReleaseWrappers().get(0).getSupportedOsVersions(), - publicAppWrapper.getDeviceType())) { - String msg = "You are trying to add application release which has invalid or unsupported OS versions in " - + "the supportedOsVersions section. Hence, please re-evaluate the request payload."; - log.error(msg); - throw new BadRequestException(msg); + log.debug("Public app creating request is received. App name: " + publicAppWrapper.getName() + + " Device Type: " + publicAppWrapper.getDeviceType()); } + String publicAppStorePath = ""; if (DeviceTypes.ANDROID.toString().equals(publicAppWrapper.getDeviceType())) { publicAppStorePath = Constants.GOOGLE_PLAY_STORE_URL; } else if (DeviceTypes.IOS.toString().equals(publicAppWrapper.getDeviceType())) { publicAppStorePath = Constants.APPLE_STORE_URL; } - ApplicationDTO applicationDTO = APIUtil.convertToAppDTO(publicAppWrapper); ApplicationReleaseDTO applicationReleaseDTO = applicationDTO.getApplicationReleaseDTOs().get(0); - String uuid = UUID.randomUUID().toString(); String appInstallerUrl = publicAppStorePath + applicationReleaseDTO.getPackageName(); //todo check app package name exist or not, do it in validation method applicationReleaseDTO.setInstallerName(appInstallerUrl); - String md5 = DigestUtils.md5Hex(appInstallerUrl); - applicationReleaseDTO.setUuid(uuid); - applicationReleaseDTO.setAppHashValue(md5); - + applicationReleaseDTO.setUuid(UUID.randomUUID().toString()); + applicationReleaseDTO.setAppHashValue(DigestUtils.md5Hex(appInstallerUrl)); //uploading application artifacts try { applicationDTO.getApplicationReleaseDTOs().clear(); @@ -235,9 +204,70 @@ public class ApplicationManagerImpl implements ApplicationManager { log.error(msg, e); throw new ApplicationManagementException(msg, e); } - //insert application data into database - return addAppDataIntoDB(applicationDTO, tenantId); + return addAppDataIntoDB(applicationDTO); + } + + @Override + public Application createCustomApp(CustomAppWrapper customAppWrapper, ApplicationArtifact applicationArtifact) + throws ApplicationManagementException { + try { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + ApplicationStorageManager applicationStorageManager = DAOUtil.getApplicationStorageManager(); + byte[] content = IOUtils.toByteArray(applicationArtifact.getInstallerStream()); + String md5OfApp = StorageManagementUtil.getMD5(new ByteArrayInputStream(content)); + if (md5OfApp == null) { + String msg = + "Error occurred while getting md5sum value of custom app. Application name: " + customAppWrapper + .getName() + " Device type: " + customAppWrapper.getDeviceType(); + log.error(msg); + throw new ApplicationManagementException(msg); + } + try { + ConnectionManagerUtil.openDBConnection(); + if (this.applicationReleaseDAO.verifyReleaseExistenceByHash(md5OfApp, tenantId)) { + String msg = "Application release exists for the uploaded binary file. Application name: " + + customAppWrapper.getName() + " Device type: " + customAppWrapper.getDeviceType(); + log.error(msg); + throw new BadRequestException(msg); + } + } catch (ApplicationManagementDAOException e) { + String msg = "Error occurred while adding application data into the database. Application name: " + + customAppWrapper.getName() + " Device type: " + customAppWrapper.getDeviceType(); + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + + ApplicationDTO applicationDTO = APIUtil.convertToAppDTO(customAppWrapper); + ApplicationReleaseDTO applicationReleaseDTO = applicationDTO.getApplicationReleaseDTOs().get(0); + applicationReleaseDTO.setUuid(UUID.randomUUID().toString()); + applicationReleaseDTO.setAppHashValue(md5OfApp); + applicationReleaseDTO.setInstallerName(applicationArtifact.getInstallerName()); + try (ByteArrayInputStream binaryDuplicate = new ByteArrayInputStream(content)) { + applicationStorageManager.uploadReleaseArtifact(applicationReleaseDTO, customAppWrapper.getDeviceType(), + binaryDuplicate); + } catch (IOException e) { + String msg = "Error occurred when uploading release artifact into the server."; + log.error(msg); + throw new ApplicationManagementException(msg); + } + applicationReleaseDTO = addImageArtifacts(applicationReleaseDTO, applicationArtifact); + applicationDTO.getApplicationReleaseDTOs().clear(); + applicationDTO.getApplicationReleaseDTOs().add(applicationReleaseDTO); + return addAppDataIntoDB(applicationDTO); + } catch (ResourceManagementException e) { + String msg = "Error occurred while uploading application artifact into the server. Application name: " + + customAppWrapper.getName() + " Device type: " + customAppWrapper.getDeviceType(); + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (IOException e) { + String msg = "Error occurred while getting bytes from application release artifact. Application name: " + + customAppWrapper.getName() + " Device type: " + customAppWrapper.getDeviceType(); + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } } private void deleteApplicationArtifacts(List directoryPaths) throws ApplicationManagementException { @@ -264,9 +294,7 @@ public class ApplicationManagerImpl implements ApplicationManager { // The application executable artifacts such as apks are uploaded. try { byte[] content = IOUtils.toByteArray(applicationArtifact.getInstallerStream()); - applicationReleaseDTO.setInstallerName(applicationArtifact.getInstallerName()); - try (ByteArrayInputStream binary = new ByteArrayInputStream(content)) { ApplicationInstaller applicationInstaller = applicationStorageManager .getAppInstallerData(binary, deviceType); @@ -286,7 +314,6 @@ public class ApplicationManagerImpl implements ApplicationManager { } 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 " @@ -303,7 +330,7 @@ public class ApplicationManagerImpl implements ApplicationManager { applicationReleaseDTO.setAppHashValue(md5OfApp); try (ByteArrayInputStream binaryDuplicate = new ByteArrayInputStream(content)) { - applicationReleaseDTO = applicationStorageManager + applicationStorageManager .uploadReleaseArtifact(applicationReleaseDTO, deviceType, binaryDuplicate); } } catch (DBConnectionException e) { @@ -384,7 +411,7 @@ public class ApplicationManagerImpl implements ApplicationManager { String deletingAppHashValue = applicationReleaseDTO.getAppHashValue(); applicationReleaseDTO.setAppHashValue(md5OfApp); try (ByteArrayInputStream binaryDuplicate = new ByteArrayInputStream(content)) { - applicationReleaseDTO = applicationStorageManager + applicationStorageManager .uploadReleaseArtifact(applicationReleaseDTO, deviceType, binaryDuplicate); applicationStorageManager.copyImageArtifactsAndDeleteInstaller(deletingAppHashValue, applicationReleaseDTO); @@ -624,12 +651,11 @@ public class ApplicationManagerImpl implements ApplicationManager { * required to do the validation of request and check the existence of application releaseDTO. * * @param applicationDTO Application DTO object. - * @param tenantId Tenant Id * @return {@link Application} * @throws ApplicationManagementException which throws if error occurs while during application management. */ - private Application addAppDataIntoDB(ApplicationDTO applicationDTO, int tenantId) - throws ApplicationManagementException { + private Application addAppDataIntoDB(ApplicationDTO applicationDTO) throws ApplicationManagementException { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); ApplicationStorageManager applicationStorageManager = DAOUtil.getApplicationStorageManager(); List unrestrictedRoles = applicationDTO.getUnrestrictedRoles(); ApplicationReleaseDTO applicationReleaseDTO = applicationDTO.getApplicationReleaseDTOs().get(0); @@ -2492,6 +2518,130 @@ public class ApplicationManagerImpl implements ApplicationManager { } } + @Override + public ApplicationRelease updateCustomAppRelease(String releaseUuid, + CustomAppReleaseWrapper customAppReleaseWrapper, ApplicationArtifact applicationArtifact) + throws ApplicationManagementException { + int tenantId = PrivilegedCarbonContext.getThreadLocalCarbonContext().getTenantId(true); + ApplicationStorageManager applicationStorageManager = DAOUtil.getApplicationStorageManager(); + try { + ConnectionManagerUtil.beginDBTransaction(); + ApplicationDTO applicationDTO = this.applicationDAO.getAppWithRelatedRelease(releaseUuid, tenantId); + AtomicReference applicationReleaseDTO = new AtomicReference<>( + applicationDTO.getApplicationReleaseDTOs().get(0)); + validateAppReleaseUpdating(applicationDTO, ApplicationType.ENTERPRISE.toString()); + applicationReleaseDTO.get().setPrice(customAppReleaseWrapper.getPrice()); + applicationReleaseDTO.get() + .setIsSharedWithAllTenants(applicationReleaseDTO.get().getIsSharedWithAllTenants()); + if (!StringUtils.isEmpty(customAppReleaseWrapper.getPackageName())) { + applicationReleaseDTO.get().setVersion(customAppReleaseWrapper.getVersion()); + } + if (!StringUtils.isEmpty(customAppReleaseWrapper.getVersion())) { + applicationReleaseDTO.get().setVersion(customAppReleaseWrapper.getVersion()); + } + if (!StringUtils.isEmpty(customAppReleaseWrapper.getDescription())) { + applicationReleaseDTO.get().setDescription(customAppReleaseWrapper.getDescription()); + } + if (!StringUtils.isEmpty(customAppReleaseWrapper.getReleaseType())) { + applicationReleaseDTO.get().setReleaseType(customAppReleaseWrapper.getReleaseType()); + } + if (!StringUtils.isEmpty(customAppReleaseWrapper.getMetaData())) { + applicationReleaseDTO.get().setMetaData(customAppReleaseWrapper.getMetaData()); + } + + if (!StringUtils.isEmpty(applicationArtifact.getInstallerName()) + && applicationArtifact.getInstallerStream() != null) { + DeviceType deviceTypeObj = APIUtil.getDeviceTypeData(applicationDTO.getDeviceTypeId()); + // The application executable artifacts such as deb are uploaded. + try { + byte[] content = IOUtils.toByteArray(applicationArtifact.getInstallerStream()); + try (ByteArrayInputStream binaryClone = new ByteArrayInputStream(content)) { + String md5OfApp = StorageManagementUtil.getMD5(binaryClone); + if (md5OfApp == null) { + String msg = "Error occurred while retrieving md5sum value from the binary file for " + + "application release UUID " + applicationReleaseDTO.get().getUuid(); + log.error(msg); + throw new ApplicationStorageManagementException(msg); + } + if (!applicationReleaseDTO.get().getAppHashValue().equals(md5OfApp)) { + applicationReleaseDTO.get().setInstallerName(applicationArtifact.getInstallerName()); + try { + ConnectionManagerUtil.getDBConnection(); + if (this.applicationReleaseDAO.verifyReleaseExistenceByHash(md5OfApp, tenantId)) { + String msg = + "Same binary file is in the server. Hence you can't add same file into the " + + "server. Device Type: " + deviceTypeObj.getName() + + " and package name: " + applicationDTO.getApplicationReleaseDTOs() + .get(0).getPackageName(); + log.error(msg); + throw new BadRequestException(msg); + } + + String deletingAppHashValue = applicationReleaseDTO.get().getAppHashValue(); + applicationReleaseDTO.get().setAppHashValue(md5OfApp); + try (ByteArrayInputStream binaryDuplicate = new ByteArrayInputStream(content)) { + applicationStorageManager + .uploadReleaseArtifact(applicationReleaseDTO.get(), deviceTypeObj.getName(), + binaryDuplicate); + applicationStorageManager.copyImageArtifactsAndDeleteInstaller(deletingAppHashValue, + applicationReleaseDTO.get()); + } + } catch (DBConnectionException e) { + String msg = "Error occurred when getting database connection for verifying application" + + " release existing for new app hash value."; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (ApplicationManagementDAOException e) { + String msg = + "Error occurred when executing the query for verifying application release " + + "existence for the new app hash value."; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + } + } catch (IOException e) { + String msg = "Error occurred when getting byte array of binary file. Installer name: " + + applicationArtifact.getInstallerName(); + log.error(msg, e); + throw new ApplicationStorageManagementException(msg, e); + } + } + applicationReleaseDTO.set(updateImageArtifacts(applicationReleaseDTO.get(), applicationArtifact)); + boolean updateStatus = applicationReleaseDAO.updateRelease(applicationReleaseDTO.get(), tenantId) != null; + if (!updateStatus) { + ConnectionManagerUtil.rollbackDBTransaction(); + return null; + } + ConnectionManagerUtil.commitDBTransaction(); + return APIUtil.releaseDtoToRelease(applicationReleaseDTO.get()); + } catch (DBConnectionException e) { + String msg = "Error occurred while getting the database connection to update enterprise app release which " + + "has release UUID: " + releaseUuid; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (TransactionManagementException e) { + String msg = "Database access error is occurred when updating enterprise app release which has release " + + "UUID: " + releaseUuid; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (ApplicationManagementDAOException e) { + ConnectionManagerUtil.rollbackDBTransaction(); + String msg = "Error occured when updating Ent Application release of UUID: " + releaseUuid; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } catch (ResourceManagementException e) { + String msg = "Error occured when updating application release artifact in the file system. Ent App release " + + "UUID:" + releaseUuid; + log.error(msg, e); + throw new ApplicationManagementException(msg, e); + } finally { + ConnectionManagerUtil.closeDBConnection(); + } + } + private void validateAppReleaseUpdating(ApplicationDTO applicationDTO, String appType) throws ApplicationManagementException { if (applicationDTO == null) { @@ -2660,6 +2810,43 @@ public class ApplicationManagerImpl implements ApplicationManager { throw new BadRequestException(msg); } unrestrictedRoles = publicAppWrapper.getUnrestrictedRoles(); + } else if (param instanceof CustomAppWrapper) { + CustomAppWrapper customAppWrapper = (CustomAppWrapper) param; + appName = customAppWrapper.getName(); + if (StringUtils.isEmpty(appName)) { + String msg = "Application name cannot be empty."; + log.error(msg); + throw new BadRequestException(msg); + } + appCategories = customAppWrapper.getCategories(); + if (appCategories == null) { + String msg = "Application category can't be null."; + log.error(msg); + throw new BadRequestException(msg); + } + if (appCategories.isEmpty()) { + String msg = "Application category can't be empty."; + log.error(msg); + throw new BadRequestException(msg); + } + if (StringUtils.isEmpty(customAppWrapper.getDeviceType())) { + String msg = "Device type can't be empty for the application."; + log.error(msg); + throw new BadRequestException(msg); + } + DeviceType deviceType = APIUtil.getDeviceTypeData(customAppWrapper.getDeviceType()); + deviceTypeId = deviceType.getId(); + + List customAppReleaseWrappers; + customAppReleaseWrappers = customAppWrapper.getCustomAppReleaseWrappers(); + + if (customAppReleaseWrappers == null || customAppReleaseWrappers.size() != 1) { + String msg = "Invalid custom app creating request. Application creating request must have single " + + "application release. Application name:" + customAppWrapper.getName() + "."; + log.error(msg); + throw new BadRequestException(msg); + } + unrestrictedRoles = customAppWrapper.getUnrestrictedRoles(); } else { String msg = "Invalid payload found with the request. Hence verify the request payload object."; log.error(msg); @@ -2738,7 +2925,7 @@ public class ApplicationManagerImpl implements ApplicationManager { } @Override - public void validateReleaseCreatingRequest(T param) throws ApplicationManagementException { + public void validateReleaseCreatingRequest(T param, String deviceType) throws ApplicationManagementException { if (param == null) { String msg = "In order to validate release creating request param shouldn't be null."; log.error(msg); @@ -2746,12 +2933,20 @@ public class ApplicationManagerImpl implements ApplicationManager { } if (param instanceof EntAppReleaseWrapper) { - EntAppReleaseWrapper entAppReleaseWrapper = (EntAppReleaseWrapper) param; - if (StringUtils.isEmpty(entAppReleaseWrapper.getSupportedOsVersions())) { + AtomicReference entAppReleaseWrapper = new AtomicReference<>( + (EntAppReleaseWrapper) param); + if (StringUtils.isEmpty(entAppReleaseWrapper.get().getSupportedOsVersions())) { String msg = "Supported OS Version shouldn't be null or empty."; log.error(msg); throw new BadRequestException(msg); } + if (!isValidOsVersions(entAppReleaseWrapper.get().getSupportedOsVersions(), deviceType)) { + String msg = "You are trying to create application which has an application release contains invalid or " + + "unsupported OS versions in the supportedOsVersions section. Hence, please re-evaluate the " + + "request payload."; + log.error(msg); + throw new BadRequestException(msg); + } } else if (param instanceof WebAppReleaseWrapper) { WebAppReleaseWrapper webAppReleaseWrapper = (WebAppReleaseWrapper) param; UrlValidator urlValidator = new UrlValidator(); @@ -2788,7 +2983,25 @@ public class ApplicationManagerImpl implements ApplicationManager { log.error(msg); throw new BadRequestException(msg); } - + if (!isValidOsVersions(publicAppReleaseWrapper.getSupportedOsVersions(), deviceType)) { + String msg = "You are trying to create application which has an application release contains invalid or " + + "unsupported OS versions in the supportedOsVersions section. Hence, please re-evaluate the " + + "request payload."; + log.error(msg); + throw new BadRequestException(msg); + } + } else if (param instanceof CustomAppReleaseWrapper) { + CustomAppReleaseWrapper customAppReleaseWrapper = (CustomAppReleaseWrapper) param; + if (StringUtils.isEmpty(customAppReleaseWrapper.getVersion())) { + String msg = "Version shouldn't be empty or null for the custom App release creating request."; + log.error(msg); + throw new BadRequestException(msg); + } + if (StringUtils.isEmpty(customAppReleaseWrapper.getPackageName())) { + String msg = "Package name shouldn't be empty or null for the custom App release creating request."; + log.error(msg); + throw new BadRequestException(msg); + } } else { String msg = "Invalid payload found with the release creating request. Hence verify the release creating " + "request payload object."; @@ -2814,7 +3027,6 @@ public class ApplicationManagerImpl implements ApplicationManager { @Override public void validateBinaryArtifact(Attachment binaryFile) throws RequestValidatingException { - if (binaryFile == null) { String msg = "Binary file is not found with the application release creating request for ENTERPRISE app " + "creating request."; diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationStorageManagerImpl.java b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationStorageManagerImpl.java index 35f140e129..6480226143 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationStorageManagerImpl.java +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.core/src/main/java/org/wso2/carbon/device/application/mgt/core/impl/ApplicationStorageManagerImpl.java @@ -143,25 +143,21 @@ public class ApplicationStorageManagerImpl implements ApplicationStorageManager } @Override - public ApplicationReleaseDTO uploadReleaseArtifact(ApplicationReleaseDTO applicationReleaseDTO, + public void uploadReleaseArtifact(ApplicationReleaseDTO applicationReleaseDTO, String deviceType, InputStream binaryFile) throws ResourceManagementException { try { - String artifactDirectoryPath; - String artifactPath; byte [] content = IOUtils.toByteArray(binaryFile); - - artifactDirectoryPath = + String artifactDirectoryPath = storagePath + applicationReleaseDTO.getAppHashValue() + File.separator + Constants.APP_ARTIFACT; StorageManagementUtil.createArtifactDirectory(artifactDirectoryPath); - artifactPath = artifactDirectoryPath + File.separator + applicationReleaseDTO.getInstallerName(); + String artifactPath = artifactDirectoryPath + File.separator + applicationReleaseDTO.getInstallerName(); saveFile(new ByteArrayInputStream(content), artifactPath); } catch (IOException e) { String msg = "IO Exception while saving the release artifacts in the server for the application UUID " + applicationReleaseDTO.getUuid(); log.error(msg, e); - throw new ApplicationStorageManagementException( msg, e); + throw new ResourceManagementException( msg, e); } - return applicationReleaseDTO; } @Override 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 6da7558bc2..cf3ba51bde 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 @@ -51,8 +51,9 @@ import org.wso2.carbon.device.application.mgt.core.util.HelperUtil; 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.app.mgt.MobileApp; +import org.wso2.carbon.device.mgt.common.app.mgt.App; import org.wso2.carbon.device.mgt.common.app.mgt.MobileAppTypes; +import org.wso2.carbon.device.mgt.common.app.mgt.android.CustomApplication; import org.wso2.carbon.device.mgt.common.exceptions.DeviceManagementException; import org.wso2.carbon.device.mgt.common.exceptions.InvalidDeviceException; import org.wso2.carbon.device.mgt.common.exceptions.UnknownApplicationTypeException; @@ -62,6 +63,7 @@ import org.wso2.carbon.device.mgt.common.operation.mgt.ActivityStatus; import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; import org.wso2.carbon.device.mgt.common.operation.mgt.OperationManagementException; import org.wso2.carbon.device.mgt.core.dto.DeviceType; +import org.wso2.carbon.device.mgt.core.operation.mgt.ProfileOperation; 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; @@ -460,16 +462,16 @@ public class SubscriptionManagerImpl implements SubscriptionManager { private Operation generateOperationPayloadByDeviceType(String deviceType, Application application, String action) throws ApplicationManagementException { try { - //todo rethink and modify the {@link MobileApp} usage - MobileApp mobileApp = new MobileApp(); + //todo rethink and modify the {@link App} usage + App app = new App(); MobileAppTypes mobileAppType = MobileAppTypes.valueOf(application.getType()); if (DeviceTypes.ANDROID.toString().equalsIgnoreCase(deviceType)) { if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) { - mobileApp.setType(mobileAppType); - mobileApp.setLocation(application.getApplicationReleases().get(0).getInstallerPath()); - return MDMAndroidOperationUtil.createInstallAppOperation(mobileApp); + app.setType(mobileAppType); + app.setLocation(application.getApplicationReleases().get(0).getInstallerPath()); + return MDMAndroidOperationUtil.createInstallAppOperation(app); } else if (SubAction.UNINSTALL.toString().equalsIgnoreCase(action)) { - return MDMAndroidOperationUtil.createAppUninstallOperation(mobileApp); + return MDMAndroidOperationUtil.createAppUninstallOperation(app); } else { String msg = "Invalid Action is found. Action: " + action; log.error(msg); @@ -480,24 +482,50 @@ public class SubscriptionManagerImpl implements SubscriptionManager { String plistDownloadEndpoint = APIUtil.getArtifactDownloadBaseURL() + MDMAppConstants.IOSConstants.PLIST + Constants.FORWARD_SLASH + application.getApplicationReleases().get(0).getUuid(); - mobileApp.setType(mobileAppType); - mobileApp.setLocation(plistDownloadEndpoint); + app.setType(mobileAppType); + app.setLocation(plistDownloadEndpoint); Properties properties = new Properties(); properties.put(MDMAppConstants.IOSConstants.IS_PREVENT_BACKUP, true); properties.put(MDMAppConstants.IOSConstants.IS_REMOVE_APP, true); - mobileApp.setProperties(properties); - return MDMIOSOperationUtil.createInstallAppOperation(mobileApp); + app.setProperties(properties); + return MDMIOSOperationUtil.createInstallAppOperation(app); } else if (SubAction.UNINSTALL.toString().equalsIgnoreCase(action)) { - return MDMIOSOperationUtil.createAppUninstallOperation(mobileApp); + return MDMIOSOperationUtil.createAppUninstallOperation(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); - throw new ApplicationManagementException(msg); + if (ApplicationType.CUSTOM.toString().equalsIgnoreCase(application.getType())) { + if (SubAction.INSTALL.toString().equalsIgnoreCase(action)) { + ProfileOperation operation = new ProfileOperation(); + operation.setCode(MDMAppConstants.AndroidConstants.OPCODE_INSTALL_APPLICATION); + operation.setType(Operation.Type.PROFILE); + CustomApplication customApplication = new CustomApplication(); + customApplication.setType(application.getType()); + customApplication.setUrl(application.getApplicationReleases().get(0).getInstallerPath()); + operation.setPayLoad(customApplication.toJSON()); + return operation; + } else if (SubAction.UNINSTALL.toString().equalsIgnoreCase(action)) { + ProfileOperation operation = new ProfileOperation(); + operation.setCode(MDMAppConstants.AndroidConstants.OPCODE_UNINSTALL_APPLICATION); + operation.setType(Operation.Type.PROFILE); + CustomApplication customApplication = new CustomApplication(); + customApplication.setType(application.getType()); + //todo get application package name and set + operation.setPayLoad(customApplication.toJSON()); + return MDMAndroidOperationUtil.createAppUninstallOperation(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); + throw new ApplicationManagementException(msg); + } } } catch (UnknownApplicationTypeException e) { String msg = "Unknown Application type is found."; 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 7983fc7ea1..21158781a0 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 @@ -32,6 +32,8 @@ import org.wso2.carbon.device.application.mgt.common.response.Application; import org.wso2.carbon.device.application.mgt.common.response.ApplicationRelease; import org.wso2.carbon.device.application.mgt.common.services.*; import org.wso2.carbon.device.application.mgt.common.ErrorResponse; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppReleaseWrapper; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.EntAppReleaseWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.ApplicationWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.PublicAppReleaseWrapper; @@ -266,6 +268,21 @@ public class APIUtil { List applicationReleaseEntities = publicAppWrapper.getPublicAppReleaseWrappers() .stream().map(APIUtil::releaseWrapperToReleaseDTO).collect(Collectors.toList()); applicationDTO.setApplicationReleaseDTOs(applicationReleaseEntities); + } else if (param instanceof CustomAppWrapper){ + CustomAppWrapper customAppWrapper = (CustomAppWrapper) param; + DeviceType deviceType = getDeviceTypeData(customAppWrapper.getDeviceType()); + applicationDTO.setName(customAppWrapper.getName()); + applicationDTO.setDescription(customAppWrapper.getDescription()); + applicationDTO.setAppCategories(customAppWrapper.getCategories()); + applicationDTO.setType(ApplicationType.CUSTOM.toString()); + applicationDTO.setSubType(customAppWrapper.getSubMethod()); + applicationDTO.setPaymentCurrency(customAppWrapper.getPaymentCurrency()); + applicationDTO.setTags(customAppWrapper.getTags()); + applicationDTO.setUnrestrictedRoles(customAppWrapper.getUnrestrictedRoles()); + applicationDTO.setDeviceTypeId(deviceType.getId()); + List applicationReleaseEntities = customAppWrapper.getCustomAppReleaseWrappers() + .stream().map(APIUtil::releaseWrapperToReleaseDTO).collect(Collectors.toList()); + applicationDTO.setApplicationReleaseDTOs(applicationReleaseEntities); } return applicationDTO; } @@ -301,6 +318,15 @@ public class APIUtil { applicationReleaseDTO.setIsSharedWithAllTenants(publicAppReleaseWrapper.getIsSharedWithAllTenants()); applicationReleaseDTO.setMetaData(publicAppReleaseWrapper.getMetaData()); applicationReleaseDTO.setSupportedOsVersions(publicAppReleaseWrapper.getSupportedOsVersions()); + } else if (param instanceof CustomAppReleaseWrapper) { + CustomAppReleaseWrapper customAppReleaseWrapper = (CustomAppReleaseWrapper) param; + applicationReleaseDTO.setDescription(customAppReleaseWrapper.getDescription()); + applicationReleaseDTO.setReleaseType(customAppReleaseWrapper.getReleaseType()); + applicationReleaseDTO.setVersion(customAppReleaseWrapper.getVersion()); + applicationReleaseDTO.setPackageName(customAppReleaseWrapper.getPackageName()); + applicationReleaseDTO.setPrice(customAppReleaseWrapper.getPrice()); + applicationReleaseDTO.setIsSharedWithAllTenants(customAppReleaseWrapper.getIsSharedWithAllTenants()); + applicationReleaseDTO.setMetaData(customAppReleaseWrapper.getMetaData()); } return applicationReleaseDTO; } 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/ApplicationManagementPublisherAPI.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/ApplicationManagementPublisherAPI.java index abb08c77f9..aaa579b840 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/ApplicationManagementPublisherAPI.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/ApplicationManagementPublisherAPI.java @@ -39,6 +39,8 @@ import org.wso2.carbon.device.application.mgt.common.LifecycleChanger; import org.wso2.carbon.device.application.mgt.common.dto.ApplicationDTO; import org.wso2.carbon.device.application.mgt.common.dto.ApplicationReleaseDTO; import org.wso2.carbon.device.application.mgt.common.response.ApplicationRelease; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppReleaseWrapper; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.EntAppReleaseWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.ApplicationUpdateWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.ApplicationWrapper; @@ -468,9 +470,78 @@ public interface ApplicationManagementPublisherAPI { ); @POST + @Path("/custom-app") @Produces(MediaType.APPLICATION_JSON) @Consumes({"multipart/mixed", MediaType.MULTIPART_FORM_DATA}) - @Path("/ent-app/{appId}") + @ApiOperation( + consumes = MediaType.APPLICATION_JSON, + produces = MediaType.APPLICATION_JSON, + httpMethod = "POST", + value = "Create an custom application", + notes = "This will create a new custom application", + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:app:publisher:update") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 201, + message = "OK. \n Successfully created an application.", + response = ApplicationDTO.class), + @ApiResponse( + code = 400, + message = "Bad Request. \n " + + "ApplicationDTO creating payload contains unacceptable or vulnerable data"), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while creating the application.", + response = ErrorResponse.class) + }) + Response createCustomApp( + @ApiParam( + name = "application", + value = "The application that need to be created.", + required = true) + @Multipart("application") CustomAppWrapper customAppWrapper, + @ApiParam( + name = "binaryFile", + value = "Binary file of uploading application", + required = true) + @Multipart(value = "binaryFile") Attachment binaryFile, + @ApiParam( + name = "icon", + value = "Icon of the uploading application", + required = true) + @Multipart(value = "icon") Attachment iconFile, + @ApiParam( + name = "banner", + value = "Banner of the uploading application") + @Multipart(value = "banner") Attachment bannerFile, + @ApiParam( + name = "screenshot1", + value = "Screen Shots of the uploading application", + required = true) + @Multipart(value = "screenshot1") Attachment screenshot1, + @ApiParam( + name = "screenshot2", + value = "Screen Shots of the uploading application", + required = false) + @Multipart(value = "screenshot2") Attachment screenshot2, + @ApiParam( + name = "screenshot3", + value = "Screen Shots of the uploading application", + required = false) + @Multipart(value = "screenshot3") Attachment screenshot3 + ); + + @POST + @Produces(MediaType.APPLICATION_JSON) + @Consumes({"multipart/mixed", MediaType.MULTIPART_FORM_DATA}) + @Path("/{deviceType}/ent-app/{appId}") @ApiOperation( consumes = MediaType.APPLICATION_JSON, produces = MediaType.APPLICATION_JSON, @@ -500,6 +571,11 @@ public interface ApplicationManagementPublisherAPI { response = ErrorResponse.class) }) Response createEntAppRelease( + @ApiParam( + name = "deviceType", + value = "Device type that application is compatible with.", + required = true) + @PathParam("deviceType") String deviceType, @ApiParam( name = "appId", value = "Id of the application.", @@ -895,6 +971,77 @@ public interface ApplicationManagementPublisherAPI { value = "Third screenshot of the uploading application") @Multipart(value = "screenshot3") Attachment screenshot3); + @PUT + @Path("/custom-app-release/{uuid}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @ApiOperation( + consumes = MediaType.MULTIPART_FORM_DATA, + produces = MediaType.APPLICATION_JSON, + httpMethod = "PUT", + value = "Update an custom application release", + notes = "This will update a custom app release", + tags = "Application Management", + extensions = { + @Extension(properties = { + @ExtensionProperty(name = SCOPE, value = "perm:app:publisher:update") + }) + } + ) + @ApiResponses( + value = { + @ApiResponse( + code = 201, + message = "OK. \n Successfully created an application release.", + response = ApplicationReleaseDTO.class), + @ApiResponse( + code = 400, + message = "Bad Request. \n " + + "ApplicationDTO release updating payload contains unacceptable or vulnerable data"), + @ApiResponse( + code = 500, + message = "Internal Server Error. \n Error occurred while releasing the application.", + response = ErrorResponse.class) + }) + Response updateCustomAppRelease( + @ApiParam( + name = "UUID", + value = "Unique identifier of the ApplicationDTO Release", + required = true) + @PathParam("uuid") String applicationUUID, + @ApiParam( + name = "entAppReleaseWrapper", + value = "Application release wrapper which is going to update.", + required = true) + @Multipart( + value = "entAppReleaseWrapper", + type = "application/json") CustomAppReleaseWrapper customAppReleaseWrapper, + @ApiParam( + name = "binaryFile", + value = "Application installer file.", + required = true) + @Multipart(value = "binaryFile") Attachment binaryFile, + @ApiParam( + name = "icon", + value = "Icon file of the application release.") + @Multipart(value = "icon") Attachment iconFile, + @ApiParam( + name = "banner", + value = "banner file of the application release.") + @Multipart(value = "banner") Attachment bannerFile, + @ApiParam( + name = "screenshot1", + value = "First screenshot of the uploading application") + @Multipart(value = "screenshot1") Attachment screenshot1, + @ApiParam( + name = "screenshot2", + value = "Second screenshot 2 of the uploading application") + @Multipart(value = "screenshot2") Attachment screenshot2, + @ApiParam( + name = "screenshot3", + value = "Third screenshot of the uploading application") + @Multipart(value = "screenshot3") Attachment screenshot3); + @GET @Path("/life-cycle/state-changes/{uuid}") @Produces(MediaType.APPLICATION_JSON) 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 237a99f6d8..89721347c1 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 @@ -29,6 +29,8 @@ import org.wso2.carbon.device.application.mgt.common.response.ApplicationRelease import org.wso2.carbon.device.application.mgt.common.response.Category; import org.wso2.carbon.device.application.mgt.common.response.Tag; import org.wso2.carbon.device.application.mgt.common.services.AppmDataHandler; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppReleaseWrapper; +import org.wso2.carbon.device.application.mgt.common.wrapper.CustomAppWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.EntAppReleaseWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.ApplicationUpdateWrapper; import org.wso2.carbon.device.application.mgt.common.wrapper.ApplicationWrapper; @@ -40,6 +42,7 @@ 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.UnexpectedServerErrorException; import org.wso2.carbon.device.application.mgt.core.util.APIUtil; +import org.wso2.carbon.device.application.mgt.core.util.Constants; import org.wso2.carbon.device.application.mgt.publisher.api.services.ApplicationManagementPublisherAPI; import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManagementException; import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; @@ -180,7 +183,8 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem List attachmentList = constructAttachmentList(screenshot1, screenshot2, screenshot3); try { applicationManager.validateAppCreatingRequest(applicationWrapper); - applicationManager.validateReleaseCreatingRequest(applicationWrapper.getEntAppReleaseWrappers().get(0)); + applicationManager.validateReleaseCreatingRequest(applicationWrapper.getEntAppReleaseWrappers().get(0), + applicationWrapper.getDeviceType()); applicationManager.validateBinaryArtifact(binaryFile); applicationManager.validateImageArtifacts(iconFile, bannerFile, attachmentList); @@ -195,11 +199,11 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } } catch (ApplicationManagementException e) { - String msg = "Error occurred while creating the application"; + String msg = "Error occurred while creating the ent. application"; log.error(msg, e); return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); } catch (RequestValidatingException e) { - String msg = "Error occurred while handling the application creating request"; + String msg = "Error occurred while handling the ent. application creating request"; log.error(msg, e); return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); } @@ -219,7 +223,8 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem List attachmentList = constructAttachmentList(screenshot1, screenshot2, screenshot3); try { applicationManager.validateAppCreatingRequest(webAppWrapper); - applicationManager.validateReleaseCreatingRequest(webAppWrapper.getWebAppReleaseWrappers().get(0)); + applicationManager + .validateReleaseCreatingRequest(webAppWrapper.getWebAppReleaseWrappers().get(0), Constants.ANY); applicationManager.validateImageArtifacts(iconFile, bannerFile, attachmentList); // Created new Web App @@ -257,7 +262,8 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem List attachmentList = constructAttachmentList(screenshot1, screenshot2, screenshot3); try { applicationManager.validateAppCreatingRequest(publicAppWrapper); - applicationManager.validateReleaseCreatingRequest(publicAppWrapper.getPublicAppReleaseWrappers().get(0)); + applicationManager.validateReleaseCreatingRequest(publicAppWrapper.getPublicAppReleaseWrappers().get(0), + publicAppWrapper.getDeviceType()); applicationManager.validateImageArtifacts(iconFile, bannerFile, attachmentList); // Created new Public App @@ -283,8 +289,50 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem @POST @Consumes({"multipart/mixed", MediaType.MULTIPART_FORM_DATA}) - @Path("/ent-app/{appId}") + @Path("/custom-app") + public Response createCustomApp( + @Multipart("application") CustomAppWrapper customAppWrapper, + @Multipart("binaryFile") Attachment binaryFile, + @Multipart("icon") Attachment iconFile, + @Multipart(value = "banner", required = false) Attachment bannerFile, + @Multipart("screenshot1") Attachment screenshot1, + @Multipart("screenshot2") Attachment screenshot2, + @Multipart("screenshot3") Attachment screenshot3) { + ApplicationManager applicationManager = APIUtil.getApplicationManager(); + List attachmentList = constructAttachmentList(screenshot1, screenshot2, screenshot3); + try { + applicationManager.validateAppCreatingRequest(customAppWrapper); + applicationManager.validateReleaseCreatingRequest(customAppWrapper.getCustomAppReleaseWrappers().get(0), + customAppWrapper.getDeviceType()); + applicationManager.validateBinaryArtifact(binaryFile); + applicationManager.validateImageArtifacts(iconFile, bannerFile, attachmentList); + + // Created new Ent App + Application application = applicationManager.createCustomApp(customAppWrapper, + constructApplicationArtifact(binaryFile, iconFile, bannerFile, attachmentList)); + if (application != null) { + return Response.status(Response.Status.CREATED).entity(application).build(); + } else { + String msg = "Application creation is failed"; + log.error(msg); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + } catch (ApplicationManagementException e) { + String msg = "Error occurred while creating the application"; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } catch (RequestValidatingException e) { + String msg = "Error occurred while handling the application creating request"; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } + } + + @POST + @Consumes({"multipart/mixed", MediaType.MULTIPART_FORM_DATA}) + @Path("/{deviceType}/ent-app/{appId}") public Response createEntAppRelease( + @PathParam("deviceType") String deviceType, @PathParam("appId") int appId, @Multipart("applicationRelease") EntAppReleaseWrapper entAppReleaseWrapper, @Multipart("binaryFile") Attachment binaryFile, @@ -296,7 +344,7 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem ApplicationManager applicationManager = APIUtil.getApplicationManager(); List attachmentList = constructAttachmentList(screenshot1, screenshot2, screenshot3); try { - applicationManager.validateReleaseCreatingRequest(entAppReleaseWrapper); + applicationManager.validateReleaseCreatingRequest(entAppReleaseWrapper, deviceType); applicationManager.validateBinaryArtifact(binaryFile); applicationManager.validateImageArtifacts(iconFile, bannerFile, attachmentList); @@ -544,6 +592,52 @@ public class ApplicationManagementPublisherAPIImpl implements ApplicationManagem } } + @Override + @PUT + @Path("/custom-app-release/{uuid}") + public Response updateCustomAppRelease( + @PathParam("uuid") String applicationUUID, + @Multipart("applicationRelease") CustomAppReleaseWrapper customAppReleaseWrapper, + @Multipart(value = "binaryFile", required = false) Attachment binaryFile, + @Multipart(value = "icon", required = false) Attachment iconFile, + @Multipart(value = "banner", required = false) Attachment bannerFile, + @Multipart(value = "screenshot1", required = false) Attachment screenshot1, + @Multipart(value = "screenshot2", required = false) Attachment screenshot2, + @Multipart(value = "screenshot3", required = false) Attachment screenshot3) { + ApplicationManager applicationManager = APIUtil.getApplicationManager(); + List screenshots = constructAttachmentList(screenshot1, screenshot2, screenshot3); + try { + ApplicationRelease applicationRelease = applicationManager + .updateCustomAppRelease(applicationUUID, customAppReleaseWrapper, + constructApplicationArtifact(binaryFile, iconFile, bannerFile, screenshots)); + if (applicationRelease == null) { + String msg ="Custom app release updating is failed. Please contact the administrator. Application " + + "release UUID: " + applicationUUID; + log.error(msg); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + return Response.status(Response.Status.OK).entity(applicationRelease).build(); + } catch (BadRequestException e) { + String msg = + "Invalid request to update ent app release for application release UUID " + applicationUUID; + log.error(msg, e); + return Response.status(Response.Status.BAD_REQUEST).entity(msg).build(); + } catch (NotFoundException e) { + String msg = + "Couldn't found an ent app or ent app release for application release UUID " + applicationUUID; + log.error(msg, e); + return Response.status(Response.Status.NOT_FOUND).entity(msg).build(); + } catch (ForbiddenException e) { + String msg = "You don't have require permission to update the ent app release which has UUID " + + applicationUUID; + log.error(msg, e); + return Response.status(Response.Status.FORBIDDEN).entity(msg).build(); + } catch (ApplicationManagementException e) { + String msg = "Error occurred while updating the ent app release which has UUID " + applicationUUID; + log.error(msg, e); + return Response.status(Response.Status.INTERNAL_SERVER_ERROR).entity(msg).build(); + } + } @PUT @Path("/retire/{appId}") diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js index de0521b61e..791cde970d 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/Filters.js @@ -291,6 +291,7 @@ class FiltersForm extends React.Component { + )} diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/edit-release/EditRelease.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/edit-release/EditRelease.js index 0cc5c8e880..cd5708c845 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/edit-release/EditRelease.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/release/edit-release/EditRelease.js @@ -92,6 +92,20 @@ class EditReleaseModal extends React.Component { } }; break; + case "CUSTOM": + formConfig.endpoint = "/custom-app-release"; + formConfig.specificElements = { + binaryFile: { + required: true + }, + packageName: { + required: true + }, + version: { + required: true + } + }; + break; } this.setState({ diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/index.js index 3e106016ed..998a140937 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/index.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/index.js @@ -30,6 +30,7 @@ import './index.css'; import AddNewPublicApp from "./pages/dashboard/add-new-app/AddNewPublicApp"; import AddNewWebClip from "./pages/dashboard/add-new-app/AddNewWebClip"; import AddNewRelease from "./pages/dashboard/add-new-release/AddNewRelease"; +import AddNewCustomApp from "./pages/dashboard/add-new-app/AddNewCustomApp"; const routes = [ @@ -73,6 +74,11 @@ const routes = [ component: AddNewWebClip, exact: true }, + { + path: '/publisher/add-new-app/custom-app', + component: AddNewCustomApp, + exact: true + }, { path: '/publisher/manage', component: Mange, diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/Dashboard.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/Dashboard.js index 3639c4e4f7..009735c7fb 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/Dashboard.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/Dashboard.js @@ -67,6 +67,8 @@ class Dashboard extends React.Component { APP Web Clip + Custom + App Manage diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/add-new-app/AddNewCustomApp.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/add-new-app/AddNewCustomApp.js new file mode 100644 index 0000000000..8cc1bdb4d4 --- /dev/null +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/add-new-app/AddNewCustomApp.js @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2019, Entgra (pvt) Ltd. (http://entgra.io) All Rights Reserved. + * + * Entgra (pvt) Ltd. licenses this file to you under the Apache License, + * Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +import React from "react"; +import { + PageHeader, + Typography, + Breadcrumb, + Icon +} from "antd"; +import AddNewAppForm from "../../../components/new-app/AddNewAppForm"; +import {Link} from "react-router-dom"; + +const {Paragraph} = Typography; + +const formConfig = { + installationType: "CUSTOM", + endpoint: "/custom-app", + jsonPayloadName: "application", + releaseWrapperName: "customAppReleaseWrappers", + specificElements: { + binaryFile: { + required: true + }, + packageName : { + required: true + }, + version : { + required: true + } + } +}; + +class AddNewCustomApp extends React.Component { + + constructor(props) { + super(props); + this.state = { + current: 0, + categories: [] + }; + } + + render() { + return ( +
+ + + + Home + + Add New Custom App + +
+

Add New Custom App

+ Submit and share your own application to the corporate app store. +
+
+
+ +
+ +
+ + ); + } +} + +export default AddNewCustomApp; diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/beans/ApplicationWrapper.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/beans/ApplicationWrapper.java index 10d2953782..340386ccd2 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/beans/ApplicationWrapper.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/beans/ApplicationWrapper.java @@ -21,7 +21,7 @@ package org.wso2.carbon.device.mgt.jaxrs.beans; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; import org.wso2.carbon.device.mgt.common.DeviceIdentifier; -import org.wso2.carbon.device.mgt.common.app.mgt.MobileApp; +import org.wso2.carbon.device.mgt.common.app.mgt.App; import java.util.List; @@ -53,13 +53,13 @@ public class ApplicationWrapper { name = "application", value = "Details of the mobile application.", required = true) - private MobileApp application; + private App application; - public MobileApp getApplication() { + public App getApplication() { return application; } - public void setApplication(MobileApp application) { + public void setApplication(App application) { this.application = application; } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/admin/ApplicationManagementAdminServiceImpl.java b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/admin/ApplicationManagementAdminServiceImpl.java index f42ebe4591..d942c77e7b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/admin/ApplicationManagementAdminServiceImpl.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.api/src/main/java/org/wso2/carbon/device/mgt/jaxrs/service/impl/admin/ApplicationManagementAdminServiceImpl.java @@ -34,7 +34,7 @@ import org.wso2.carbon.device.mgt.jaxrs.util.DeviceMgtAPIUtils; import org.wso2.carbon.device.mgt.core.util.MDMAndroidOperationUtil; import org.wso2.carbon.device.mgt.core.util.MDMIOSOperationUtil; import org.wso2.carbon.device.mgt.jaxrs.beans.ApplicationWrapper; -import org.wso2.carbon.device.mgt.common.app.mgt.MobileApp; +import org.wso2.carbon.device.mgt.common.app.mgt.App; import javax.ws.rs.Consumes; import javax.ws.rs.POST; @@ -61,15 +61,15 @@ public class ApplicationManagementAdminServiceImpl implements ApplicationManagem RequestValidationUtil.validateApplicationInstallationContext(applicationWrapper); try { appManagerConnector = DeviceMgtAPIUtils.getAppManagementService(); - MobileApp mobileApp = applicationWrapper.getApplication(); + App app = applicationWrapper.getApplication(); if (applicationWrapper.getDeviceIdentifiers() != null) { for (DeviceIdentifier deviceIdentifier : applicationWrapper.getDeviceIdentifiers()) { String deviceType = deviceIdentifier.getType().toUpperCase(); if (Platform.ANDROID.toString().equals(deviceType)) { - operation = MDMAndroidOperationUtil.createInstallAppOperation(mobileApp); + operation = MDMAndroidOperationUtil.createInstallAppOperation(app); } else if (Platform.IOS.toString().equals(deviceType)) { - operation = MDMIOSOperationUtil.createInstallAppOperation(mobileApp); + operation = MDMIOSOperationUtil.createInstallAppOperation(app); } } if (applicationWrapper.getRoleNameList() != null && applicationWrapper.getRoleNameList().size() > 0) { @@ -111,15 +111,15 @@ public class ApplicationManagementAdminServiceImpl implements ApplicationManagem RequestValidationUtil.validateApplicationInstallationContext(applicationWrapper); try { appManagerConnector = DeviceMgtAPIUtils.getAppManagementService(); - MobileApp mobileApp = applicationWrapper.getApplication(); + App app = applicationWrapper.getApplication(); if (applicationWrapper.getDeviceIdentifiers() != null) { for (DeviceIdentifier deviceIdentifier : applicationWrapper.getDeviceIdentifiers()) { String deviceType = deviceIdentifier.getType().toUpperCase(); if (Platform.ANDROID.toString().equals(deviceType)) { - operation = MDMAndroidOperationUtil.createAppUninstallOperation(mobileApp); + operation = MDMAndroidOperationUtil.createAppUninstallOperation(app); } else if (deviceType.equals(Platform.IOS.toString())) { - operation = MDMIOSOperationUtil.createAppUninstallOperation(mobileApp); + operation = MDMIOSOperationUtil.createAppUninstallOperation(app); } } if (applicationWrapper.getRoleNameList() != null && applicationWrapper.getRoleNameList().size() > 0) { diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/MobileApp.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/App.java similarity index 98% rename from components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/MobileApp.java rename to components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/App.java index 0e85d4bafe..a7a9d95c6b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/MobileApp.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/App.java @@ -20,7 +20,6 @@ package org.wso2.carbon.device.mgt.common.app.mgt; import io.swagger.annotations.ApiModel; import io.swagger.annotations.ApiModelProperty; -import org.wso2.carbon.device.mgt.common.app.mgt.MobileAppTypes; import java.util.Properties; @@ -29,7 +28,7 @@ import java.util.Properties; * which is used by AppM. */ @ApiModel(value = "MobileApp", description = "Details of a mobile application.") -public class MobileApp { +public class App { @ApiModelProperty(name = "id", value = "Id of the app used internally.", required = true) private String id; @@ -153,5 +152,4 @@ public class MobileApp { public void setProperties(Properties properties) { this.properties = properties; } - } diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/CustomApplication.java b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/CustomApplication.java new file mode 100644 index 0000000000..c7ec81f4a4 --- /dev/null +++ b/components/device-mgt/org.wso2.carbon.device.mgt.common/src/main/java/org/wso2/carbon/device/mgt/common/app/mgt/android/CustomApplication.java @@ -0,0 +1,62 @@ +/* Copyright (c) 2019, 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 org.wso2.carbon.device.mgt.common.app.mgt.android; + +import com.google.gson.Gson; + +import java.io.Serializable; + +/** + * This class represents the Enterprise AuthenticationImpl information. + */ +public class CustomApplication implements Serializable { + + private String type; + private String url; + private String appIdentifier; + + public String getAppIdentifier() { + return appIdentifier; + } + + public void setAppIdentifier(String appIdentifier) { + this.appIdentifier = appIdentifier; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String toJSON() { + Gson gson = new Gson(); + return gson.toJson(this); + } + +} diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMAndroidOperationUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMAndroidOperationUtil.java index cce871d009..37adcde87b 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMAndroidOperationUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMAndroidOperationUtil.java @@ -24,7 +24,7 @@ import org.wso2.carbon.device.mgt.common.app.mgt.android.EnterpriseApplication; import org.wso2.carbon.device.mgt.common.app.mgt.android.WebApplication; import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; import org.wso2.carbon.device.mgt.core.operation.mgt.ProfileOperation; -import org.wso2.carbon.device.mgt.common.app.mgt.MobileApp; +import org.wso2.carbon.device.mgt.common.app.mgt.App; import org.wso2.carbon.device.mgt.common.exceptions.UnknownApplicationTypeException; /** @@ -40,7 +40,7 @@ public class MDMAndroidOperationUtil { * @return operation * */ - public static Operation createInstallAppOperation(MobileApp application) throws UnknownApplicationTypeException { + public static Operation createInstallAppOperation(App application) throws UnknownApplicationTypeException { ProfileOperation operation = new ProfileOperation(); operation.setCode(MDMAppConstants.AndroidConstants.OPCODE_INSTALL_APPLICATION); @@ -79,7 +79,7 @@ public class MDMAndroidOperationUtil { * @param application MobileApp application * @return operation */ - public static Operation createAppUninstallOperation(MobileApp application) throws UnknownApplicationTypeException { + public static Operation createAppUninstallOperation(App application) throws UnknownApplicationTypeException { ProfileOperation operation = new ProfileOperation(); operation.setCode(MDMAppConstants.AndroidConstants.OPCODE_UNINSTALL_APPLICATION); diff --git a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMIOSOperationUtil.java b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMIOSOperationUtil.java index fb14f72ed6..d769cc6e64 100644 --- a/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMIOSOperationUtil.java +++ b/components/device-mgt/org.wso2.carbon.device.mgt.core/src/main/java/org/wso2/carbon/device/mgt/core/util/MDMIOSOperationUtil.java @@ -23,7 +23,7 @@ import org.wso2.carbon.device.mgt.common.app.mgt.ios.AppStoreApplication; import org.wso2.carbon.device.mgt.common.app.mgt.ios.EnterpriseApplication; import org.wso2.carbon.device.mgt.common.app.mgt.ios.RemoveApplication; import org.wso2.carbon.device.mgt.common.app.mgt.ios.WebClip; -import org.wso2.carbon.device.mgt.common.app.mgt.MobileApp; +import org.wso2.carbon.device.mgt.common.app.mgt.App; import org.wso2.carbon.device.mgt.common.operation.mgt.Operation; import org.wso2.carbon.device.mgt.core.operation.mgt.ProfileOperation; @@ -40,7 +40,7 @@ public class MDMIOSOperationUtil { * @param application MobileApp application * @return operation */ - public static Operation createInstallAppOperation(MobileApp application) { + public static Operation createInstallAppOperation(App application) { ProfileOperation operation = new ProfileOperation(); @@ -90,7 +90,7 @@ public class MDMIOSOperationUtil { return operation; } - public static Operation createAppUninstallOperation(MobileApp application) { + public static Operation createAppUninstallOperation(App application) { ProfileOperation operation = new ProfileOperation(); operation.setCode(MDMAppConstants.IOSConstants.OPCODE_REMOVE_APPLICATION); From c204e6f9b76927915b269f5bd8361d15a855fcc8 Mon Sep 17 00:00:00 2001 From: Jayasanka Weerasinghe Date: Sun, 15 Sep 2019 14:26:53 +0000 Subject: [PATCH 2/2] Fix server starting issue This issue is occurred as a result of having java8 codes in the source. Hence remove and replace the code. --- ...ApplicationManagementPublisherAPIImpl.java | 1 + .../react-app/public/conf/config.json | 24 +++++++++++++++++++ .../AppDetailsDrawer/AppDetailsDrawer.js | 2 +- .../new-app/subForms/NewAppDetailsForm.js | 21 +++++++++++++--- .../new-app/subForms/NewAppUploadForm.js | 2 +- .../components/new-release/AddReleaseForm.js | 4 ++-- .../react-app/src/index.js | 2 +- .../add-new-release/AddNewRelease.js | 6 ++--- 8 files changed, 51 insertions(+), 11 deletions(-) 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 89721347c1..163ddc84a1 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 @@ -48,6 +48,7 @@ import org.wso2.carbon.device.application.mgt.common.exception.ApplicationManage import org.wso2.carbon.device.application.mgt.common.services.ApplicationManager; import org.wso2.carbon.device.application.mgt.core.exception.NotFoundException; +import java.beans.Customizer; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; 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 61464fb6f4..a79fbbc55a 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 @@ -66,5 +66,29 @@ "RETIRED": { "text": "The final state of an application, where no transition of states will be allowed after this." } + }, + "installationTypes": { + "ENTERPRISE": { + "deviceTypes": [ + "android", + "ios" + ] + }, + "PUBLIC": { + "deviceTypes": [ + "android", + "ios" + ] + }, + "WEB_CLIP": { + "deviceTypes": [ + "android", + "ios" + ] + }, + "CUSTOM": { + "deviceTypes": [ + ] + } } } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js index 1aa6b0bb91..b7751cf67b 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/apps/list-apps/AppDetailsDrawer/AppDetailsDrawer.js @@ -474,7 +474,7 @@ class AppDetailsDrawer extends React.Component { Releases {/*display add new release only if app type is enterprise*/} {(app.type === "ENTERPRISE") && ( - )} { const config = this.props.context; + const {formConfig} = this.props; + console.log("test"); + + axios.get( window.location.origin + config.serverConfig.invoker.uri + config.serverConfig.invoker.deviceMgt + "/device-types" ).then(res => { if (res.status === 200) { - const deviceTypes = JSON.parse(res.data.data); + const allDeviceTypes = JSON.parse(res.data.data); + const whitelistedDeviceTypes = config.installationTypes[formConfig.installationType].deviceTypes; + const allowedDeviceTypes = []; + allDeviceTypes.forEach(deviceType=>{ + if(whitelistedDeviceTypes.includes(deviceType.name)){ + allowedDeviceTypes.push(deviceType); + } + }); this.setState({ - deviceTypes, + deviceTypes: allowedDeviceTypes, loading: false, }); } + }).catch((error) => { + console.log(error); if (error.hasOwnProperty("response") && error.response.status === 401) { window.location.href = window.location.origin + '/publisher/login'; } else { @@ -179,6 +193,7 @@ class NewAppDetailsForm extends React.Component { const {categories, tags, deviceTypes} = this.state; const {getFieldDecorator} = this.props.form; + return (
diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js index df1d2aab7c..196c058771 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-app/subForms/NewAppUploadForm.js @@ -77,7 +77,7 @@ class NewAppUploadForm extends React.Component { releaseType: releaseType }; - if (formConfig.installationType !== "WEB_CLIP") { + if (formConfig.installationType !== "WEB_CLIP" && formConfig.installationType !== "CUSTOM") { release.supportedOsVersions = "4.0-10.0"; } diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-release/AddReleaseForm.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-release/AddReleaseForm.js index ccb34f69d1..533d360a0a 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-release/AddReleaseForm.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/components/new-release/AddReleaseForm.js @@ -69,7 +69,7 @@ class AddNewReleaseFormComponent extends React.Component { handleSubmit = e => { const config = this.props.context; e.preventDefault(); - const {appId} = this.props; + const {appId, deviceType} = this.props; this.props.form.validateFields((err, values) => { if (!err) { @@ -104,7 +104,7 @@ class AddNewReleaseFormComponent extends React.Component { data.append("applicationRelease", blob); - const url = window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/ent-app/" + appId; + const url = window.location.origin+ config.serverConfig.invoker.uri + config.serverConfig.invoker.publisher + "/applications/"+deviceType+"/ent-app/" + appId; axios.post( url, diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/index.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/index.js index 998a140937..42542fa73b 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/index.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/index.js @@ -55,7 +55,7 @@ const routes = [ component: Release }, { - path: '/publisher/apps/:appId/add-release', + path: '/publisher/apps/:deviceType/:appId/add-release', component: AddNewRelease, exact: true }, diff --git a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/add-new-release/AddNewRelease.js b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/add-new-release/AddNewRelease.js index 3a9a395850..fdc4d3623a 100644 --- a/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/add-new-release/AddNewRelease.js +++ b/components/application-mgt/org.wso2.carbon.device.application.mgt.publisher.ui/react-app/src/pages/dashboard/add-new-release/AddNewRelease.js @@ -39,7 +39,7 @@ class AddNewRelease extends React.Component { } render() { - const {appId} = this.props.match.params; + const {appId, deviceType} = this.props.match.params; return (
@@ -51,11 +51,11 @@ class AddNewRelease extends React.Component {

Add New Release

- Maintain and manage categories and tags here.. + Add new release for the application
- +