From 2cdbe6f306b00d4bb95b0208f58147baf829284f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20=C5=BBurek?= Date: Thu, 2 Feb 2023 12:22:08 +0100 Subject: [PATCH] ACS-4180 Add localized properties to the repo event (#1714) --- .../service/cmr/repository/MLText.java | 24 +++- .../repo/event2/EventConsolidator.java | 10 +- .../repo/event2/NodeResourceHelper.java | 87 +++++++++++-- .../event2/AbstractContextAwareRepoEvent.java | 23 +++- .../repo/event2/CreateRepoEventIT.java | 11 +- .../repo/event2/DeleteRepoEventIT.java | 14 ++- .../event2/NodeResourceHelperUnitTest.java | 114 ++++++++++++++++++ .../repo/event2/RepoEvent2UnitSuite.java | 5 +- .../repo/event2/UpdateRepoEventIT.java | 71 ++++++++++- 9 files changed, 332 insertions(+), 27 deletions(-) create mode 100644 repository/src/test/java/org/alfresco/repo/event2/NodeResourceHelperUnitTest.java diff --git a/data-model/src/main/java/org/alfresco/service/cmr/repository/MLText.java b/data-model/src/main/java/org/alfresco/service/cmr/repository/MLText.java index 7825628f38..80c2e84e59 100644 --- a/data-model/src/main/java/org/alfresco/service/cmr/repository/MLText.java +++ b/data-model/src/main/java/org/alfresco/service/cmr/repository/MLText.java @@ -2,7 +2,7 @@ * #%L * Alfresco Data model classes * %% - * Copyright (C) 2005 - 2016 Alfresco Software Limited + * Copyright (C) 2005 - 2023 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -51,6 +51,18 @@ public class MLText extends HashMap { private static final long serialVersionUID = -3696135175650511841L; + /** + * Returns default locale used by the {@link MLText} implementation + * + * @see I18NUtil#getLocale() + * + * @return default locale + */ + public static Locale getDefaultLocale() + { + return I18NUtil.getLocale(); + } + public MLText() { super(3, 0.75F); @@ -61,13 +73,13 @@ public class MLText extends HashMap * * @param value the value for the current default locale * - * @see I18NUtil#getLocale() + * @see #getDefaultLocale() * @see #MLText(Locale, String) * @see #getDefaultValue() */ public MLText(String value) { - this(I18NUtil.getLocale(), value); + this(getDefaultLocale(), value); } /** @@ -124,7 +136,7 @@ public class MLText extends HashMap /** * Retrieves a default value from the set of available locales.
* - * @see I18NUtil#getLocale() + * @see #getDefaultLocale() * @see #getClosestValue(Locale) */ public String getDefaultValue() @@ -135,7 +147,7 @@ public class MLText extends HashMap return null; } // There is some hope of getting a match - Locale locale = I18NUtil.getLocale(); + Locale locale = getDefaultLocale(); return getClosestValue(locale); } @@ -168,7 +180,7 @@ public class MLText extends HashMap if (match == null) { // No close matches for the locale - go for the default locale - locale = I18NUtil.getLocale(); + locale = getDefaultLocale(); match = I18NUtil.getNearestLocale(locale, options); if (match == null) { diff --git a/repository/src/main/java/org/alfresco/repo/event2/EventConsolidator.java b/repository/src/main/java/org/alfresco/repo/event2/EventConsolidator.java index adbf5faf46..dafa4abdc1 100644 --- a/repository/src/main/java/org/alfresco/repo/event2/EventConsolidator.java +++ b/repository/src/main/java/org/alfresco/repo/event2/EventConsolidator.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2023 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -304,6 +304,14 @@ public class EventConsolidator implements EventSupportedPolicies builder.setProperties(mappedProps); resourceBeforeAllFieldsNull = false; } + + Map> localizedProps =helper.getLocalizedPropertiesBefore(changedPropsBefore, after); + if (!localizedProps.isEmpty()) + { + builder.setLocalizedProperties(localizedProps); + resourceBeforeAllFieldsNull = false; + } + String name = (String) changedPropsBefore.get(ContentModel.PROP_NAME); if (name != null) { diff --git a/repository/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java b/repository/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java index 9212f2a1b8..3dc0b2d0a4 100644 --- a/repository/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java +++ b/repository/src/main/java/org/alfresco/repo/event2/NodeResourceHelper.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2023 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -25,6 +25,8 @@ */ package org.alfresco.repo.event2; +import static java.util.Optional.ofNullable; + import java.io.Serializable; import java.time.ZoneId; import java.time.ZonedDateTime; @@ -34,8 +36,11 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import com.google.common.collect.Sets; + import org.alfresco.model.ContentModel; import org.alfresco.repo.event.v1.model.ContentInfo; import org.alfresco.repo.event.v1.model.NodeResource; @@ -43,6 +48,7 @@ import org.alfresco.repo.event.v1.model.UserInfo; import org.alfresco.repo.event2.filter.EventFilterRegistry; import org.alfresco.repo.event2.filter.NodeAspectFilter; import org.alfresco.repo.event2.filter.NodePropertyFilter; +import org.alfresco.repo.node.MLPropertyInterceptor; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.security.permissions.AccessDeniedException; import org.alfresco.service.cmr.dictionary.DictionaryService; @@ -152,6 +158,7 @@ public class NodeResourceHelper implements InitializingBean .setPrimaryAssocQName(getPrimaryAssocQName(nodeRef)) .setPrimaryHierarchy(PathUtil.getNodeIdsInReverse(path, false)) .setProperties(mapToNodeProperties(properties)) + .setLocalizedProperties(mapToNodeLocalizedProperties(properties)) .setAspectNames(getMappedAspects(nodeRef)); } @@ -200,9 +207,8 @@ public class NodeResourceHelper implements InitializingBean props.forEach((k, v) -> { if (!nodePropertyFilter.isExcluded(k)) { - if (v != null && v instanceof MLText) + if (v instanceof MLText) { - //TODO - should we send all of the values if multiple locales exist? v = ((MLText) v).getDefaultValue(); } @@ -213,6 +219,23 @@ public class NodeResourceHelper implements InitializingBean return filteredProps; } + public Map> mapToNodeLocalizedProperties(Map props) + { + Map> filteredProps = new HashMap<>(props.size()); + + props.forEach((k, v) -> { + if (!nodePropertyFilter.isExcluded(k) && v instanceof MLText) + { + final MLText mlTextValue = (MLText) v; + final HashMap localizedValues = new HashMap<>(mlTextValue.size()); + mlTextValue.forEach((locale, text) -> localizedValues.put(locale.toString(), text)); + filteredProps.put(getQNamePrefixString(k), localizedValues); + } + }); + + return filteredProps.isEmpty() ? null : filteredProps; + } + public ContentInfo getContentInfo(Map props) { final Serializable content = props.get(ContentModel.PROP_CONTENT); @@ -313,11 +336,6 @@ public class NodeResourceHelper implements InitializingBean return filteredAspects; } - private boolean isNotEmptyString(Serializable ser) - { - return !(ser instanceof String) || !((String) ser).isEmpty(); - } - public QName getNodeType(NodeRef nodeRef) { return nodeService.getType(nodeRef); @@ -330,7 +348,58 @@ public class NodeResourceHelper implements InitializingBean public Map getProperties(NodeRef nodeRef) { - return nodeService.getProperties(nodeRef); + //We need to have full MLText properties here. This is why we are marking the current thread as MLAware + final boolean toRestore = MLPropertyInterceptor.isMLAware(); + MLPropertyInterceptor.setMLAware(true); + try + { + return nodeService.getProperties(nodeRef); + } finally + { + MLPropertyInterceptor.setMLAware(toRestore); + } + } + + public Map> getLocalizedPropertiesBefore(Map propsBefore, NodeResource nodeAfter) + { + final Map> locPropsBefore = ofNullable(propsBefore) + .map(this::mapToNodeLocalizedProperties) + .orElseGet(Map::of); + final Map> locPropsAfter = ofNullable(nodeAfter) + .map(NodeResource::getLocalizedProperties) + .orElseGet(Map::of); + + return getLocalizedPropertiesBefore(locPropsBefore, locPropsAfter); + } + + static Map> getLocalizedPropertiesBefore(Map> locPropsBefore, + Map> locPropsAfter) + { + final Map> result = new HashMap<>(locPropsBefore.size()); + + Sets.union(locPropsBefore.keySet(), locPropsAfter.keySet()).forEach(propertyName -> { + final Map valuesBefore = ofNullable(locPropsBefore.get(propertyName)).orElseGet(Map::of); + final Map valuesAfter = ofNullable(locPropsAfter.get(propertyName)).orElseGet(Map::of); + + if (!valuesAfter.isEmpty() || !valuesBefore.isEmpty()) + { + final Map diff = new HashMap<>(valuesBefore.size()); + Sets.union(valuesBefore.keySet(), valuesAfter.keySet()).forEach(lang -> { + final String valueBefore = valuesBefore.get(lang); + final String valueAfter = valuesAfter.get(lang); + if (!Objects.equals(valueBefore, valueAfter)) + { + diff.put(lang, valueBefore); + } + }); + if (!diff.isEmpty()) + { + result.put(propertyName, diff); + } + } + }); + + return result; } public Set getMappedAspects(NodeRef nodeRef) diff --git a/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java b/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java index 995bec4cfe..08cc2f3830 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java +++ b/repository/src/test/java/org/alfresco/repo/event2/AbstractContextAwareRepoEvent.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2023 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -32,6 +32,7 @@ import static org.awaitility.Awaitility.await; import java.util.ArrayList; import java.util.Collections; import java.util.List; +import java.util.Locale; import javax.jms.ConnectionFactory; @@ -47,6 +48,7 @@ import org.alfresco.repo.event.v1.model.Resource; import org.alfresco.repo.security.authentication.AuthenticationUtil; import org.alfresco.repo.transaction.RetryingTransactionHelper; import org.alfresco.service.cmr.dictionary.CustomModelService; +import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.repository.StoreRef; @@ -89,6 +91,11 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest private static final String CAMEL_ROUTE = "jms:topic:" + TOPIC_NAME; private static final CamelContext CAMEL_CONTEXT = new DefaultCamelContext(); + protected final Locale defaultLocale = new Locale(MLText.getDefaultLocale().getLanguage()); + protected final Locale germanLocale = new Locale(Locale.GERMAN.getLanguage(), "XX"); + protected final Locale frenchLocale = new Locale(Locale.FRENCH.getLanguage()); + protected final Locale japaneseLocale = new Locale(Locale.JAPANESE.getLanguage(), "YY", "ZZ"); + private static boolean isCamelConfigured; private static DataFormat dataFormat; @@ -388,6 +395,20 @@ public abstract class AbstractContextAwareRepoEvent extends BaseSpringTest return (T) resource.getProperties().get(propertyName); } + protected String getLocalizedProperty(NodeResource resource, String propertyName, Locale locale) + { + assertTrue(containsLocalizedProperty(resource, propertyName, locale)); + return resource.getLocalizedProperties().get(propertyName).get(locale.toString()); + } + + protected boolean containsLocalizedProperty(NodeResource resource, String propertyName, Locale locale) + { + assertNotNull(resource); + assertNotNull(resource.getLocalizedProperties()); + assertNotNull(resource.getLocalizedProperties().get(propertyName)); + return resource.getLocalizedProperties().get(propertyName).containsKey(locale.toString()); + } + /** * Await at most 5 seconds for the condition (ie. {@code numOfEvents}) * to be met before throwing a timeout exception. diff --git a/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java b/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java index 1e801ad206..5e860cc58b 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java +++ b/repository/src/test/java/org/alfresco/repo/event2/CreateRepoEventIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2023 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -38,6 +38,7 @@ import org.alfresco.repo.event.v1.model.EventData; import org.alfresco.repo.event.v1.model.EventType; import org.alfresco.repo.event.v1.model.NodeResource; import org.alfresco.repo.event.v1.model.RepoEvent; +import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; @@ -50,7 +51,6 @@ import org.springframework.beans.factory.annotation.Autowired; */ public class CreateRepoEventIT extends AbstractContextAwareRepoEvent { - @Autowired private NodeDAO nodeDAO; @@ -63,6 +63,9 @@ public class CreateRepoEventIT extends AbstractContextAwareRepoEvent PropertyMap propertyMap = new PropertyMap(); propertyMap.put(ContentModel.PROP_TITLE, "test title"); propertyMap.put(ContentModel.PROP_NAME, name); + final MLText localizedDescription = new MLText(germanLocale, "german description"); + localizedDescription.addValue(defaultLocale, "default description"); + propertyMap.put(ContentModel.PROP_DESCRIPTION, localizedDescription); final NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT, localName, propertyMap); final RepoEvent> resultRepoEvent = getRepoEvent(1); @@ -91,6 +94,10 @@ public class CreateRepoEventIT extends AbstractContextAwareRepoEvent assertNotNull(nodeResource.getPrimaryHierarchy()); assertNotNull("Default aspects were not added. ", nodeResource.getAspectNames()); assertEquals("test title", getProperty(nodeResource, "cm:title")); + assertEquals("test title", getLocalizedProperty(nodeResource, "cm:title", defaultLocale)); + assertEquals("default description", getProperty(nodeResource, "cm:description")); + assertEquals("default description", getLocalizedProperty(nodeResource, "cm:description", defaultLocale)); + assertEquals("german description", getLocalizedProperty(nodeResource, "cm:description", germanLocale)); assertNull("There is no content.", nodeResource.getContent()); assertNotNull("Missing createdByUser property.", nodeResource.getCreatedByUser()); diff --git a/repository/src/test/java/org/alfresco/repo/event2/DeleteRepoEventIT.java b/repository/src/test/java/org/alfresco/repo/event2/DeleteRepoEventIT.java index d7115c6677..62d6264c90 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/DeleteRepoEventIT.java +++ b/repository/src/test/java/org/alfresco/repo/event2/DeleteRepoEventIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2023 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -31,6 +31,7 @@ import org.alfresco.repo.event.v1.model.EventData; import org.alfresco.repo.event.v1.model.EventType; import org.alfresco.repo.event.v1.model.NodeResource; import org.alfresco.repo.event.v1.model.RepoEvent; +import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; @@ -48,6 +49,9 @@ public class DeleteRepoEventIT extends AbstractContextAwareRepoEvent String localName = GUID.generate(); PropertyMap propertyMap = new PropertyMap(); propertyMap.put(ContentModel.PROP_TITLE, "test title"); + final MLText localizedDescription = new MLText(germanLocale, "german description"); + localizedDescription.addValue(defaultLocale, "default description"); + propertyMap.put(ContentModel.PROP_DESCRIPTION, localizedDescription); NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT, localName, propertyMap); NodeResource createdResource = getNodeResource(1); @@ -62,10 +66,16 @@ public class DeleteRepoEventIT extends AbstractContextAwareRepoEvent deleteNode(nodeRef); final RepoEvent> resultRepoEvent = getRepoEvent(2); + final NodeResource nodeResource = getNodeResource(resultRepoEvent); assertEquals("Repo event type:", EventType.NODE_DELETED.getType(), resultRepoEvent.getType()); - assertEquals(createdResource.getId(), getNodeResource(resultRepoEvent).getId()); + assertEquals(createdResource.getId(), nodeResource.getId()); assertEquals("Wrong primaryAssocQName prefix.", "ce:" + localName, createdResource.getPrimaryAssocQName()); + assertEquals("test title", getProperty(nodeResource, "cm:title")); + assertEquals("test title", getLocalizedProperty(nodeResource, "cm:title", defaultLocale)); + assertEquals("default description", getProperty(nodeResource, "cm:description")); + assertEquals("default description", getLocalizedProperty(nodeResource, "cm:description", defaultLocale)); + assertEquals("german description", getLocalizedProperty(nodeResource, "cm:description", germanLocale)); // There should be no resourceBefore EventData eventData = getEventData(resultRepoEvent); diff --git a/repository/src/test/java/org/alfresco/repo/event2/NodeResourceHelperUnitTest.java b/repository/src/test/java/org/alfresco/repo/event2/NodeResourceHelperUnitTest.java new file mode 100644 index 0000000000..9c905b8196 --- /dev/null +++ b/repository/src/test/java/org/alfresco/repo/event2/NodeResourceHelperUnitTest.java @@ -0,0 +1,114 @@ +/* + * #%L + * Alfresco Repository + * %% + * Copyright (C) 2005 - 2023 Alfresco Software Limited + * %% + * This file is part of the Alfresco software. + * If the software was purchased under a paid Alfresco license, the terms of + * the paid license agreement will prevail. Otherwise, the software is + * provided under the following open source license terms: + * + * Alfresco 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, either version 3 of the License, or + * (at your option) any later version. + * + * Alfresco 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 Alfresco. If not, see . + * #L% + */ +package org.alfresco.repo.event2; + +import static org.alfresco.repo.event2.NodeResourceHelper.getLocalizedPropertiesBefore; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; + +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +public class NodeResourceHelperUnitTest +{ + @Test + public void shouldExtractOnlyRelevantPropertiesForBeforeNode() + { + final Map> before = + Map.of( + "unchanged-empty", locValues(), + "unchanged-non-empty", locValues("pl", "Kiełbasa", "en", "Sausage"), + "changed-added", locValues("pl", "Kiełbasa"), + "changed-modified", locValues("pl", "XYZ", "en", "Sausage"), + "changed-deleted", locValues("pl", "Kiełbasa", "en", "Sausage"), + "changed-added-modified-deleted", locValues("pl", "XYZ", "en", "Sausage"), + "changed-to-empty", locValues("pl", "Kiełbasa", "en", "Sausage"), + "changed-from-empty", locValues(), + "removed-empty", locValues(), + "removed-non-empty", locValues("pl", "Kiełbasa", "en", "Sausage") + ); + + final Map> after = + Map.of( + "unchanged-empty", locValues(), + "unchanged-non-empty", locValues("pl", "Kiełbasa", "en", "Sausage"), + "changed-added", locValues("pl", "Kiełbasa", "en", "Sausage"), + "changed-modified", locValues("pl", "Kiełbasa", "en", "Sausage"), + "changed-deleted", locValues("en", "Sausage"), + "changed-added-modified-deleted", locValues("pl", "Kiełbasa", "de", "Würst"), + "changed-to-empty", locValues(), + "changed-from-empty", locValues("pl", "Kiełbasa", "en", "Sausage"), + "new-empty", locValues(), + "new-non-empty", locValues("de", "Würst") + ); + + final Map> diff = getLocalizedPropertiesBefore(before, after); + + assertFalse(diff.containsKey("unchanged-empty")); + assertFalse(diff.containsKey("unchanged-non-empty")); + assertEquals(locValues("en", null), diff.get("changed-added")); + assertEquals(locValues("pl", "XYZ"), diff.get("changed-modified")); + assertEquals(locValues("pl", "Kiełbasa"), diff.get("changed-deleted")); + assertEquals(locValues("pl", "XYZ", "en", "Sausage", "de", null), diff.get("changed-added-modified-deleted")); + assertEquals(locValues("pl", "Kiełbasa", "en", "Sausage"), diff.get("changed-to-empty")); + assertEquals(locValues("pl", null, "en", null), diff.get("changed-from-empty")); + assertFalse(diff.containsKey("removed-empty")); + assertEquals(locValues("pl", "Kiełbasa", "en", "Sausage"), diff.get("removed-non-empty")); + assertFalse(diff.containsKey("new-empty")); + assertEquals(locValues("de", null), diff.get("new-non-empty")); + } + + private LocalizedValues locValues(String l1, String v1, String l2, String v2, String l3, String v3) + { + return locValues(l1, v1, l2, v2).append(l3, v3); + } + + private LocalizedValues locValues(String l1, String v1, String l2, String v2) + { + return locValues(l1, v1).append(l2, v2); + } + + private LocalizedValues locValues(String l1, String v1) + { + return locValues().append(l1, v1); + } + + private LocalizedValues locValues() + { + return new LocalizedValues(); + } + + private static class LocalizedValues extends HashMap + { + public LocalizedValues append(String language, String value) + { + this.put(language, value); + return this; + } + } +} diff --git a/repository/src/test/java/org/alfresco/repo/event2/RepoEvent2UnitSuite.java b/repository/src/test/java/org/alfresco/repo/event2/RepoEvent2UnitSuite.java index 462f21c464..9b73a48539 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/RepoEvent2UnitSuite.java +++ b/repository/src/test/java/org/alfresco/repo/event2/RepoEvent2UnitSuite.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2023 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -34,7 +34,8 @@ import org.junit.runners.Suite.SuiteClasses; @SuiteClasses({ EventFilterUnitTest.class, EventConsolidatorUnitTest.class, EventJSONSchemaUnitTest.class, - EventGeneratorQueueUnitTest.class + EventGeneratorQueueUnitTest.class, + NodeResourceHelperUnitTest.class }) public class RepoEvent2UnitSuite { diff --git a/repository/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java b/repository/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java index 6075c2fd0c..a7a5ad071a 100644 --- a/repository/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java +++ b/repository/src/test/java/org/alfresco/repo/event2/UpdateRepoEventIT.java @@ -2,7 +2,7 @@ * #%L * Alfresco Repository * %% - * Copyright (C) 2005 - 2020 Alfresco Software Limited + * Copyright (C) 2005 - 2023 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of @@ -26,6 +26,8 @@ package org.alfresco.repo.event2; +import static org.alfresco.model.ContentModel.PROP_DESCRIPTION; + import java.io.Serializable; import java.util.Collection; import java.util.HashMap; @@ -46,6 +48,7 @@ import org.alfresco.service.cmr.dictionary.CustomModelDefinition; import org.alfresco.service.cmr.dictionary.TypeDefinition; import org.alfresco.service.cmr.repository.ContentService; import org.alfresco.service.cmr.repository.ContentWriter; +import org.alfresco.service.cmr.repository.MLText; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.namespace.QName; import org.alfresco.util.GUID; @@ -279,6 +282,60 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent assertNull(resourceBefore.getPrimaryAssocQName()); } + @Test + public void testUpdateContentWithLocalizedProperties() + { + final String description = "cm:description"; + final NodeRef nodeRef = createNode(ContentModel.TYPE_CONTENT); + NodeResource resource = getNodeResource(1); + + assertNull(getProperty(resource, description)); + assertNull(resource.getLocalizedProperties()); + assertNull(getEventData(1).getResourceBefore()); + + retryingTransactionHelper.doInTransaction(() -> { + final MLText localizedDescription = new MLText(germanLocale, "german description"); + localizedDescription.addValue(defaultLocale, "default description"); + localizedDescription.addValue(japaneseLocale, "japanese description"); + + nodeService.setProperty(nodeRef, PROP_DESCRIPTION, localizedDescription); + return null; + }); + + resource = getNodeResource(2); + NodeResource resourceBefore = getNodeResourceBefore(2); + + assertEquals("default description", getProperty(resource, description)); + assertEquals("default description", getLocalizedProperty(resource, description, defaultLocale)); + assertEquals("german description", getLocalizedProperty(resource, description, germanLocale)); + assertEquals("japanese description", getLocalizedProperty(resource, description, japaneseLocale)); + assertNull(getLocalizedProperty(resourceBefore, description, defaultLocale)); + assertNull(getLocalizedProperty(resourceBefore, description, germanLocale)); + assertNull(getLocalizedProperty(resourceBefore, description, japaneseLocale)); + + retryingTransactionHelper.doInTransaction(() -> { + final MLText localizedDescription = new MLText(frenchLocale, "french description added"); + localizedDescription.addValue(defaultLocale, "default description modified"); + localizedDescription.addValue(japaneseLocale, "japanese description"); + + nodeService.setProperty(nodeRef, PROP_DESCRIPTION, localizedDescription); + return null; + }); + + resource = getNodeResource(3); + resourceBefore = getNodeResourceBefore(3); + + assertEquals("default description modified", getProperty(resource, description)); + assertEquals("default description modified", getLocalizedProperty(resource, description, defaultLocale)); + assertEquals("french description added", getLocalizedProperty(resource, description, frenchLocale)); + assertEquals("japanese description", getLocalizedProperty(resource, description, japaneseLocale)); + assertFalse(containsLocalizedProperty(resource, description, germanLocale)); + assertEquals("default description", getLocalizedProperty(resourceBefore, description, defaultLocale)); + assertEquals("german description", getLocalizedProperty(resourceBefore, description, germanLocale)); + assertNull(getLocalizedProperty(resourceBefore, description, frenchLocale)); + assertFalse(containsLocalizedProperty(resourceBefore, description, japaneseLocale)); + } + @Test public void testUpdateContentTitle() { @@ -296,8 +353,11 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent }); resource = getNodeResource(2); + NodeResource resourceBefore = getNodeResourceBefore(2); title = getProperty(resource, "cm:title"); assertEquals("test title", title); + assertEquals("test title", getLocalizedProperty(resource, "cm:title", defaultLocale)); + assertNull(getLocalizedProperty(resourceBefore, "cm:title", defaultLocale)); // update content cm:title property again with "new test title" value retryingTransactionHelper.doInTransaction(() -> { @@ -308,10 +368,13 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent resource = getNodeResource(3); title = getProperty(resource, "cm:title"); assertEquals("new test title", title); + assertEquals("new test title", getLocalizedProperty(resource, "cm:title", defaultLocale)); - NodeResource resourceBefore = getNodeResourceBefore(3); + + resourceBefore = getNodeResourceBefore(3); title = getProperty(resourceBefore, "cm:title"); assertEquals("Wrong old property.", "test title", title); + assertEquals("test title", getLocalizedProperty(resourceBefore, "cm:title", defaultLocale)); assertNotNull(resourceBefore.getModifiedAt()); } @@ -354,7 +417,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent // update content cm:description property with "test_description" value retryingTransactionHelper.doInTransaction(() -> { - nodeService.setProperty(nodeRef, ContentModel.PROP_DESCRIPTION, "test description"); + nodeService.setProperty(nodeRef, PROP_DESCRIPTION, "test description"); return null; }); @@ -492,7 +555,7 @@ public class UpdateRepoEventIT extends AbstractContextAwareRepoEvent QName.createQName(TEST_NAMESPACE, GUID.generate()), ContentModel.TYPE_CONTENT).getChildRef(); - nodeService.setProperty(node1, ContentModel.PROP_DESCRIPTION, "test description"); + nodeService.setProperty(node1, PROP_DESCRIPTION, "test description"); return null; }); //Create and update node are done in the same transaction so one event is expected