Skip to content

Commit c5866a4

Browse files
Ca config collections - showing as outdated reference when quick publishing (#8)
Co-authored-by: Stefan Seifert <[email protected]>
1 parent 457f822 commit c5866a4

File tree

6 files changed

+171
-12
lines changed

6 files changed

+171
-12
lines changed

changes.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@
2323
xsi:schemaLocation="http://maven.apache.org/changes/1.0.0 http://maven.apache.org/plugins/maven-changes-plugin/xsd/changes-1.0.0.xsd">
2424
<body>
2525

26+
<release version="1.9.8" date="not released">
27+
<action type="fix" dev="srikanthgurram" issue="8">
28+
Saving of Context-Aware configuration collections: Update last modified date of item pages only if the configuration of it has actually changed.
29+
</action>
30+
</release>
31+
2632
<release version="1.9.6" date="2024-07-08">
2733
<action type="update" dev="cnagel" issue="4">
2834
Adds context path allow list to ToolsConfigPagePersistenceStrategy configuration.

src/main/java/io/wcm/caconfig/extensions/persistence/impl/PagePersistenceStrategy.java

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static io.wcm.caconfig.extensions.persistence.impl.PersistenceUtils.ensureContainingPage;
2929
import static io.wcm.caconfig.extensions.persistence.impl.PersistenceUtils.ensurePageIfNotContainingPage;
3030
import static io.wcm.caconfig.extensions.persistence.impl.PersistenceUtils.getOrCreateResource;
31+
import static io.wcm.caconfig.extensions.persistence.impl.PersistenceUtils.isItemModifiedOrNewlyAdded;
3132
import static io.wcm.caconfig.extensions.persistence.impl.PersistenceUtils.replaceProperties;
3233
import static io.wcm.caconfig.extensions.persistence.impl.PersistenceUtils.updatePageLastMod;
3334

@@ -211,9 +212,11 @@ public boolean persistConfigurationCollection(@NotNull ResourceResolver resolver
211212
// create new or overwrite existing children
212213
for (ConfigurationPersistData item : data.getItems()) {
213214
String path = getCollectionItemResourcePath(parentPath + "/" + item.getCollectionItemName());
214-
ensureContainingPage(resolver, path, resourceType, configurationManagementSettings);
215-
getOrCreateResource(resolver, path, DEFAULT_CONFIG_NODE_TYPE, item.getProperties(), configurationManagementSettings);
216-
updatePageLastMod(resolver, pageManager, path);
215+
if (isItemModifiedOrNewlyAdded(resolver, path, item, configurationManagementSettings)) {
216+
ensureContainingPage(resolver, path, resourceType, configurationManagementSettings);
217+
getOrCreateResource(resolver, path, DEFAULT_CONFIG_NODE_TYPE, item.getProperties(), configurationManagementSettings);
218+
updatePageLastMod(resolver, pageManager, path);
219+
}
217220
}
218221

219222
// if resource collection parent properties are given replace them as well

src/main/java/io/wcm/caconfig/extensions/persistence/impl/PersistenceUtils.java

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,31 @@ public static void commit(ResourceResolver resourceResolver, String relatedResou
281281
}
282282
}
283283

284+
/**
285+
* Checks if the given item is modified or newly added by comparing its properties with the current state of the resource.
286+
*
287+
* @param resolver The ResourceResolver to access the resource.
288+
* @param resourcePath The path of the resource to compare against.
289+
* @param item The ConfigurationPersistData item containing the properties to compare.
290+
* @param settings The ConfigurationManagementSettings to determine which properties to ignore.
291+
* @return true if the resource does not exist or if any property value differs, false otherwise.
292+
*/
293+
public static boolean isItemModifiedOrNewlyAdded(ResourceResolver resolver, String resourcePath, ConfigurationPersistData item, ConfigurationManagementSettings settings) {
294+
Resource resource = resolver.getResource(resourcePath);
295+
if (resource == null) {
296+
return true; // Resource does not exist, so it is considered modified
297+
}
298+
299+
Map<String, Object> currentProperties = new HashMap<>(resource.getValueMap());
300+
Map<String, Object> newProperties = new HashMap<>(item.getProperties());
301+
302+
// Filter out ignored properties
303+
PropertiesFilterUtil.removeIgnoredProperties(currentProperties, settings);
304+
PropertiesFilterUtil.removeIgnoredProperties(newProperties, settings);
305+
306+
return !currentProperties.equals(newProperties);
307+
}
308+
284309
/**
285310
* If the given resource points to an AEM page, delete the page using PageManager.
286311
* Otherwise delete the resource using ResourceResolver.

src/main/java/io/wcm/caconfig/extensions/persistence/impl/PropertiesFilterUtil.java

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
*/
2020
package io.wcm.caconfig.extensions.persistence.impl;
2121

22+
import java.util.HashSet;
23+
import java.util.Map;
2224
import java.util.Set;
2325

2426
import org.apache.sling.caconfig.management.ConfigurationManagementSettings;
@@ -28,13 +30,19 @@
2830
*/
2931
final class PropertiesFilterUtil {
3032

31-
private PropertiesFilterUtil() {
32-
// static methods only
33-
}
33+
private PropertiesFilterUtil() {
34+
// static methods only
35+
}
3436

35-
public static void removeIgnoredProperties(Set<String> propertyNames, ConfigurationManagementSettings settings) {
36-
Set<String> ignoredProperties = settings.getIgnoredPropertyNames(propertyNames);
37-
propertyNames.removeAll(ignoredProperties);
38-
}
37+
public static void removeIgnoredProperties(Set<String> propertyNames, ConfigurationManagementSettings settings) {
38+
Set<String> ignoredProperties = settings.getIgnoredPropertyNames(propertyNames);
39+
propertyNames.removeAll(ignoredProperties);
40+
}
41+
42+
public static void removeIgnoredProperties(Map<String, Object> properties, ConfigurationManagementSettings settings) {
43+
Set<String> propertyNames = new HashSet<>(properties.keySet());
44+
removeIgnoredProperties(propertyNames, settings);
45+
properties.keySet().retainAll(propertyNames);
46+
}
3947

4048
}

src/test/java/io/wcm/caconfig/extensions/persistence/impl/PagePersistenceStrategyTest.java

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,19 @@
1919
*/
2020
package io.wcm.caconfig.extensions.persistence.impl;
2121

22+
import static com.day.cq.wcm.api.NameConstants.PN_LAST_MOD;
2223
import static io.wcm.caconfig.extensions.persistence.testcontext.PersistenceTestUtils.writeConfiguration;
2324
import static io.wcm.caconfig.extensions.persistence.testcontext.PersistenceTestUtils.writeConfigurationCollection;
2425
import static org.apache.sling.api.resource.ResourceResolver.PROPERTY_RESOURCE_TYPE;
2526
import static org.apache.sling.testing.mock.caconfig.ContextPlugins.CACONFIG;
2627
import static org.hamcrest.MatcherAssert.assertThat;
2728
import static org.junit.jupiter.api.Assertions.assertEquals;
29+
import static org.junit.jupiter.api.Assertions.assertFalse;
2830
import static org.junit.jupiter.api.Assertions.assertNotNull;
2931
import static org.junit.jupiter.api.Assertions.assertNull;
3032
import static org.junit.jupiter.api.Assertions.assertTrue;
3133

34+
import java.util.Calendar;
3235
import java.util.List;
3336

3437
import org.apache.sling.caconfig.ConfigurationBuilder;
@@ -162,6 +165,36 @@ void testListConfig() {
162165
assertEquals(234, config2.intParam());
163166
}
164167

168+
@Test
169+
void testListConfig_updateLastModifiedIfPropertyRemoved() {
170+
context.registerInjectActivateService(new PagePersistenceStrategy(), "enabled", true);
171+
172+
// write config
173+
writeConfigurationCollection(context, contentPage.getPath(), ListConfig.class.getName(), List.of(
174+
ImmutableValueMap.of("stringParam", "value1", "intParam", 123),
175+
ImmutableValueMap.of("stringParam", "value2", "intParam", 234)
176+
));
177+
178+
// assert storage in page in /conf
179+
Page parentPage = context.pageManager().getPage("/conf/test/site1/sling:configs/" + ListConfig.class.getName());
180+
assertNotNull(parentPage);
181+
182+
Page configPage1 = context.pageManager().getPage("/conf/test/site1/sling:configs/" + ListConfig.class.getName() + "/item0");
183+
assertThat(configPage1.getContentResource(), ResourceMatchers.props("stringParam", "value1", "intParam", 123));
184+
185+
Page configPage2 = context.pageManager().getPage("/conf/test/site1/sling:configs/" + ListConfig.class.getName() + "/item1");
186+
assertThat(configPage2.getContentResource(), ResourceMatchers.props("stringParam", "value2", "intParam", 234));
187+
188+
writeConfigurationCollection(context, contentPage.getPath(), ListConfig.class.getName(), List.of(
189+
ImmutableValueMap.of("stringParam", "value1", "intParam", 123),
190+
ImmutableValueMap.of("stringParam", "value2")
191+
));
192+
Calendar lastModifiedConfigPage2AfterUpdate = configPage2.getContentResource().getValueMap().get(PN_LAST_MOD, Calendar.class);
193+
System.out.println(lastModifiedConfigPage2AfterUpdate);
194+
//ConfigPage2 last modified date should be updated because it is updated
195+
assertNotNull(lastModifiedConfigPage2AfterUpdate);
196+
}
197+
165198
@Test
166199
void testListConfig_Nested() {
167200
context.registerInjectActivateService(new PagePersistenceStrategy(), "enabled", true);
@@ -207,10 +240,14 @@ void testListConfig_Nested() {
207240
assertEquals(1, config2.subListConfig().length);
208241
assertEquals("value21", config2.subListConfig()[0].stringParam());
209242

243+
244+
Calendar lastModifiedConfigPage1 = configPage1.getContentResource().getValueMap().get(PN_LAST_MOD, Calendar.class);
245+
Calendar lastModifiedConfigPage2 = configPage2.getContentResource().getValueMap().get(PN_LAST_MOD, Calendar.class);
246+
210247
// update config collection items
211248
writeConfigurationCollection(context, contentPage.getPath(), ListNestedConfig.class.getName(), List.of(
212249
ImmutableValueMap.of("stringParam", "value1-new", "intParam", 123),
213-
ImmutableValueMap.of("stringParam", "value2-new", "intParam", 234),
250+
ImmutableValueMap.of("stringParam", "value2", "intParam", 234),
214251
ImmutableValueMap.of("stringParam", "value3-new", "intParam", 345)));
215252

216253
// read config
@@ -224,18 +261,34 @@ void testListConfig_Nested() {
224261
assertEquals(2, config1.subListConfig().length);
225262
assertEquals("value11", config1.subListConfig()[0].stringParam());
226263
assertEquals("value12", config1.subListConfig()[1].stringParam());
264+
//ConfigPage1 last modified date should be updated because it is updated
265+
assertTrue(configLastModifiedUpdated(configPage1, false, lastModifiedConfigPage1));
227266

228267
config2 = configs.get(1);
229-
assertEquals("value2-new", config2.stringParam());
268+
assertEquals("value2", config2.stringParam());
230269
assertEquals(234, config2.intParam());
231270
assertEquals(1, config2.subListConfig().length);
232271
assertEquals("value21", config2.subListConfig()[0].stringParam());
272+
//ConfigPage2 last modified date should not be updated because it is not updated
273+
assertFalse(configLastModifiedUpdated(configPage2, false, lastModifiedConfigPage2));
233274

234275
ListNestedConfig config3 = configs.get(2);
235276
assertEquals("value3-new", config3.stringParam());
236277
assertEquals(345, config3.intParam());
237278
assertEquals(0, config3.subListConfig().length);
279+
Page configPage3 = context.pageManager().getPage("/conf/test/site1/sling:configs/" + ListNestedConfig.class.getName() + "/item2");
280+
//ConfigPage3 last modified date should be added because it is newly created
281+
assertTrue(configLastModifiedUpdated(configPage3, true, null));
282+
}
238283

284+
private boolean configLastModifiedUpdated(Page configPage, boolean newlyCreatedConfig, Calendar lastModifiedBeforeUpdateOrCreate) {
285+
Calendar lastModifiedAfterUpdateOrCreate = configPage.getContentResource().getValueMap().get(PN_LAST_MOD, Calendar.class);
286+
if (newlyCreatedConfig) {
287+
//If the config is newly created then last modified date should be updated
288+
return lastModifiedAfterUpdateOrCreate != null;
289+
}
290+
//If the config is updated then last modified date should be updated
291+
return lastModifiedAfterUpdateOrCreate != null && lastModifiedAfterUpdateOrCreate.after(lastModifiedBeforeUpdateOrCreate);
239292
}
240293

241294
@Test
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
/*
2+
* #%L
3+
* wcm.io
4+
* %%
5+
* Copyright (C) 2024 wcm.io
6+
* %%
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* #L%
19+
*/
20+
package io.wcm.caconfig.extensions.persistence.impl;
21+
22+
import static io.wcm.caconfig.extensions.persistence.impl.PropertiesFilterUtil.removeIgnoredProperties;
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
import static org.mockito.ArgumentMatchers.any;
25+
import static org.mockito.Mockito.when;
26+
27+
import java.util.HashMap;
28+
import java.util.HashSet;
29+
import java.util.Map;
30+
import java.util.Set;
31+
32+
import org.apache.sling.caconfig.management.ConfigurationManagementSettings;
33+
import org.junit.jupiter.api.BeforeEach;
34+
import org.junit.jupiter.api.Test;
35+
import org.junit.jupiter.api.extension.ExtendWith;
36+
import org.mockito.Mock;
37+
import org.mockito.junit.jupiter.MockitoExtension;
38+
39+
@ExtendWith(MockitoExtension.class)
40+
class PropertiesFilterUtilTest {
41+
42+
@Mock
43+
private ConfigurationManagementSettings settings;
44+
45+
@BeforeEach
46+
void setUp() {
47+
when(settings.getIgnoredPropertyNames(any())).thenReturn(Set.of("prop1", "prop3"));
48+
}
49+
50+
@Test
51+
void testRemoveIgnoredPropertiesSet() {
52+
Set<String> names = new HashSet<>(Set.of("prop1", "prop2", "prop3", "prop4"));
53+
removeIgnoredProperties(names, settings);
54+
assertEquals(Set.of("prop2", "prop4"), names);
55+
}
56+
57+
@Test
58+
void testRemoveIgnoredPropertiesMap() {
59+
Map<String, Object> properties = new HashMap<>(Map.of("prop1", 1, "prop2", 2, "prop3", 3, "prop4", 4));
60+
removeIgnoredProperties(properties, settings);
61+
assertEquals(Map.of("prop2", 2, "prop4", 4), properties);
62+
}
63+
64+
}

0 commit comments

Comments
 (0)