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

cziegeler pushed a commit to branch master
in repository 
https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-extension-apiregions.git


The following commit(s) were added to refs/heads/master by this push:
     new 7c82754  SLING-12821 : Allow enforceOn date for artifact rules
7c82754 is described below

commit 7c82754223af7f324d1636a5133e9708f250e56b
Author: Carsten Ziegeler <[email protected]>
AuthorDate: Thu Jun 5 07:25:59 2025 +0200

    SLING-12821 : Allow enforceOn date for artifact rules
---
 .../apiregions/analyser/CheckArtifactRules.java    | 14 +++-
 .../api/artifacts/InternalConstants.java           |  3 +
 .../apiregions/api/artifacts/VersionRule.java      | 81 +++++++++++++++++++++-
 .../apiregions/api/artifacts/package-info.java     |  2 +-
 .../analyser/CheckArtifactRulesTest.java           | 69 +++++++++++++++++-
 .../apiregions/api/artifacts/VersionRuleTest.java  | 56 ++++++++++++++-
 6 files changed, 216 insertions(+), 9 deletions(-)

diff --git 
a/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckArtifactRules.java
 
b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckArtifactRules.java
index d35cc25..e27d272 100644
--- 
a/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckArtifactRules.java
+++ 
b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckArtifactRules.java
@@ -16,6 +16,7 @@
  */
 package org.apache.sling.feature.extension.apiregions.analyser;
 
+import java.util.Calendar;
 import java.util.List;
 
 import org.apache.sling.feature.ArtifactId;
