diff --git a/bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationIT.java b/bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationIT.java index f96b6f5b421..b703d2fb8df 100644 --- a/bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationIT.java +++ b/bonita-integration-tests/bonita-integration-tests-client/src/test/java/org/bonitasoft/engine/business/application/LivingApplicationIT.java @@ -63,6 +63,35 @@ public void createApplication_returns_application_based_on_ApplicationCreator_in getLivingApplicationAPI().deleteApplication(application.getId()); } + @Test + public void createApplicationLink_returns_application_based_on_ApplicationCreator_information() throws Exception { + //given + final Profile profile = getProfileUser(); + final ApplicationLinkCreator creator = new ApplicationLinkCreator("My-Application", + "My application display name", + "1.0"); + creator.setDescription("This is my application"); + creator.setIconPath("/icon.jpg"); + creator.setProfileId(profile.getId()); + + //when + final ApplicationLink application = getLivingApplicationAPI().createApplicationLink(creator); + + //then + assertThat(application).isNotNull(); + assertThat(application.getToken()).isEqualTo("My-Application"); + assertThat(application.getDisplayName()).isEqualTo("My application display name"); + assertThat(application.getVersion()).isEqualTo("1.0"); + assertThat(application.getId()).isGreaterThan(0); + assertThat(application.getDescription()).isEqualTo("This is my application"); + assertThat(application.getIconPath()).isEqualTo("/icon.jpg"); + assertThat(application.getCreatedBy()).isEqualTo(getUser().getId()); + assertThat(application.getUpdatedBy()).isEqualTo(getUser().getId()); + assertThat(application.getProfileId()).isEqualTo(profile.getId()); + + getLivingApplicationAPI().deleteApplication(application.getId()); + } + @Test public void createApplication_without_profile_should_have_null_profileId() throws Exception { //given @@ -112,6 +141,42 @@ public void updateApplication_should_return_application_up_to_date() throws Exce getLivingApplicationAPI().deleteApplication(application.getId()); } + @Test + public void updateApplicationLink_should_return_application_up_to_date() throws Exception { + //given + final Profile profile = getProfileUser(); + final ApplicationLinkCreator creator = new ApplicationLinkCreator("My-Application", + "My application display name", + "1.0"); + final ApplicationLink application = getLivingApplicationAPI().createApplicationLink(creator); + + final ApplicationLinkUpdater updater = new ApplicationLinkUpdater(); + updater.setToken("My-updated-app"); + updater.setDisplayName("Updated display name"); + updater.setVersion("1.1"); + updater.setDescription("Up description"); + updater.setIconPath("/newIcon.jpg"); + updater.setProfileId(profile.getId()); + updater.setState(ApplicationState.ACTIVATED.name()); + + //when + final ApplicationLink updatedApplication = getLivingApplicationAPI().updateApplicationLink(application.getId(), + updater); + + //then + assertThat(updatedApplication).isNotNull(); + assertThat(updatedApplication.getToken()).isEqualTo("My-updated-app"); + assertThat(updatedApplication.getDisplayName()).isEqualTo("Updated display name"); + assertThat(updatedApplication.getVersion()).isEqualTo("1.1"); + assertThat(updatedApplication.getDescription()).isEqualTo("Up description"); + assertThat(updatedApplication.getIconPath()).isEqualTo("/newIcon.jpg"); + assertThat(updatedApplication.getProfileId()).isEqualTo(profile.getId()); + assertThat(updatedApplication.getState()).isEqualTo(ApplicationState.ACTIVATED.name()); + assertThat(updatedApplication).isEqualTo(getLivingApplicationAPI().getIApplication(application.getId())); + + getLivingApplicationAPI().deleteApplication(application.getId()); + } + @Test public void getApplication_returns_application_with_the_given_id() throws Exception { //given diff --git a/bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationIT.java b/bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationIT.java index 8ac17226adc..4c61f6541af 100644 --- a/bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationIT.java +++ b/bonita-integration-tests/bonita-integration-tests-web/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationIT.java @@ -13,6 +13,7 @@ **/ package org.bonitasoft.web.rest.server.api.application; +import static org.assertj.core.api.Assertions.assertThat; import static org.bonitasoft.web.rest.server.api.page.builder.PageItemBuilder.aPageItem; import static org.junit.Assert.*; import static org.mockito.Mockito.spy; @@ -22,6 +23,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import org.apache.commons.io.IOUtils; import org.bonitasoft.engine.api.ApplicationAPI; @@ -39,7 +41,6 @@ import org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationDataStoreFactory; import org.bonitasoft.web.rest.server.datastore.application.ApplicationDataStoreCreator; import org.bonitasoft.web.test.AbstractConsoleTest; -import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.junit.After; import org.junit.Assert; import org.junit.Test; @@ -99,33 +100,41 @@ private PageItem addPageItemToRepository(final String pageContentName, final byt } @Test - public void add_ApplicationLink_is_forbidden() { + public void should_add_ApplicationLink() { // Given final ApplicationLinkItem linkItem = ApplicationLinkDefinition.get().createItem(); linkItem.setToken("tokenLink"); linkItem.setDisplayName("Link"); linkItem.setVersion("1.0"); linkItem.setProfileId(2L); - linkItem.setState("Activated"); + linkItem.setState("ACTIVATED"); - // When, Then exception - assertThrows("Expected exception: This deprecated API is not supported for application links.", - APIException.class, () -> apiApplication.add(linkItem)); + // When + var createdLink = apiApplication.add(linkItem); + + // Then + Map attributes = new HashMap<>(linkItem.getAttributes().size()); + linkItem.getAttributes().keySet().forEach(k -> attributes.put(k, createdLink.getAttributes().get(k))); + Assert.assertEquals(new HashMap<>(linkItem.getAttributes()), attributes); + assertThat(createdLink.isLink()).isTrue(); } @Test public void should_add_LegacyApplication() throws Exception { // Given - var legacyApp = createLegacyApplication(); + var pair = createLegacyApplicationWithOriginal(); + var original = pair.getKey(); + var legacyApp = pair.getValue(); // Then - Map attributes = new HashMap<>(legacyApp.getAttributes().size()); - legacyApp.getAttributes().keySet().forEach(k -> attributes.put(k, legacyApp.getAttributes().get(k))); - Assert.assertEquals(new HashMap<>(legacyApp.getAttributes()), attributes); + Map attributes = new HashMap<>(original.getAttributes().size()); + original.getAttributes().keySet().forEach(k -> attributes.put(k, legacyApp.getAttributes().get(k))); + Assert.assertEquals(new HashMap<>(original.getAttributes()), attributes); + assertThat(legacyApp.isLink()).isFalse(); } @Test - public void update_ApplicationLink_is_forbidden_and_not_effective() throws Exception { + public void should_update_ApplicationLink() throws Exception { // Given getApplicationAPI().importApplications( IOUtils.toByteArray(getClass().getResourceAsStream(APP_LINK_DESCRIPTOR)), @@ -134,12 +143,13 @@ public void update_ApplicationLink_is_forbidden_and_not_effective() throws Excep .search(0, 1, null, null, Collections.singletonMap("token", "app1")).getResults().get(0); Map attributes = Map.of(AbstractApplicationItem.ATTRIBUTE_DISPLAY_NAME, "Link Updated"); - // When, Then exception - assertThrows( - "Expected exception: This deprecated API is not supported for application links.", - APIException.class, () -> apiApplication.update(linkItem.getId(), attributes)); - // Then not updated - assertEquals("Application 1", apiApplication.get(linkItem.getId()).getDisplayName()); + // When + var updatedLink = apiApplication.update(linkItem.getId(), attributes); + + // Then updated + assertEquals("Link Updated", updatedLink.getDisplayName()); + assertEquals("Link Updated", apiApplication.get(linkItem.getId()).getDisplayName()); + assertThat(linkItem.isLink()).isTrue(); } @Test @@ -153,6 +163,7 @@ public void should_update_LegacyApplication() throws Exception { // Then assertEquals("Legacy Updated", updatedItem.getDisplayName()); + assertThat(updatedItem.isLink()).isFalse(); } @Test @@ -172,7 +183,17 @@ public void should_search_not_support_filtering_on_ApplicationLinks() throws Exc BonitaRuntimeException.class, () -> apiApplication.search(0, 1, search, orders, filters)); } - private AbstractApplicationItem createLegacyApplication() throws Exception { + private ApplicationItem createLegacyApplication() throws Exception { + return createLegacyApplicationWithOriginal().getValue(); + } + + /** + * Create a legacy application. + * + * @return a pair with the constructed item as key and the api call result as value. + * @throws Exception exception during creation + */ + private Entry createLegacyApplicationWithOriginal() throws Exception { addPage(HOME_PAGE_ZIP); final PageItem layout = addPage(LAYOUT_ZIP); final PageItem theme = addPage(THEME_ZIP); @@ -185,7 +206,8 @@ private AbstractApplicationItem createLegacyApplication() throws Exception { legacyItem.setLayoutId(layout.getId().toLong()); legacyItem.setThemeId(theme.getId().toLong()); - return apiApplication.add(legacyItem); + return Collections.singletonMap(legacyItem, (ApplicationItem) apiApplication.add(legacyItem)).entrySet() + .iterator().next(); } } diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java index 683df607c8c..1c307fb7fc9 100755 --- a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java +++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/api/ApplicationAPI.java @@ -18,6 +18,9 @@ import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationCreator; import org.bonitasoft.engine.business.application.ApplicationImportPolicy; +import org.bonitasoft.engine.business.application.ApplicationLink; +import org.bonitasoft.engine.business.application.ApplicationLinkCreator; +import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationMenu; import org.bonitasoft.engine.business.application.ApplicationMenuCreator; import org.bonitasoft.engine.business.application.ApplicationMenuNotFoundException; @@ -64,6 +67,22 @@ public interface ApplicationAPI { Application createApplication(ApplicationCreator applicationCreator) throws AlreadyExistsException, CreationException; + /** + * Creates a new {@link ApplicationLink} based on the supplied {@link ApplicationLinkCreator} + * + * @param applicationLinkCreator creator describing characteristics of application link to be created + * @return the created ApplicationLink + * @throws AlreadyExistsException if an application already exists with the same name + * @throws CreationException if an error occurs during the creation + * @see ApplicationLink + * @see ApplicationLinkCreator + * @deprecated as of 9.0.0, Applications should be created at startup. This also concerns application links + * introduced in 10.2.0. + */ + @Deprecated(since = "10.2.0") + ApplicationLink createApplicationLink(ApplicationLinkCreator applicationLinkCreator) + throws AlreadyExistsException, CreationException; + /** * Retrieves an {@link Application} from its identifier. * @@ -142,7 +161,7 @@ default Application getApplicationByToken(final String applicationToken) throws * @param updater an ApplicationUpdater describing the fields to be updated. * @return the Application as it is after the update. * @throws ApplicationNotFoundException if no Application is found for the given id - * @throws AlreadyExistsException if another Application already exists with the new name value + * @throws AlreadyExistsException if another IApplication already exists with the new name value * @throws UpdateException if an error occurs during the update * @see Application * @see ApplicationUpdater @@ -152,6 +171,24 @@ default Application getApplicationByToken(final String applicationToken) throws Application updateApplication(long applicationId, ApplicationUpdater updater) throws ApplicationNotFoundException, UpdateException, AlreadyExistsException; + /** + * Updates an {@link ApplicationLink} based on the information supplied by the {@link ApplicationLinkUpdater} + * + * @param applicationId a long representing the application identifier + * @param updater an ApplicationLinkUpdater describing the fields to be updated. + * @return the ApplicationLink as it is after the update. + * @throws ApplicationNotFoundException if no ApplicationLink is found for the given id + * @throws AlreadyExistsException if another IApplication already exists with the new name value + * @throws UpdateException if an error occurs during the update + * @see ApplicationLink + * @see ApplicationLinkUpdater + * @deprecated as of 9.0.0, Applications should be updated at startup. This also concerns application links + * introduced in 10.2.0. + */ + @Deprecated(since = "10.2.0") + ApplicationLink updateApplicationLink(long applicationId, ApplicationLinkUpdater updater) + throws ApplicationNotFoundException, UpdateException, AlreadyExistsException; + /** * Searches for {@link Application}s with specific search criteria. Use * {@link org.bonitasoft.engine.business.application.ApplicationSearchDescriptor} to diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationCreator.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationCreator.java new file mode 100644 index 00000000000..63f2060af14 --- /dev/null +++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationCreator.java @@ -0,0 +1,165 @@ +/** + * Copyright (C) 2019 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.business.application; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.bonitasoft.engine.profile.Profile; + +/** + * Describes the information about an {@link IApplication} to be created + * + * @see IApplication + * @deprecated This class should no longer be used. Since 9.0.0, Applications should be created at startup. + * @param the concrete subtype of {@link AbstractApplicationCreator} (for returning self, subclasses must ensure to + * use the concrete instance) + */ +@Deprecated(since = "10.2.0") +public abstract class AbstractApplicationCreator> implements Serializable { + + private static final long serialVersionUID = -8837280485050745580L; + + private final Map fields; + + /** + * Creates an instance of ApplicationCreator containing mandatory information. + *

The created {@link Application} will used the default layout.

+ * + * @param token the {@code Application} token. The token will be part of application URL. It cannot be null or empty + * and should contain only alpha numeric + * characters and the following special characters '-', '.', '_' or '~'. In addition, the following words are + * reserved key words and cannot be used + * as token: 'api', 'content', 'theme'. + * @param displayName the Application display name. It cannot be null or empty + * @param version the Application version + * @see Application + */ + public AbstractApplicationCreator(final String token, final String displayName, final String version) { + fields = new HashMap<>(3); + fields.put(ApplicationField.TOKEN, token); + fields.put(ApplicationField.VERSION, version); + fields.put(ApplicationField.DISPLAY_NAME, displayName); + } + + /** + * Retrieves the {@link Application} token + * + * @return the Application token + * @see Application + */ + public String getToken() { + return (String) fields.get(ApplicationField.TOKEN); + } + + /** + * Defines the {@link Application} description and returns the current ApplicationCreator + * + * @param description the Application description + * @return the current ApplicationCreator + * @see Application + */ + public T setDescription(final String description) { + fields.put(ApplicationField.DESCRIPTION, description); + return (T) this; + } + + /** + * Defines the {@link Application} icon path and returns the current ApplicationCreator + * + * @param iconPath the Application icon path + * @return the current ApplicationCreator + * @see Application + * @deprecated since 7.13.0, use {@link #setIcon(String, byte[])} + */ + @Deprecated(since = "7.13.0") + public T setIconPath(final String iconPath) { + fields.put(ApplicationField.ICON_PATH, iconPath); + return (T) this; + } + + /** + * Defines the icon for the {@link Application}. + * + * @param fileName of the icon + * @param content of the icon + * @return the current builder + * @since 7.13.0 + */ + public T setIcon(String fileName, byte[] content) { + fields.put(ApplicationField.ICON_FILE_NAME, fileName); + fields.put(ApplicationField.ICON_CONTENT, content); + return (T) this; + } + + /** + * Defines the identifier of the {@link Profile} related to this {@link Application} and returns the current + * ApplicationCreator + * + * @param profileId the Profile identifier + * @return the current ApplicationCreator + * @see Application + * @see Profile + */ + public T setProfileId(final Long profileId) { + fields.put(ApplicationField.PROFILE_ID, profileId); + return (T) this; + } + + /** + * Retrieves all fields defined in this ApplicationCreator + * + * @return a {@link Map}<{@link ApplicationField}, {@link Serializable}> containing all fields defined in this + * ApplicationCreator + * @see ApplicationField + */ + public Map getFields() { + return fields; + } + + /** Test whether application is an application link. */ + public abstract boolean isLink(); + + @Override + public int hashCode() { + final int prime = 31; + int result = getClass().hashCode(); + result = prime * result + (fields == null ? 0 : fields.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final AbstractApplicationCreator other = (AbstractApplicationCreator) obj; + if (fields == null) { + if (other.fields != null) { + return false; + } + } else if (!fields.equals(other.fields)) { + return false; + } + return true; + } + +} diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationUpdater.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationUpdater.java new file mode 100644 index 00000000000..7fed447331f --- /dev/null +++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/AbstractApplicationUpdater.java @@ -0,0 +1,167 @@ +/** + * Copyright (C) 2019 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.business.application; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.Map; + +import org.bonitasoft.engine.profile.Profile; + +/** + * Allows to define which {@link IApplication} fields will be updated + * + * @see IApplication + * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup. + * @param the concrete subtype of {@link AbstractApplicationUpdater} (for returning self, subclasses must ensure to + * use the concrete instance) + */ +@Deprecated(since = "10.2.0") +public abstract class AbstractApplicationUpdater> implements Serializable { + + private static final long serialVersionUID = -5301609836484089900L; + + private final Map fields; + + /** + * Creates an instance of ApplicationUpdater + */ + public AbstractApplicationUpdater() { + fields = new HashMap<>(8); + } + + /** + * Retrieves all fields to be updated + * + * @return a {@link Map}<{@link ApplicationField}, {@link Serializable}> containing all fields to be updated + * @see ApplicationField + */ + public Map getFields() { + return fields; + } + + /** + * Defines the new value for the {@link IApplication} token. It cannot be empty or null and should contain only + * alpha numeric characters and the following special characters '-', '.', '_' or '~'. + * + * @param token the new value for the {@code Application} token + * @return the current {@code ApplicationUpdater} + * @see IApplication + */ + public T setToken(final String token) { + fields.put(ApplicationField.TOKEN, token); + return (T) this; + } + + /** + * Defines the new value for the {@link IApplication} display name. It cannot be empty or null. + * + * @param displayName the new value for the {@code Application} display name + * @return the current {@code ApplicationUpdater} + * @see IApplication + */ + public T setDisplayName(final String displayName) { + fields.put(ApplicationField.DISPLAY_NAME, displayName); + return (T) this; + } + + /** + * Defines the new value for the {@link IApplication} version + * + * @param version the new value for the {@code Application} version + * @return the current {@code ApplicationUpdater} + * @see IApplication + */ + public T setVersion(final String version) { + fields.put(ApplicationField.VERSION, version); + return (T) this; + } + + /** + * Defines the new value for the {@link IApplication} description + * + * @param description the new value for the {@code Application} description + * @return the current {@code ApplicationUpdater} + * @see IApplication + */ + public T setDescription(final String description) { + fields.put(ApplicationField.DESCRIPTION, description); + return (T) this; + } + + /** + * Defines the new value for the {@link IApplication} icon path + * + * @param iconPath the new value for the {@code Application} icon path + * @return the current {@code ApplicationUpdater} + * @see IApplication + * @deprecated since 7.13.0, use {@link #setIcon(String, byte[])} + */ + @Deprecated(since = "7.13.0") + public T setIconPath(final String iconPath) { + fields.put(ApplicationField.ICON_PATH, iconPath); + return (T) this; + } + + /** + * Defines the new icon for the {@link IApplication}. + *

+ * The icons are accessible using {@link org.bonitasoft.engine.api.ApplicationAPI#getIconOfApplication(long)} + * Calling that method with {@code setIcon(null, null)} will remove the icon. + * + * @param iconFileName of the icon + * @param content of the icon + * @return the current builder + * @since 7.13.0 + */ + public T setIcon(String iconFileName, byte[] content) { + fields.put(ApplicationField.ICON_FILE_NAME, iconFileName); + fields.put(ApplicationField.ICON_CONTENT, content); + return (T) this; + } + + /** + * Defines the new value for the {@link IApplication} state + * + * @param state the new value for the {@code Application} state + * @return the current {@code ApplicationUpdater} + * @see IApplication + */ + public T setState(final String state) { + fields.put(ApplicationField.STATE, state); + return (T) this; + } + + /** + * Defines the identifier of the new {@link Profile} associated to the {@link IApplication} + * + * @param profileId the identifier of {@code Profile} associated to the {@code Application} + * @return the current {@code ApplicationUpdater} + * @see IApplication + * @see Profile + */ + public T setProfileId(final Long profileId) { + fields.put(ApplicationField.PROFILE_ID, profileId); + return (T) this; + } + + /** + * Determines if this updater has at least one field to update + * + * @return true if there is at least one field to update; false otherwise + */ + public boolean hasFields() { + return !getFields().isEmpty(); + } +} diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationCreator.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationCreator.java index 33b264c8fbc..9112014e012 100755 --- a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationCreator.java +++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationCreator.java @@ -13,14 +13,8 @@ **/ package org.bonitasoft.engine.business.application; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import org.bonitasoft.engine.profile.Profile; - /** - * Describes the information about an {@link Application} to be created + * Describes the information about a Legacy {@link Application} to be created * * @author Elias Ricken de Medeiros * @see Application @@ -28,12 +22,10 @@ * @deprecated This class should no longer be used. Since 9.0.0, Applications should be created at startup. */ @Deprecated(since = "10.2.0") -public class ApplicationCreator implements Serializable { +public class ApplicationCreator extends AbstractApplicationCreator { private static final long serialVersionUID = -916041825489100271L; - private final Map fields; - /** * Creates an instance of ApplicationCreator containing mandatory information. *

The created {@link Application} will used the default layout.

@@ -48,115 +40,12 @@ public class ApplicationCreator implements Serializable { * @see Application */ public ApplicationCreator(final String token, final String displayName, final String version) { - fields = new HashMap<>(3); - fields.put(ApplicationField.TOKEN, token); - fields.put(ApplicationField.VERSION, version); - fields.put(ApplicationField.DISPLAY_NAME, displayName); - } - - /** - * Retrieves the {@link Application} token - * - * @return the Application token - * @see Application - */ - public String getToken() { - return (String) fields.get(ApplicationField.TOKEN); - } - - /** - * Defines the {@link Application} description and returns the current ApplicationCreator - * - * @param description the Application description - * @return the current ApplicationCreator - * @see Application - */ - public ApplicationCreator setDescription(final String description) { - fields.put(ApplicationField.DESCRIPTION, description); - return this; - } - - /** - * Defines the {@link Application} icon path and returns the current ApplicationCreator - * - * @param iconPath the Application icon path - * @return the current ApplicationCreator - * @see Application - * @deprecated since 7.13.0, use {@link #setIcon(String, byte[])} - */ - @Deprecated(since = "7.13.0") - public ApplicationCreator setIconPath(final String iconPath) { - fields.put(ApplicationField.ICON_PATH, iconPath); - return this; - } - - /** - * Defines the icon for the {@link Application}. - * - * @param fileName of the icon - * @param content of the icon - * @return the current builder - * @since 7.13.0 - */ - public ApplicationCreator setIcon(String fileName, byte[] content) { - fields.put(ApplicationField.ICON_FILE_NAME, fileName); - fields.put(ApplicationField.ICON_CONTENT, content); - return this; - } - - /** - * Defines the identifier of the {@link Profile} related to this {@link Application} and returns the current - * ApplicationCreator - * - * @param profileId the Profile identifier - * @return the current ApplicationCreator - * @see Application - * @see Profile - */ - public ApplicationCreator setProfileId(final Long profileId) { - fields.put(ApplicationField.PROFILE_ID, profileId); - return this; - } - - /** - * Retrieves all fields defined in this ApplicationCreator - * - * @return a {@link Map}<{@link ApplicationField}, {@link Serializable}> containing all fields defined in this - * ApplicationCreator - * @see ApplicationField - */ - public Map getFields() { - return fields; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (fields == null ? 0 : fields.hashCode()); - return result; + super(token, displayName, version); } @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ApplicationCreator other = (ApplicationCreator) obj; - if (fields == null) { - if (other.fields != null) { - return false; - } - } else if (!fields.equals(other.fields)) { - return false; - } - return true; + public boolean isLink() { + return false; } } diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationField.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationField.java index 8bca8f01c71..d5ce2b8f9f0 100755 --- a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationField.java +++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationField.java @@ -28,67 +28,67 @@ public enum ApplicationField { /** - * References the {@link Application} token + * References the {@link IApplication} token * - * @see Application + * @see IApplication */ TOKEN, /** - * References the {@link Application} display name + * References the {@link IApplication} display name * - * @see Application + * @see IApplication */ DISPLAY_NAME, /** - * References the {@link Application} version + * References the {@link IApplication} version * - * @see Application + * @see IApplication */ VERSION, /** - * References the {@link Application} description + * References the {@link IApplication} description * - * @see Application + * @see IApplication */ DESCRIPTION, /** - * References the {@link Application} icon path + * References the {@link IApplication} icon path * - * @see Application + * @see IApplication * @deprecated since 7.13.0, use {@link #ICON_CONTENT} and {@link #ICON_FILE_NAME} instead */ @Deprecated(since = "7.13.0") ICON_PATH, /** - * byte array content of the icon of the {@link Application} + * byte array content of the icon of the {@link IApplication} * * @since 7.13.0 */ ICON_CONTENT, /** - * Filename of the icon of the {@link Application} + * Filename of the icon of the {@link IApplication} * * @since 7.13.0 */ ICON_FILE_NAME, /** - * References the {@link Application} state + * References the {@link IApplication} state * - * @see Application + * @see IApplication */ STATE, /** - * References the identifier of the {@link Profile} associated to the {@link Application} + * References the identifier of the {@link Profile} associated to the {@link IApplication} * - * @see Application + * @see IApplication * @see Profile */ PROFILE_ID, @@ -99,7 +99,7 @@ public enum ApplicationField { * @see org.bonitasoft.engine.business.application.ApplicationPage * @see org.bonitasoft.engine.business.application.Application */ - HOME_PAGE_ID, + HOME_PAGE_ID(Application.class), /** * References the identifier of the {@link org.bonitasoft.engine.page.Page} defined as the {@link Application} @@ -109,7 +109,7 @@ public enum ApplicationField { * @see org.bonitasoft.engine.business.application.Application * @since 7.0.0 */ - LAYOUT_ID, + LAYOUT_ID(Application.class), /** * References the identifier of the {@link org.bonitasoft.engine.page.Page} defined as the {@link Application} @@ -119,6 +119,37 @@ public enum ApplicationField { * @see org.bonitasoft.engine.business.application.Application * @since 7.0.0 */ - THEME_ID + THEME_ID(Application.class); + + /** The class which support this type of field */ + private Class supportingClass; + + /** + * Private Constructor for fields which are suitable for all application types. + */ + private ApplicationField() { + this(IApplication.class); + } + + /** + * Private Constructor for fields which are suitable only for a particular application type (e.g. Legacy, but not + * Link). + * + * @param appropriateClazz the class which support this type of field. + */ + private ApplicationField(Class appropriateClazz) { + supportingClass = appropriateClazz; + } + + /** + * Test whether this application field is suitable for a particular application type + * + * @param clazz the application type to test (usually {@link Application} for legacy applications of + * {@link ApplicationLink}) + * @return + */ + public boolean isForClass(Class clazz) { + return supportingClass.isAssignableFrom(clazz); + } } diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkCreator.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkCreator.java new file mode 100644 index 00000000000..0cd0493d649 --- /dev/null +++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkCreator.java @@ -0,0 +1,63 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.business.application; + +import java.io.Serializable; +import java.util.Map; + +/** + * Describes the information about an {@link ApplicationLink} to be created + * + * @see ApplicationLink + * @since 10.2.0 + * @deprecated This class should no longer be used. Since 9.0.0, Applications should be created at startup. + */ +@Deprecated(since = "10.2.0") +public class ApplicationLinkCreator extends AbstractApplicationCreator { + + private static final long serialVersionUID = 5045658936235401181L; + private transient Map fieldsCheckedMap; + + /** + * Creates an instance of ApplicationCreator containing mandatory information. + * + * @param token the {@code ApplicationLink} token. The token will be part of application URL. It cannot be null or + * empty and should contain only alpha numeric + * characters and the following special characters '-', '.', '_' or '~'. In addition, the following words are + * reserved key words and cannot be used + * as token: 'api', 'content', 'theme'. + * @param displayName the ApplicationLink display name. It cannot be null or empty + * @param version the ApplicationLink version + * @see ApplicationLink + */ + public ApplicationLinkCreator(final String token, final String displayName, final String version) { + super(token, displayName, version); + } + + @Override + public boolean isLink() { + return true; + } + + @Override + public Map getFields() { + // make sure no field for Legacy Application and not suitable for Application Link will get inserted there + if (fieldsCheckedMap == null) { + fieldsCheckedMap = new CheckedApplicationFieldMap(super.getFields(), + k -> k.isForClass(ApplicationLink.class)); + } + return fieldsCheckedMap; + } + +} diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdater.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdater.java new file mode 100644 index 00000000000..1ea8ca2de97 --- /dev/null +++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdater.java @@ -0,0 +1,41 @@ +/** + * Copyright (C) 2019 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.business.application; + +import java.io.Serializable; +import java.util.Map; + +/** + * Allows to define which {@link ApplicationLink} fields will be updated + * + * @see ApplicationLink + * @since 10.2.0 + * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup. + */ +@Deprecated(since = "10.2.0") +public class ApplicationLinkUpdater extends AbstractApplicationUpdater { + + private static final long serialVersionUID = 1732835829535757371L; + private transient Map fieldsCheckedMap; + + @Override + public Map getFields() { + // make sure no field for Legacy Application and not suitable for Application Link will get inserted there + if (fieldsCheckedMap == null) { + fieldsCheckedMap = new CheckedApplicationFieldMap(super.getFields(), + k -> k.isForClass(ApplicationLink.class)); + } + return fieldsCheckedMap; + } +} diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationUpdater.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationUpdater.java index 8526a95ad78..657117349da 100755 --- a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationUpdater.java +++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/ApplicationUpdater.java @@ -13,12 +13,6 @@ **/ package org.bonitasoft.engine.business.application; -import java.io.Serializable; -import java.util.HashMap; -import java.util.Map; - -import org.bonitasoft.engine.profile.Profile; - /** * Allows to define which {@link Application} fields will be updated * @@ -28,135 +22,10 @@ * @deprecated This class should no longer be used. Since 9.0.0, Applications should be updated at startup. */ @Deprecated(since = "10.2.0") -public class ApplicationUpdater implements Serializable { +public class ApplicationUpdater extends AbstractApplicationUpdater { private static final long serialVersionUID = 4565052647320534796L; - private final Map fields; - - /** - * Creates an instance of ApplicationUpdater - */ - public ApplicationUpdater() { - fields = new HashMap<>(8); - } - - /** - * Retrieves all fields to be updated - * - * @return a {@link Map}<{@link ApplicationField}, {@link Serializable}> containing all fields to be updated - * @see ApplicationField - */ - public Map getFields() { - return fields; - } - - /** - * Defines the new value for the {@link Application} token. It cannot be empty or null and should contain only alpha - * numeric - * characters and the following special characters '-', '.', '_' or '~'. - * - * @param token the new value for the {@code Application} token - * @return the current {@code ApplicationUpdater} - * @see Application - */ - public ApplicationUpdater setToken(final String token) { - fields.put(ApplicationField.TOKEN, token); - return this; - } - - /** - * Defines the new value for the {@link Application} display name. It cannot be empty or null. - * - * @param displayName the new value for the {@code Application} display name - * @return the current {@code ApplicationUpdater} - * @see Application - */ - public ApplicationUpdater setDisplayName(final String displayName) { - fields.put(ApplicationField.DISPLAY_NAME, displayName); - return this; - } - - /** - * Defines the new value for the {@link Application} version - * - * @param version the new value for the {@code Application} version - * @return the current {@code ApplicationUpdater} - * @see Application - */ - public ApplicationUpdater setVersion(final String version) { - fields.put(ApplicationField.VERSION, version); - return this; - } - - /** - * Defines the new value for the {@link Application} description - * - * @param description the new value for the {@code Application} description - * @return the current {@code ApplicationUpdater} - * @see Application - */ - public ApplicationUpdater setDescription(final String description) { - fields.put(ApplicationField.DESCRIPTION, description); - return this; - } - - /** - * Defines the new value for the {@link Application} icon path - * - * @param iconPath the new value for the {@code Application} icon path - * @return the current {@code ApplicationUpdater} - * @see Application - * @deprecated since 7.13.0, use {@link #setIcon(String, byte[])} - */ - @Deprecated(since = "7.13.0") - public ApplicationUpdater setIconPath(final String iconPath) { - fields.put(ApplicationField.ICON_PATH, iconPath); - return this; - } - - /** - * Defines the new icon for the {@link Application}. - *

- * The icons are accessible using {@link org.bonitasoft.engine.api.ApplicationAPI#getIconOfApplication(long)} - * Calling that method with {@code setIcon(null, null)} will remove the icon. - * - * @param iconFileName of the icon - * @param content of the icon - * @return the current builder - * @since 7.13.0 - */ - public ApplicationUpdater setIcon(String iconFileName, byte[] content) { - fields.put(ApplicationField.ICON_FILE_NAME, iconFileName); - fields.put(ApplicationField.ICON_CONTENT, content); - return this; - } - - /** - * Defines the new value for the {@link Application} state - * - * @param state the new value for the {@code Application} state - * @return the current {@code ApplicationUpdater} - * @see Application - */ - public ApplicationUpdater setState(final String state) { - fields.put(ApplicationField.STATE, state); - return this; - } - - /** - * Defines the identifier of the new {@link Profile} associated to the {@link Application} - * - * @param profileId the identifier of {@code Profile} associated to the {@code Application} - * @return the current {@code ApplicationUpdater} - * @see Application - * @see Profile - */ - public ApplicationUpdater setProfileId(final Long profileId) { - fields.put(ApplicationField.PROFILE_ID, profileId); - return this; - } - /** * Defines the identifier of the new {@link org.bonitasoft.engine.business.application.ApplicationPage} defined as * the {@link Application} home page @@ -167,16 +36,7 @@ public ApplicationUpdater setProfileId(final Long profileId) { * @see org.bonitasoft.engine.business.application.ApplicationPage */ public ApplicationUpdater setHomePageId(final Long applicationPageId) { - fields.put(ApplicationField.HOME_PAGE_ID, applicationPageId); + getFields().put(ApplicationField.HOME_PAGE_ID, applicationPageId); return this; } - - /** - * Determines if this updater has at least one field to update - * - * @return true if there is at least one field to update; false otherwise - */ - public boolean hasFields() { - return !getFields().isEmpty(); - } } diff --git a/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/CheckedApplicationFieldMap.java b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/CheckedApplicationFieldMap.java new file mode 100644 index 00000000000..e27b6349399 --- /dev/null +++ b/bpm/bonita-common/src/main/java/org/bonitasoft/engine/business/application/CheckedApplicationFieldMap.java @@ -0,0 +1,107 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.business.application; + +import java.io.Serializable; +import java.text.MessageFormat; +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Predicate; + +/** + * A Map which first checks that the key {@link ApplicationField} is correct. + */ +class CheckedApplicationFieldMap implements Map { + + private final Map m; + private final Predicate isValidKey; + + CheckedApplicationFieldMap(Map m, Predicate isValidKey) { + this.m = Objects.requireNonNull(m); + this.isValidKey = Objects.requireNonNull(isValidKey); + } + + private ApplicationField checkKey(ApplicationField key) { + if (!isValidKey.test(key)) { + throw new IllegalArgumentException( + MessageFormat.format("Attempt to insert {0} in a specialized map which does not support it.", + key.name())); + } + return key; + } + + @Override + public int size() { + return m.size(); + } + + @Override + public boolean isEmpty() { + return m.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return m.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return m.containsValue(value); + } + + @Override + public Serializable get(Object key) { + return m.get(key); + } + + @Override + public Serializable put(ApplicationField key, Serializable value) { + return m.put(checkKey(key), value); + } + + @Override + public Serializable remove(Object key) { + return m.remove(key); + } + + @Override + public void putAll(Map map) { + map.keySet().forEach(this::checkKey); + m.putAll(map); + } + + @Override + public void clear() { + m.clear(); + } + + @Override + public Set keySet() { + return m.keySet(); + } + + @Override + public Collection values() { + return m.values(); + } + + @Override + public Set> entrySet() { + return m.entrySet(); + } + +} diff --git a/bpm/bonita-common/src/test/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdaterTest.java b/bpm/bonita-common/src/test/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdaterTest.java new file mode 100644 index 00000000000..0266ab11f1a --- /dev/null +++ b/bpm/bonita-common/src/test/java/org/bonitasoft/engine/business/application/ApplicationLinkUpdaterTest.java @@ -0,0 +1,29 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.engine.business.application; + +import static org.junit.Assert.assertThrows; + +import org.junit.Test; + +public class ApplicationLinkUpdaterTest { + + @Test + public void should_not_update_home_page_field() { + ApplicationLinkUpdater linkUpdater = new ApplicationLinkUpdater(); + + assertThrows(IllegalArgumentException.class, + () -> linkUpdater.getFields().put(ApplicationField.HOME_PAGE_ID, 2L)); + } +} diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ApplicationAPIImpl.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ApplicationAPIImpl.java index 031099fad69..9f7651e3163 100755 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ApplicationAPIImpl.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/ApplicationAPIImpl.java @@ -72,6 +72,16 @@ public Application createApplication(final ApplicationCreator applicationCreator return getLivingApplicationAPIDelegate().createApplication(applicationCreator); } + /** + * {@inheritDoc} + */ + @Override + @Deprecated(since = "10.2.0") + public ApplicationLink createApplicationLink(final ApplicationLinkCreator applicationLinkCreator) + throws CreationException { + return getLivingApplicationAPIDelegate().createApplicationLink(applicationLinkCreator); + } + private LivingApplicationAPIDelegate getLivingApplicationAPIDelegate() { return new LivingApplicationAPIDelegate(getServiceAccessor(), getApplicationModelConverter(getServiceAccessor().getPageService()), @@ -142,6 +152,17 @@ public Application updateApplication(final long applicationId, final Application return getLivingApplicationAPIDelegate().updateApplication(applicationId, updater); } + /** + * {@inheritDoc} + */ + @Override + @Deprecated(since = "10.2.0") + public ApplicationLink updateApplicationLink(final long applicationId, final ApplicationLinkUpdater updater) + throws ApplicationNotFoundException, UpdateException, + AlreadyExistsException { + return getLivingApplicationAPIDelegate().updateApplicationLink(applicationId, updater); + } + protected ServiceAccessor getServiceAccessor() { try { return ServiceAccessorFactory.getInstance().createServiceAccessor(); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationModelConverter.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationModelConverter.java index a562f9c55b6..eeb78a034dc 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationModelConverter.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/converter/ApplicationModelConverter.java @@ -47,14 +47,16 @@ public ApplicationModelConverter(PageService pageService) { this.pageService = pageService; } - public SApplicationWithIcon buildSApplication(final ApplicationCreator creator, final long creatorUserId) + public SApplicationWithIcon buildSApplication(final AbstractApplicationCreator creator, final long creatorUserId) throws CreationException { final Map fields = creator.getFields(); final String description = (String) fields.get(ApplicationField.DESCRIPTION); final String iconPath = (String) fields.get(ApplicationField.ICON_PATH); final Long profileId = (Long) fields.get(ApplicationField.PROFILE_ID); final long now = System.currentTimeMillis(); + final boolean isLink = creator.isLink(); SApplicationWithIcon application = new SApplicationWithIcon(); + application.setLink(isLink); application.setToken((String) fields.get(ApplicationField.TOKEN)); application.setDisplayName((String) fields.get(ApplicationField.DISPLAY_NAME)); application.setVersion((String) fields.get(ApplicationField.VERSION)); @@ -62,8 +64,10 @@ public SApplicationWithIcon buildSApplication(final ApplicationCreator creator, application.setLastUpdateDate(now); application.setCreatedBy(creatorUserId); application.setState(SApplicationState.ACTIVATED.name()); - application.setLayoutId(getLayoutId(creator)); - application.setThemeId(getThemeId(creator)); + if (creator instanceof ApplicationCreator) { + application.setLayoutId(getLayoutId((ApplicationCreator) creator)); + application.setThemeId(getThemeId((ApplicationCreator) creator)); + } application.setUpdatedBy(creatorUserId); byte[] iconContent = (byte[]) fields.get(ApplicationField.ICON_CONTENT); String iconFileName = (String) fields.get(ApplicationField.ICON_FILE_NAME); @@ -154,7 +158,7 @@ public List toApplication(final List sApplications) return applications; } - public EntityUpdateDescriptor toApplicationUpdateDescriptor(final ApplicationUpdater updater, + public EntityUpdateDescriptor toApplicationUpdateDescriptor(final AbstractApplicationUpdater updater, final long updaterUserId) { final SApplicationUpdateBuilder builder = new SApplicationUpdateBuilder(updaterUserId); updateFields(updater, builder); @@ -162,7 +166,7 @@ public EntityUpdateDescriptor toApplicationUpdateDescriptor(final ApplicationUpd return builder.done(); } - protected void updateFields(final ApplicationUpdater updater, final SApplicationUpdateBuilder builder) { + protected void updateFields(final AbstractApplicationUpdater updater, final SApplicationUpdateBuilder builder) { for (final Entry entry : updater.getFields().entrySet()) { switch (entry.getKey()) { case TOKEN: diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegate.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegate.java index 71fe96f9dff..a983c78a82c 100755 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegate.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegate.java @@ -14,6 +14,7 @@ package org.bonitasoft.engine.api.impl.livingapplication; import java.util.Optional; +import java.util.function.Predicate; import org.bonitasoft.engine.api.impl.converter.ApplicationModelConverter; import org.bonitasoft.engine.business.application.*; @@ -65,7 +66,7 @@ public Application createApplication(final ApplicationCreator applicationCreator return res; } else { // should not occur anyway - throw new CreationException("This deprecated API is not supported for application links."); + throw new CreationException("Use dedicated API for application links."); } } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); @@ -74,7 +75,32 @@ public Application createApplication(final ApplicationCreator applicationCreator } } - private void validateCreator(final ApplicationCreator applicationCreator) throws CreationException { + /** + * @deprecated as of 9.0.0, Applications should be created at startup. This also concerns application links + * introduced in 10.2.0. + */ + @Deprecated(since = "10.2.0") + public ApplicationLink createApplicationLink(final ApplicationLinkCreator applicationLinkCreator) + throws CreationException { + try { + validateCreator(applicationLinkCreator); + final SApplicationWithIcon sApplicationWithIcon = applicationService + .createApplication(converter.buildSApplication(applicationLinkCreator, loggedUserId)); + var converted = converter.toApplication(sApplicationWithIcon); + if (converted instanceof ApplicationLink res) { + return res; + } else { + // should not occur anyway + throw new CreationException("Use dedicated API for legacy applications."); + } + } catch (final SObjectAlreadyExistsException e) { + throw new AlreadyExistsException(e.getMessage()); + } catch (final SBonitaException e) { + throw new CreationException(e); + } + } + + private void validateCreator(final AbstractApplicationCreator applicationCreator) throws CreationException { ValidationStatus validationStatus = tokenValidator.validate(applicationCreator.getToken()); if (!validationStatus.isValid()) { throw new CreationException(validationStatus.getMessage()); @@ -83,6 +109,11 @@ private void validateCreator(final ApplicationCreator applicationCreator) throws if (displayName == null || displayName.trim().isEmpty()) { throw new CreationException("The application display name can not be null or empty"); } + Class appClass = applicationCreator.isLink() ? ApplicationLink.class + : Application.class; + if (!applicationCreator.getFields().keySet().stream().allMatch(k -> k.isForClass(appClass))) { + throw new CreationException("The application fields used must be valid for the application type"); + } } public IApplication getIApplication(final long applicationId) throws ApplicationNotFoundException { @@ -160,7 +191,7 @@ public Application updateApplication(final long applicationId, final Application */ if (Optional.ofNullable(applicationService.getApplicationWithIcon(applicationId)) .filter(AbstractSApplication::isLink).isPresent()) { - throw new UpdateException("This deprecated API is not supported for application links."); + throw new UpdateException("Use dedicated API for application links."); } application = applicationService.updateApplication(applicationId, converter.toApplicationUpdateDescriptor(updater, loggedUserId)); @@ -171,7 +202,7 @@ public Application updateApplication(final long applicationId, final Application if (converted instanceof Application res) { return res; } else { - throw new UpdateException("This deprecated API is not supported for application links."); + throw new UpdateException("Use dedicated API for application links."); } } catch (final SObjectAlreadyExistsException e) { throw new AlreadyExistsException(e.getMessage()); @@ -182,12 +213,59 @@ public Application updateApplication(final long applicationId, final Application } } - private void validateUpdater(final ApplicationUpdater updater) throws UpdateException { + /** + * @deprecated as of 9.0.0, Applications should be updated at startup. This also concerns application links + * introduced in 10.2.0. + */ + @Deprecated(since = "10.2.0") + public ApplicationLink updateApplicationLink(final long applicationId, final ApplicationLinkUpdater updater) + throws UpdateException, + AlreadyExistsException, ApplicationNotFoundException { + try { + validateUpdater(updater); + AbstractSApplication application; + if (!updater.getFields().isEmpty()) { + /* + * This API may be called within our without a transaction. + * So we must check first whether the application is a link to have a consistent behavior + * and never update the legacy application. + */ + Predicate isLink = AbstractSApplication::isLink; + if (Optional.ofNullable(applicationService.getApplicationWithIcon(applicationId)) + .filter(isLink.negate()).isPresent()) { + throw new UpdateException("Use dedicated API for legacy applications."); + } + application = applicationService.updateApplication(applicationId, + converter.toApplicationUpdateDescriptor(updater, loggedUserId)); + } else { + application = applicationService.getApplication(applicationId); + } + var converted = converter.toApplication(application); + if (converted instanceof ApplicationLink res) { + return res; + } else { + throw new UpdateException("Use dedicated API for legacy applications."); + } + } catch (final SObjectAlreadyExistsException e) { + throw new AlreadyExistsException(e.getMessage()); + } catch (final SObjectNotFoundException e) { + throw new ApplicationNotFoundException(applicationId); + } catch (final SBonitaException e) { + throw new UpdateException(e); + } + } + + private void validateUpdater(final AbstractApplicationUpdater updater) throws UpdateException { validateToken(updater); validateDisplayName(updater); + Class appClass = updater instanceof ApplicationLinkUpdater ? ApplicationLink.class + : Application.class; + if (!updater.getFields().keySet().stream().allMatch(k -> k.isForClass(appClass))) { + throw new UpdateException("The application fields used must be valid for the application type"); + } } - private void validateDisplayName(final ApplicationUpdater updater) throws UpdateException { + private void validateDisplayName(final AbstractApplicationUpdater updater) throws UpdateException { if (updater.getFields().keySet().contains(ApplicationField.DISPLAY_NAME)) { final String displayName = (String) updater.getFields().get(ApplicationField.DISPLAY_NAME); if (displayName == null || displayName.trim().isEmpty()) { @@ -196,7 +274,7 @@ private void validateDisplayName(final ApplicationUpdater updater) throws Update } } - private void validateToken(final ApplicationUpdater updater) throws UpdateException { + private void validateToken(final AbstractApplicationUpdater updater) throws UpdateException { if (updater.getFields().keySet().contains(ApplicationField.TOKEN)) { final String token = (String) updater.getFields().get(ApplicationField.TOKEN); ValidationStatus validationStatus = tokenValidator.validate(token); diff --git a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverter.java b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverter.java index c06d1ed311a..4a16d8e07e0 100644 --- a/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverter.java +++ b/bpm/bonita-core/bonita-process-engine/src/main/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverter.java @@ -19,6 +19,8 @@ import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationPage; +import org.bonitasoft.engine.business.application.xml.AbstractApplicationNode; +import org.bonitasoft.engine.business.application.xml.ApplicationLinkNode; import org.bonitasoft.engine.business.application.xml.ApplicationNode; import org.bonitasoft.engine.commons.exceptions.SBonitaException; import org.bonitasoft.engine.commons.exceptions.SObjectNotFoundException; @@ -56,21 +58,28 @@ public ApplicationToNodeConverter(final ProfileService profileService, final App this.pageService = pageService; } - public ApplicationNode toNode(final SApplication application) throws ExportException { + public AbstractApplicationNode toNode(final SApplication application) throws ExportException { try { - final ApplicationNode applicationNode = new ApplicationNode(); + final AbstractApplicationNode applicationNode; + if (application.isLink()) { + applicationNode = new ApplicationLinkNode(); + } else { + ApplicationNode legacyApplicationNode = new ApplicationNode(); + setLayout(application, legacyApplicationNode); + setTheme(application, legacyApplicationNode); + setHomePage(application, legacyApplicationNode); + setPages(application.getId(), legacyApplicationNode); + applicationMenuToNodeConverter.addMenusToApplicationNode(application.getId(), null, + legacyApplicationNode, null); + applicationNode = legacyApplicationNode; + } applicationNode.setToken(application.getToken()); applicationNode.setDisplayName(application.getDisplayName()); applicationNode.setVersion(application.getVersion()); applicationNode.setDescription(application.getDescription()); applicationNode.setState(application.getState()); applicationNode.setIconPath(application.getIconPath()); - setLayout(application, applicationNode); - setTheme(application, applicationNode); setProfile(application, applicationNode); - setHomePage(application, applicationNode); - setPages(application.getId(), applicationNode); - applicationMenuToNodeConverter.addMenusToApplicationNode(application.getId(), null, applicationNode, null); return applicationNode; } catch (SBonitaException e) { throw new ExportException(e); @@ -125,7 +134,7 @@ private void setHomePage(final SApplication application, final ApplicationNode a } } - private void setProfile(final SApplication application, final ApplicationNode applicationNode) + private void setProfile(final SApplication application, final AbstractApplicationNode applicationNode) throws SProfileNotFoundException { if (application.getProfileId() != null) { final SProfile profile = profileService.getProfile(application.getProfileId()); diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegateTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegateTest.java index a958c679fa6..d2af91475de 100755 --- a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegateTest.java +++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/api/impl/livingapplication/LivingApplicationAPIDelegateTest.java @@ -26,11 +26,14 @@ import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationCreator; import org.bonitasoft.engine.business.application.ApplicationLink; +import org.bonitasoft.engine.business.application.ApplicationLinkCreator; +import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.ApplicationUpdater; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.business.application.impl.ApplicationImpl; +import org.bonitasoft.engine.business.application.impl.ApplicationLinkImpl; import org.bonitasoft.engine.business.application.importer.validator.ApplicationTokenValidator; import org.bonitasoft.engine.business.application.importer.validator.ValidationStatus; import org.bonitasoft.engine.business.application.model.SApplication; @@ -131,6 +134,26 @@ public void createApplication_should_call_applicationService_createApplication_a assertThat(createdApplication).isEqualTo(application); } + @Test + public void createApplicationLink_should_call_applicationService_createApplication_and_return_created_application() + throws Exception { + //given + final ApplicationLinkCreator creator = new ApplicationLinkCreator(APP_TOKEN, APP_DISP_NAME, VERSION); + creator.setDescription(DESCRIPTION); + final SApplicationWithIcon sApp = buildDefaultApplicationWithMetadata(); + sApp.setDescription(DESCRIPTION); + final ApplicationLinkImpl applicationLink = new ApplicationLinkImpl(APP_TOKEN, VERSION, DESCRIPTION); + given(converter.buildSApplication(creator, LOGGED_USER_ID)).willReturn(sApp); + given(converter.toApplication(sApp)).willReturn(applicationLink); + given(applicationService.createApplication(sApp)).willReturn(sApp); + + //when + final ApplicationLink createdApplication = delegate.createApplicationLink(creator); + + //then + assertThat(createdApplication).isEqualTo(applicationLink); + } + private SApplication buildDefaultApplication() { return new SApplication(APP_TOKEN, APP_DISP_NAME, VERSION, System.currentTimeMillis(), LOGGED_USER_ID, SApplicationState.DEACTIVATED.name()); @@ -343,7 +366,27 @@ public void updateApplication_should_return_result_of_applicationservice_updateA } @Test - public void updateApplicationLink_should_not_return() throws Exception { + public void updateApplicationLink_should_return_result_of_applicationservice_updateApplication() throws Exception { + //given + final SApplicationWithIcon sApplicationWithIcon = mock(SApplicationWithIcon.class); + final ApplicationLink applicationLink = mock(ApplicationLink.class); + final ApplicationLinkUpdater updater = new ApplicationLinkUpdater(); + updater.setToken("newToken"); + final EntityUpdateDescriptor updateDescriptor = new EntityUpdateDescriptor(); + given(converter.toApplicationUpdateDescriptor(updater, LOGGED_USER_ID)).willReturn(updateDescriptor); + given(applicationService.updateApplication(APPLICATION_ID, updateDescriptor)) + .willReturn(sApplicationWithIcon); + given(converter.toApplication(sApplicationWithIcon)).willReturn(applicationLink); + + //when + final ApplicationLink updatedApplication = delegate.updateApplicationLink(APPLICATION_ID, updater); + + //then + assertThat(updatedApplication).isEqualTo(applicationLink); + } + + @Test + public void updateApplicationLink_should_not_return_when_using_ApplicationUpdater() throws Exception { //given final SApplicationWithIcon sApplicationWithIcon = mock(SApplicationWithIcon.class); final ApplicationLink applicationLink = mock(ApplicationLink.class); diff --git a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverterTest.java b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverterTest.java index c05d9a4111a..94c7861ba07 100644 --- a/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverterTest.java +++ b/bpm/bonita-core/bonita-process-engine/src/test/java/org/bonitasoft/engine/business/application/converter/ApplicationToNodeConverterTest.java @@ -29,6 +29,8 @@ import org.bonitasoft.engine.business.application.ApplicationService; import org.bonitasoft.engine.business.application.model.SApplication; import org.bonitasoft.engine.business.application.model.SApplicationPage; +import org.bonitasoft.engine.business.application.xml.AbstractApplicationNode; +import org.bonitasoft.engine.business.application.xml.ApplicationLinkNode; import org.bonitasoft.engine.business.application.xml.ApplicationMenuNode; import org.bonitasoft.engine.business.application.xml.ApplicationNode; import org.bonitasoft.engine.business.application.xml.ApplicationPageNode; @@ -68,6 +70,22 @@ public class ApplicationToNodeConverterTest { @InjectMocks private ApplicationToNodeConverter converter; + @Test + public void toNode_should_return_application_link() throws Exception { + //given + final SApplication application = new SApplication("app", "my app", "1.0", new Date().getTime(), 10L, + "enabled"); + application.setLink(true); + + //when + final AbstractApplicationNode applicationNode = converter.toNode(application); + + //then + assertThat(applicationNode).isNotNull(); + assertThat(applicationNode).isInstanceOf(ApplicationLinkNode.class); + + } + @Test public void toNode_should_return_convert_all_string_fields() throws Exception { //given @@ -78,7 +96,7 @@ public void toNode_should_return_convert_all_string_fields() throws Exception { application.setIconPath("/icon.jpg"); //when - final ApplicationNode applicationNode = converter.toNode(application); + final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application); //then assertThat(applicationNode).isNotNull(); @@ -104,7 +122,7 @@ public void toNode_should_replace_profile_id_by_profile_name() throws Exception given(profileService.getProfile(7L)).willReturn(profile); //when - final ApplicationNode applicationNode = converter.toNode(application); + final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application); //then assertThat(applicationNode).isNotNull(); @@ -136,7 +154,7 @@ public void toNode_should_replaceHomePageId_by_application_page_token() throws E given(applicationService.getApplicationPage(8L)).willReturn(homePage); //when - final ApplicationNode applicationNode = converter.toNode(application); + final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application); //then assertThat(applicationNode).isNotNull(); @@ -168,7 +186,7 @@ public void toNode_should_replaceLayoutId_by_page_name() throws Exception { given(pageService.getPage(9L)).willReturn(layout); //when - final ApplicationNode applicationNode = converter.toNode(application); + final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application); //then assertThat(applicationNode).isNotNull(); @@ -186,7 +204,7 @@ public void toNode_should_replaceThemeId_by_page_name() throws Exception { given(pageService.getPage(20L)).willReturn(layout); //when - final ApplicationNode applicationNode = converter.toNode(application); + final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application); //then assertThat(applicationNode).isNotNull(); @@ -247,7 +265,7 @@ public void toNodeShouldAddConvertedPages() throws Exception { .willReturn(Collections.emptyList()); //when - final ApplicationNode applicationNode = converter.toNode(application); + final ApplicationNode applicationNode = (ApplicationNode) converter.toNode(application); //then assertThat(applicationNode.getApplicationPages().size()).isEqualTo(1); diff --git a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/AbstractApplicationDefinition.java b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/AbstractApplicationDefinition.java index 058c7625975..66d9e9ef894 100644 --- a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/AbstractApplicationDefinition.java +++ b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/model/application/AbstractApplicationDefinition.java @@ -13,9 +13,19 @@ **/ package org.bonitasoft.web.rest.model.application; +import java.util.Collections; +import java.util.Map; +import java.util.Optional; +import java.util.function.Supplier; + +import org.bonitasoft.web.toolkit.client.common.TreeIndexed; +import org.bonitasoft.web.toolkit.client.common.TreeLeaf; import org.bonitasoft.web.toolkit.client.data.item.Definitions; +import org.bonitasoft.web.toolkit.client.data.item.DiscriminatedItemDefinitionHelper; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; +import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationError; +import org.bonitasoft.web.toolkit.client.data.item.attribute.ValidationException; import org.bonitasoft.web.toolkit.client.data.item.attribute.validator.FileIsImageOrServletPathValidator; /** @@ -66,8 +76,44 @@ public static AbstractApplicationDefinition g @Override protected ITEM _createItem() { - // this might be called by deprecated PUT and POST methods which work only with legacy applications... - return (ITEM) ApplicationDefinition.get()._createItem(); + // this must not be called by deprecated PUT and POST methods which need to discriminate on "link". + throw new ValidationException(Collections.singletonList( + new ValidationError("link", "%attribute% is mandatory to discriminate the application type"))); + } + + @Override + public Optional> getDiscriminatedHelper() { + if (AbstractApplicationDefinition.class.equals(getClass())) { + return Optional.of(new DiscriminatedItemDefinitionHelper() { + + @Override + @SuppressWarnings("unchecked") + public Supplier findItemCreator(Map attributes) { + // We need the "link" attribute to discriminate between legacy application and application link. + boolean isLink = attributes != null + && Boolean.parseBoolean(attributes.get(AbstractApplicationItem.ATTRIBUTE_LINK)); + return isLink ? () -> (ITEM) ApplicationLinkDefinition.get()._createItem() + : () -> (ITEM) ApplicationDefinition.get()._createItem(); + } + + @Override + public Supplier findItemCreator(TreeIndexed tree) { + // We need the "link" attribute to discriminate between legacy application and application link. + boolean isLink; + if (tree != null && tree.get(AbstractApplicationItem.ATTRIBUTE_LINK)instanceof TreeLeaf v) { + isLink = Optional.ofNullable(v.getValue()).map(Object::toString).map(Boolean::parseBoolean) + .orElse(Boolean.FALSE); + } else { + isLink = false; + } + return isLink ? () -> (ITEM) ApplicationLinkDefinition.get()._createItem() + : () -> (ITEM) ApplicationDefinition.get()._createItem(); + } + }); + } else { + // subclasses do not need the helper and use a concrete implementation + return Optional.empty(); + } } } diff --git a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/application/APIApplication.java b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/application/APIApplication.java index 98ca9c89095..d03fb92205a 100644 --- a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/application/APIApplication.java +++ b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/api/application/APIApplication.java @@ -32,7 +32,6 @@ import org.bonitasoft.web.rest.server.framework.api.APIHasSearch; import org.bonitasoft.web.rest.server.framework.api.APIHasUpdate; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; -import org.bonitasoft.web.toolkit.client.common.exception.api.APIException; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.ItemDefinition; @@ -46,7 +45,7 @@ */ public class APIApplication extends ConsoleAPI implements APIHasAdd, APIHasSearch, - APIHasGet, APIHasUpdate, APIHasDelete { + APIHasGet, APIHasUpdate, APIHasDelete { private final ApplicationDataStoreCreator creator; @@ -64,11 +63,7 @@ public APIApplication(final ApplicationDataStoreCreator creator, @Override @Deprecated(since = "9.0.0") public AbstractApplicationItem add(final AbstractApplicationItem item) { - if (item instanceof ApplicationItem legacy) { - return creator.create(getEngineSession()).add(legacy); - } else { - throw new APIException("This deprecated API is not supported for application links."); - } + return creator.create(getEngineSession()).add(item); } /** @@ -76,7 +71,7 @@ public AbstractApplicationItem add(final AbstractApplicationItem item) { */ @Override @Deprecated(since = "9.0.0") - public ApplicationItem update(final APIID id, final Map attributes) { + public AbstractApplicationItem update(final APIID id, final Map attributes) { return creator.create(getEngineSession()).update(id, attributes); } diff --git a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStore.java b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStore.java index 15d0cb02d06..a613effbc76 100644 --- a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStore.java +++ b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStore.java @@ -21,6 +21,9 @@ import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationCreator; +import org.bonitasoft.engine.business.application.ApplicationLink; +import org.bonitasoft.engine.business.application.ApplicationLinkCreator; +import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationUpdater; @@ -33,6 +36,7 @@ import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationDefinition; import org.bonitasoft.web.rest.model.application.ApplicationItem; +import org.bonitasoft.web.rest.model.application.ApplicationLinkItem; import org.bonitasoft.web.rest.server.datastore.CommonDatastore; import org.bonitasoft.web.rest.server.datastore.filter.Filters; import org.bonitasoft.web.rest.server.datastore.utils.SearchOptionsCreator; @@ -51,8 +55,8 @@ * @author Elias Ricken de Medeiros */ public class ApplicationDataStore extends CommonDatastore - implements DatastoreHasAdd, - DatastoreHasUpdate, + implements DatastoreHasAdd, + DatastoreHasUpdate, DatastoreHasGet, DatastoreHasSearch, DatastoreHasDelete { private final ApplicationAPI applicationAPI; @@ -98,6 +102,21 @@ public AbstractApplicationItem get(final APIID id) { */ @Override @Deprecated(since = "9.0.0") + public AbstractApplicationItem add(final AbstractApplicationItem item) { + if (item instanceof ApplicationItem legacy) { + return add(legacy); + } else if (item instanceof ApplicationLinkItem link) { + return add(link); + } else { + // should not occur anyway + throw new APIException("Unknown application type."); + } + } + + /** + * @deprecated as of 9.0.0, Applications should be created at startup. + */ + @Deprecated(since = "9.0.0") public ApplicationItem add(final ApplicationItem item) { try { @@ -114,7 +133,7 @@ public ApplicationItem add(final ApplicationItem item) { return res; } else { // should not occur anyway - throw new APIException("This deprecated API is not supported for application links."); + throw new APIException("Use dedicated API for application links."); } } catch (final BonitaException e) { throw new APIException(e); @@ -122,20 +141,47 @@ public ApplicationItem add(final ApplicationItem item) { } /** - * @deprecated as of 9.0.0, Applications should be updated at startup. + * @deprecated as of 9.0.0, Applications should be created at startup. */ - @Override @Deprecated(since = "9.0.0") - public ApplicationItem update(final APIID id, final Map attributes) { + public ApplicationLinkItem add(final ApplicationLinkItem item) { + try { - final ApplicationUpdater applicationUpdater = converter.toApplicationUpdater(attributes); - final Application application = applicationAPI.updateApplication(id.toLong(), applicationUpdater); + final ApplicationLinkCreator creator = converter.toApplicationLinkCreator(item); + + final ApplicationLink application = applicationAPI.createApplicationLink(creator); var converted = converter.toApplicationItem(application); - if (converted instanceof ApplicationItem res) { + if (converted instanceof ApplicationLinkItem res) { return res; } else { - throw new APIException( - "This deprecated API is not supported for application links."); + // should not occur anyway + throw new APIException("Use dedicated API for legacy applications."); + } + } catch (final BonitaException e) { + throw new APIException(e); + } + } + + /** + * @deprecated as of 9.0.0, Applications should be updated at startup. + */ + @Override + @Deprecated(since = "9.0.0") + public AbstractApplicationItem update(final APIID id, final Map attributes) { + try { + // no way to know the application type without getting it first + IApplication app = applicationAPI.getIApplication(id.toLong()); + if (app instanceof Application) { + final ApplicationUpdater applicationUpdater = converter.toApplicationUpdater(attributes); + final Application application = applicationAPI.updateApplication(id.toLong(), applicationUpdater); + return converter.toApplicationItem(application); + } else if (app instanceof ApplicationLink) { + final ApplicationLinkUpdater applicationUpdater = converter.toApplicationLinkUpdater(attributes); + final ApplicationLink link = applicationAPI.updateApplicationLink(id.toLong(), applicationUpdater); + return converter.toApplicationItem(link); + } else { + // should not occur anyway + throw new APIException("Unknown application type."); } } catch (final BonitaException e) { throw new APIException(e); diff --git a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationItemConverter.java b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationItemConverter.java index e294efa423c..313ec71a0be 100644 --- a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationItemConverter.java +++ b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationItemConverter.java @@ -19,6 +19,8 @@ import org.bonitasoft.console.common.server.utils.IconDescriptor; import org.bonitasoft.engine.business.application.Application; import org.bonitasoft.engine.business.application.ApplicationCreator; +import org.bonitasoft.engine.business.application.ApplicationLinkCreator; +import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationUpdater; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; @@ -98,6 +100,19 @@ public ApplicationCreator toApplicationCreator(final ApplicationItem appItem) { return creator; } + /** + * @deprecated ApplicationCreator should no longer be used. Since 9.0.0, Applications should be created at startup. + */ + @Deprecated(since = "10.2.0") + public ApplicationLinkCreator toApplicationLinkCreator(final ApplicationLinkItem appLinkItem) { + final ApplicationLinkCreator creator = new ApplicationLinkCreator(appLinkItem.getToken(), + appLinkItem.getDisplayName(), + appLinkItem.getVersion()); + creator.setDescription(appLinkItem.getDescription()); + creator.setProfileId(appLinkItem.getProfileId().toLong()); + return creator; + } + /** * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup. */ @@ -142,6 +157,43 @@ public ApplicationUpdater toApplicationUpdater(final Map attribu } + /** + * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup. + */ + @Deprecated(since = "10.2.0") + public ApplicationLinkUpdater toApplicationLinkUpdater(final Map attributes) { + final ApplicationLinkUpdater applicationUpdater = getApplicationLinkUpdater(); + + if (attributes.containsKey(ApplicationItem.ATTRIBUTE_TOKEN)) { + applicationUpdater.setToken(attributes.get(ApplicationItem.ATTRIBUTE_TOKEN)); + } + if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DISPLAY_NAME)) { + applicationUpdater.setDisplayName(attributes.get(ApplicationItem.ATTRIBUTE_DISPLAY_NAME)); + } + if (attributes.containsKey(ApplicationItem.ATTRIBUTE_DESCRIPTION)) { + applicationUpdater.setDescription(attributes.get(ApplicationItem.ATTRIBUTE_DESCRIPTION)); + } + if (attributes.containsKey(ApplicationItem.ATTRIBUTE_PROFILE_ID)) { + applicationUpdater.setProfileId(Long.parseLong(attributes.get(ApplicationItem.ATTRIBUTE_PROFILE_ID))); + } + + if (attributes.containsKey(ApplicationItem.ATTRIBUTE_STATE)) { + applicationUpdater.setState(attributes.get(ApplicationItem.ATTRIBUTE_STATE)); + } + if (attributes.containsKey(ApplicationItem.ATTRIBUTE_VERSION)) { + applicationUpdater.setVersion(attributes.get(ApplicationItem.ATTRIBUTE_VERSION)); + } + if (!MapUtil.isBlank(attributes, ItemHasIcon.ATTRIBUTE_ICON) + && !attributes.get(ItemHasIcon.ATTRIBUTE_ICON).startsWith(ApplicationItem.ICON_PATH_API_PREFIX)) { + IconDescriptor iconDescriptor = bonitaHomeFolderAccessor + .getIconFromFileSystem(attributes.get(ItemHasIcon.ATTRIBUTE_ICON)); + applicationUpdater.setIcon(iconDescriptor.getFilename(), iconDescriptor.getContent()); + } + + return applicationUpdater; + + } + /** * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup. */ @@ -150,4 +202,12 @@ protected ApplicationUpdater getApplicationUpdater() { return new ApplicationUpdater(); } + /** + * @deprecated ApplicationUpdater should no longer be used. Since 9.0.0, Applications should be created at startup. + */ + @Deprecated(since = "10.2.0") + protected ApplicationLinkUpdater getApplicationLinkUpdater() { + return new ApplicationLinkUpdater(); + } + } diff --git a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReader.java b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReader.java index 8dc8078400b..5fe82b04bcb 100644 --- a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReader.java +++ b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReader.java @@ -16,6 +16,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import org.bonitasoft.web.toolkit.client.common.AbstractTreeNode; import org.bonitasoft.web.toolkit.client.common.Tree; @@ -117,7 +118,9 @@ private static E parseItem(final TreeIndexed tree, */ private static E parseItem(final TreeIndexed tree, final ItemDefinition itemDefinition, final boolean applyValidators) { - final E item = itemDefinition.createItem(); + final Optional discriminatedItem = itemDefinition.getDiscriminatedHelper() + .map(h -> h.findItemCreator(tree).get()); + final E item = discriminatedItem.orElseGet(itemDefinition::createItem); item.setApplyValidators(applyValidators); diff --git a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/DiscriminatedItemDefinitionHelper.java b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/DiscriminatedItemDefinitionHelper.java new file mode 100644 index 00000000000..381d4b9c655 --- /dev/null +++ b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/DiscriminatedItemDefinitionHelper.java @@ -0,0 +1,51 @@ +/** + * Copyright (C) 2022 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.web.toolkit.client.data.item; + +import java.util.Map; +import java.util.function.Supplier; + +import org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition; +import org.bonitasoft.web.toolkit.client.common.TreeIndexed; + +/** + * This helps Items definitions which needs an attribute value to determine their concrete implementation. + * + * @see ItemDefinition#getDiscriminatedHelper() + * @see AbstractApplicationDefinition#getDiscriminatedHelper() for an example + * @param the {@link IItem} which is defined, same as in {@link ItemDefinition} + */ +public interface DiscriminatedItemDefinitionHelper { + + /** + * Find the appropriate creator with attributes to discriminate + * + * @param attributes the attributes for creation, one of which should be used to discriminate + * @return the item creator + */ + public Supplier findItemCreator(final Map attributes); + + /** + * Find the appropriate creator with properties tree to discriminate + * + * @param tree the tree of properties, one of which should be used to discriminate + * @return the item creator + */ + /* + * We could delegate to the attributes method with a default implementation, + * but it wouldn't be as effective as getting the direct value. + */ + public Supplier findItemCreator(final TreeIndexed tree); + +} diff --git a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/ItemDefinition.java b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/ItemDefinition.java index 434406cb7af..0c020e01e33 100644 --- a/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/ItemDefinition.java +++ b/bpm/bonita-web-server/src/main/java/org/bonitasoft/web/toolkit/client/data/item/ItemDefinition.java @@ -19,6 +19,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import org.bonitasoft.web.toolkit.client.data.APIID; import org.bonitasoft.web.toolkit.client.data.item.attribute.ItemAttribute; @@ -231,6 +232,15 @@ public final Map> getValidators() { // AUTOMATICALY INSTANCIATE ITEM AND ITEM DEPENDENT TOOLS // /////////////////////////////////////////////////////////////////////////////////////////////////// + /** + * This helps Items definitions which needs an attribute value to determine their concrete implementation. + * + * @return helper for discriminating and find the concrete implementation + */ + public Optional> getDiscriminatedHelper() { + return Optional.empty(); + } + /** * This function create a new empty Item * @@ -248,7 +258,8 @@ public final E createItem() { } public final E createItem(final Map attributes) throws ValidationException { - final E item = _createItem(); + final Optional discriminatedItem = getDiscriminatedHelper().map(h -> h.findItemCreator(attributes).get()); + final E item = discriminatedItem.orElseGet(this::_createItem); if (attributes != null) { item.setAttributes(attributes); diff --git a/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationTest.java b/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationTest.java index 5ef42351f6c..450ed8e592f 100644 --- a/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationTest.java +++ b/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/api/application/APIApplicationTest.java @@ -28,6 +28,7 @@ import org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition; import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationItem; +import org.bonitasoft.web.rest.model.application.ApplicationLinkItem; import org.bonitasoft.web.rest.model.portal.profile.ProfileItem; import org.bonitasoft.web.rest.server.api.applicationpage.APIApplicationDataStoreFactory; import org.bonitasoft.web.rest.server.api.deployer.DeployerFactory; @@ -97,12 +98,11 @@ public void setUp() throws Exception { } @Test - public void add_should_return_the_result_of_dataStore_add() throws Exception { + public void add_legacy_should_return_the_result_of_dataStore_add() throws Exception { //given final ApplicationItem itemToCreate = mock(ApplicationItem.class); final ApplicationItem createdItem = mock(ApplicationItem.class); - given(dataStore.add(itemToCreate)).willReturn(createdItem); - given(dataStore.add(itemToCreate)).willReturn(createdItem); + given(dataStore.add((AbstractApplicationItem) itemToCreate)).willReturn(createdItem); //when final ApplicationItem retrievedItem = (ApplicationItem) apiApplication.add(itemToCreate); @@ -110,6 +110,19 @@ public void add_should_return_the_result_of_dataStore_add() throws Exception { assertThat(retrievedItem).isEqualTo(createdItem); } + @Test + public void add_link_should_return_the_result_of_dataStore_add() throws Exception { + //given + final ApplicationLinkItem itemToCreate = mock(ApplicationLinkItem.class); + final ApplicationLinkItem createdItem = mock(ApplicationLinkItem.class); + given(dataStore.add((AbstractApplicationItem) itemToCreate)).willReturn(createdItem); + //when + final ApplicationLinkItem retrievedItem = (ApplicationLinkItem) apiApplication.add(itemToCreate); + + //then + assertThat(retrievedItem).isEqualTo(createdItem); + } + @Test public void search_should_return_the_result_of_dataStore_search() throws Exception { //given @@ -174,7 +187,7 @@ public void update_should_return_the_appropriate_application_kind() throws Excep given(dataStore.update(idToUpdate, attributes)).willReturn(updatedItem); //when - final ApplicationItem retrievedItem = apiApplication.update(idToUpdate, attributes); + final AbstractApplicationItem retrievedItem = apiApplication.update(idToUpdate, attributes); //then assertThat(retrievedItem).isEqualTo(updatedItem); diff --git a/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStoreTest.java b/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStoreTest.java index b52477a92ca..b8eb64b8a93 100644 --- a/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStoreTest.java +++ b/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/rest/server/datastore/application/ApplicationDataStoreTest.java @@ -18,6 +18,7 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -30,12 +31,15 @@ import org.bonitasoft.engine.api.ApplicationAPI; import org.bonitasoft.engine.api.PageAPI; import org.bonitasoft.engine.business.application.ApplicationCreator; +import org.bonitasoft.engine.business.application.ApplicationLinkCreator; +import org.bonitasoft.engine.business.application.ApplicationLinkUpdater; import org.bonitasoft.engine.business.application.ApplicationNotFoundException; import org.bonitasoft.engine.business.application.ApplicationPage; import org.bonitasoft.engine.business.application.ApplicationUpdater; import org.bonitasoft.engine.business.application.ApplicationVisibility; import org.bonitasoft.engine.business.application.IApplication; import org.bonitasoft.engine.business.application.impl.ApplicationImpl; +import org.bonitasoft.engine.business.application.impl.ApplicationLinkImpl; import org.bonitasoft.engine.exception.CreationException; import org.bonitasoft.engine.exception.DeletionException; import org.bonitasoft.engine.exception.SearchException; @@ -47,6 +51,7 @@ import org.bonitasoft.web.rest.model.application.AbstractApplicationItem; import org.bonitasoft.web.rest.model.application.ApplicationDefinition; import org.bonitasoft.web.rest.model.application.ApplicationItem; +import org.bonitasoft.web.rest.model.application.ApplicationLinkItem; import org.bonitasoft.web.rest.server.APITestWithMock; import org.bonitasoft.web.rest.server.framework.search.ItemSearchResult; import org.bonitasoft.web.toolkit.client.ItemDefinitionFactory; @@ -117,6 +122,25 @@ public void should_return_application_created_by_ApplicationAPI_converted_to_App assertThat(createdItem).isEqualTo(item); } + @Test + public void should_return_application_link_created_by_ApplicationAPI_converted_to_ApplicationLinkItem_on_add() + throws Exception { + //given + final ApplicationLinkCreator creator = new ApplicationLinkCreator("app", "My application", "1.0"); + ApplicationLinkItem app = new ApplicationLinkItem(); + given(converter.toApplicationLinkCreator(app)).willReturn(creator); + final ApplicationLinkImpl application = new ApplicationLinkImpl("app", "1.0", "app desc"); + given(applicationAPI.createApplicationLink(creator)).willReturn(application); + final ApplicationLinkItem item = new ApplicationLinkItem(); + given(converter.toApplicationItem(application)).willReturn(item); + + //when + final ApplicationLinkItem createdItem = dataStore.add(new ApplicationLinkItem()); + + //then + assertThat(createdItem).isEqualTo(item); + } + @Test public void should_create_default_home_page_on_add() throws Exception { //given @@ -152,11 +176,12 @@ public void should_return_application_updated_by_ApplicationAPI_converted_to_App final ApplicationImpl application = new ApplicationImpl("app", "1.0", "app desc", 2L, 3L); given(applicationAPI.updateApplication(1, applicationUpdater)).willReturn(application); + given(applicationAPI.getIApplication(1)).willReturn(application); final ApplicationItem item = new ApplicationItem(); given(converter.toApplicationItem(application)).willReturn(item); //when - final ApplicationItem createdItem = dataStore.update(APIID.makeAPIID(1L), attributesToUpDate); + final ApplicationItem createdItem = (ApplicationItem) dataStore.update(APIID.makeAPIID(1L), attributesToUpDate); //then verify(converter, times(1)).toApplicationUpdater(attributesToUpDate); @@ -165,6 +190,33 @@ public void should_return_application_updated_by_ApplicationAPI_converted_to_App assertThat(createdItem).isEqualTo(new ApplicationItem()); } + @Test + public void should_return_application_link_updated_by_ApplicationAPI_converted_to_ApplicationLinkItem_on_update() + throws Exception { + //given + final HashMap attributesToUpDate = new HashMap<>(); + attributesToUpDate.put(ApplicationLinkItem.ATTRIBUTE_TOKEN, "app_name"); + attributesToUpDate.put(ApplicationLinkItem.ATTRIBUTE_DISPLAY_NAME, "App display name"); + final ApplicationLinkUpdater applicationUpdater = new ApplicationLinkUpdater(); + given(converter.toApplicationLinkUpdater(attributesToUpDate)).willReturn(applicationUpdater); + + final ApplicationLinkImpl application = new ApplicationLinkImpl("app", "1.0", "app desc"); + given(applicationAPI.updateApplicationLink(1, applicationUpdater)).willReturn(application); + given(applicationAPI.getIApplication(1)).willReturn(application); + final ApplicationLinkItem item = new ApplicationLinkItem(); + given(converter.toApplicationItem(application)).willReturn(item); + + //when + final ApplicationLinkItem createdItem = (ApplicationLinkItem) dataStore.update(APIID.makeAPIID(1L), + attributesToUpDate); + + //then + verify(converter, times(1)).toApplicationLinkUpdater(attributesToUpDate); + verify(applicationAPI, times(1)).updateApplicationLink(1, applicationUpdater); + verify(converter, times(1)).toApplicationItem(application); + assertThat(createdItem).isEqualTo(new ApplicationLinkItem()); + } + @Test(expected = APIException.class) public void should_throw_APIException_when_ApplicationAPI_throws_an_exception_on_add() throws Exception { ApplicationItem app = new ApplicationItem(); @@ -183,8 +235,8 @@ public void should_throw_APIException_when_ApplicationAPI_throws_an_exception_on @Test(expected = APIException.class) public void should_throw_APIException_when_ApplicationAPI_throws_an_exception_on_UpDate() throws Exception { //given + given(applicationAPI.getIApplication(eq(1L))).willReturn(mock(ApplicationImpl.class)); when(applicationAPI.updateApplication(eq(1L), any())).thenThrow(new UpdateException("")); - //when dataStore.update(APIID.makeAPIID(1L), new HashMap<>()); } @@ -205,6 +257,22 @@ public void should_return_the_good_application_on_get() throws Exception { assertThat(retrivedItem).isEqualTo(item); } + @Test + public void should_return_the_good_application_link_on_get() throws Exception { + //given + final ApplicationLinkImpl application = new ApplicationLinkImpl("app", "1.0", "app desc"); + final ApplicationLinkItem item = new ApplicationLinkItem(); + given(converter.toApplicationItem(application)).willReturn(item); + application.setId(1); + given(applicationAPI.getIApplication(1)).willReturn(application); + + //when + final AbstractApplicationItem retrivedItem = dataStore.get(APIID.makeAPIID("1")); + + //then + assertThat(retrivedItem).isEqualTo(item); + } + @Test(expected = APIException.class) public void should_return_throw_APIException_on_get_when_engine_throws_exception() throws Exception { //given diff --git a/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReaderTest.java b/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReaderTest.java new file mode 100644 index 00000000000..43b7d876f57 --- /dev/null +++ b/bpm/bonita-web-server/src/test/java/org/bonitasoft/web/toolkit/client/common/json/JSonItemReaderTest.java @@ -0,0 +1,81 @@ +/** + * Copyright (C) 2024 Bonitasoft S.A. + * Bonitasoft, 32 rue Gustave Eiffel - 38000 Grenoble + * This library is free software; you can redistribute it and/or modify it under the terms + * of the GNU Lesser General Public License as published by the Free Software Foundation + * version 2.1 of the License. + * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License along with this + * program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth + * Floor, Boston, MA 02110-1301, USA. + **/ +package org.bonitasoft.web.toolkit.client.common.json; + +import static org.assertj.core.api.Assertions.assertThat; + +import org.bonitasoft.web.rest.model.application.AbstractApplicationDefinition; +import org.bonitasoft.web.rest.model.application.ApplicationItem; +import org.bonitasoft.web.rest.model.application.ApplicationLinkItem; +import org.junit.Test; + +public class JSonItemReaderTest { + + @Test + public void should_parse_an_implicit_LegacyApplication_from_AbstractApplicationDefinition() throws Exception { + // given + String json = """ + { + "version": "1.0", + "profileId": "2", + "token": "myapp", + "displayName": "My app", + "description": "My application description" + } + """; + // when + var app = JSonItemReader.parseItem(json, AbstractApplicationDefinition.get()); + // then + assertThat(app).isExactlyInstanceOf(ApplicationItem.class); + } + + @Test + public void should_parse_an_explicit_LegacyApplication_from_AbstractApplicationDefinition() throws Exception { + // given + String json = """ + { + "link": "false", + "version": "1.0", + "profileId": "2", + "token": "myapp", + "displayName": "My app", + "description": "My application description" + } + """; + // when + var app = JSonItemReader.parseItem(json, AbstractApplicationDefinition.get()); + // then + assertThat(app).isExactlyInstanceOf(ApplicationItem.class); + } + + @Test + public void should_parse_an_ApplicationLink_from_AbstractApplicationDefinition() throws Exception { + // given + String json = """ + { + "link": "true", + "version": "1.0", + "profileId": "2", + "token": "myapp", + "displayName": "My app", + "description": "My application description" + } + """; + // when + var app = JSonItemReader.parseItem(json, AbstractApplicationDefinition.get()); + // then + assertThat(app).isExactlyInstanceOf(ApplicationLinkItem.class); + } + +}