You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by bd...@apache.org on 2020/03/26 10:53:00 UTC

[sling-org-apache-sling-jcr-repoinit] branch master updated: SLING-9171 Support setting node properties via repoinit

This is an automated email from the ASF dual-hosted git repository.

bdelacretaz pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-jcr-repoinit.git


The following commit(s) were added to refs/heads/master by this push:
     new ec68d91  SLING-9171 Support setting node properties via repoinit
     new f9f67c2  Merge pull request #10 from nit23uec/master
ec68d91 is described below

commit ec68d91857bb8a8a54b3718eb54bd8b5db5b5728
Author: nitigupt <ni...@adobe.com>
AuthorDate: Tue Mar 24 08:56:56 2020 +0530

    SLING-9171 Support setting node properties via repoinit
---
 pom.xml                                            |   2 +-
 .../sling/jcr/repoinit/impl/DoNothingVisitor.java  |   8 +-
 .../repoinit/impl/JcrRepoInitOpsProcessorImpl.java |   3 +-
 .../jcr/repoinit/impl/NodePropertiesVisitor.java   | 111 +++++++++++++++++++++
 .../sling/jcr/repoinit/SetPropertiesTest.java      | 105 +++++++++++++++++++
 .../apache/sling/jcr/repoinit/impl/TestUtil.java   |  24 +++++
 .../sling/jcr/repoinit/it/RepoInitTextIT.java      |  59 +++++++++++
 .../java/org/apache/sling/jcr/repoinit/it/U.java   |  26 +++++
 src/test/resources/repoinit.txt                    |  18 +++-
 9 files changed, 351 insertions(+), 5 deletions(-)

diff --git a/pom.xml b/pom.xml
index 37960d5..bb0dc28 100644
--- a/pom.xml
+++ b/pom.xml
@@ -220,7 +220,7 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.repoinit.parser</artifactId>
-            <version>1.5.3-SNAPSHOT</version>
+            <version>1.6.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
index 29c5d75..c2ae6ed 100644
--- a/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/DoNothingVisitor.java
@@ -35,8 +35,8 @@ import org.apache.sling.repoinit.parser.operations.RemoveGroupMembers;
 import org.apache.sling.repoinit.parser.operations.SetAclPaths;
 import org.apache.sling.repoinit.parser.operations.SetAclPrincipalBased;
 import org.apache.sling.repoinit.parser.operations.SetAclPrincipals;