@@ -51,11 +52,16 @@ public class CheckArtifactRules implements AnalyserTask{
             }
             for(final ArtifactDescriptor desc : 
context.getFeatureDescriptor().getArtifactDescriptors()) {
                 this.checkArtifact(context, rules.getArtifactVersionRules(), 
rules.getMode(), desc.getArtifact().getId());
-            }    
+            }
         }
        }
 
     void checkArtifact(final AnalyserTaskContext context, final 
List<VersionRule> rules, final Mode defaultMode, final ArtifactId id) {
+        final Calendar now = Calendar.getInstance();
+        now.set(Calendar.HOUR_OF_DAY, 1);
+        now.set(Calendar.MINUTE, 0);
+        now.set(Calendar.SECOND, 0);
+        now.set(Calendar.MILLISECOND, 0);
         for(final VersionRule rule : rules) {
             if ( rule.getArtifactId() != null && 
rule.getArtifactId().isSame(id)) {
                 if ( ! rule.isAllowed(id.getOSGiVersion())) {
@@ -63,11 +69,15 @@ public class CheckArtifactRules implements AnalyserTask{
                     if ( msg == null ) {
                         msg = "Artifact with version " + id.getVersion() + " 
is not allowed.";
                     }
+                    if ( rule.getEnforceOn() != null ) {
+                        msg = msg.concat(" Enforce on: " + 
rule.getEnforceOn());
+                    }
+                    final boolean enforce = 
!rule.getEnforceOnDate().after(now);
                     Mode m = defaultMode;
                     if ( rule.getMode() != null ) {
                         m = rule.getMode();
                     }
-                    if ( m == Mode.LENIENT ) {
+                    if ( m == Mode.LENIENT || !enforce) {
                         context.reportArtifactWarning(id, msg);
                     } else {
                         context.reportArtifactError(id, msg);
diff --git 
a/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/InternalConstants.java
 
b/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/InternalConstants.java
index eb3ac01..880240d 100755
--- 
a/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/InternalConstants.java
+++ 
b/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/InternalConstants.java
@@ -34,4 +34,7 @@ abstract class InternalConstants {
     static final String KEY_BUNDLE_VERSION_RULES = "bundle-version-rules";
 
     static final String KEY_ARTIFACT_VERSION_RULES = "artifact-version-rules";
+
+    /** @since 2.10 */
+    static final String KEY_ENFORCE_ON = "enforce-on";
 }
diff --git 
a/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/VersionRule.java
 
b/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/VersionRule.java
index 72d12ac..9da1f83 100755
--- 
a/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/VersionRule.java
+++ 
b/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/VersionRule.java
@@ -17,6 +17,7 @@
 package org.apache.sling.feature.extension.apiregions.api.artifacts;
 
 import java.io.IOException;
+import java.util.Calendar;
 
 import jakarta.json.JsonException;
 import jakarta.json.JsonObject;
@@ -48,6 +49,13 @@ public class VersionRule extends AttributeableEntity {
     /** The denied version ranges */
     private VersionRange[] deniedVersionRanges;
 
+       /**
+        * Optional enforce on information.
+        * @since 2.1.0
+        */
+       private String enforceOn;
+
+
     /**
      * Create a new rules object
      */
@@ -66,6 +74,7 @@ public class VersionRule extends AttributeableEntity {
         this.setMessage(null);
         this.setAllowedVersionRanges(null);
         this.setDeniedVersionRanges(null);
+        this.setEnforceOn(null);
     }
 
     /**
@@ -103,6 +112,8 @@ public class VersionRule extends AttributeableEntity {
             this.setStringArray(objBuilder, 
InternalConstants.KEY_DENIED_VERSION_RANGES, arr);
         }
 
+        this.setString(objBuilder, InternalConstants.KEY_ENFORCE_ON, 
this.getEnforceOn());
+
         return objBuilder;
     }
 
@@ -121,7 +132,7 @@ public class VersionRule extends AttributeableEntity {
                        if ( val != null ) {
                 this.setMode(Mode.valueOf(val.toUpperCase()));
                        }
-            
+
             val = this.getString(InternalConstants.KEY_ARTIFACT_ID);
             if ( val != null ) {
                 this.setArtifactId(ArtifactId.parse(val));
@@ -154,6 +165,8 @@ public class VersionRule extends AttributeableEntity {
                 }
                 this.setDeniedVersionRanges(ranges);
             }
+
+            
this.setEnforceOn(this.getString(InternalConstants.KEY_ENFORCE_ON));
         } catch (final JsonException | IllegalArgumentException e) {
             throw new IOException(e);
         }
@@ -266,4 +279,70 @@ public class VersionRule extends AttributeableEntity {
         return result;
 
     }
+
+    private Calendar parseDate(final String value) {
+        final String[] parts = value.split("-");
+        if ( parts.length == 3 ) {
+            if ( parts[0].length() == 4 && parts[1].length() == 2 && 
parts[2].length() == 2 ) {
+                try {
+                    final int year = Integer.parseInt(parts[0]);
+                    final int month = Integer.parseInt(parts[1]);
+                    final int day = Integer.parseInt(parts[2]);
+
+                    final Calendar c = Calendar.getInstance();
+                    c.set(Calendar.YEAR, year);
+                    c.set(Calendar.MONTH, month - 1);
+                    c.set(Calendar.DAY_OF_MONTH, day);
+
+                    c.set(Calendar.HOUR_OF_DAY, 1);
+                    c.set(Calendar.MINUTE, 0);
+                    c.set(Calendar.SECOND, 0);
+                    c.set(Calendar.MILLISECOND, 0);
+
+                    return c;
+                } catch ( final NumberFormatException ignore ) {
+                    // ignore
+                }
+            }
+        }
+        return null;
+    }
+
+       /**
+        * Get the optional enforce on information. This must be a date in the 
format 'YYYY-MM-DD'.
+        * @return The since information or {@code null}
+     * @since 2.1.0
+        */
+       public String getEnforceOn() {
+               return enforceOn;
+       }
+
+       /**
+        * Set the enforce on information. This must be a date in the format 
'YYYY-MM-DD'.
+        * @param enforceOn The new info or {@code null} to remove it
+     * @since 2.1.0
+     * @throw IllegalArgumentException If the format is not correct
+        */
+       public void setEnforceOn(final String enforceOn) {
+        if (enforceOn == null || parseDate(enforceOn) != null) {
+                   this.enforceOn = enforceOn;
+        } else {
+            throw new IllegalArgumentException("Enforce on date must be in the 
format 'YYYY-MM-DD'");
+        }
+       }
+
+    /**
+     * Return a date by which this rule is enforced
+     * @return A calendar if the value from {@link #getEnforceOn()} is set or 
yesterday
+     * @since 2.1.0
+     */
+    public Calendar getEnforceOnDate() {
+        Calendar result = this.enforceOn == null ? null : 
this.parseDate(this.enforceOn);
+        if ( result == null ) {
+            // if not set, return yesterday
+            result = Calendar.getInstance();
+            result.add(Calendar.DAY_OF_YEAR, -1);
+        }
+        return result;
+    }
 }
diff --git 
a/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/package-info.java
 
b/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/package-info.java
index 03ef7f2..f45cfd5 100755
--- 
a/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/package-info.java
+++ 
b/src/main/java/org/apache/sling/feature/extension/apiregions/api/artifacts/package-info.java
@@ -17,7 +17,7 @@
  * under the License.
  */
 
[email protected]("2.0.0")
[email protected]("2.1.0")
 package org.apache.sling.feature.extension.apiregions.api.artifacts;
 
 
diff --git 
a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckArtifactRulesTest.java
 
b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckArtifactRulesTest.java
index 96814e1..401c045 100644
--- 
a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckArtifactRulesTest.java
+++ 
b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckArtifactRulesTest.java
@@ -18,6 +18,10 @@ package 
org.apache.sling.feature.extension.apiregions.analyser;
 
 import static org.mockito.Mockito.when;
 
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+
 import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Extension;
@@ -36,7 +40,7 @@ import org.junit.Test;
 import org.mockito.Mockito;
 
 public class CheckArtifactRulesTest {
-    
+
     private CheckArtifactRules analyser = new CheckArtifactRules();
 
     private AnalyserTaskContext newContext(final Feature f) {
@@ -57,7 +61,7 @@ public class CheckArtifactRulesTest {
                 for(final Artifact a : ext.getArtifacts()) {
                     final ArtifactDescriptor bd = 
Mockito.mock(ArtifactDescriptor.class);
                     when(bd.getArtifact()).thenReturn(a);
-                    fd.getArtifactDescriptors().add(bd);        
+                    fd.getArtifactDescriptors().add(bd);
                 }
             }
         }
@@ -98,7 +102,7 @@ public class CheckArtifactRulesTest {
         final ArtifactRules rules = new ArtifactRules();
         rules.getBundleVersionRules().add(r);
         rules.getArtifactVersionRules().add(r2);
-        
+
         ArtifactRules.setArtifactRules(f, rules);
         final AnalyserTaskContext context = newContext(f);
         analyser.execute(context);
@@ -106,4 +110,63 @@ public class CheckArtifactRulesTest {
         Mockito.verify(context, 
Mockito.atLeastOnce()).reportArtifactError(Mockito.eq(bundle.getId()), 
Mockito.eq(r.getMessage()));
         Mockito.verify(context, 
Mockito.atLeastOnce()).reportArtifactError(Mockito.eq(artifact.getId()), 
Mockito.eq(r2.getMessage()));
     }
+
+    @Test public void testValidateFeatureEnforceOnNotReached() throws 
Exception {
+        final Feature f = new Feature(ArtifactId.parse("g:a:1"));
+        final Artifact bundle = new Artifact(ArtifactId.parse("g:b:1.1"));
+        f.getBundles().add(bundle);
+
+        final Calendar tomorrow = Calendar.getInstance();
+        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        tomorrow.add(Calendar.DAY_OF_MONTH, 1);
+        final VersionRule r = new VersionRule();
+        r.setArtifactId(bundle.getId());
+        r.setMode(Mode.STRICT);
+        r.setMessage("foo");
+        r.setEnforceOn(df.format(tomorrow.getTime()));
+
+        final ArtifactRules rules = new ArtifactRules();
+        rules.getBundleVersionRules().add(r);
+
+        ArtifactRules.setArtifactRules(f, rules);
+        final AnalyserTaskContext context = newContext(f);
+        analyser.execute(context);
+
+        final String reportMsg = r.getMessage().concat(" Enforce on: 
").concat(r.getEnforceOn());
+        Mockito.verify(context, 
Mockito.atLeastOnce()).reportArtifactWarning(Mockito.eq(bundle.getId()), 
Mockito.eq(reportMsg));
+    }
+
+    @Test public void testValidateFeatureEnforceOnReached() throws Exception {
+        final Feature f = new Feature(ArtifactId.parse("g:a:1"));
+        final Artifact bundle = new Artifact(ArtifactId.parse("g:b:1.1"));
+        f.getBundles().add(bundle);
+
+        final Calendar tomorrow = Calendar.getInstance();
+        final DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
+        tomorrow.add(Calendar.DAY_OF_MONTH, -5);
+        final VersionRule r = new VersionRule();
+        r.setArtifactId(bundle.getId());
+        r.setMode(Mode.STRICT);
+        r.setMessage("foo");
+        r.setEnforceOn(df.format(tomorrow.getTime()));
+
+        final ArtifactRules rules = new ArtifactRules();
+        rules.getBundleVersionRules().add(r);
+
+        ArtifactRules.setArtifactRules(f, rules);
+        final AnalyserTaskContext context = newContext(f);
+        analyser.execute(context);
+
+        final String reportMsg = r.getMessage().concat(" Enforce on: 
").concat(r.getEnforceOn());
+        Mockito.verify(context, 
Mockito.atLeastOnce()).reportArtifactError(Mockito.eq(bundle.getId()), 
Mockito.eq(reportMsg));
+
+        // test again with date of today
+        r.setEnforceOn(df.format(Calendar.getInstance().getTime()));
+        ArtifactRules.setArtifactRules(f, rules);
+        final AnalyserTaskContext context2 = newContext(f);
+        analyser.execute(context2);
+
+        final String reportMsg2 = r.getMessage().concat(" Enforce on: 
").concat(r.getEnforceOn());
+        Mockito.verify(context2, 
Mockito.atLeastOnce()).reportArtifactError(Mockito.eq(bundle.getId()), 
Mockito.eq(reportMsg2));
+    }
 }
diff --git 
a/src/test/java/org/apache/sling/feature/extension/apiregions/api/artifacts/VersionRuleTest.java
 
b/src/test/java/org/apache/sling/feature/extension/apiregions/api/artifacts/VersionRuleTest.java
index e0d2dc0..6497eb5 100644
--- 
a/src/test/java/org/apache/sling/feature/extension/apiregions/api/artifacts/VersionRuleTest.java
+++ 
b/src/test/java/org/apache/sling/feature/extension/apiregions/api/artifacts/VersionRuleTest.java
@@ -22,6 +22,7 @@ import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
+import java.util.Calendar;
 
 import jakarta.json.Json;
 
@@ -43,6 +44,7 @@ public class VersionRuleTest {
         entity.setMode(Mode.LENIENT);
         entity.setMessage("msg");
         entity.setArtifactId(ArtifactId.parse("g:a:1"));
+        entity.setEnforceOn("2024-01-01");
         entity.clear();
         assertTrue(entity.getAttributes().isEmpty());
         assertNull(entity.getAllowedVersionRanges());
@@ -50,11 +52,12 @@ public class VersionRuleTest {
         assertNull(entity.getMessage());
         assertNull(entity.getArtifactId());
         assertNull(entity.getMode());
+        assertNull(entity.getEnforceOn());
     }
 
     @Test public void testFromJSONObject() throws IOException {
         final Extension ext = new Extension(ExtensionType.JSON, "a", 
ExtensionState.OPTIONAL);
-        ext.setJSON("{ \"mode\" : \"LENIENT\", \"message\" : \"msg\", 
\"artifact-id\":\"g:a:1\"," 
+        ext.setJSON("{ \"mode\" : \"LENIENT\", \"message\" : \"msg\", 
\"artifact-id\":\"g:a:1\","
             + 
"\"allowed-version-ranges\":[\"1.0\"],\"denied-version-ranges\":[\"2.0\"]}");
 
         final VersionRule entity = new VersionRule();
@@ -66,6 +69,7 @@ public class VersionRuleTest {
         assertEquals(new VersionRange("1.0"), 
entity.getAllowedVersionRanges()[0]);
         assertEquals(1, entity.getDeniedVersionRanges().length);
         assertEquals(new VersionRange("2.0"), 
entity.getDeniedVersionRanges()[0]);
+        assertNull(entity.getEnforceOn());
     }
 
     @Test public void testToJSONObject() throws IOException {
@@ -77,12 +81,45 @@ public class VersionRuleTest {
         entity.setDeniedVersionRanges(new VersionRange[] {new 
VersionRange("2.0.0")});
 
         final Extension ext = new Extension(ExtensionType.JSON, "a", 
ExtensionState.OPTIONAL);
-        ext.setJSON("{ \"mode\" : \"LENIENT\", \"artifact-id\":\"g:a:1\", 
\"message\" : \"msg\"," 
+        ext.setJSON("{ \"mode\" : \"LENIENT\", \"artifact-id\":\"g:a:1\", 
\"message\" : \"msg\","
             + 
"\"allowed-version-ranges\":[\"1.0.0\"],\"denied-version-ranges\":[\"2.0.0\"]}");
 
         assertEquals(ext.getJSONStructure().asJsonObject(), 
entity.toJSONObject());
     }
 
+    @Test public void testFromJSONObjectWithEnforceOn() throws IOException {
+        final Extension ext = new Extension(ExtensionType.JSON, "a", 
ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"mode\" : \"LENIENT\", \"message\" : \"msg\", 
\"artifact-id\":\"g:a:1\","
+            + 
"\"allowed-version-ranges\":[\"1.0\"],\"denied-version-ranges\":[\"2.0\"],\"enforce-on\":\"2024-02-02\"}");
+
+        final VersionRule entity = new VersionRule();
+        entity.fromJSONObject(ext.getJSONStructure().asJsonObject());
+        assertEquals(Mode.LENIENT, entity.getMode());
+        assertEquals("msg", entity.getMessage());
+        assertEquals(ArtifactId.parse("g:a:1"), entity.getArtifactId());
+        assertEquals(1, entity.getAllowedVersionRanges().length);
+        assertEquals(new VersionRange("1.0"), 
entity.getAllowedVersionRanges()[0]);
+        assertEquals(1, entity.getDeniedVersionRanges().length);
+        assertEquals(new VersionRange("2.0"), 
entity.getDeniedVersionRanges()[0]);
+        assertEquals("2024-02-02", entity.getEnforceOn());
+    }
+
+    @Test public void testToJSONObjectWithEnforceOn() throws IOException {
+        final VersionRule entity = new VersionRule();
+        entity.setMode(Mode.LENIENT);
+        entity.setMessage("msg");
+        entity.setArtifactId(ArtifactId.parse("g:a:1"));
+        entity.setAllowedVersionRanges(new VersionRange[] {new 
VersionRange("1.0.0")});
+        entity.setDeniedVersionRanges(new VersionRange[] {new 
VersionRange("2.0.0")});
+        entity.setEnforceOn("2024-02-02");
+
+        final Extension ext = new Extension(ExtensionType.JSON, "a", 
ExtensionState.OPTIONAL);
+        ext.setJSON("{ \"mode\" : \"LENIENT\", \"artifact-id\":\"g:a:1\", 
\"message\" : \"msg\","
+            + 
"\"allowed-version-ranges\":[\"1.0.0\"],\"denied-version-ranges\":[\"2.0.0\"],\"enforce-on\":\"2024-02-02\"}");
+
+        assertEquals(ext.getJSONStructure().asJsonObject(), 
entity.toJSONObject());
+    }
+
     @Test public void testIsAllowedNoRanges() {
         final VersionRule entity = new VersionRule();
         assertFalse(entity.isAllowed(new Version("1.0")));
@@ -108,4 +145,19 @@ public class VersionRuleTest {
         assertTrue(entity.isAllowed(new Version("1.3.2")));
         assertFalse(entity.isAllowed(new Version("2.1")));
     }
+
+    @Test public void testSetEnforceOn() {
+        final VersionRule entity = new VersionRule();
+        entity.setEnforceOn("2024-01-01");
+        assertEquals("2024-01-01", entity.getEnforceOn());
+        final Calendar c = entity.getEnforceOnDate();
+        assertEquals(2024, c.get(Calendar.YEAR));
+        assertEquals(0, c.get(Calendar.MONTH));
+        assertEquals(1, c.get(Calendar.DAY_OF_MONTH));
+    }
+
+    @Test(expected = IllegalArgumentException.class) public void 
testSetEnforceOnInvalid() {
+        final VersionRule entity = new VersionRule();
+        entity.setEnforceOn("invalid-date");
+    }
 }

Reply via email to