Skip to content

Commit

Permalink
add support for resolving merged / moved tags (wcm-io#51)
Browse files Browse the repository at this point in the history
  • Loading branch information
dennisfisch committed Oct 25, 2024
1 parent 47a77c6 commit bed76d4
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public final class MockAemAdapterFactory implements AdapterFactory {
return (AdapterType)new MockTemplate(resource);
}
if (type == Tag.class && isPrimaryType(resource, NT_TAG)) {
return (AdapterType)new MockTag(resource);
return (AdapterType)new MockTag(MockTag.resolveSuccessorOrSelf(resource));
}
if (type == ContentFragment.class && DamUtil.isAsset(resource)) {
return (AdapterType)new MockContentFragment(resource);
Expand Down
36 changes: 35 additions & 1 deletion core/src/main/java/io/wcm/testing/mock/aem/MockTag.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
import org.apache.jackrabbit.util.ISO9075;
import org.apache.sling.api.adapter.SlingAdaptable;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -299,7 +300,11 @@ private Iterator<Tag> listChildren(Filter<Tag> filter, boolean recurse) {
}

Tag tag = tagResource.adaptTo(Tag.class);
if (tag == null) {
if (tag == null || tags.contains(tag)) {
/*
* may happen when the tag was already added from a previous tag
* that had its cq:movedTo property set to this resource
*/
continue;
}

Expand Down Expand Up @@ -411,4 +416,33 @@ private String extractPathPart(final String property, final BinaryOperator<Strin
: defaultValue,
String.valueOf('@'));
}

/**
* Looks into the cq:movedTo property and returns that resource. If the
* successor tag also has a successor, the process is repeated until a
* final successor is found.
*
* If no successor is found, the resource passed in is returned.
*
* If the chain is broken along the path, TagManager throws an NPE.
*/
static Resource resolveSuccessorOrSelf(final Resource resource) {
final ValueMap valueMap = resource.getValueMap();

final String movedToResourcePath = valueMap.get("cq:movedTo", String.class);
if (movedToResourcePath == null) {
return resource;
}

final ResourceResolver resourceResolver = resource.getResourceResolver();
final Resource successorResource = resourceResolver.getResource(movedToResourcePath);
if (successorResource == null) {
/*
* This is what TagManager does.
*/
throw new NullPointerException();
}

return resolveSuccessorOrSelf(successorResource);
}
}
27 changes: 9 additions & 18 deletions core/src/main/java/io/wcm/testing/mock/aem/MockTagManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import static com.day.cq.tagging.TagConstants.TAG_ROOT_PATH;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
Expand All @@ -34,6 +35,7 @@
import java.util.Map;
import java.util.Queue;
import java.util.Set;
import java.util.stream.Collectors;

import javax.jcr.Session;

Expand Down Expand Up @@ -248,9 +250,9 @@ public RangeIterator<Resource> find(String basePath, String[] tagIDs, boolean on
if (tag == null) {
return null;
}
Resource tagResource = tag.adaptTo(Resource.class);
if (tagResource != null) {
tagPaths.add(tagResource.getPath());

if (!tagPaths.contains(tag.getPath())) {
tagPaths.add(tag.getPath());
}
}

Expand All @@ -265,20 +267,9 @@ public RangeIterator<Resource> find(String basePath, String[] tagIDs, boolean on
CollectionUtils.addAll(searchResources, resource.listChildren());

// now process the tags
String[] resourceTags = resource.getValueMap().get(TagConstants.PN_TAGS, String[].class);
if (resourceTags == null) {
continue;
}

List<String> resourceTagPaths = new ArrayList<>(resourceTags.length);
try {
for (String resourceTag : resourceTags) {
resourceTagPaths.add(getPathFromID(resourceTag));
}
} catch (InvalidTagFormatException e) {
log.error("invalid tag id encountered", e);
}

List<String> resourceTagPaths = Arrays.stream(getTags(resource))
.map(Tag::getPath)
.collect(Collectors.toList());
if (resourceTagPaths.isEmpty()) {
continue;
}
Expand Down Expand Up @@ -338,7 +329,7 @@ private List<Tag> getNamespacesList() {
for (Iterator<Resource> resources = tagRoot.listChildren(); resources.hasNext();) {
Resource resource = resources.next();
Tag tag = resource.adaptTo(Tag.class);
if (tag != null) {
if (tag != null && !namespaces.contains(tag)) {
namespaces.add(tag);
}
}
Expand Down
64 changes: 57 additions & 7 deletions core/src/test/java/io/wcm/testing/mock/aem/MockTagManagerTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import java.util.Arrays;
import java.util.Iterator;
import java.util.Set;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;


import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.resource.PersistenceException;
Expand Down Expand Up @@ -79,9 +85,11 @@ public void testGetPageTags() {
Page page = rootPage;
Tag[] pageTags = page.getTags();
assertNotNull(pageTags);
assertEquals(2, pageTags.length);
assertEquals(4, pageTags.length);
assertTrue(containsPath(pageTags, tagRoot + "/default/tagA"));
assertTrue(containsPath(pageTags, tagRoot + "/wcmio/aem/api"));
assertTrue(containsPath(pageTags, tagRoot + "/wcmio/tag-operations/merged-tag"));
assertTrue(containsPath(pageTags, tagRoot + "/wcmio/moved-tag"));

page = page.listChildren().next();
pageTags = page.getTags();
Expand All @@ -96,6 +104,21 @@ public void testGetPageTags() {
assertEquals(0, pageTags.length);
}

@Test
public void testGetPageTagsMoved() {
Page page = rootPage;
Tag[] pageTags = page.getTags();
Set<String> pageTagPaths = Arrays.stream(page.getContentResource().getValueMap().get(TagConstants.PN_TAGS, String[].class)).collect(Collectors.toSet());

assertTrue(pageTagPaths.contains(tagRoot + "/wcmio/tag-operations/tag-to-be-merged"));
assertFalse(containsPath(pageTags, tagRoot + "/wcmio/tag-operations/tag-to-be-merged"));
assertTrue(containsPath(pageTags, tagRoot + "/wcmio/tag-operations/merged-tag"));

assertTrue(pageTagPaths.contains(tagRoot + "/wcmio/tag-operations/tag-to-be-moved"));
assertFalse(containsPath(pageTags, tagRoot + "/wcmio/tag-operations/tag-to-be-moved"));
assertTrue(containsPath(pageTags, tagRoot + "/wcmio/moved-tag"));
}

@Test
public void testGetNamespaces() {
Tag[] namespaces = tagManager.getNamespaces();
Expand All @@ -106,11 +129,12 @@ public void testGetNamespaces() {

Iterator<Tag> namespacesIter = tagManager.getNamespacesIter();
assertNotNull(namespacesIter);
assertTrue(namespacesIter.hasNext());
assertEquals(tagRoot + "/default", namespacesIter.next().getPath());
assertTrue(namespacesIter.hasNext());
assertEquals(tagRoot + "/wcmio", namespacesIter.next().getPath());
assertFalse(namespacesIter.hasNext());

final Set<String> namespacePaths = StreamSupport.stream(Spliterators.spliteratorUnknownSize(namespacesIter, 0), false)
.map(Tag::getPath)
.collect(Collectors.toSet());
assertTrue(namespacePaths.stream().anyMatch(path -> path.contains("/default")));
assertTrue(namespacePaths.stream().anyMatch(path -> path.contains("/wcmio")));
}

@Test
Expand Down Expand Up @@ -234,6 +258,23 @@ public void testFind() {
assertNull(resources);
}

@Test
public void testFindWithMovedTag() {
RangeIterator<Resource> resources = tagManager.find("/content", new String[]{ "wcmio:moved-tag"}, true);
assertNotNull(resources);
assertTrue(resources.hasNext());
assertEquals(1, resources.getSize());
assertEquals(0, resources.getPosition());
assertEquals("/content/sample/en/jcr:content", resources.next().getPath());

resources = tagManager.find("/content", new String[]{ "wcmio:tag-operations/tag-to-be-moved"}, true);
assertNotNull(resources);
assertTrue(resources.hasNext());
assertEquals(1, resources.getSize());
assertEquals(0, resources.getPosition());
assertEquals("/content/sample/en/jcr:content", resources.next().getPath());
}

@Test
public void testResolve() {
Tag tag = tagManager.resolve("wcmio:");
Expand All @@ -244,12 +285,21 @@ public void testResolve() {
assertNull(tag);
}

@Test
public void testResolveMovedTag() {
final Tag movedTag = tagManager.resolve("wcmio:tag-operations/tag-to-be-moved");
assertNotNull(movedTag);

assertEquals("wcmio:moved-tag", movedTag.getTagID());
assertEquals(tagRoot + "/wcmio/moved-tag", movedTag.getPath());
}

@Test
public void testGetTagsForSubtree() {
Tag[] tags = tagManager.getTagsForSubtree(rootPage.adaptTo(Resource.class), false);

assertNotNull(tags);
assertEquals(4, tags.length);
assertEquals(6, tags.length);
assertTrue(containsPath(tags, tagRoot + "/default/tagA"));
assertTrue(containsPath(tags, tagRoot + "/wcmio/aem/api"));
assertTrue(containsPath(tags, tagRoot + "/default/tagB"));
Expand Down
19 changes: 17 additions & 2 deletions core/src/test/java/io/wcm/testing/mock/aem/MockTagTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
Expand Down Expand Up @@ -121,7 +125,7 @@ public void testListChildren() {
assertEquals(childTag.getName(), childTag.getLocalTagID());
++childCount;
}
assertEquals(3, childCount);
assertEquals(5, childCount);

assertNotNull(aem);
children = aem.listChildren();
Expand Down Expand Up @@ -160,10 +164,21 @@ public void testListChildren() {
assertTrue(childTag.getTagID().startsWith(wcmio.getTagID()));
++childCount;
}
assertEquals(6, childCount);
assertEquals(9, childCount);

}

@Test
public void testListChildrenSkipsMovedTags() {
final Iterator<Tag> children = wcmio.listAllSubTags();
final Set<String> tagPaths = StreamSupport.stream(Spliterators.spliteratorUnknownSize(children, 0), false)
.map(Tag::getPath)
.collect(Collectors.toSet());

assertFalse(tagPaths.contains(tagRoot + "/wcmio/tag-operations/tag-to-be-merged"));
assertFalse(tagPaths.contains(tagRoot + "/wcmio/tag-operations/tag-to-be-moved"));
}

@Test
public void testLocalizedTitles() {
assertNull(wcmio.getLocalizedTitle(null));
Expand Down
9 changes: 8 additions & 1 deletion core/src/test/resources/json-import-samples/content.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,14 @@
"jcr:createdBy": "admin",
"jcr:title": "English",
"cq:template": "/apps/sample/templates/homepage",
"cq:tags": ["/etc/tags/default/tagA", "/etc/tags/wcmio/aem/api","/content/cq:tags/default/tagA", "/content/cq:tags/wcmio/aem/api"],
"cq:tags": [
"/etc/tags/default/tagA",
"/etc/tags/wcmio/aem/api",
"/content/cq:tags/default/tagA",
"/content/cq:tags/wcmio/aem/api",
"/content/cq:tags/wcmio/tag-operations/tag-to-be-moved",
"/content/cq:tags/wcmio/tag-operations/tag-to-be-merged"
],
"jcr:created": "Thu Aug 07 2014 16:32:59 GMT+0200",
"cq:lastModified": "Tue Apr 22 2014 11:11:24 GMT+0200",
"pageTitle": "Sample Homepage",
Expand Down
28 changes: 28 additions & 0 deletions core/src/test/resources/json-import-samples/tags.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,34 @@
"jcr:primaryType": "cq:Tag",
"sling:resourceType": "cq/tagging/components/tag"
}
},
"tag-operations": {
"jcr:primaryType": "cq:Tag",
"sling:resourceType": "cq/tagging/components/tag",
"tag-to-be-merged": {
"jcr:primaryType": "cq:Tag",
"cq:movedTo": "/content/cq:tags/wcmio/tag-operations/merged-tag",
"sling:resourceType": "cq/tagging/components/tag"
},
"merged-tag": {
"jcr:primaryType": "cq:Tag",
"sling:resourceType": "cq/tagging/components/tag",
"cq:backlinks": [
"/content/cq:tags/wcmio/tag-operations/tag-to-be-merged"
]
},
"tag-to-be-moved": {
"jcr:primaryType": "cq:Tag",
"cq:movedTo": "/content/cq:tags/wcmio/moved-tag",
"sling:resourceType": "cq/tagging/components/tag"
}
},
"moved-tag": {
"jcr:primaryType": "cq:Tag",
"sling:resourceType": "cq/tagging/components/tag",
"cq:backlinks": [
"/content/cq:tags/wcmio/tag-operations/tag-to-be-moved"
]
}
}
}

0 comments on commit bed76d4

Please sign in to comment.