You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by np...@apache.org on 2021/10/05 15:22:24 UTC

[sling-org-apache-sling-pipes] branch master updated: SLING-10846 introduce markWithJcrLastModified api

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

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


The following commit(s) were added to refs/heads/master by this push:
     new de3179f  SLING-10846 introduce markWithJcrLastModified api
de3179f is described below

commit de3179f618476c40de8b832b1165b31364384759
Author: Nicolas Peltier <np...@apache.org>
AuthorDate: Tue Oct 5 17:21:01 2021 +0200

    SLING-10846 introduce markWithJcrLastModified api
    
    - new plumber api markWithJcrLastModified that writes date, user, and eventually pipe path (if configured, defaults to false),
    - usage of api in write pipe, when properties are written (we might miss some cases, or update too much in other case but for now that can work),
    - usage of api in path pipe, only in case the resource is created
---
 src/main/java/org/apache/sling/pipes/Plumber.java  |  7 +++++
 .../org/apache/sling/pipes/internal/PathPipe.java  |  4 +++
 .../apache/sling/pipes/internal/PlumberImpl.java   | 23 +++++++++++++++-
 .../org/apache/sling/pipes/internal/WritePipe.java |  5 ++++
 .../java/org/apache/sling/pipes/package-info.java  |  2 +-
 .../org/apache/sling/pipes/AbstractPipeTest.java   |  1 +
 .../apache/sling/pipes/internal/PathPipeTest.java  | 25 +++++++++++++++--
 .../apache/sling/pipes/internal/WritePipeTest.java | 31 +++++++++++++++-------
 8 files changed, 85 insertions(+), 13 deletions(-)

diff --git a/src/main/java/org/apache/sling/pipes/Plumber.java b/src/main/java/org/apache/sling/pipes/Plumber.java
index c625c6c..a2fc9f0 100644
--- a/src/main/java/org/apache/sling/pipes/Plumber.java
+++ b/src/main/java/org/apache/sling/pipes/Plumber.java
@@ -20,6 +20,7 @@ import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
 import org.apache.sling.event.jobs.Job;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.osgi.annotation.versioning.ProviderType;
 
