You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tinkerpop.apache.org by dk...@apache.org on 2019/04/02 17:46:11 UTC
[tinkerpop] 01/01: Implemented EdgeLabelVerificationStrategy
This is an automated email from the ASF dual-hosted git repository.
dkuppitz pushed a commit to branch TINKERPOP-2191
in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit 21bfb55e12ffd86dad9de39bbaf155ae6bac39fc
Author: Daniel Kuppitz <da...@hotmail.com>
AuthorDate: Thu Mar 14 15:05:09 2019 -0700
Implemented EdgeLabelVerificationStrategy
---
.../tinkerpop/gremlin/jsr223/CoreImports.java | 2 +
.../EdgeLabelVerificationStrategy.java | 130 ++++++++++++++
.../structure/io/graphson/GraphSONModule.java | 5 +
.../gremlin/structure/io/gryo/GryoVersion.java | 7 +-
.../EdgeLabelVerificationStrategyTest.java | 188 +++++++++++++++++++++
5 files changed, 330 insertions(+), 2 deletions(-)
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
index 576d0de..e298951 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/CoreImports.java
@@ -86,6 +86,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Orde
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.StandardVerificationStrategy;
@@ -241,6 +242,7 @@ public final class CoreImports {
CLASS_IMPORTS.add(LambdaRestrictionStrategy.class);
CLASS_IMPORTS.add(ReadOnlyStrategy.class);
CLASS_IMPORTS.add(StandardVerificationStrategy.class);
+ CLASS_IMPORTS.add(EdgeLabelVerificationStrategy.class);
// graph traversal
CLASS_IMPORTS.add(AnonymousTraversalSource.class);
CLASS_IMPORTS.add(__.class);
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategy.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategy.java
new file mode 100644
index 0000000..1b8876a
--- /dev/null
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategy.java
@@ -0,0 +1,130 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.strategy.verification;
+
+import org.apache.commons.configuration.Configuration;
+import org.apache.commons.configuration.MapConfiguration;
+import org.apache.tinkerpop.gremlin.process.traversal.Step;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.step.map.VertexStep;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * {@code EdgeLabelVerificationStrategy} does not allow edge traversal steps to have no label specified.
+ * Providing one or more labels is considered to be a best practice, however, TinkerPop will not force the specification
+ * of edge labels; instead, providers or users will have to enable this strategy explicitly.
+ * <p/>
+ *
+ * @author Daniel Kujppitz (http://gremlin.guru)
+ * @example <pre>
+ * __.outE() // throws an IllegalStateException
+ * __.out() // throws an IllegalStateException
+ * __.bothE() // throws an IllegalStateException
+ * __.to(OUT) // throws an IllegalStateException
+ * __.toE(IN) // throws an IllegalStateException
+ * </pre>
+ */
+public final class EdgeLabelVerificationStrategy
+ extends AbstractTraversalStrategy<TraversalStrategy.VerificationStrategy>
+ implements TraversalStrategy.VerificationStrategy {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(EdgeLabelVerificationStrategy.class);
+
+ private static final String THROW_EXCEPTION = "throwException";
+ private static final String LOG_WARNING = "logWarning";
+
+ private final boolean throwException;
+ private final boolean logWarning;
+
+ private EdgeLabelVerificationStrategy(final boolean throwException, final boolean logWarning) {
+ this.throwException = throwException;
+ this.logWarning = logWarning;
+ }
+
+ @Override
+ public void apply(final Traversal.Admin<?, ?> traversal) {
+ for (final Step<?, ?> step : traversal.getSteps()) {
+ if (step instanceof VertexStep && ((VertexStep) step).getEdgeLabels().length == 0) {
+ final String msg = String.format(
+ "The provided traversal contains a vertex step without any specified edge label: %s", step);
+ if (logWarning) {
+ LOGGER.warn(msg);
+ }
+ if (throwException) {
+ throw new VerificationException(msg, traversal);
+ }
+ }
+ }
+ }
+
+ public static EdgeLabelVerificationStrategy create(final Configuration configuration) {
+ return new EdgeLabelVerificationStrategy(
+ configuration.getBoolean(THROW_EXCEPTION, false),
+ configuration.getBoolean(LOG_WARNING, false));
+ }
+
+ @Override
+ public Configuration getConfiguration() {
+ final Map<String, Object> m = new HashMap<>(2);
+ m.put(THROW_EXCEPTION, this.throwException);
+ m.put(LOG_WARNING, this.logWarning);
+ return new MapConfiguration(m);
+ }
+
+ public static EdgeLabelVerificationStrategy.Builder build() {
+ return new EdgeLabelVerificationStrategy.Builder();
+ }
+
+ public final static class Builder {
+
+ private boolean throwException;
+ private boolean logWarning;
+
+ private Builder() {
+ }
+
+ public EdgeLabelVerificationStrategy.Builder throwException() {
+ return this.throwException(true);
+ }
+
+ public EdgeLabelVerificationStrategy.Builder throwException(final boolean throwException) {
+ this.throwException = throwException;
+ return this;
+ }
+
+ public EdgeLabelVerificationStrategy.Builder logWarning() {
+ return this.logWarning(true);
+ }
+
+ public EdgeLabelVerificationStrategy.Builder logWarning(final boolean logWarning) {
+ this.logWarning = logWarning;
+ return this;
+ }
+
+ public EdgeLabelVerificationStrategy create() {
+ return new EdgeLabelVerificationStrategy(this.throwException, this.logWarning);
+ }
+ }
+}
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
index 1078a6b..3bbea66 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/graphson/GraphSONModule.java
@@ -54,6 +54,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Path
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.StandardVerificationStrategy;
@@ -183,6 +184,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
ReadOnlyStrategy.class,
StandardVerificationStrategy.class,
EarlyLimitStrategy.class,
+ EdgeLabelVerificationStrategy.class,
//
GraphFilterStrategy.class,
VertexProgramStrategy.class
@@ -300,6 +302,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
ReadOnlyStrategy.class,
StandardVerificationStrategy.class,
EarlyLimitStrategy.class,
+ EdgeLabelVerificationStrategy.class,
//
GraphFilterStrategy.class,
VertexProgramStrategy.class
@@ -400,6 +403,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
ReadOnlyStrategy.class,
StandardVerificationStrategy.class,
EarlyLimitStrategy.class,
+ EdgeLabelVerificationStrategy.class,
//
GraphFilterStrategy.class,
VertexProgramStrategy.class
@@ -509,6 +513,7 @@ abstract class GraphSONModule extends TinkerPopJacksonModule {
ReadOnlyStrategy.class,
StandardVerificationStrategy.class,
EarlyLimitStrategy.class,
+ EdgeLabelVerificationStrategy.class,
//
GraphFilterStrategy.class,
VertexProgramStrategy.class
diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
index 7c1dd5a..974542d 100644
--- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
+++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/structure/io/gryo/GryoVersion.java
@@ -63,6 +63,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Path
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy;
+import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.traverser.B_LP_O_P_S_SE_SL_Traverser;
@@ -336,9 +337,10 @@ public enum GryoVersion {
add(GryoTypeReg.of(GraphFilterStrategy.class, 157));
add(GryoTypeReg.of(LambdaRestrictionStrategy.class, 158));
add(GryoTypeReg.of(ReadOnlyStrategy.class, 159));
- add(GryoTypeReg.of(EarlyLimitStrategy.class, 188)); // ***LAST ID***
+ add(GryoTypeReg.of(EarlyLimitStrategy.class, 188));
add(GryoTypeReg.of(MatchStep.CountMatchAlgorithm.class, 160));
add(GryoTypeReg.of(MatchStep.GreedyMatchAlgorithm.class, 164));
+ add(GryoTypeReg.of(EdgeLabelVerificationStrategy.class, 189)); // ***LAST ID***
add(GryoTypeReg.of(TraverserSet.class, 58));
add(GryoTypeReg.of(Tree.class, 61));
@@ -555,9 +557,10 @@ public enum GryoVersion {
add(GryoTypeReg.of(GraphFilterStrategy.class, 157));
add(GryoTypeReg.of(LambdaRestrictionStrategy.class, 158));
add(GryoTypeReg.of(ReadOnlyStrategy.class, 159));
- add(GryoTypeReg.of(EarlyLimitStrategy.class, 188)); // ***LAST ID***
+ add(GryoTypeReg.of(EarlyLimitStrategy.class, 188));
add(GryoTypeReg.of(MatchStep.CountMatchAlgorithm.class, 160));
add(GryoTypeReg.of(MatchStep.GreedyMatchAlgorithm.class, 167));
+ add(GryoTypeReg.of(EdgeLabelVerificationStrategy.class, 189)); // ***LAST ID***
// skip 171, 172 to sync with tp33
add(GryoTypeReg.of(IndexedTraverserSet.VertexIndexedTraverserSet.class, 173));
}};
diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategyTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategyTest.java
new file mode 100644
index 0000000..72a8841
--- /dev/null
+++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/process/traversal/strategy/verification/EdgeLabelVerificationStrategyTest.java
@@ -0,0 +1,188 @@
+/*
+ * 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.tinkerpop.gremlin.process.traversal.strategy.verification;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
+import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.__;
+import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies;
+import org.apache.tinkerpop.gremlin.structure.Direction;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.function.Predicate;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+/**
+ * @author Daniel Kuppitz (http://gremlin.guru)
+ */
+@RunWith(Parameterized.class)
+public class EdgeLabelVerificationStrategyTest {
+
+ private final static Predicate<String> MSG_PREDICATE = Pattern.compile(
+ "^The provided traversal contains a vertex step without any specified edge label: VertexStep.*")
+ .asPredicate();
+
+ private TestLogAppender logAppender;
+ private Level previousLogLevel;
+
+ @Before
+ public void setupForEachTest() {
+ final org.apache.log4j.Logger strategyLogger = org.apache.log4j.Logger.getLogger(EdgeLabelVerificationStrategy.class);
+ previousLogLevel = strategyLogger.getLevel();
+ strategyLogger.setLevel(Level.WARN);
+ Logger.getRootLogger().addAppender(logAppender = new TestLogAppender());
+ }
+
+ @After
+ public void teardownForEachTest() {
+ final org.apache.log4j.Logger strategyLogger = org.apache.log4j.Logger.getLogger(EdgeLabelVerificationStrategy.class);
+ strategyLogger.setLevel(previousLogLevel);
+ Logger.getRootLogger().removeAppender(logAppender);
+ }
+
+ @Parameterized.Parameters(name = "{0}")
+ public static Iterable<Object[]> data() {
+ return Arrays.asList(new Object[][]{
+ {"__.inE()", __.inE(), false},
+ {"__.outE()", __.outE(), false},
+ {"__.bothE()", __.bothE(), false},
+ {"__.to(OUT)", __.to(Direction.OUT), false},
+ {"__.toE(IN)", __.toE(Direction.IN), false},
+ {"__.inE('knows')", __.inE("knows"), true},
+ {"__.outE('knows')", __.outE("knows"), true},
+ {"__.bothE('created','knows')", __.bothE("created", "knows"), true},
+ {"__.to(OUT,'created','knows')", __.to(Direction.OUT, "created", "knows"), true},
+ {"__.toE(IN,'knows')", __.toE(Direction.IN, "knows"), true}
+ });
+ }
+
+ @Parameterized.Parameter(value = 0)
+ public String name;
+
+ @Parameterized.Parameter(value = 1)
+ public Traversal traversal;
+
+ @Parameterized.Parameter(value = 2)
+ public boolean allow;
+
+ @Test
+ public void shouldIgnore() {
+ final TraversalStrategies strategies = new DefaultTraversalStrategies();
+ strategies.addStrategies(EdgeLabelVerificationStrategy.build().create());
+ final Traversal traversal = this.traversal.asAdmin().clone();
+ traversal.asAdmin().setStrategies(strategies);
+ traversal.asAdmin().applyStrategies();
+ assertTrue(logAppender.isEmpty());
+ }
+
+ @Test
+ public void shouldOnlyThrow() {
+ final TraversalStrategies strategies = new DefaultTraversalStrategies();
+ strategies.addStrategies(EdgeLabelVerificationStrategy.build().throwException().create());
+ final Traversal traversal = this.traversal.asAdmin().clone();
+ traversal.asAdmin().setStrategies(strategies);
+ if (allow) {
+ traversal.asAdmin().applyStrategies();
+ } else {
+ try {
+ traversal.asAdmin().applyStrategies();
+ fail("The strategy should not allow vertex steps with unspecified edge labels: " + this.traversal);
+ } catch (VerificationException ise) {
+ assertTrue(MSG_PREDICATE.test(ise.getMessage()));
+ }
+ }
+ assertTrue(logAppender.isEmpty());
+ }
+
+ @Test
+ public void shouldOnlyLog() {
+ final TraversalStrategies strategies = new DefaultTraversalStrategies();
+ strategies.addStrategies(EdgeLabelVerificationStrategy.build().logWarning().create());
+ final Traversal traversal = this.traversal.asAdmin().clone();
+ traversal.asAdmin().setStrategies(strategies);
+ traversal.asAdmin().applyStrategies();
+ if (!allow) {
+ assertTrue(String.format("Expected log entry not found in %s", logAppender.messages),
+ logAppender.messages().anyMatch(MSG_PREDICATE));
+ }
+ }
+
+ @Test
+ public void shouldThrowAndLog() {
+ final TraversalStrategies strategies = new DefaultTraversalStrategies();
+ strategies.addStrategies(EdgeLabelVerificationStrategy.build().throwException().logWarning().create());
+ final Traversal traversal = this.traversal.asAdmin().clone();
+ traversal.asAdmin().setStrategies(strategies);
+ if (allow) {
+ traversal.asAdmin().applyStrategies();
+ assertTrue(logAppender.isEmpty());
+ } else {
+ try {
+ traversal.asAdmin().applyStrategies();
+ fail("The strategy should not allow vertex steps with unspecified edge labels: " + this.traversal);
+ } catch (VerificationException ise) {
+ assertTrue(MSG_PREDICATE.test(ise.getMessage()));
+ }
+ assertTrue(String.format("Expected log entry not found in %s", logAppender.messages),
+ logAppender.messages().anyMatch(MSG_PREDICATE));
+ }
+ }
+
+ class TestLogAppender extends AppenderSkeleton {
+
+ private List<String> messages = new ArrayList<>();
+
+ boolean isEmpty() {
+ return messages.isEmpty();
+ }
+
+ Stream<String> messages() {
+ return messages.stream();
+ }
+
+ @Override
+ protected void append(org.apache.log4j.spi.LoggingEvent loggingEvent) {
+ messages.add(loggingEvent.getMessage().toString());
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public boolean requiresLayout() {
+ return false;
+ }
+ }
+}
\ No newline at end of file