This is an automated email from the ASF dual-hosted git repository. kwin pushed a commit to branch feature/feature-id-analyser in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-analyser.git
commit fd099328e74a0b0ab7838e36952c23dadb128785 Author: Konrad Windszus <[email protected]> AuthorDate: Fri Apr 9 14:53:16 2021 +0200 SLING-10285 add analyser to validate that feature id matches one of a given set --- readme.md | 12 +++ .../feature/analyser/task/impl/CheckFeatureId.java | 93 ++++++++++++++++++ ...apache.sling.feature.analyser.task.AnalyserTask | 1 + .../task/impl/AnalyserTaskContextImpl.java | 109 +++++++++++++++++++++ .../analyser/task/impl/CheckFeatureIdTest.java | 52 ++++++++++ .../analyser/task/impl/CheckRepoinitTest.java | 75 -------------- 6 files changed, 267 insertions(+), 75 deletions(-) diff --git a/readme.md b/readme.md index 4a4d137..9f17ff9 100644 --- a/readme.md +++ b/readme.md @@ -32,6 +32,8 @@ The following analysers are defined: * `repoinit`: Checks the syntax of all repoinit sections. +* `feature-id`: Checks if the used feature id matches one of the given Maven coordinates. + Additional analysers in relation to Feature Model API Regions can be found here: https://github.com/apache/sling-org-apache-sling-feature-extension-apiregions For further documentation see: https://github.com/apache/sling-org-apache-sling-feature/blob/master/readme.md @@ -49,3 +51,13 @@ This analyser requires additional configuration: `compare-extension` | extension name | If this configuration is absent, the feature's bundles are compared. Otherwise the extensions with the specified name are compared. These extensions must be of type `ARTIFACTS`. `compare-mode` | `SAME` or `DIFFERENT` | Whether the sections must be the same or must be different. Defaults to `SAME`. `compare-metadata` | `true` or `false` | Whether to include the artifact metadata in the comparison. Defaults to `false`. + +## `feature-id` + +This analyser checks that the feature id matches one of the given accepted feature ids. If it doesn't it will emit an error. + +This analyser requires additional configuration: + + Configuration key | Allowed values | Description + ----- | ----- | ----- +`accepted-feature-ids` | comma-separated list of Maven IDs | The Maven ID/coordinates have the format `groupId:artifactId[:packaging[:classifier]]:version`. Each item is either a string which must be equal to the according item of the feature id, or a `*` which acts as wildcard (i.e. everything matches). diff --git a/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckFeatureId.java b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckFeatureId.java new file mode 100644 index 0000000..050db36 --- /dev/null +++ b/src/main/java/org/apache/sling/feature/analyser/task/impl/CheckFeatureId.java @@ -0,0 +1,93 @@ +/* + * 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.feature.analyser.task.impl; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Map; + +import org.apache.sling.feature.ArtifactId; +import org.apache.sling.feature.analyser.task.AnalyserTask; +import org.apache.sling.feature.analyser.task.AnalyserTaskContext; + +public class CheckFeatureId implements AnalyserTask { + + static final String CONFIG_KEY_ACCEPTED_FEATURE_IDS = "accepted-feature-ids"; + + @Override + public String getId() { + return "feature-id"; + } + + @Override + public String getName() { + return "Restrict feature id format"; + } + + @Override + public void execute(AnalyserTaskContext ctx) throws Exception { + Map<String, String> cfg = ctx.getConfiguration(); + String acceptedFeatureIds = cfg.get(CONFIG_KEY_ACCEPTED_FEATURE_IDS); + if (acceptedFeatureIds == null) { + // this is a comma-separated list of accepted + throw new IllegalArgumentException("Missing 'accepted-feature-ids' configuration for feature-id analyser."); + } + Collection<ArtifactId> acceptedArtifactIds = new ArrayList<>(); + for (String acceptedFeatureId : acceptedFeatureIds.split(",")) { + try { + acceptedArtifactIds.add(ArtifactId.fromMvnId(acceptedFeatureId)); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Invalid 'accepted-feature-ids' configuration for feature-id analyser, element '" + acceptedFeatureId + "' is not a valid maven coordinate string in format 'groupId:artifactId[:packaging[:classifier]]:version'", e); + } + } + if (!matchesAnyOf(ctx.getFeature().getId(), acceptedArtifactIds)) { + ctx.reportError("Feature " + ctx.getFeature().getId() + " does not match any of the accepted feature ids "); + } + } + + static boolean matchesAnyOf(ArtifactId artifactId, Collection<ArtifactId> expectedArtifactIds) { + for (ArtifactId expectedArtifactId : expectedArtifactIds) { + if (matches(artifactId, expectedArtifactId)) { + return true; + } + } + return false; + } + + static boolean matches(ArtifactId artifactId, ArtifactId expectedArtifactId) { + if (!expectedArtifactId.getGroupId().equals(artifactId.getGroupId()) && !expectedArtifactId.getGroupId().equals("*")) { + return false; + } + if (!expectedArtifactId.getArtifactId().equals(artifactId.getArtifactId()) && !expectedArtifactId.getArtifactId().equals("*")) { + return false; + } + if (!expectedArtifactId.getVersion().equals(artifactId.getVersion()) && !expectedArtifactId.getVersion().equals("*")) { + return false; + } + if (!expectedArtifactId.getType().equals(artifactId.getType()) && !expectedArtifactId.getVersion().equals("*")) { + return false; + } + // classifier is optional + if (expectedArtifactId.getClassifier() != null && !expectedArtifactId.getClassifier().equals(artifactId.getClassifier()) && !expectedArtifactId.getClassifier().equals("*")) { + return false; + } + return true; + } + +} diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask b/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask index 9a0111e..503dc36 100644 --- a/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask +++ b/src/main/resources/META-INF/services/org.apache.sling.feature.analyser.task.AnalyserTask @@ -11,3 +11,4 @@ org.apache.sling.feature.analyser.task.impl.CheckDuplicateSymbolicName org.apache.sling.feature.analyser.task.impl.CheckRepoinit org.apache.sling.feature.analyser.task.impl.CheckRequirementsCapabilities org.apache.sling.feature.analyser.task.impl.CheckUnusedBundles +org.apache.sling.feature.analyser.task.impl.CheckFeatureId \ No newline at end of file diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/AnalyserTaskContextImpl.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/AnalyserTaskContextImpl.java new file mode 100644 index 0000000..1ee90d2 --- /dev/null +++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/AnalyserTaskContextImpl.java @@ -0,0 +1,109 @@ +/* + * 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.feature.analyser.task.impl; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.sling.feature.ArtifactId; +import org.apache.sling.feature.Feature; +import org.apache.sling.feature.analyser.task.AnalyserTaskContext; +import org.apache.sling.feature.builder.FeatureProvider; +import org.apache.sling.feature.scanner.BundleDescriptor; +import org.apache.sling.feature.scanner.FeatureDescriptor; + +public class AnalyserTaskContextImpl implements AnalyserTaskContext { + + private final Feature f; + private final Map<String, String> configuration = new HashMap<>(); + + private final List<String> errors = new ArrayList<>(); + + public AnalyserTaskContextImpl() { + this("g:a:1"); + } + + public AnalyserTaskContextImpl(String artifactId) { + f = new Feature(ArtifactId.parse(artifactId)); + } + + @Override + public Feature getFeature() { + return f; + } + + @Override + public FeatureDescriptor getFeatureDescriptor() { + return null; + } + + @Override + public BundleDescriptor getFrameworkDescriptor() { + return null; + } + + @Override + public Map<String, String> getConfiguration() { + return configuration; + } + + public void putConfigurationValue(String key, String value) { + configuration.put(key, value); + } + + @Override + public FeatureProvider getFeatureProvider() { + return null; + } + + @Override + public void reportWarning(String message) { + } + + @Override + public void reportArtifactWarning(ArtifactId artifactId, String message) { + + } + + @Override + public void reportArtifactError(ArtifactId artifactId, String message) { + errors.add(message); + } + + @Override + public void reportExtensionWarning(String extension, String message) { + + } + + @Override + public void reportExtensionError(String extension, String message) { + errors.add(message); + } + + @Override + public void reportError(String message) { + errors.add(message); + } + + public List<String> getErrors() { + return this.errors; + } +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckFeatureIdTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckFeatureIdTest.java new file mode 100644 index 0000000..fdde3a8 --- /dev/null +++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckFeatureIdTest.java @@ -0,0 +1,52 @@ +/* + * 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.feature.analyser.task.impl; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import org.apache.sling.feature.analyser.task.AnalyserTask; +import org.junit.Before; +import org.junit.Test; + +public class CheckFeatureIdTest { + + private AnalyserTaskContextImpl ctx; + private AnalyserTask task; + + @Before + public void setUp() { + ctx = new AnalyserTaskContextImpl("myGroupId:myArtifactId:jar:myClassifier"); + task = new CheckFeatureId(); + } + + @Test + public void testValidFeatureId() throws Exception { + ctx.getConfiguration().put(CheckFeatureId.CONFIG_KEY_ACCEPTED_FEATURE_IDS, "myGroupId:*:jar:myClassifier"); + task.execute(ctx); + assertTrue(ctx.getErrors().isEmpty()); + } + + @Test + public void testInValidFeatureId() throws Exception { + ctx.getConfiguration().put(CheckFeatureId.CONFIG_KEY_ACCEPTED_FEATURE_IDS, "myGroupId:*:jar:myOtherClassifier"); + task.execute(ctx); + assertEquals(1, ctx.getErrors().size()); + } +} diff --git a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckRepoinitTest.java b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckRepoinitTest.java index 0cf89bc..74a87c6 100644 --- a/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckRepoinitTest.java +++ b/src/test/java/org/apache/sling/feature/analyser/task/impl/CheckRepoinitTest.java @@ -20,21 +20,11 @@ package org.apache.sling.feature.analyser.task.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -import org.apache.sling.feature.ArtifactId; import org.apache.sling.feature.Configuration; import org.apache.sling.feature.Extension; import org.apache.sling.feature.ExtensionState; import org.apache.sling.feature.ExtensionType; -import org.apache.sling.feature.Feature; import org.apache.sling.feature.analyser.task.AnalyserTask; -import org.apache.sling.feature.analyser.task.AnalyserTaskContext; -import org.apache.sling.feature.builder.FeatureProvider; -import org.apache.sling.feature.scanner.BundleDescriptor; -import org.apache.sling.feature.scanner.FeatureDescriptor; import org.junit.Test; public class CheckRepoinitTest { @@ -104,69 +94,4 @@ public class CheckRepoinitTest { task.execute(ctx); assertEquals(1, ctx.getErrors().size()); } - - public static class AnalyserTaskContextImpl implements AnalyserTaskContext { - - private final Feature f = new Feature(ArtifactId.parse("g:a:1")); - - private final List<String> errors = new ArrayList<>(); - - @Override - public Feature getFeature() { - return f; - } - - @Override - public FeatureDescriptor getFeatureDescriptor() { - return null; - } - - @Override - public BundleDescriptor getFrameworkDescriptor() { - return null; - } - - @Override - public Map<String, String> getConfiguration() { - return null; - } - - @Override - public FeatureProvider getFeatureProvider() { - return null; - } - - @Override - public void reportWarning(String message) { - } - - @Override - public void reportArtifactWarning(ArtifactId artifactId, String message) { - - } - - @Override - public void reportArtifactError(ArtifactId artifactId, String message) { - errors.add(message); - } - - @Override - public void reportExtensionWarning(String extension, String message) { - - } - - @Override - public void reportExtensionError(String extension, String message) { - errors.add(message); - } - - @Override - public void reportError(String message) { - errors.add(message); - } - - public List<String> getErrors() { - return this.errors; - } - } }