-import org.apache.sling.repoinit.parser.operations.AddGroupMembers;
-import org.apache.sling.repoinit.parser.operations.RemoveGroupMembers;
+import org.apache.sling.repoinit.parser.operations.SetProperties;
+
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -132,4 +132,8 @@ class DoNothingVisitor implements OperationVisitor {
     @Override
     public void visitRemoveGroupMembers(RemoveGroupMembers rm) {
     }
+
+    @Override
+    public void visitSetProperties(SetProperties sp) {
+    }
 }
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/JcrRepoInitOpsProcessorImpl.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/JcrRepoInitOpsProcessorImpl.java
index 51347c0..be30809 100644
--- a/src/main/java/org/apache/sling/jcr/repoinit/impl/JcrRepoInitOpsProcessorImpl.java
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/JcrRepoInitOpsProcessorImpl.java
@@ -47,7 +47,8 @@ public class JcrRepoInitOpsProcessorImpl implements JcrRepoInitOpsProcessor {
                 new PrivilegeVisitor(session),
                 new UserVisitor(session),
                 new AclVisitor(session),
-                new GroupMembershipVisitor(session)
+                new GroupMembershipVisitor(session),
+                new NodePropertiesVisitor(session)
         };
 
         for(OperationVisitor v : visitors) {
diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/NodePropertiesVisitor.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/NodePropertiesVisitor.java
new file mode 100644
index 0000000..79b2db9
--- /dev/null
+++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/NodePropertiesVisitor.java
@@ -0,0 +1,111 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.jcr.repoinit.impl;
+
+import java.util.List;
+import java.util.Calendar;
+
+import javax.jcr.Session;
+import javax.jcr.Node;
+import javax.jcr.Value;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.value.BooleanValue;
+import org.apache.jackrabbit.value.DateValue;
+import org.apache.jackrabbit.value.DoubleValue;
+import org.apache.jackrabbit.value.LongValue;
+import org.apache.jackrabbit.value.StringValue;
+
+import org.apache.sling.repoinit.parser.operations.PropertyLine;
+import org.apache.sling.repoinit.parser.operations.SetProperties;
+
+/**
+ * OperationVisitor which processes only operations related to setting node properties. Having several such specialized visitors makes it easy to control the
+ * execution order.
+ */
+class NodePropertiesVisitor extends DoNothingVisitor {
+
+    /**
+     * Create a visitor using the supplied JCR Session.
+     *
+     * @param s must have sufficient rights to set properties on a path.
+     */
+    public NodePropertiesVisitor(Session s) {
+        super(s);
+    }
+
+    @Override
+    public void visitSetProperties(SetProperties sp) {
+        List<String> nodePaths = sp.getPaths();
+        List<PropertyLine> propertyLines = sp.getPropertyLines();
+        for (String nodePath : nodePaths) {
+            try {
+                log.info("Setting properties on nodePath '{}'", nodePath);
+                Node n = session.getNode(nodePath);
+                for (PropertyLine pl : propertyLines) {
+                    String pName = pl.getPropertyName();
+                    PropertyLine.PropertyType pType = pl.getPropertyType();
+                    List<Object> values = pl.getPropertyValues();
+                    boolean setOnlyIfNull = pl.isDefault();
+                    int type = PropertyType.valueFromName(pType.name());
+                    boolean isExistingNonNullProperty = n.hasProperty(pName) && n.getProperty(pName) != null;
+                    if (!setOnlyIfNull || (setOnlyIfNull && !isExistingNonNullProperty)) {
+                        if (values.size() > 1) {
+                            Value[] pValues = convertToValues(values);
+                            n.setProperty(pName, pValues, type);
+                        } else {
+                            Value pValue = convertToValue(values.get(0));
+                            n.setProperty(pName, pValue, type);
+                        }
+                    } else {
+                        log.info("Property '{}' is already set on path '{}'. Will not overwrite the default", pName, nodePath);
+                    }
+                }
+            } catch (RepositoryException e) {
+                report(e, "Unable to set properties on path [" + nodePath + "]:" + e);
+            }
+        }
+    }
+
+    private Value[] convertToValues(List<Object> values) {
+        int size = values.size();
+        Value[] valueArray = new Value[size];
+        for(int i = 0; i < size; i++) {
+           valueArray[i] = convertToValue(values.get(i));
+        }
+        return valueArray;
+    }
+
+    private Value convertToValue(Object value) {
+        Value convertedValue = null;
+        if (value instanceof String) {
+            convertedValue = new StringValue((String) value);
+        } else if (value instanceof Double) {
+            convertedValue = new DoubleValue((Double) value);
+        } else if (value instanceof Long) {
+            convertedValue = new LongValue((Long) value);
+        } else if (value instanceof Boolean) {
+            convertedValue = new BooleanValue((Boolean) value);
+        } else if (value instanceof Calendar) {
+            convertedValue = new DateValue((Calendar) value);
+        } else {
+            throw new RuntimeException("Unable to convert " + value + " to jcr Value");
+        }
+        return convertedValue;
+    }
+}
diff --git a/src/test/java/org/apache/sling/jcr/repoinit/SetPropertiesTest.java b/src/test/java/org/apache/sling/jcr/repoinit/SetPropertiesTest.java
new file mode 100644
index 0000000..2d572fb
--- /dev/null
+++ b/src/test/java/org/apache/sling/jcr/repoinit/SetPropertiesTest.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sling.jcr.repoinit;
+
+import org.apache.sling.commons.testing.jcr.RepositoryUtil;
+import org.apache.sling.jcr.repoinit.impl.TestUtil;
+import org.apache.sling.repoinit.parser.RepoInitParsingException;
+import org.apache.sling.testing.mock.sling.ResourceResolverType;
+import org.apache.sling.testing.mock.sling.junit.SlingContext;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+import javax.jcr.ValueFactory;
+import javax.jcr.Value;
+import java.io.IOException;
+
+
+/** Test the setting of properties on nodes */
+public class SetPropertiesTest {
+    
+    @Rule
+    public final SlingContext context = new SlingContext(ResourceResolverType.JCR_OAK);
+    
+    private TestUtil U;
+    final String path1 = "/one/two/three";
+    final String path2 = "/one/two/four";
+
+    @Before
+    public void setup() throws RepositoryException, IOException, RepoInitParsingException {
+        U = new TestUtil(context);
+        RepositoryUtil.registerSlingNodeTypes(U.adminSession);
+        U.parseAndExecute("create path " + path1);
+        U.assertNodeExists(path1);
+        U.parseAndExecute("create path " + path2);
+        U.assertNodeExists(path2);
+    }
+
+    @Test
+    public void setStringPropertyTest() throws Exception {
+        U.parseAndExecute("set properties on " + path1 + " \n set sling:ResourceType{String} to /x/y/z \n end");
+        ValueFactory vf = U.adminSession.getValueFactory();
+        Value expectedValue = vf.createValue("/x/y/z");
+        U.assertSVPropertyExists(path1, "sling:ResourceType", expectedValue);
+    }
+
+    @Test
+    public void setMultiplePropertiesTest() throws Exception {
+        final String setProps =
+                "set properties on " + path2 + "\n"
+                        + "set sling:ResourceType{String} to /x/y/z \n"
+                        + "set allowedTemplates to /d/e/f/*, m/n/* \n"
+                        + "set someInteger{Long} to 42 \n"
+                        + "set someFlag{Boolean} to true \n"
+                        + "set someDate{Date} to \"2020-03-19T11:39:33.437+05:30\" \n"
+                        + "set customSingleValueQuotedStringProp to \"hello, you!\" \n"
+                + "end"
+                ;
+        U.parseAndExecute(setProps);
+        ValueFactory vf = U.adminSession.getValueFactory();
+        Value expectedValue1 = vf.createValue("/x/y/z");
+        U.assertSVPropertyExists(path2, "sling:ResourceType", expectedValue1);
+        Value[] expectedValues2 = new Value[2];
+        expectedValues2[0] = vf.createValue("/d/e/f/*");
+        expectedValues2[1] = vf.createValue("m/n/*");
+        U.assertMVPropertyExists(path2, "allowedTemplates", expectedValues2);
+        Value expectedValue3 = vf.createValue("42", PropertyType.valueFromName("Long"));
+        U.assertSVPropertyExists(path2, "someInteger", expectedValue3);
+        Value expectedValue4 = vf.createValue("true", PropertyType.valueFromName("Boolean"));
+        U.assertSVPropertyExists(path2, "someFlag", expectedValue4);
+        Value expectedValue5 = vf.createValue("2020-03-19T11:39:33.437+05:30", PropertyType.valueFromName("Date"));
+        U.assertSVPropertyExists(path2, "someDate", expectedValue5);
+        Value expectedValue6 = vf.createValue("hello, you!");
+        U.assertSVPropertyExists(path2, "customSingleValueQuotedStringProp", expectedValue6);
+    }
+
+    @Test
+    public void setPropertyOnNonExistentPathTest() throws Exception {
+        String nonExistingPath =  "/someNonExistingPath/A/B";
+        try {
+            U.parseAndExecute("set properties on " + nonExistingPath + " \n set sling:ResourceType{String} to /x/y/z \n end");
+            Assert.fail();
+        } catch (RuntimeException e) {
+            Assert.assertTrue("expected repository exception",  e.getMessage().contains("Unable to set properties on path [" + nonExistingPath + "]:"));
+        }
+    }
+
+}
diff --git a/src/test/java/org/apache/sling/jcr/repoinit/impl/TestUtil.java b/src/test/java/org/apache/sling/jcr/repoinit/impl/TestUtil.java
index 1e6c6fb..ac4c1d3 100644
--- a/src/test/java/org/apache/sling/jcr/repoinit/impl/TestUtil.java
+++ b/src/test/java/org/apache/sling/jcr/repoinit/impl/TestUtil.java
@@ -29,6 +29,8 @@ import java.util.List;
 import java.util.UUID;
 
 import javax.jcr.Node;
+import javax.jcr.Property;
+import javax.jcr.Value;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
 import javax.jcr.SimpleCredentials;
@@ -192,6 +194,28 @@ public class TestUtil {
         assertEquals(message +  " to be member of " + groupId, expectToBeMember, isMember);
     }
 
+    public void assertSVPropertyExists(String path, String propertyName, Value expectedValue) throws RepositoryException {
+        final Node n = adminSession.getNode(path);
+        if(!n.hasProperty(propertyName)) {
+            fail("No " + propertyName + " property at " + path);
+        } else {
+            Property p = n.getProperty(propertyName);
+            Value actualValue = p.getValue();
+            assertEquals("Value mismatch for property: " + propertyName, actualValue, expectedValue);
+        }
+    }
+
+    public void assertMVPropertyExists(String path, String propertyName, Value[] expectedValues) throws RepositoryException {
+        final Node n = adminSession.getNode(path);
+        if(!n.hasProperty(propertyName)) {
+            fail("No " + propertyName + " property at " + path);
+        } else {
+            Property p = n.getProperty(propertyName);
+            Value[] actualValues = p.getValues();
+            assertEquals("Values mismatch for property: " + propertyName, actualValues, expectedValues);
+        }
+    }
+
     public void parseAndExecute(String input) throws RepositoryException, RepoInitParsingException {
         final JcrRepoInitOpsProcessorImpl p = new JcrRepoInitOpsProcessorImpl();
         p.apply(adminSession, parse(input));
diff --git a/src/test/java/org/apache/sling/jcr/repoinit/it/RepoInitTextIT.java b/src/test/java/org/apache/sling/jcr/repoinit/it/RepoInitTextIT.java
index e137fed..11e2e2b 100644
--- a/src/test/java/org/apache/sling/jcr/repoinit/it/RepoInitTextIT.java
+++ b/src/test/java/org/apache/sling/jcr/repoinit/it/RepoInitTextIT.java
@@ -24,6 +24,9 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.UUID;
 
+import javax.jcr.PropertyType;
+import javax.jcr.ValueFactory;
+import javax.jcr.Value;
 import javax.inject.Inject;
 
 import org.apache.sling.jcr.repoinit.JcrRepoInitOpsProcessor;
@@ -49,6 +52,17 @@ public class RepoInitTextIT extends RepoInitTestSupport {
     private static final String GROUP_A = "grpA";
     private static final String GROUP_B = "grpB";
     private static final String GROUP_C = "grpC";
+    private static final String PROP_A = "pathArray";
+    private static final String PROP_B = "someInteger";
+    private static final String PROP_C = "someFlag";
+    private static final String PROP_D = "someDate";
+    private static final String PROP_E = "customSingleValueStringProp";
+    private static final String PROP_F = "customSingleValueQuotedStringProp";
+    private static final String PROP_G = "stringArray";
+    private static final String PROP_H = "quotedA";
+    private static final String PROP_I = "quotedMix";
+    private static final String PROP_NODE_PATH = "/proptest/X/Y";
+
 
     public static final String REPO_INIT_FILE = "/repoinit.txt";
 
@@ -152,4 +166,49 @@ public class RepoInitTextIT extends RepoInitTestSupport {
             }
         };
     }
+
+    @Test
+    public void setProperties() throws Exception {
+        new Retry() {
+            @Override
+            public Void call() throws Exception {
+                ValueFactory vf = session.getValueFactory();
+                Value[] expectedValues1 = new Value[2];
+                expectedValues1[0] = vf.createValue("/d/e/f/*");
+                expectedValues1[1] = vf.createValue("m/n/*");
+                assertTrue("Expecting array type property " + PROP_A + " to be present ", U.hasProperty(session, PROP_NODE_PATH, PROP_A, expectedValues1));
+
+                Value expectedValue2 = vf.createValue("42", PropertyType.valueFromName("Long"));
+                assertTrue("Expecting Long type default property " + PROP_B + " to be present ", U.hasProperty(session, PROP_NODE_PATH, PROP_B, expectedValue2));
+
+                Value expectedValue3  = vf.createValue("true", PropertyType.valueFromName("Boolean"));
+                assertTrue("Expecting bool type property " + PROP_C + " to be present ", U.hasProperty(session, PROP_NODE_PATH, PROP_C, expectedValue3));
+
+                Value expectedValue4 = vf.createValue("2020-03-19T11:39:33.437+05:30", PropertyType.valueFromName("Date"));
+                assertTrue("Expecting date type property " + PROP_D + " to be present " , U.hasProperty(session, PROP_NODE_PATH, PROP_D, expectedValue4));
+
+                Value expectedValue5 = vf.createValue("test");
+                assertTrue("Expecting string type property " + PROP_E + " to be present " , U.hasProperty(session, PROP_NODE_PATH, PROP_E, expectedValue5));
+
+                Value expectedValue6 = vf.createValue("hello, you!");
+                assertTrue("Expecting quoted string type property " + PROP_F + " to be present " , U.hasProperty(session, PROP_NODE_PATH, PROP_F, expectedValue6));
+
+                Value[] expectedValues7 = new Value[2];
+                expectedValues7[0] = vf.createValue("test1");
+                expectedValues7[1] = vf.createValue("test2");
+                assertTrue("Expecting string array type property " + PROP_G + " to be present " , U.hasProperty(session, PROP_NODE_PATH, PROP_G, expectedValues7));
+
+                Value expectedValue8 = vf.createValue("Here's a \"double quoted string\" with suffix");
+                assertTrue("Expecting quoted string type property " + PROP_H + " to be present " , U.hasProperty(session, PROP_NODE_PATH, PROP_H, expectedValue8));
+
+                Value[] expectedValues9 = new Value[3];
+                expectedValues9[0] = vf.createValue("quoted");
+                expectedValues9[1] = vf.createValue("non-quoted");
+                expectedValues9[2] = vf.createValue("the last \" one");
+                assertTrue("Expecting string array type property " + PROP_I + " to be present " , U.hasProperty(session, PROP_NODE_PATH, PROP_I, expectedValues9));
+
+                return null;
+            }
+        };
+    }
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/jcr/repoinit/it/U.java b/src/test/java/org/apache/sling/jcr/repoinit/it/U.java
index 15ffb04..1a404ff 100644
--- a/src/test/java/org/apache/sling/jcr/repoinit/it/U.java
+++ b/src/test/java/org/apache/sling/jcr/repoinit/it/U.java
@@ -17,10 +17,12 @@
 package org.apache.sling.jcr.repoinit.it;
 
 import java.util.UUID;
+import java.util.Arrays;
 
 import javax.jcr.AccessDeniedException;
 import javax.jcr.LoginException;
 import javax.jcr.Node;
+import javax.jcr.Value;
 import javax.jcr.PathNotFoundException;
 import javax.jcr.RepositoryException;
 import javax.jcr.Session;
@@ -109,4 +111,28 @@ public class U {
         final Authorizable member = ((JackrabbitSession)session).getUserManager().getAuthorizable(userId);
         return ((Group) a).isMember(member);
     }
+
+    public static boolean hasProperty(Session session, String nodePath, String propertyName, Value propertyValue) throws  RepositoryException {
+        final Node n = session.getNode(nodePath);
+        if (n != null) {
+            boolean isPropertyPresent = n.hasProperty(propertyName);
+            if (isPropertyPresent) {
+                Value v = n.getProperty(propertyName).getValue();
+                return isPropertyPresent && (v.equals(propertyValue));
+            }
+        }
+        return false;
+    }
+
+    public static boolean hasProperty(Session session, String nodePath, String propertyName, Value[] propertyValues) throws  RepositoryException {
+        final Node n = session.getNode(nodePath);
+        if (n != null) {
+            boolean isPropertyPresent = n.hasProperty(propertyName);
+            if (isPropertyPresent) {
+                Value[] v = n.getProperty(propertyName).getValues();
+                return isPropertyPresent && Arrays.equals(v, propertyValues);
+            }
+        }
+        return false;
+    }
 }