@@ -169,6 +170,12 @@ public interface Plumber {
      */
     @Nullable Resource getReferencedResource(Resource referrer, String reference);
 
+    /**
+     * marks a given resource as updated
+     * @param resource resource to mark
+     */
+    void markWithJcrLastModified(@NotNull Pipe pipe, @NotNull Resource resource);
+
     /*
      * Generates unique pipe path for persistence sake
      */
diff --git a/src/main/java/org/apache/sling/pipes/internal/PathPipe.java b/src/main/java/org/apache/sling/pipes/internal/PathPipe.java
index e804c11..e51b476 100644
--- a/src/main/java/org/apache/sling/pipes/internal/PathPipe.java
+++ b/src/main/java/org/apache/sling/pipes/internal/PathPipe.java
@@ -80,6 +80,7 @@ public class PathPipe extends BasePipe {
         try {
             String path = isRootPath(expr) ? expr : getInput().getPath() + SLASH + expr;
             logger.info("creating path {}", path);
+            boolean modified = resolver.getResource(path) == null;
             if (!isDryRun()) {
                 if (StringUtils.isNotBlank(nodeType)) {
                     //in that case we are in a "JCR" mode
@@ -88,6 +89,9 @@ public class PathPipe extends BasePipe {
                     ResourceUtil.getOrCreateResource(resolver, path, resourceType, intermediateType, autosave);
                 }
                 Resource resource = resolver.getResource(path);
+                if (modified) {
+                    plumber.markWithJcrLastModified(this, resource);
+                }
                 output = Collections.singleton(resource).iterator();
             }
         } catch (PersistenceException | RepositoryException e) {
diff --git a/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java b/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java
index 855afe7..2e1c948 100644
--- a/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java
+++ b/src/main/java/org/apache/sling/pipes/internal/PlumberImpl.java
@@ -48,6 +48,7 @@ import org.apache.sling.pipes.PipeExecutor;
 import org.apache.sling.pipes.Plumber;
 import org.apache.sling.pipes.PlumberMXBean;
 import org.apache.sling.pipes.internal.bindings.ConfigurationMap;
+import org.jetbrains.annotations.NotNull;
 import org.jetbrains.annotations.Nullable;
 import org.osgi.service.component.annotations.Activate;
 import org.osgi.service.component.annotations.Component;
@@ -85,6 +86,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.UUID;
 
+import static org.apache.jackrabbit.JcrConstants.JCR_LASTMODIFIED;
 import static org.apache.sling.api.resource.ResourceResolverFactory.SUBSERVICE;
 import static org.apache.sling.pipes.BasePipe.PN_STATUS;
 import static org.apache.sling.pipes.BasePipe.PN_STATUS_MODIFIED;
@@ -114,7 +116,9 @@ public class PlumberImpl implements Plumber, JobConsumer, PlumberMXBean, Runnabl
 
     static final String PERMISSION_EXECUTION = "/system/sling/permissions/pipes/exec";
 
-    static final int MAX_LENGTH = 1000;
+    static final String JCR_LAST_MODIFIED_BY = JCR_LASTMODIFIED + "By";
+
+    static final String JCR_LAST_MODIFIED_BY_PIPE = JCR_LAST_MODIFIED_BY + "Pipe";
 
     public static final String PIPES_REPOSITORY_PATH = "/var/pipes";
 
@@ -141,6 +145,9 @@ public class PlumberImpl implements Plumber, JobConsumer, PlumberMXBean, Runnabl
         @AttributeDefinition(description = "max age (in days) of automatically generated pipe persistence")
         int maxAge() default 31;
 
+        @AttributeDefinition(description = "should add pipe path to updated properties")
+        boolean mark_pipe_path() default false;
+
         @AttributeDefinition(description = "schedule of purge process")
         String scheduler_expression() default "0 0 12 */7 * ?";
     }
@@ -278,6 +285,20 @@ public class PlumberImpl implements Plumber, JobConsumer, PlumberMXBean, Runnabl
     }
 
     @Override
+    public void markWithJcrLastModified(@NotNull Pipe pipe, @NotNull Resource resource) {
+        if (!pipe.isDryRun()) {
+            ModifiableValueMap mvm = resource.adaptTo(ModifiableValueMap.class);
+            if (mvm != null) {
+                mvm.put(JCR_LASTMODIFIED, Calendar.getInstance());
+                mvm.put(JCR_LAST_MODIFIED_BY, resource.getResourceResolver().getUserID());
+                if (configuration.mark_pipe_path()) {
+                    mvm.put(JCR_LAST_MODIFIED_BY_PIPE, pipe.getResource().getPath());
+                }
+            }
+        }
+    }
+
+    @Override
     public Map<String, Object> getBindingsFromRequest(SlingHttpServletRequest request, boolean writeAllowed) throws IOException
     {
         Map<String, Object> bindings = new HashMap<>();
diff --git a/src/main/java/org/apache/sling/pipes/internal/WritePipe.java b/src/main/java/org/apache/sling/pipes/internal/WritePipe.java
index 7e243d6..bb0fad7 100644
--- a/src/main/java/org/apache/sling/pipes/internal/WritePipe.java
+++ b/src/main/java/org/apache/sling/pipes/internal/WritePipe.java
@@ -165,6 +165,7 @@ public class WritePipe extends BasePipe {
     private void copyProperties(@Nullable Resource conf, Resource target)  {
         ValueMap writeMap = conf != null ? conf.adaptTo(ValueMap.class) : null;
         ModifiableValueMap targetProperties = target.adaptTo(ModifiableValueMap.class);
+        boolean modified = false;
 
         //writing current node
         if (properties != null && writeMap != null) {
@@ -173,9 +174,13 @@ public class WritePipe extends BasePipe {
                     String key = parent != null ? bindings.instantiateExpression(entry.getKey()) : entry.getKey();
                     Object value = computeValue(target, key, entry.getValue());
                     copyProperty(targetProperties, target, key, value);
+                    modified = true;
                 }
             }
         }
+        if (modified) {
+            plumber.markWithJcrLastModified(this, target);
+        }
     }
 
     /**
diff --git a/src/main/java/org/apache/sling/pipes/package-info.java b/src/main/java/org/apache/sling/pipes/package-info.java
index 711bf13..cc775f0 100644
--- a/src/main/java/org/apache/sling/pipes/package-info.java
+++ b/src/main/java/org/apache/sling/pipes/package-info.java
@@ -14,7 +14,7 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-@Version("4.2.0")
+@Version("4.3.0")
 package org.apache.sling.pipes;
 
 import org.osgi.annotation.versioning.Version;
diff --git a/src/test/java/org/apache/sling/pipes/AbstractPipeTest.java b/src/test/java/org/apache/sling/pipes/AbstractPipeTest.java
index 105ec2f..683a6ec 100644
--- a/src/test/java/org/apache/sling/pipes/AbstractPipeTest.java
+++ b/src/test/java/org/apache/sling/pipes/AbstractPipeTest.java
@@ -79,6 +79,7 @@ public class AbstractPipeTest {
         context.registerInjectActivateService(plumber, "authorizedUsers", new String[]{},
                 "bufferSize", PlumberImpl.DEFAULT_BUFFER_SIZE,
                 "executionPermissionResource", PATH_FRUITS,
+                "mark.pipe.path",true,
                 "referencesPaths", new String [] { "/conf/global/sling/pipes", "/apps/scripts" });
         plumber.registerPipe("slingPipes/dummyNull", DummyNull.class);
         plumber.registerPipe("slingPipes/dummySearch", DummySearch.class);
diff --git a/src/test/java/org/apache/sling/pipes/internal/PathPipeTest.java b/src/test/java/org/apache/sling/pipes/internal/PathPipeTest.java
index afde834..7265b5e 100644
--- a/src/test/java/org/apache/sling/pipes/internal/PathPipeTest.java
+++ b/src/test/java/org/apache/sling/pipes/internal/PathPipeTest.java
@@ -19,9 +19,9 @@ package org.apache.sling.pipes.internal;
 import org.apache.sling.api.resource.PersistenceException;
 import org.apache.sling.api.resource.Resource;
 import org.apache.sling.api.resource.ResourceResolver;
+import org.apache.sling.api.resource.ValueMap;
 import org.apache.sling.pipes.AbstractPipeTest;
 import org.apache.sling.pipes.Pipe;
-import org.junit.Ignore;
 import org.junit.Test;
 
 import javax.jcr.Node;
@@ -29,9 +29,12 @@ import javax.jcr.Node;
 import static org.apache.sling.jcr.resource.JcrResourceConstants.NT_SLING_FOLDER;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import java.lang.reflect.InvocationTargetException;
+import java.time.Instant;
+import java.util.Calendar;
+
 /**
  * Testing path pipe using pipe builder
  */
@@ -73,4 +76,22 @@ public class PathPipeTest extends AbstractPipeTest {
         plumber.newPipe(resolver).echo(PATH_FRUITS).mkdir(WATERMELON_RELATIVEPATH).run();
         assertNotNull("Resource should be here & saved", resolver.getResource(WATERMELON_FULL_PATH));
     }
+    @Test
+    public void testJcrMark() throws InvocationTargetException, IllegalAccessException {
+        Instant now = Instant.now();
+        String path = "/content/my/new/path";
+        execute("mkdir " + path);
+        ValueMap fruits = context.resourceResolver().getResource(path).adaptTo(ValueMap.class);
+        assertNotNull(fruits.get("jcr:lastModified", Calendar.class));
+        Instant modified = Instant.ofEpochMilli(fruits.get("jcr:lastModified", Calendar.class).getTimeInMillis());
+        assertTrue(modified.isAfter(now));
+        assertNotNull(fruits.get("jcr:lastModifiedBy", String.class));
+        //we configured the plumber to mark pipe path
+        assertNotNull(fruits.get("jcr:lastModifiedByPipe", String.class));
+        execute("mkdir " + path);
+        fruits = context.resourceResolver().getResource(path).adaptTo(ValueMap.class);
+        Instant modifiedAgain = Instant.ofEpochMilli(fruits.get("jcr:lastModified", Calendar.class).getTimeInMillis());
+        assertEquals("path should not mark *again* a path already created", modified, modifiedAgain);
+    }
+    
 }
\ No newline at end of file
diff --git a/src/test/java/org/apache/sling/pipes/internal/WritePipeTest.java b/src/test/java/org/apache/sling/pipes/internal/WritePipeTest.java
index 5914f74..9db23a4 100644
--- a/src/test/java/org/apache/sling/pipes/internal/WritePipeTest.java
+++ b/src/test/java/org/apache/sling/pipes/internal/WritePipeTest.java
@@ -31,6 +31,7 @@ import javax.jcr.Node;
 import javax.jcr.NodeIterator;
 import javax.jcr.RepositoryException;
 import java.lang.reflect.InvocationTargetException;
+import java.time.Instant;
 import java.util.Calendar;
 import java.util.GregorianCalendar;
 import java.util.Iterator;
@@ -67,20 +68,19 @@ public class WritePipeTest extends AbstractPipeTest {
         assertTrue("this pipe should be marked as content modifier", pipe.modifiesContent());
         pipe.getOutput();
         context.resourceResolver().commit();
-        ValueMap properties =  context.resourceResolver().getResource(PATH_APPLE).adaptTo(ValueMap.class);
+        ValueMap properties = context.resourceResolver().getResource(PATH_APPLE).adaptTo(ValueMap.class);
         assertTrue("There should be hasSeed set to true", properties.get("hasSeed", false));
         assertArrayEquals("Colors should be correctly set", new String[]{"green", "red"}, properties.get("colors", String[].class));
         assertFalse("worm property should be gone (${null} conf)", properties.get("worm", false));
     }
 
     /**
-     *
      * @param resource
      */
     public static void assertPiped(Resource resource) {
         ValueMap properties = resource.adaptTo(ValueMap.class);
-        String[] array = new String[]{"cabbage","carrot"};
-        assertArrayEquals("Second fruit should have been correctly instantiated & patched, added to the first", new String[]{"apple","banana"}, properties.get("fruits", String[].class));
+        String[] array = new String[]{"cabbage", "carrot"};
+        assertArrayEquals("Second fruit should have been correctly instantiated & patched, added to the first", new String[]{"apple", "banana"}, properties.get("fruits", String[].class));
         assertArrayEquals("Fixed mv should be there", array, properties.get("fixedVegetables", String[].class));
         assertArrayEquals("Expr fixed mv should there and computed", array, properties.get("computedVegetables", String[].class));
     }
@@ -127,7 +127,7 @@ public class WritePipeTest extends AbstractPipeTest {
         pipe.getOutput();
         context.resourceResolver().commit();
         Resource appleResource = context.resourceResolver().getResource(PATH_APPLE);
-        ValueMap properties =  appleResource.adaptTo(ValueMap.class);
+        ValueMap properties = appleResource.adaptTo(ValueMap.class);
         assertTrue("There should be hasSeed set to true", properties.get("hasSeed", false));
         assertArrayEquals("Colors should be correctly set", new String[]{"green", "red"}, properties.get("colors", String[].class));
         Node appleNode = appleResource.adaptTo(Node.class);
@@ -143,9 +143,9 @@ public class WritePipeTest extends AbstractPipeTest {
         assertEquals("result should have 1", 1, result.size());
         Resource root = resolver.getResource(path);
         assertNotNull("target resource should be created", root);
-        Resource property =  root.getChild("index");
+        Resource property = root.getChild("index");
         assertNotNull("property should be here", property);
-        assertArrayEquals("index property should be the same", new String[] {"apple","banana"}, property.adaptTo(String[].class));
+        assertArrayEquals("index property should be the same", new String[]{"apple", "banana"}, property.adaptTo(String[].class));
         List<Resource> resources = IteratorUtils.toList(root.listChildren());
         List<String> children = resources.stream().map(r -> r.getPath()).collect(Collectors.toList());
         assertEquals("there should be 2 subpipes", 2, children.size());
@@ -169,12 +169,12 @@ public class WritePipeTest extends AbstractPipeTest {
         pipe.getOutput().next();
         context.resourceResolver().commit();
         Resource resource = context.resourceResolver().getResource(expectedPath);
-        if (nodeExpected){
+        if (nodeExpected) {
             assertNotNull("there should be isTrue node for test binding " + bindingValue, resource);
         } else {
             assertNull("there should be no isTrue node created for test binding " + bindingValue, resource);
         }
-        if (resource != null){
+        if (resource != null) {
             resource.adaptTo(Node.class).remove();
         }
     }
@@ -204,4 +204,17 @@ public class WritePipeTest extends AbstractPipeTest {
         execute("mkdir /content/copies/one | write @ expr /content${number} @ bindings number=/1");
         assertTrue(context.resourceResolver().getResource("/content/copies/one/to/copy") != null);
     }
+
+    @Test
+    public void testJcrMark() throws InvocationTargetException, IllegalAccessException {
+        Instant now = Instant.now();
+        execute("echo /content/fruits | write foo=bar");
+        ValueMap fruits = context.resourceResolver().getResource("/content/fruits").adaptTo(ValueMap.class);
+        assertNotNull(fruits.get("jcr:lastModified", Calendar.class));
+        Instant modified = Instant.ofEpochMilli(fruits.get("jcr:lastModified", Calendar.class).getTimeInMillis());
+        assertTrue(modified.isAfter(now));
+        assertNotNull(fruits.get("jcr:lastModifiedBy", String.class));
+        //we configured the plumber to mark pipe path
+        assertNotNull(fruits.get("jcr:lastModifiedByPipe", String.class));
+    }
 }