diff --git a/README.md b/README.md index d2e96db..d5d4960 100644 --- a/README.md +++ b/README.md @@ -67,9 +67,9 @@ Install hooks have no limitations when being used with the AEMaaCS SDK Quickstar Content packages of type `mixed` are allowed to have both mutable and immutable nodes. AEMaaCS will only ever install the immutable part of it. The mutable part won't be installed as that cannot be successful (due to missing write access at the time of installation). Further details at . -## Enforce Oak index definitions of type `lucene` +## Enforce Oak index definitions of type `lucene` with `compatVersion` set to 2 -Currently only Oak index definitions of type `lucene` are supported in AEMaaCS. Further details in . +Currently only Oak index definitions of type `lucene` with property `compatVersion` set to the (Long) value `2` are supported in AEMaaCS. Further details in . ## Follow naming policy for Oak index definition node names diff --git a/pom.xml b/pom.xml index 3bba813..fb1ceed 100644 --- a/pom.xml +++ b/pom.xml @@ -69,7 +69,11 @@ slf4j-api 1.7.20 - + + javax.jcr + jcr + 2.0 + org.kohsuke.metainf-services @@ -112,12 +116,6 @@ jackrabbit-jcr-commons ${jackrabbit.version} - - javax.jcr - jcr - 2.0 - test - org.apache.jackrabbit oak-jackrabbit-api diff --git a/src/main/java/biz/netcentric/filevault/validator/aem/cloud/AemCloudValidator.java b/src/main/java/biz/netcentric/filevault/validator/aem/cloud/AemCloudValidator.java index cac817e..c4e451f 100644 --- a/src/main/java/biz/netcentric/filevault/validator/aem/cloud/AemCloudValidator.java +++ b/src/main/java/biz/netcentric/filevault/validator/aem/cloud/AemCloudValidator.java @@ -20,8 +20,11 @@ import java.util.Collection; import java.util.Collections; +import java.util.Optional; import java.util.regex.Pattern; +import javax.jcr.PropertyType; + import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; import org.apache.jackrabbit.util.Text; @@ -29,6 +32,7 @@ import org.apache.jackrabbit.vault.packaging.PackageType; import org.apache.jackrabbit.vault.util.Constants; import org.apache.jackrabbit.vault.util.DocViewNode2; +import org.apache.jackrabbit.vault.util.DocViewProperty2; import org.apache.jackrabbit.vault.validation.spi.DocumentViewXmlValidator; import org.apache.jackrabbit.vault.validation.spi.MetaInfPathValidator; import org.apache.jackrabbit.vault.validation.spi.NodeContext; @@ -48,8 +52,9 @@ public class AemCloudValidator implements NodePathValidator, MetaInfPathValidato static final String VIOLATION_MESSAGE_LIBS_NODES = "Nodes below '/libs' may be overwritten by future product upgrades. Rather use '/apps'. Further details at https://experienceleague.adobe.com/docs/experience-manager-cloud-service/implementing/developing/full-stack/overlays.html?lang=en#developing"; static final String VIOLATION_MESSAGE_MUTABLE_NODES_IN_MIXED_PACKAGE = "Mutable nodes in mixed package types are not installed!"; static final String VIOLATION_MESSAGE_MUTABLE_NODES_AND_IMMUTABLE_NODES_IN_SAME_PACKAGE = "Mutable and immutable nodes must not be mixed in the same package. You must separate those into two packages and give them both a dedicated package type!"; - static final String VIOLATION_MESSAGE_NON_LUCENE_TYPE_INDEX_DEFINITION = "Only oak:QueryIndexDefinitions of type='lucene' are supported in AEMaaCS but found type='%s'. Compare with https://experienceleague.adobe.com/docs/experience-manager-cloud-service/operations/indexing.html?lang=en#changes-in-aem-as-a-cloud-service"; - + static final String VIOLATION_MESSAGE_NON_LUCENE_TYPE_INDEX_DEFINITION = "Only oak:QueryIndexDefinitions of type='lucene' are supported in AEMaaCS but found type='%s'. Compare with https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/operations/indexing#current-limitations"; + static final String VIOLATION_MESSAGE_INVALID_COMPAT_VERSION_IN_INDEX_DEFINITION = "The compatVersion property of an oak:QueryIndexDefinition must be set to the Long value '2' but found '%s'. Compare with https://experienceleague.adobe.com/en/docs/experience-manager-cloud-service/content/operations/indexing#current-limitations"; + // this path is relative to META-INF private static final Path INSTALL_HOOK_PATH = Paths.get(Constants.VAULT_DIR, Constants.HOOKS_DIR); /** @@ -78,6 +83,7 @@ public class AemCloudValidator implements NodePathValidator, MetaInfPathValidato private static final int MAX_NUM_VIOLATIONS_PER_TYPE = 5; private static final Name PN_TYPE = NameFactoryImpl.getInstance().create(Name.NS_DEFAULT_URI, "type"); + private static final Name PN_COMPAT_VERSION = NameFactoryImpl.getInstance().create(Name.NS_DEFAULT_URI, "compatVersion"); private int numVarNodeViolations = 0; private int numLibNodeViolations = 0; private int numMutableNodeViolations = 0; @@ -212,6 +218,12 @@ static boolean isPackagePathInstalledConditionally(String runMode, Path packageR if (!"lucene".equals(indexType)) { messages.add(new ValidationMessage(defaultSeverity, String.format(VIOLATION_MESSAGE_NON_LUCENE_TYPE_INDEX_DEFINITION, indexType))); + } else { + Optional compatVersionProperty = node.getProperty(PN_COMPAT_VERSION); + if (!compatVersionProperty.isPresent() || compatVersionProperty.get().getType() != PropertyType.LONG || !compatVersionProperty.get().getStringValue().orElse("").equals("2")) { + messages.add(new ValidationMessage(defaultSeverity, + String.format(VIOLATION_MESSAGE_INVALID_COMPAT_VERSION_IN_INDEX_DEFINITION, compatVersionProperty.map(p -> p.formatValue()).orElse("not set")))); + } } // check node name (jcr qualified name as contained in the path) String qualifiedName = Text.getName(nodeContext.getNodePath()); diff --git a/src/test/java/biz/netcentric/filevault/validator/aem/cloud/AemCloudValidatorTest.java b/src/test/java/biz/netcentric/filevault/validator/aem/cloud/AemCloudValidatorTest.java index eb46a54..3eb96fa 100644 --- a/src/test/java/biz/netcentric/filevault/validator/aem/cloud/AemCloudValidatorTest.java +++ b/src/test/java/biz/netcentric/filevault/validator/aem/cloud/AemCloudValidatorTest.java @@ -1,5 +1,7 @@ package biz.netcentric.filevault.validator.aem.cloud; +import static org.junit.jupiter.api.Assertions.assertEquals; + /*- * #%L * AEM Cloud Validator @@ -20,6 +22,8 @@ import java.util.List; import java.util.Optional; +import javax.jcr.PropertyType; + import org.apache.jackrabbit.spi.Name; import org.apache.jackrabbit.spi.commons.name.NameConstants; import org.apache.jackrabbit.spi.commons.name.NameFactoryImpl; @@ -30,11 +34,11 @@ import org.apache.jackrabbit.vault.validation.spi.ValidationMessage; import org.apache.jackrabbit.vault.validation.spi.ValidationMessageSeverity; import org.apache.jackrabbit.vault.validation.spi.util.NodeContextImpl; +import org.hamcrest.MatcherAssert; +import org.hamcrest.Matchers; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; -import static org.junit.jupiter.api.Assertions.assertEquals; - class AemCloudValidatorTest { @Test @@ -99,12 +103,13 @@ void testValidIndexDefinitions() { Collection messages = new ArrayList<>(); List properties = Arrays.asList( new DocViewProperty2(NameConstants.JCR_PRIMARYTYPE, "oak:QueryIndexDefinition"), - new DocViewProperty2(NameFactoryImpl.getInstance().create(Name.NS_DEFAULT_URI, "type"), "lucene")); + new DocViewProperty2(NameFactoryImpl.getInstance().create(Name.NS_DEFAULT_URI, "type"), "lucene"), + new DocViewProperty2(NameFactoryImpl.getInstance().create(Name.NS_DEFAULT_URI, "compatVersion"), "2", PropertyType.LONG)); // valid lucene index definition NodeContext context = new NodeContextImpl("/oak:index/prefix.myindex-1-custom-1", Paths.get("_oak_index/test"),Paths.get("./jcr_root")); DocViewNode2 node = new DocViewNode2(NameConstants.JCR_ROOT, properties); Optional.ofNullable(validator.validate(node, context, true)).ifPresent(messages::addAll); - Assertions.assertTrue(messages.isEmpty()); + MatcherAssert.assertThat(messages, Matchers.empty()); context = new NodeContextImpl("/oak:index/productindex-1-custom-1", Paths.get("_oak_index/test"),Paths.get("./jcr_root")); Optional.ofNullable(validator.validate(node, context, true)).ifPresent(messages::addAll); Assertions.assertTrue(messages.isEmpty()); @@ -117,11 +122,14 @@ void testInvalidLuceneIndexDefinitions() { List properties = Arrays.asList( new DocViewProperty2(NameConstants.JCR_PRIMARYTYPE, "oak:QueryIndexDefinition"), new DocViewProperty2(NameFactoryImpl.getInstance().create(Name.NS_DEFAULT_URI, "type"), "lucene")); + // invalid name and also compatVersion not set NodeContext context = new NodeContextImpl("/oak:index/myindex", Paths.get("_oak_index/test"),Paths.get("./jcr_root")); DocViewNode2 node = new DocViewNode2(NameConstants.JCR_ROOT, properties); Optional.ofNullable(validator.validate(node, context, true)).ifPresent(messages::addAll); - assertEquals(1, messages.size()); - assertEquals(String.format(AemCloudValidator.VIOLATION_MESSAGE_INVALID_INDEX_DEFINITION_NODE_NAME, "myindex"), messages.iterator().next().getMessage()); + MatcherAssert.assertThat(messages, Matchers.containsInAnyOrder( + new ValidationMessage(ValidationMessageSeverity.ERROR, String.format(AemCloudValidator.VIOLATION_MESSAGE_INVALID_INDEX_DEFINITION_NODE_NAME, "myindex")), + new ValidationMessage(ValidationMessageSeverity.ERROR, String.format(AemCloudValidator.VIOLATION_MESSAGE_INVALID_COMPAT_VERSION_IN_INDEX_DEFINITION, "not set")) + )); } @Test