\ No newline at end of file
diff --git a/src/test/resources/repoinit.txt b/src/test/resources/repoinit.txt
index 2faea12..d6fd943 100644
--- a/src/test/resources/repoinit.txt
+++ b/src/test/resources/repoinit.txt
@@ -20,6 +20,9 @@
 # Paths on which ACLs will be set
 create path /acltest/A/B
 
+# Path on which node properties will be set
+create path /proptest/X/Y(nt:unstructured)
+
 # Service users and ACLs for our integration tests
 create service user fredWilmaService
 create service user anotherService
@@ -65,4 +68,17 @@ add anotherService,bob,grpA to group grpB
 remove bob,grpA from group grpB
 
 create group grpC
-add grpA, bob, grpB to group grpC
\ No newline at end of file
+add grpA, bob, grpB to group grpC
+
+set properties on /proptest/X/Y
+  set pathArray to /d/e/f/*, m/n/*
+  default someInteger{Long} to 42
+  set someFlag{Boolean} to true
+  default someDate{Date} to "2020-03-19T11:39:33.437+05:30"
+  set customSingleValueStringProp to test
+  set customSingleValueQuotedStringProp to "hello, you!"
+  set stringArray to test1, test2
+  default someInteger{Long} to 65
+  set quotedA to "Here's a \"double quoted string\" with suffix"
+  set quotedMix to "quoted", non-quoted, "the last \" one"
+end
\ No newline at end of file