You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by en...@apache.org on 2023/05/15 16:02:31 UTC
[sling-org-apache-sling-testing-jcr-mock] branch master updated: SLING-11874 auto-set created/lastModified prop values when appropriate (#23)
This is an automated email from the ASF dual-hosted git repository.
enorman pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-testing-jcr-mock.git
The following commit(s) were added to refs/heads/master by this push:
new dbfcc6b SLING-11874 auto-set created/lastModified prop values when appropriate (#23)
dbfcc6b is described below
commit dbfcc6bc20a857d5397d0d91de80601c1beee05f
Author: Eric Norman <en...@apache.org>
AuthorDate: Mon May 15 09:02:25 2023 -0700
SLING-11874 auto-set created/lastModified prop values when appropriate (#23)
---
.../apache/sling/testing/mock/jcr/MockNode.java | 95 ++++++++++++-------
.../mock/jcr/MockNodeOnlyRegisteredTypesTest.java | 3 +
.../sling/testing/mock/jcr/MockNodeTest.java | 69 ++++++++++++++
.../sling/testing/mock/jcr/MockNodeTypeTest.java | 13 ++-
.../sling/testing/mock/jcr/test_nodetypes.cnd | 104 +++++++++++++++++++++
5 files changed, 249 insertions(+), 35 deletions(-)
diff --git a/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java b/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java
index 56a88e7..485f56e 100644
--- a/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java
+++ b/src/main/java/org/apache/sling/testing/mock/jcr/MockNode.java
@@ -59,6 +59,12 @@ import org.apache.sling.testing.mock.jcr.MockNodeTypeManager.ResolveMode;
* Mock {@link Node} implementation
*/
class MockNode extends AbstractItem implements Node {
+ // constants for some well-known types and properties
+ private static final String MIX_CREATED = "mix:created";
+ private static final String JCR_CREATEDBY = "jcr:createdBy";
+
+ private static final String MIX_LASTMODIFIED = "mix:lastModified";
+ private static final String JCR_LASTMODIFIEDBY = "jcr:lastModifiedBy";
public MockNode(final ItemData itemData, final Session session) {
super(itemData, session);
@@ -81,26 +87,25 @@ class MockNode extends AbstractItem implements Node {
if (((MockNodeTypeManager)getSession().getWorkspace().getNodeTypeManager()).isMode(ResolveMode.ONLY_REGISTERED)) {
// auto-add any autocreated children
NodeDefinition[] childNodeDefinitions = nodeType.getChildNodeDefinitions();
- if (childNodeDefinitions != null) {
- for (NodeDefinition nodeDefinition : childNodeDefinitions) {
- if (nodeDefinition.isAutoCreated()) {
- String defaultPrimaryTypeName = nodeDefinition.getDefaultPrimaryTypeName();
- node.addNode(nodeDefinition.getName(), defaultPrimaryTypeName);
- }
+ for (NodeDefinition nodeDefinition : childNodeDefinitions) {
+ if (nodeDefinition.isAutoCreated()) {
+ String defaultPrimaryTypeName = nodeDefinition.getDefaultPrimaryTypeName();
+ node.addNode(nodeDefinition.getName(), defaultPrimaryTypeName);
}
}
// auto-add any autocreated properties
PropertyDefinition[] propDefinitions = nodeType.getPropertyDefinitions();
- if (propDefinitions != null) {
- for (PropertyDefinition propDefinition : propDefinitions) {
- if (propDefinition.isAutoCreated()) {
- Value[] defaultValues = propDefinition.getDefaultValues();
- if (propDefinition.isMultiple()) {
- node.setProperty(propDefinition.getName(), defaultValues);
- } else if (defaultValues.length > 0) {
- node.setProperty(propDefinition.getName(), defaultValues[0]);
- }
+ for (PropertyDefinition propDefinition : propDefinitions) {
+ if (propDefinition.isAutoCreated()) {
+ Value[] defaultValues = propDefinition.getDefaultValues();
+ if (propDefinition.isMultiple()) {
+ node.setProperty(propDefinition.getName(), defaultValues);
+ } else if (defaultValues.length > 0) {
+ node.setProperty(propDefinition.getName(), defaultValues[0]);
+ } else {
+ // compute system generated property values
+ maybeSetGeneratedPropertyValue(node, propDefinition);
}
}
}
@@ -109,12 +114,39 @@ class MockNode extends AbstractItem implements Node {
// special handling for some node types
if (StringUtils.equals(primaryNodeTypeName, JcrConstants.NT_FILE)) {
node.setProperty(JcrConstants.JCR_CREATED, Calendar.getInstance());
- node.setProperty("jcr:createdBy", getMockedSession().getUserID());
+ node.setProperty(JCR_CREATEDBY, getMockedSession().getUserID());
}
return node;
}
+ /**
+ * Sets a system generated value if the specified property definition is known
+ * to need it.
+ *
+ * @param node the node to set property on
+ * @param propDefinition the property definition to consider
+ */
+ private void maybeSetGeneratedPropertyValue(Node node, PropertyDefinition propDefinition)
+ throws RepositoryException {
+ String name = propDefinition.getName();
+ String declaringNT = propDefinition.getDeclaringNodeType().getName();
+ if (JcrConstants.JCR_CREATED.equals(name) &&
+ (MIX_CREATED.equals(declaringNT) || JcrConstants.NT_VERSION.equals(declaringNT))) {
+ // jcr:created property of a version or a mix:created
+ node.setProperty(name, Calendar.getInstance());
+ } else if (JCR_CREATEDBY.equals(name) && MIX_CREATED.equals(declaringNT)) {
+ // jcr:createdBy property of a mix:created
+ node.setProperty(name, getSession().getUserID());
+ } else if (JcrConstants.JCR_LASTMODIFIED.equals(name) && MIX_LASTMODIFIED.equals(declaringNT)) {
+ // jcr:lastModified property of a mix:lastModified
+ node.setProperty(name, Calendar.getInstance());
+ } else if (JCR_LASTMODIFIEDBY.equals(name) && MIX_LASTMODIFIED.equals(declaringNT)) {
+ // jcr:lastModifiedBy property of a mix:lastModified
+ node.setProperty(name, getSession().getUserID());
+ }
+ }
+
@Override
public Node getNode(final String relPath) throws RepositoryException {
String path = makeAbsolutePath(relPath);
@@ -511,31 +543,30 @@ class MockNode extends AbstractItem implements Node {
).toArray(Value[]::new);
this.setProperty(JcrConstants.JCR_MIXINTYPES, newValues);
}
-
+
if (((MockNodeTypeManager)getSession().getWorkspace().getNodeTypeManager()).isMode(ResolveMode.ONLY_REGISTERED)) {
NodeType mixinNodeType = getSession().getWorkspace().getNodeTypeManager().getNodeType(mixinName);
// auto-add any autocreated children
NodeDefinition[] childNodeDefinitions = mixinNodeType.getChildNodeDefinitions();
- if (childNodeDefinitions != null) {
- for (NodeDefinition nodeDefinition : childNodeDefinitions) {
- if (nodeDefinition.isAutoCreated()) {
- String defaultPrimaryTypeName = nodeDefinition.getDefaultPrimaryTypeName();
- addNode(nodeDefinition.getName(), defaultPrimaryTypeName);
- }
+ for (NodeDefinition nodeDefinition : childNodeDefinitions) {
+ if (nodeDefinition.isAutoCreated()) {
+ String defaultPrimaryTypeName = nodeDefinition.getDefaultPrimaryTypeName();
+ addNode(nodeDefinition.getName(), defaultPrimaryTypeName);
}
}
// auto-add any autocreated properties
PropertyDefinition[] propDefinitions = mixinNodeType.getPropertyDefinitions();
- if (propDefinitions != null) {
- for (PropertyDefinition propDefinition : propDefinitions) {
- if (propDefinition.isAutoCreated()) {
- Value[] defaultValues = propDefinition.getDefaultValues();
- if (propDefinition.isMultiple()) {
- setProperty(propDefinition.getName(), defaultValues);
- } else if (defaultValues.length > 0) {
- setProperty(propDefinition.getName(), defaultValues[0]);
- }
+ for (PropertyDefinition propDefinition : propDefinitions) {
+ if (propDefinition.isAutoCreated()) {
+ Value[] defaultValues = propDefinition.getDefaultValues();
+ if (propDefinition.isMultiple()) {
+ setProperty(propDefinition.getName(), defaultValues);
+ } else if (defaultValues.length > 0) {
+ setProperty(propDefinition.getName(), defaultValues[0]);
+ } else {
+ // compute system generated property values
+ maybeSetGeneratedPropertyValue(this, propDefinition);
}
}
}
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeOnlyRegisteredTypesTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeOnlyRegisteredTypesTest.java
index 63f2408..b56e456 100644
--- a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeOnlyRegisteredTypesTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeOnlyRegisteredTypesTest.java
@@ -19,6 +19,7 @@
package org.apache.sling.testing.mock.jcr;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -67,7 +68,9 @@ public class MockNodeOnlyRegisteredTypesTest {
auto1.addMixin("mix:autocreatedChildAndProp");
assertTrue(auto1.hasProperty("prop1"));
assertTrue(auto1.hasProperty("prop2"));
+ assertFalse(auto1.hasProperty("prop3"));
assertTrue(auto1.hasNode("child1"));
+ assertFalse(auto1.hasNode("child2"));
}
@Test
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java
index 6fc055d..e9976f9 100644
--- a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTest.java
@@ -26,6 +26,9 @@ import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
@@ -44,6 +47,7 @@ import javax.jcr.nodetype.NoSuchNodeTypeException;
import javax.jcr.util.TraversingItemVisitor;
import org.apache.jackrabbit.JcrConstants;
+import org.apache.jackrabbit.commons.cnd.ParseException;
import org.junit.Assert;
import org.junit.Test;
@@ -407,4 +411,69 @@ public class MockNodeTest extends AbstractItemTest {
assertEquals(4, leaveProperties.size());
}
+ /**
+ * load the node types and switch the nodetypemanager to ONLY_REGISTERED mode
+ */
+ protected void loadNodeTypes() throws ParseException, RepositoryException, IOException {
+ // load the node types and switch the nodetypemanager to ONLY_REGISTERED mode
+ try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("test_nodetypes.cnd"))) {
+ MockJcr.loadNodeTypeDefs(this.session, reader);
+ }
+ }
+
+ /**
+ * SLING-11874 auto-set created/lastModified prop values when appropriate
+ */
+ @Test
+ public void testAddNodeCreatesSystemGeneratedAutoPropertyValues() throws RepositoryException, IOException, ParseException {
+ loadNodeTypes();
+ Node foo = this.session.getRootNode().addNode("foo", JcrConstants.NT_HIERARCHYNODE);
+ Node versionFoo = this.session.getRootNode().addNode("versionFoo", JcrConstants.NT_VERSION);
+
+ // verify that the autocreated props exist
+ assertTrue(foo.hasProperty(JcrConstants.JCR_CREATED));
+ assertTrue(foo.hasProperty("jcr:createdBy"));
+ assertTrue(versionFoo.hasProperty(JcrConstants.JCR_CREATED));
+ }
+
+ /**
+ * SLING-11874 auto-set created/lastModified prop values when appropriate
+ */
+ @Test
+ public void testAddMixinCreatesSystemGeneratedAutoPropertyValues() throws RepositoryException, IOException, ParseException {
+ loadNodeTypes();
+
+ Node foo = this.session.getRootNode().addNode("foo", JcrConstants.NT_UNSTRUCTURED);
+ // verify that the autocreated props do not exist
+ assertFalse(foo.hasProperty(JcrConstants.JCR_CREATED));
+ assertFalse(foo.hasProperty("jcr:createdBy"));
+ assertFalse(foo.hasProperty(JcrConstants.JCR_LASTMODIFIED));
+ assertFalse(foo.hasProperty("jcr:lastModifiedBy"));
+
+ // add mixins
+ foo.addMixin("mix:created");
+ foo.addMixin("mix:lastModified");
+
+ // verify that the autocreated props do exist
+ assertTrue(foo.hasProperty(JcrConstants.JCR_CREATED));
+ assertTrue(foo.hasProperty("jcr:createdBy"));
+ assertTrue(foo.hasProperty(JcrConstants.JCR_LASTMODIFIED));
+ assertTrue(foo.hasProperty("jcr:lastModifiedBy"));
+ }
+
+ /**
+ * SLING-11874 To test created and lastModified props are not autocreated when not declared in their mixins
+ */
+ @Test
+ public void testAddNodeDoesNotCreateNonMixinCreatedAndLastModified() throws RepositoryException, IOException, ParseException {
+ loadNodeTypes();
+
+ Node foo = this.session.getRootNode().addNode("foo", "nt:nonMixinCreatedAndLastModified");
+ // verify that the autocreated props do not exist
+ assertFalse(foo.hasProperty(JcrConstants.JCR_CREATED));
+ assertFalse(foo.hasProperty("jcr:createdBy"));
+ assertFalse(foo.hasProperty(JcrConstants.JCR_LASTMODIFIED));
+ assertFalse(foo.hasProperty("jcr:lastModifiedBy"));
+ }
+
}
diff --git a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTypeTest.java b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTypeTest.java
index b11b9fe..94f3096 100644
--- a/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTypeTest.java
+++ b/src/test/java/org/apache/sling/testing/mock/jcr/MockNodeTypeTest.java
@@ -24,6 +24,8 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.util.HashSet;
+import java.util.Set;
import java.util.stream.Stream;
import javax.jcr.RepositoryException;
@@ -212,9 +214,14 @@ public class MockNodeTypeTest extends AbstractMockNodeTypeTest {
NodeType mixCreated = nodeTypeManager.getNodeType("mix:created");
NodeTypeIterator subtypes = mixCreated.getSubtypes();
assertEquals(3, subtypes.getSize());
- assertEquals(JcrConstants.NT_FILE, subtypes.nextNodeType().getName());
- assertEquals(JcrConstants.NT_HIERARCHYNODE, subtypes.nextNodeType().getName());
- assertEquals(JcrConstants.NT_FOLDER, subtypes.nextNodeType().getName());
+ // convert to a set to avoid iteration order troubles
+ Set<String> subtypesSet = new HashSet<>();
+ while (subtypes.hasNext()) {
+ subtypesSet.add(subtypes.nextNodeType().getName());
+ }
+ assertTrue(subtypesSet.contains(JcrConstants.NT_FILE));
+ assertTrue(subtypesSet.contains(JcrConstants.NT_HIERARCHYNODE));
+ assertTrue(subtypesSet.contains(JcrConstants.NT_FOLDER));
NodeType ntFolder = nodeTypeManager.getNodeType(JcrConstants.NT_FOLDER);
subtypes = ntFolder.getSubtypes();
diff --git a/src/test/resources/org/apache/sling/testing/mock/jcr/test_nodetypes.cnd b/src/test/resources/org/apache/sling/testing/mock/jcr/test_nodetypes.cnd
index 35985d0..689b837 100644
--- a/src/test/resources/org/apache/sling/testing/mock/jcr/test_nodetypes.cnd
+++ b/src/test/resources/org/apache/sling/testing/mock/jcr/test_nodetypes.cnd
@@ -152,6 +152,100 @@
mixin
- jcr:uuid (STRING) mandatory autocreated protected INITIALIZE
+//------------------------------------------------------------------------------
+// V E R S I O N I N G
+//------------------------------------------------------------------------------
+
+/**
+ * @since 2.0
+ */
+[mix:simpleVersionable]
+ mixin
+ - jcr:isCheckedOut (BOOLEAN) = 'true' mandatory autocreated protected IGNORE
+
+/**
+ * @since 1.0
+ */
+[mix:versionable] > mix:simpleVersionable, mix:referenceable
+ mixin
+ - jcr:versionHistory (REFERENCE) mandatory protected IGNORE < 'nt:versionHistory'
+ - jcr:baseVersion (REFERENCE) mandatory protected IGNORE < 'nt:version'
+ - jcr:predecessors (REFERENCE) mandatory protected multiple IGNORE < 'nt:version'
+ - jcr:mergeFailed (REFERENCE) protected multiple ABORT < 'nt:version'
+ /** @since 2.0 */
+ - jcr:activity (REFERENCE) protected < 'nt:activity'
+ /** @since 2.0 */
+ - jcr:configuration (REFERENCE) protected IGNORE < 'nt:configuration'
+
+/**
+ * @since 1.0
+ */
+[nt:versionHistory] > mix:referenceable
+ - jcr:versionableUuid (STRING) mandatory autocreated protected ABORT
+ /** @since 2.0 */
+ - jcr:copiedFrom (WEAKREFERENCE) protected ABORT < 'nt:version'
+ + jcr:rootVersion (nt:version) = nt:version mandatory autocreated protected ABORT
+ + jcr:versionLabels (nt:versionLabels) = nt:versionLabels mandatory autocreated protected ABORT
+ + * (nt:version) = nt:version protected ABORT
+
+/**
+ * @since 1.0
+ */
+[nt:versionLabels]
+ - * (REFERENCE) protected ABORT < 'nt:version'
+
+/**
+ * @since 1.0
+ */
+[nt:version] > mix:referenceable
+ - jcr:created (DATE) mandatory autocreated protected ABORT
+ - jcr:predecessors (REFERENCE) protected multiple ABORT < 'nt:version'
+ - jcr:successors (REFERENCE) protected multiple ABORT < 'nt:version'
+ /** @since 2.0 */
+ - jcr:activity (REFERENCE) protected ABORT < 'nt:activity'
+ + jcr:frozenNode (nt:frozenNode) protected ABORT
+
+/**
+ * @since 1.0
+ */
+[nt:frozenNode]
+ orderable
+ - jcr:frozenPrimaryType (NAME) mandatory autocreated protected ABORT
+ - jcr:frozenMixinTypes (NAME) protected multiple ABORT
+ - jcr:frozenUuid (STRING) mandatory autocreated protected ABORT
+ - * (UNDEFINED) protected ABORT
+ - * (UNDEFINED) protected multiple ABORT
+ + * (nt:base) protected sns ABORT
+
+/**
+ * @since 1.0
+ */
+[nt:versionedChild]
+ - jcr:childVersionHistory (REFERENCE) mandatory autocreated protected ABORT < 'nt:versionHistory'
+
+/**
+ * @since 2.0
+ */
+[nt:activity] > mix:referenceable
+ - jcr:activityTitle (STRING) mandatory autocreated protected
+
+/**
+ * @since 2.0
+ */
+[nt:configuration] > mix:versionable
+ - jcr:root (REFERENCE) mandatory autocreated protected
+
+/**
+ * @since oak 1.0
+ */
+ [rep:VersionablePaths]
+ mixin
+ - * (PATH) protected ABORT
+
+
+//------------------------------------------------------------------------------
+// O T H E R
+//------------------------------------------------------------------------------
/**
* For code coverage of the cnd loading
@@ -173,6 +267,16 @@
[mix:autocreatedChildAndProp]
mixin
+ child1 (nt:base) = nt:folder autocreated
+ + child2 (nt:base) = nt:folder
- prop1 (STRING) = 'value1' autocreated
- prop2 (STRING) = 'value1', 'value2' multiple autocreated
+ - prop3 (STRING) = 'value1'
+/**
+ * To test created and lastModified props are not autocreated when not declared in their mixins
+ */
+[nt:nonMixinCreatedAndLastModified]
+ - jcr:created (DATE) autocreated
+ - jcr:createdBy (STRING) autocreated
+ - jcr:lastModified (DATE) autocreated
+ - jcr:lastModifiedBy (STRING) autocreated