getOperationOptions() {
return new HashMap<>();
}
- /**
- * Gets the list of data auths.
- *
- * @return List of data auths.
- */
- public String[] getDataAuths() {
- if (variables.containsKey(DATA_AUTHS)) {
- return (String[]) variables.get(DATA_AUTHS);
- }
- return new String[0];
- }
-
- /**
- * Gets the current user ID.
- *
- * @return The user ID
- */
- public String getUserId() {
- return (String) variables.get(USER_ID);
+ public User getUser() {
+ return (User) variables.get(USER);
}
public Integer getAllElementsLimit() {
@@ -166,6 +165,10 @@ public String getHasStepFilterStage() {
return (String) variables.get(HAS_STEP_FILTER_STAGE);
}
+ public OperationChain> getLastOperationChain() {
+ return (OperationChain) variables.get(LAST_OPERATION_CHAIN);
+ }
+
public String toString() {
return StringFactory.graphVariablesString(this);
}
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStep.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStep.java
index 3268c35a522..fafee9b8148 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStep.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStep.java
@@ -52,12 +52,11 @@
/**
* Custom GafferPop GraphStep provides Gaffer specific optimisations
- * for the initial GraphStep in a query. Also responsible for parsing
- * any options passed via a 'with()' call on the query.
+ * for the initial GraphStep in a query.
+ * Also responsible for parsing any options passed via a 'with()' step
+ * on the query.
*
*
- * g.with("userId", "user").V() // userId extracted to be used in the operation executions
- * g.with("dataAuths", "write-access,read-access").V() // user access controls to apply on the user
* g.with("operationOptions", ["graphId:graph1", "opt1:val1"]).V() // operation options extracted and applied
*
*/
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/optimisation/GafferPopGraphStepStrategy.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/optimisation/GafferPopGraphStepStrategy.java
index dccbd3a46f5..497eae8a485 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/optimisation/GafferPopGraphStepStrategy.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/optimisation/GafferPopGraphStepStrategy.java
@@ -31,16 +31,18 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables;
import uk.gov.gchq.gaffer.tinkerpop.process.traversal.step.GafferPopGraphStep;
+import java.util.Map;
import java.util.Optional;
/**
* The {@link GraphStep} strategy for GafferPop, this will replace the default
* {@link GraphStep} of a query to add Gaffer optimisations. Such as gathering
* any {@link HasStep}s so that a Gaffer View can be constructed for the query.
- * Will also handle the translation any Cypher queries passed in a with() step
- * into a Gremlin traversal.
+ * Will also handle the translation of Cypher queries passed via a with()
+ * step in the Gremlin traversal.
*
*
* g.V().hasLabel() // replaced by GafferPopGraphStep
@@ -52,27 +54,25 @@ public final class GafferPopGraphStepStrategy extends AbstractTraversalStrategy<
private static final Logger LOGGER = LoggerFactory.getLogger(GafferPopGraphStepStrategy.class);
private static final GafferPopGraphStepStrategy INSTANCE = new GafferPopGraphStepStrategy();
- /**
- * Key used in a with step to include a opencypher query traversal
- */
- public static final String CYPHER_KEY = "cypher";
-
private GafferPopGraphStepStrategy() {
}
@Override
public void apply(final Admin, ?> traversal) {
- // Check for any options on the traversal
+ // Parse any options on the traversal
Optional optionsStrategy = traversal.getStrategies().getStrategy(OptionsStrategy.class);
- // Translate and add a cypher traversal in if that key has been set
- if (optionsStrategy.isPresent() && optionsStrategy.get().getOptions().containsKey(CYPHER_KEY)) {
- LOGGER.info("Replacing traversal with translated Cypher query");
- CypherAst ast = CypherAst.parse((String) optionsStrategy.get().getOptions().get(CYPHER_KEY));
- Admin, ?> translatedCypher = ast.buildTranslation(Translator.builder().traversal().enableCypherExtensions().build()).asAdmin();
+ if (optionsStrategy.isPresent()) {
+ Map options = optionsStrategy.get().getOptions();
+ // Translate and add a cypher traversal in if that key has been set
+ if (options.containsKey(GafferPopGraphVariables.CYPHER_KEY)) {
+ LOGGER.info("Replacing traversal with translated Cypher query");
+ CypherAst ast = CypherAst.parse((String) options.get(GafferPopGraphVariables.CYPHER_KEY));
+ Admin, ?> translatedCypher = ast.buildTranslation(Translator.builder().traversal().enableCypherExtensions().build()).asAdmin();
- // Add the cypher traversal
- TraversalHelper.insertTraversal(0, translatedCypher, traversal);
- LOGGER.debug("New traversal is: {}", traversal);
+ // Add the cypher traversal
+ TraversalHelper.insertTraversal(0, translatedCypher, traversal);
+ LOGGER.debug("New traversal is: {}", traversal);
+ }
}
TraversalHelper.getStepsOfClass(GraphStep.class, traversal).forEach(originalGraphStep -> {
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/ExampleGafferPopAuthenticator.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/ExampleGafferPopAuthenticator.java
deleted file mode 100644
index 19f997c4a3f..00000000000
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/ExampleGafferPopAuthenticator.java
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * Copyright 2024 Crown Copyright
- *
- * Licensed 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 uk.gov.gchq.gaffer.tinkerpop.server.auth;
-
-import org.apache.tinkerpop.gremlin.server.auth.AuthenticatedUser;
-import org.apache.tinkerpop.gremlin.server.auth.AuthenticationException;
-import org.apache.tinkerpop.gremlin.server.auth.Authenticator;
-
-import java.net.InetAddress;
-import java.nio.charset.StandardCharsets;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Map;
-
-/**
- * An example authenticator class for GafferPop, this should not
- * be used in production as it allows all user and password combinations.
- * The class is intended as a template for an deployment specific class
- * that hooks into a proper authorisation mechanism such as LDAP etc.
- */
-public class ExampleGafferPopAuthenticator implements Authenticator {
-
- @Override
- public boolean requireAuthentication() {
- return true;
- }
-
- @Override
- public void setup(final Map config) {
- // Nothing to do
- }
-
- @Override
- public SaslNegotiator newSaslNegotiator(final InetAddress remoteAddress) {
- return new PlainTextSaslAuthenticator();
- }
-
-
- @Override
- public AuthenticatedUser authenticate(final Map credentials) throws AuthenticationException {
- // Get the username from the credentials set by the SASL negotiator
- final String username = credentials.get("username");
- return new AuthenticatedUser(username);
- }
-
- /**
- * Very simple SASL authenticator that will just extract username and password
- * from plain text.
- */
- private class PlainTextSaslAuthenticator implements Authenticator.SaslNegotiator {
- private static final byte NUL = 0;
- private boolean complete = false;
- private String username;
- private String password;
-
- @Override
- public byte[] evaluateResponse(final byte[] clientResponse) throws AuthenticationException {
- decodeCredentials(clientResponse);
- complete = true;
- return new byte[0];
- }
-
- @Override
- public boolean isComplete() {
- return complete;
- }
-
- @Override
- public AuthenticatedUser getAuthenticatedUser() throws AuthenticationException {
- if (!complete) {
- throw new AuthenticationException("SASL negotiation not complete");
- }
- final Map credentials = new HashMap<>();
- credentials.put("username", username);
- credentials.put("password", password);
- return authenticate(credentials);
- }
-
- /**
- * SASL PLAIN mechanism specifies that credentials are encoded in a
- * sequence of UTF-8 bytes, delimited by 0 (US-ASCII NUL).
- * The form is :
- *
- *
- * authzIdNULauthnIdNULpasswordNUL
- *
- *
- * @param bytes encoded credentials string sent by the client
- * @throws AuthenticationException If issue decoding
- */
- private void decodeCredentials(final byte[] bytes) throws AuthenticationException {
- byte[] user = null;
- byte[] pass = null;
- int end = bytes.length;
- // Loop over the byte array to extract the user and password
- for (int i = bytes.length - 1; i >= 0; i--) {
- if (bytes[i] != NUL) {
- continue;
- }
-
- if (pass == null) {
- pass = Arrays.copyOfRange(bytes, i + 1, end);
- } else if (user == null) {
- user = Arrays.copyOfRange(bytes, i + 1, end);
- }
- end = i;
- }
-
- if (user == null) {
- throw new AuthenticationException("Authentication ID must not be null");
- }
- if (pass == null) {
- throw new AuthenticationException("Password must not be null");
- }
-
- username = new String(user, StandardCharsets.UTF_8);
- password = new String(pass, StandardCharsets.UTF_8);
- }
- }
-
-}
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/GafferPopAuthoriser.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/GafferPopAuthoriser.java
deleted file mode 100644
index 00a65045c91..00000000000
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/GafferPopAuthoriser.java
+++ /dev/null
@@ -1,129 +0,0 @@
-/*
- * Copyright 2024 Crown Copyright
- *
- * Licensed 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 uk.gov.gchq.gaffer.tinkerpop.server.auth;
-
-import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.verification.VertexProgramRestrictionStrategy;
-import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
-import org.apache.tinkerpop.gremlin.process.traversal.Bytecode.Instruction;
-import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
-import org.apache.tinkerpop.gremlin.process.traversal.util.BytecodeHelper;
-import org.apache.tinkerpop.gremlin.server.auth.AuthenticatedUser;
-import org.apache.tinkerpop.gremlin.server.authz.AuthorizationException;
-import org.apache.tinkerpop.gremlin.server.authz.Authorizer;
-import org.apache.tinkerpop.gremlin.util.message.RequestMessage;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import java.util.Map;
-
-/**
- * The {@link Authorizer} for GafferPop, responsible for checking a query from a
- * gremlin server user is valid and ensuring the current authorised user ID is
- * passed on to the GafferPop graph. This should be used along side a instance
- * specific Authenticator to provide user management for gremlin server connections.
- */
-public class GafferPopAuthoriser implements Authorizer {
- private static final Logger LOGGER = LoggerFactory.getLogger(GafferPopAuthoriser.class);
- public static final String REJECT_BYTECODE = "User not authorized for bytecode requests on %s";
- public static final String REJECT_LAMBDA = "lambdas";
- public static final String REJECT_MUTATE = "the ReadOnlyStrategy";
- public static final String REJECT_OLAP = "the VertexProgramRestrictionStrategy";
- public static final String REJECT_STRING = "User not authorized for string-based requests.";
-
- /**
- * This method is called once upon system startup to initialize the
- * {@code GafferAuthoriser}.
- */
- @Override
- public void setup(final Map config) {
- // Nothing to setup
- }
-
- /**
- * Checks whether a user is authorized to have a gremlin bytecode request from a client answered and raises an
- * {@link AuthorizationException} if this is not the case. If authorised will modify the bytecode to inject the
- * users details via a 'with()' step so the Gaffer graph can run with the correct user.
- *
- * @param user {@link AuthenticatedUser} that needs authorization.
- * @param bytecode The gremlin {@link Bytecode} request to authorize the user for.
- * @param aliases A {@link Map} with a single key/value pair that maps the name of the TraversalSource in the
- * {@link Bytecode} request to name of one configured in Gremlin Server.
- * @return The original or modified {@link Bytecode} to be used for further processing.
- */
- @Override
- public Bytecode authorize(final AuthenticatedUser user, final Bytecode bytecode, final Map aliases) throws AuthorizationException {
- final boolean runsLambda = BytecodeHelper.getLambdaLanguage(bytecode).isPresent();
- final boolean touchesReadOnlyStrategy = bytecode.toString().contains(ReadOnlyStrategy.class.getSimpleName());
- final boolean touchesOLAPRestriction = bytecode.toString().contains(VertexProgramRestrictionStrategy.class.getSimpleName());
-
- final List rejections = new ArrayList<>();
- // Reject use of Lambdas
- if (runsLambda) {
- rejections.add(REJECT_LAMBDA);
- }
- // Reject any modification steps to the graph via gremlin
- if (touchesReadOnlyStrategy) {
- rejections.add(REJECT_MUTATE);
- }
- // Reject use of OLAP operations
- if (touchesOLAPRestriction) {
- rejections.add(REJECT_OLAP);
- }
-
- // Formulate a rejection message
- String rejectMessage = REJECT_BYTECODE;
- if (!rejections.isEmpty()) {
- rejectMessage += " using " + String.join(", ", rejections);
- throw new AuthorizationException(String.format(rejectMessage, aliases.values()));
- }
-
- // Prevent overriding the user ID in a 'with()' block as we will set it based on the authenticated user
- for (final Instruction i : bytecode.getStepInstructions()) {
- LOGGER.debug("Found query operator: {} with args: {}", i.getOperator(), i.getArguments());
- if (i.getOperator().equals("with") && Arrays.asList(i.getArguments()).contains(GafferPopGraphVariables.USER_ID)) {
- throw new AuthorizationException("Can't override current user ID from within a query");
- }
- }
-
- // Add the user ID to the query
- bytecode.addSource("with", GafferPopGraphVariables.USER_ID, user.getName());
-
- return bytecode;
- }
-
- /**
- * Checks whether a user is authorized to have a script request from a gremlin
- * client answered and raises an {@link AuthorizationException} if this is not
- * the case.
- *
- * @param user {@link AuthenticatedUser} that needs authorization.
- * @param msg {@link RequestMessage} in which the ARGS_GREMLIN
- * argument can contain an arbitrary succession of script
- * statements.
- */
- @Override
- public void authorize(final AuthenticatedUser user, final RequestMessage msg) throws AuthorizationException {
- // Not supported in GafferPop
- throw new AuthorizationException(REJECT_STRING);
- }
-
-}
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphTest.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphTest.java
index aedc9fc2d35..25ef6aafc7e 100644
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphTest.java
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphTest.java
@@ -79,10 +79,9 @@ void shouldConstructGafferPopGraphWithOnlyConfig() {
final Map variables = graph.variables().asMap();
assertThat(variables)
.hasSize(5)
- .containsEntry(GafferPopGraphVariables.USER_ID, expectedUser.getUserId())
+ .containsEntry(GafferPopGraphVariables.USER, expectedUser)
.containsEntry(GafferPopGraphVariables.GET_ALL_ELEMENTS_LIMIT, 1)
.containsEntry(GafferPopGraphVariables.HAS_STEP_FILTER_STAGE, HasStepFilterStage.POST_TRANSFORM.toString())
- .containsEntry(GafferPopGraphVariables.DATA_AUTHS, new String[]{"auth1", "auth2"})
.containsKey(GafferPopGraphVariables.OP_OPTIONS);
final Map opOptions = (Map) variables.get(GafferPopGraphVariables.OP_OPTIONS);
@@ -105,10 +104,9 @@ void shouldConstructGafferPopGraphWithConfigFile() {
final Map variables = graph.variables().asMap();
assertThat(variables)
.hasSize(5)
- .containsEntry(GafferPopGraphVariables.USER_ID, expectedUser.getUserId())
+ .containsEntry(GafferPopGraphVariables.USER, expectedUser)
.containsEntry(GafferPopGraphVariables.GET_ALL_ELEMENTS_LIMIT, 2)
.containsEntry(GafferPopGraphVariables.HAS_STEP_FILTER_STAGE, HasStepFilterStage.POST_AGGREGATION.toString())
- .containsEntry(GafferPopGraphVariables.DATA_AUTHS, new String[]{})
.containsKey(GafferPopGraphVariables.OP_OPTIONS);
final Map opOptions = (Map) variables.get(GafferPopGraphVariables.OP_OPTIONS);
@@ -130,8 +128,7 @@ void shouldConstructGafferPopGraph() {
final Map variables = graph.variables().asMap();
assertThat(variables)
.hasSize(5)
- .containsEntry(GafferPopGraphVariables.DATA_AUTHS, expectedUser.getDataAuths().toArray())
- .containsEntry(GafferPopGraphVariables.USER_ID, expectedUser.getUserId())
+ .containsEntry(GafferPopGraphVariables.USER, expectedUser)
.containsEntry(GafferPopGraphVariables.GET_ALL_ELEMENTS_LIMIT,
GafferPopGraph.DEFAULT_GET_ALL_ELEMENTS_LIMIT)
.containsEntry(GafferPopGraphVariables.HAS_STEP_FILTER_STAGE,
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphVariablesTest.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphVariablesTest.java
index cfa6d9a629b..aa2810da1b5 100644
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphVariablesTest.java
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphVariablesTest.java
@@ -18,6 +18,8 @@
import org.junit.jupiter.api.Test;
+import uk.gov.gchq.gaffer.user.User;
+
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
@@ -48,18 +50,22 @@ void shouldAllowSettingGafferPopVariables() {
// Given
given(graph.variables()).willReturn(variables);
final String testUserId = "testUserId";
- final String testDataAuths = "auth1,auth2";
+ final String[] testDataAuths = {"auth1", "auth2"};
+ final User testUser = new User.Builder()
+ .userId(testUserId)
+ .dataAuths(testDataAuths)
+ .build();
final List testOpOptions = Arrays.asList("graphId:graph1", "other:other");
final GafferPopGraphVariables graphVariables = (GafferPopGraphVariables) graph.variables();
// When
- graphVariables.set(GafferPopGraphVariables.USER_ID, testUserId);
+ graphVariables.set(GafferPopGraphVariables.USER, testUser);
graphVariables.set(GafferPopGraphVariables.DATA_AUTHS, testDataAuths);
graphVariables.set(GafferPopGraphVariables.OP_OPTIONS, testOpOptions);
// Then
- assertThat(graphVariables.getUserId()).isEqualTo(testUserId);
- assertThat(graphVariables.getDataAuths()).containsExactlyInAnyOrder((testDataAuths.split(",")));
+ assertThat(graphVariables.getUser().getUserId()).isEqualTo(testUserId);
+ assertThat(graphVariables.getUser().getDataAuths()).containsExactlyInAnyOrder(testDataAuths);
assertThat(graphVariables.getOperationOptions()).containsOnly(
entry("graphId", "graph1"),
entry("other", "other"));
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStepTest.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStepTest.java
index 01ace2fc6bc..539eb8a7470 100644
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStepTest.java
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStepTest.java
@@ -50,21 +50,14 @@ void shouldUpdateGraphVariablesOnGremlinWithStep() {
// Given
final GafferPopGraph graph = GafferPopGraph.open(GafferPopTestUtil.TEST_CONFIGURATION_1, getGafferGraph());
final GafferPopGraphVariables graphVariables = (GafferPopGraphVariables) graph.variables();
- final String testUserId = "testUserId";
- final String testDataAuths = "auth1,auth2";
final List testOpOptions = Arrays.asList("graphId:graph1", "other:other");
final GraphTraversalSource g = graph.traversal();
// When
- g.with(GafferPopGraphVariables.USER_ID, testUserId)
- .with(GafferPopGraphVariables.DATA_AUTHS, testDataAuths)
- .with(GafferPopGraphVariables.OP_OPTIONS, testOpOptions)
- .V().toList();
+ g.with(GafferPopGraphVariables.OP_OPTIONS, testOpOptions).V().toList();
// Then
- assertThat(graphVariables.getUserId()).isEqualTo(testUserId);
- assertThat(graphVariables.getDataAuths()).containsExactlyInAnyOrder((testDataAuths.split(",")));
assertThat(graphVariables.getOperationOptions()).containsOnly(
entry("graphId", "graph1"),
entry("other", "other"));
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/GafferPopGraphStepStrategyCypherIT.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/GafferPopGraphStepStrategyCypherIT.java
index 07c8b4252a7..c4040fea4a9 100644
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/GafferPopGraphStepStrategyCypherIT.java
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/strategy/GafferPopGraphStepStrategyCypherIT.java
@@ -33,7 +33,7 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import static uk.gov.gchq.gaffer.tinkerpop.process.traversal.strategy.optimisation.GafferPopGraphStepStrategy.CYPHER_KEY;
+import static uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables.CYPHER_KEY;
import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.JOSH;
import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.MARKO;
import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.PETER;
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/ExampleGafferPopAuthenticatorTest.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/ExampleGafferPopAuthenticatorTest.java
deleted file mode 100644
index b9c0604a128..00000000000
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/ExampleGafferPopAuthenticatorTest.java
+++ /dev/null
@@ -1,90 +0,0 @@
-/*
- * Copyright 2024 Crown Copyright
- *
- * Licensed 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 uk.gov.gchq.gaffer.tinkerpop.server.auth;
-
-import org.apache.tinkerpop.gremlin.server.auth.AuthenticationException;
-import org.apache.tinkerpop.gremlin.server.auth.Authenticator;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInstance;
-import org.junit.jupiter.api.TestInstance.Lifecycle;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-
-@TestInstance(Lifecycle.PER_CLASS)
-class ExampleGafferPopAuthenticatorTest {
- private ExampleGafferPopAuthenticator authenticator;
-
- @BeforeAll
- void setup() {
- authenticator = new ExampleGafferPopAuthenticator();
- }
-
- @Test
- void shouldAlwaysRequireAuthentication() {
- assertThat(authenticator.requireAuthentication()).isTrue();
- }
-
- @Test
- void shouldCreateNewPlainTextSaslNegotiator() {
- // Given
- final Authenticator.SaslNegotiator negotiator1 = authenticator.newSaslNegotiator(null);
- final Authenticator.SaslNegotiator negotiator2 = authenticator.newSaslNegotiator(null);
-
- // Then
- assertThat(negotiator1).isNotEqualTo(negotiator2);
- }
-
- @Test
- void shouldAuthenticateWithPlainText() throws AuthenticationException {
- final String user = "test";
- final String pass = "pass";
- final Map credentials = new HashMap<>();
- credentials.put("username", user);
- credentials.put("password", pass);
-
- assertThat(authenticator.authenticate(credentials).getName()).isEqualTo(user);
- }
-
- @Test
- void shouldParseSaslPlainTextResponse() throws AuthenticationException, IOException {
- // Given
- final Authenticator.SaslNegotiator negotiator = authenticator.newSaslNegotiator(null);
- final String user = "test";
- final String pass = "pass";
-
- // Create a byte array for the response, this is representative of what the gremlin server is passed
- final ByteArrayOutputStream stream = new ByteArrayOutputStream();
- final byte[] nul = new byte[] {0};
- stream.write(nul);
- stream.write(user.getBytes());
- stream.write(nul);
- stream.write(pass.getBytes());
-
- // When
- negotiator.evaluateResponse(stream.toByteArray());
-
- // Then
- assertThat(negotiator.isComplete()).isTrue();
- assertThat(negotiator.getAuthenticatedUser().getName()).isEqualTo(user);
- }
-}
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/GafferPopAuthoriserTest.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/GafferPopAuthoriserTest.java
deleted file mode 100644
index d4598214281..00000000000
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/server/auth/GafferPopAuthoriserTest.java
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * Copyright 2024 Crown Copyright
- *
- * Licensed 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 uk.gov.gchq.gaffer.tinkerpop.server.auth;
-
-import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.verification.VertexProgramRestrictionStrategy;
-import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
-import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
-import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy;
-import org.apache.tinkerpop.gremlin.server.auth.AuthenticatedUser;
-import org.apache.tinkerpop.gremlin.server.authz.AuthorizationException;
-import org.apache.tinkerpop.gremlin.util.function.Lambda;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.TestInstance;
-import org.junit.jupiter.api.TestInstance.Lifecycle;
-
-import uk.gov.gchq.gaffer.commonutil.StreamUtil;
-import uk.gov.gchq.gaffer.graph.Graph;
-import uk.gov.gchq.gaffer.mapstore.MapStoreProperties;
-import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph;
-import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables;
-import uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTestUtil;
-
-import java.util.HashMap;
-
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-
-@TestInstance(Lifecycle.PER_CLASS)
-class GafferPopAuthoriserTest {
-
- private static final MapStoreProperties PROPERTIES = MapStoreProperties.loadStoreProperties(StreamUtil.openStream(
- GafferPopAuthoriserTest.class, "/gaffer/map-store.properties"));
- private static final String TEST_USERNAME = "testUser";
- private GafferPopAuthoriser authoriser;
-
- @BeforeAll
- void setup() {
- authoriser = new GafferPopAuthoriser();
- authoriser.setup(new HashMap());
- }
-
- @Test
- void shouldNotAuthoriseLambdaBytecodeRequest() {
- // Given
- final GafferPopGraph graph = GafferPopGraph.open(GafferPopTestUtil.TEST_CONFIGURATION_1, getGafferGraph());
- final GraphTraversalSource g = graph.traversal();
- final Bytecode bytecode = g.V().map(Lambda.function("it.get()")).asAdmin().getBytecode();
-
- // Then
- assertThatExceptionOfType(AuthorizationException.class)
- .isThrownBy(() ->
- authoriser.authorize(new AuthenticatedUser(TEST_USERNAME), bytecode, new HashMap()))
- .withMessageContaining(GafferPopAuthoriser.REJECT_LAMBDA);
- }
-
- @Test
- void shouldNotAuthoriseMutatingBytecodeRequest() {
- // Given
- final GafferPopGraph graph = GafferPopGraph.open(GafferPopTestUtil.TEST_CONFIGURATION_1, getGafferGraph());
- final GraphTraversalSource g = graph.traversal();
- final Bytecode bytecode = g.withoutStrategies(ReadOnlyStrategy.class)
- .V().addV().asAdmin().getBytecode();
-
- // Then
- assertThatExceptionOfType(AuthorizationException.class)
- .isThrownBy(() ->
- authoriser.authorize(new AuthenticatedUser(TEST_USERNAME), bytecode, new HashMap()))
- .withMessageContaining(GafferPopAuthoriser.REJECT_MUTATE);
- }
-
-
- @Test
- void shouldNotAuthoriseOLAPBytecodeRequest() {
- // Given
- final GafferPopGraph graph = GafferPopGraph.open(GafferPopTestUtil.TEST_CONFIGURATION_1, getGafferGraph());
- final GraphTraversalSource g = graph.traversal();
- final Bytecode bytecode = g.withoutStrategies(VertexProgramRestrictionStrategy.class)
- .withComputer().V().asAdmin().getBytecode();
-
- // Then
- assertThatExceptionOfType(AuthorizationException.class)
- .isThrownBy(() ->
- authoriser.authorize(new AuthenticatedUser(TEST_USERNAME), bytecode, new HashMap()))
- .withMessageContaining(GafferPopAuthoriser.REJECT_OLAP);
- }
-
- @Test
- void shouldNotAuthoriseSettingUserInRequest() {
- // Given
- final GafferPopGraph graph = GafferPopGraph.open(GafferPopTestUtil.TEST_CONFIGURATION_1, getGafferGraph());
- final GraphTraversalSource g = graph.traversal();
- final Bytecode bytecode = g.V().asAdmin().getBytecode();
- // Add the with() step separately otherwise it's converted to the OptionsStrategy
- // by the asAdmin() call before we can compare.
- bytecode.addStep("with", GafferPopGraphVariables.USER_ID, "notAllowed");
-
- // Then
- assertThatExceptionOfType(AuthorizationException.class)
- .isThrownBy(() ->
- authoriser.authorize(new AuthenticatedUser(TEST_USERNAME), bytecode, new HashMap()));
- }
-
- @Test
- void shouldInjectUserIdIntoRequest() throws AuthorizationException {
- // Given
- final GafferPopGraph graph = GafferPopGraph.open(GafferPopTestUtil.TEST_CONFIGURATION_1, getGafferGraph());
- final GraphTraversalSource g = graph.traversal();
- final Bytecode bytecode = g.V().asAdmin().getBytecode();
- final Bytecode expectedBytecode = g.V().asAdmin().getBytecode();
- // Add the with() step separately otherwise it's converted to the OptionsStrategy
- // by the asAdmin() call before we can compare.
- expectedBytecode.addSource("with", GafferPopGraphVariables.USER_ID, TEST_USERNAME);
-
- // Then
- assertThat(authoriser.authorize(new AuthenticatedUser(TEST_USERNAME), bytecode, new HashMap()))
- .isEqualTo(expectedBytecode);
- }
-
- private Graph getGafferGraph() {
- return GafferPopTestUtil.getGafferGraph(this.getClass(), PROPERTIES);
- }
-}
diff --git a/pom.xml b/pom.xml
index 8106202347e..25626464c92 100644
--- a/pom.xml
+++ b/pom.xml
@@ -98,6 +98,7 @@
3.4.14
5.15.0
1.36.0
+ 3.7.1
2.17
diff --git a/rest-api/spring-rest/pom.xml b/rest-api/spring-rest/pom.xml
index 63cf96acdc9..521ab6fb6e3 100644
--- a/rest-api/spring-rest/pom.xml
+++ b/rest-api/spring-rest/pom.xml
@@ -60,6 +60,11 @@
jcs-cache-service
${project.parent.version}
+
+ uk.gov.gchq.gaffer
+ tinkerpop
+ ${project.parent.version}
+
@@ -102,6 +107,10 @@
+
+ org.springframework.boot
+ spring-boot-starter-websocket
+
org.glassfish.jersey.inject
jersey-hk2
@@ -132,6 +141,19 @@
junit-jupiter-engine
test
+
+ uk.gov.gchq.gaffer
+ tinkerpop
+ ${project.parent.version}
+ test-jar
+ test
+
+
+ org.apache.tinkerpop
+ gremlin-driver
+ ${tinkerpop.version}
+ test
+
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/GafferWebApplication.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/GafferWebApplication.java
index 127b5faf2b1..bc4790ee084 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/GafferWebApplication.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/GafferWebApplication.java
@@ -23,7 +23,7 @@
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
@OpenAPIDefinition(servers = {
- @Server(url = "/rest", description = "Default Server URL") })
+ @Server(url = "/", description = "Default Server URL") })
@SpringBootApplication
public class GafferWebApplication extends SpringBootServletInitializer {
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/FactoryConfig.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/FactoryConfig.java
index 445eb07619d..d2d6eac7fc2 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/FactoryConfig.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/FactoryConfig.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Crown Copyright
+ * Copyright 2020-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,7 +29,8 @@
import uk.gov.gchq.gaffer.rest.factory.DefaultExamplesFactory;
import uk.gov.gchq.gaffer.rest.factory.ExamplesFactory;
import uk.gov.gchq.gaffer.rest.factory.GraphFactory;
-import uk.gov.gchq.gaffer.rest.factory.UserFactory;
+import uk.gov.gchq.gaffer.rest.factory.spring.AbstractUserFactory;
+import uk.gov.gchq.gaffer.rest.factory.spring.UnknownUserFactory;
import javax.annotation.PostConstruct;
@@ -42,6 +43,8 @@ public class FactoryConfig {
private static final Logger LOGGER = LoggerFactory.getLogger(FactoryConfig.class);
+ public static final String USER_FACTORY_CLASS_DEFAULT = UnknownUserFactory.class.getName();
+
private Environment environment;
@Autowired
@@ -77,7 +80,7 @@ public GraphFactory createGraphFactory() throws IllegalAccessException, Instanti
}
@Bean
- public UserFactory createUserFactory() throws IllegalAccessException, InstantiationException {
+ public AbstractUserFactory createUserFactory() throws IllegalAccessException, InstantiationException {
return getDefaultUserFactory().newInstance();
}
@@ -98,13 +101,12 @@ private Class extends GraphFactory> getDefaultGraphFactory() {
}
}
- private Class extends UserFactory> getDefaultUserFactory() {
- final String userFactoryClass = System.getProperty(SystemProperty.USER_FACTORY_CLASS,
- SystemProperty.USER_FACTORY_CLASS_DEFAULT);
+ private Class extends AbstractUserFactory> getDefaultUserFactory() {
+ final String userFactoryClass = System.getProperty(SystemProperty.USER_FACTORY_CLASS, USER_FACTORY_CLASS_DEFAULT);
try {
return Class.forName(userFactoryClass)
- .asSubclass(UserFactory.class);
+ .asSubclass(AbstractUserFactory.class);
} catch (final ClassNotFoundException e) {
throw new IllegalArgumentException("Unable to create user factory from class: " + userFactoryClass, e);
}
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinConfig.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinConfig.java
new file mode 100644
index 00000000000..a13d12d5c3f
--- /dev/null
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinConfig.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.rest.config;
+
+import org.apache.commons.configuration2.builder.fluent.Configurations;
+import org.apache.commons.configuration2.ex.ConfigurationException;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import uk.gov.gchq.gaffer.rest.factory.GraphFactory;
+import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph;
+
+@Configuration
+public class GremlinConfig {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GremlinConfig.class);
+
+ /**
+ * Default path to look for a GafferPop properties file if not defined in the store properties.
+ */
+ private static final String DEFAULT_PROPERTIES = "/gaffer/gafferpop.properties";
+
+ @Bean
+ public GraphTraversalSource graphTraversalSource(final GraphFactory graphFactory) throws Exception {
+ // Determine where to look for the GafferPop properties
+ String gafferPopProperties = graphFactory.getGraph().getStoreProperties().get(GafferPopGraph.GAFFERPOP_PROPERTIES);
+ if (gafferPopProperties == null) {
+ LOGGER.warn("GafferPop properties file was not specified using default location: {}", DEFAULT_PROPERTIES);
+ gafferPopProperties = DEFAULT_PROPERTIES;
+ }
+ // Obtain the graph traversal
+ try (Graph graph = GafferPopGraph.open(new Configurations().properties(gafferPopProperties), graphFactory.getGraph())) {
+ return graph.traversal();
+ } catch (final ConfigurationException e) {
+ LOGGER.error("Error loading GafferPop config, Gremlin will be unavailable: {}", e.getMessage());
+ return EmptyGraph.instance().traversal();
+ }
+ }
+}
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinWebSocketConfig.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinWebSocketConfig.java
new file mode 100644
index 00000000000..f7ebd01029b
--- /dev/null
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinWebSocketConfig.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.rest.config;
+
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.EnableWebSocket;
+import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
+import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
+
+import uk.gov.gchq.gaffer.rest.factory.spring.AbstractUserFactory;
+import uk.gov.gchq.gaffer.rest.handler.GremlinWebSocketHandler;
+
+@Configuration
+@EnableWebSocket
+public class GremlinWebSocketConfig implements WebSocketConfigurer {
+
+ private final GraphTraversalSource g;
+ private final AbstractUserFactory userFactory;
+
+ @Autowired
+ public GremlinWebSocketConfig(final GraphTraversalSource g, final AbstractUserFactory userFactory) {
+ this.g = g;
+ this.userFactory = userFactory;
+ }
+
+ @Override
+ public void registerWebSocketHandlers(final WebSocketHandlerRegistry registry) {
+ registry.addHandler(new GremlinWebSocketHandler(g, userFactory), "/gremlin");
+ }
+
+}
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/GremlinController.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/GremlinController.java
new file mode 100644
index 00000000000..af6d5e0c449
--- /dev/null
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/GremlinController.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.rest.controller;
+
+import io.swagger.v3.oas.annotations.tags.Tag;
+import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor;
+import org.apache.tinkerpop.gremlin.jsr223.ConcurrentBindings;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
+import org.json.JSONObject;
+import org.opencypher.gremlin.translation.CypherAst;
+import org.opencypher.gremlin.translation.translator.Translator;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpHeaders;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import uk.gov.gchq.gaffer.core.exception.GafferRuntimeException;
+import uk.gov.gchq.gaffer.exception.SerialisationException;
+import uk.gov.gchq.gaffer.jsonserialisation.JSONSerialiser;
+import uk.gov.gchq.gaffer.operation.Operation;
+import uk.gov.gchq.gaffer.operation.OperationChain;
+import uk.gov.gchq.gaffer.rest.factory.spring.AbstractUserFactory;
+import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph;
+import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables;
+
+import java.nio.charset.StandardCharsets;
+import java.util.LinkedList;
+
+import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
+import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
+
+@RestController
+@Tag(name = "gremlin")
+@RequestMapping("/rest/gremlin")
+public class GremlinController {
+
+ // Keys for response JSON
+ public static final String EXPLAIN_OVERVIEW_KEY = "overview";
+ public static final String EXPLAIN_OP_CHAIN_KEY = "chain";
+ public static final String EXPLAIN_GREMLIN_KEY = "gremlin";
+
+ private final ConcurrentBindings bindings = new ConcurrentBindings();
+ private final AbstractUserFactory userFactory;
+ private final Graph graph;
+
+ @Autowired
+ public GremlinController(final GraphTraversalSource g, final AbstractUserFactory userFactory) {
+ bindings.putIfAbsent("g", g);
+ graph = g.getGraph();
+ this.userFactory = userFactory;
+ }
+
+ /**
+ * Explains what Gaffer operations are run for a given gremlin query.
+ *
+ * @param httpHeaders The request headers.
+ * @param gremlinQuery The gremlin groovy query.
+ * @return JSON response with explanation in.
+ */
+ @PostMapping(path = "/explain", consumes = TEXT_PLAIN_VALUE, produces = APPLICATION_JSON_VALUE)
+ @io.swagger.v3.oas.annotations.Operation(
+ summary = "Explain a Gremlin Query",
+ description = "Runs a Gremlin query and outputs an explanation of what Gaffer operations were executed on the graph")
+ public String explain(@RequestHeader final HttpHeaders httpHeaders, @RequestBody final String gremlinQuery) {
+ return runGremlinAndGetExplain(gremlinQuery, httpHeaders).toString();
+ }
+
+ /**
+ * Explains what Gaffer operations are ran for a given cypher query,
+ * will translate to Gremlin using {@link CypherAst} before executing.
+ *
+ * @param httpHeaders The request headers.
+ * @param cypherQuery Opencypher query.
+ * @return JSON response with explanation in.
+ */
+ @PostMapping(path = "/cypher/explain", consumes = TEXT_PLAIN_VALUE, produces = APPLICATION_JSON_VALUE)
+ @io.swagger.v3.oas.annotations.Operation(
+ summary = "Explain a Cypher Query Executed via Gremlin",
+ description = "Translates a Cypher query to Gremlin and outputs an explanation of what Gaffer operations" +
+ "were executed on the graph, note will always append a '.toList()' to the translation")
+ public String cypherExplain(@RequestHeader final HttpHeaders httpHeaders, @RequestBody final String cypherQuery) {
+
+ final CypherAst ast = CypherAst.parse(cypherQuery);
+ // Translate the cypher to gremlin, always add a .toList() otherwise Gremlin wont execute it as its lazy
+ final String translation = ast.buildTranslation(Translator.builder().gremlinGroovy().enableCypherExtensions().build()) + ".toList()";
+
+ JSONObject response = runGremlinAndGetExplain(translation, httpHeaders);
+ response.put(EXPLAIN_GREMLIN_KEY, translation);
+ return response.toString();
+ }
+
+ /**
+ * Gets an explanation of the last chain of operations ran on a GafferPop graph.
+ * This essentially shows how a Gremlin query mapped to a Gaffer operation
+ * chain.
+ * Note due to how Gaffer maps to Tinkerpop some filtering steps in the Gremlin
+ * query may be absent from the operation chains in the explain as it may have
+ * been done in the Tinkerpop framework instead.
+ *
+ * @param graph The GafferPop graph
+ * @return A JSON payload with an overview and full JSON representation of the
+ * chain in.
+ */
+ public static JSONObject getGafferPopExplanation(final GafferPopGraph graph) {
+ JSONObject result = new JSONObject();
+ // Get the last operation chain that ran
+ LinkedList operations = new LinkedList<>();
+ ((GafferPopGraphVariables) graph.variables())
+ .getLastOperationChain()
+ .getOperations()
+ .forEach(op -> {
+ if (op instanceof OperationChain) {
+ operations.addAll(((OperationChain) op).flatten());
+ } else {
+ operations.add(op);
+ }
+ });
+ OperationChain> flattenedChain = new OperationChain<>(operations);
+ String overview = flattenedChain.toOverviewString();
+
+ result.put(EXPLAIN_OVERVIEW_KEY, overview);
+ try {
+ result.put(EXPLAIN_OP_CHAIN_KEY, new JSONObject(new String(JSONSerialiser.serialise(flattenedChain), StandardCharsets.UTF_8)));
+ } catch (final SerialisationException e) {
+ result.put(EXPLAIN_OP_CHAIN_KEY, "FAILED TO SERIALISE OPERATION CHAIN");
+ }
+
+ return result;
+ }
+
+ /**
+ * Executes a given Gremlin query on the graph then formats a JSON response with
+ * the executed Gaffer operations in.
+ *
+ * @param gremlinQuery The Gremlin groovy query.
+ * @param httpHeaders The headers for the request.
+ * @return JSON explanation.
+ */
+ private JSONObject runGremlinAndGetExplain(final String gremlinQuery, final HttpHeaders httpHeaders) {
+ // Check we actually have a graph instance to use
+ GafferPopGraph gafferPopGraph;
+ if (graph instanceof EmptyGraph) {
+ throw new GafferRuntimeException("There is no GafferPop Graph configured");
+ } else {
+ gafferPopGraph = (GafferPopGraph) graph;
+ }
+ // Hooks for user auth
+ userFactory.setHttpHeaders(httpHeaders);
+ graph.variables().set(GafferPopGraphVariables.USER, userFactory.createUser());
+
+ JSONObject explain = new JSONObject();
+ try (GremlinExecutor gremlinExecutor = GremlinExecutor.build().globalBindings(bindings).create()) {
+ gafferPopGraph.setDefaultVariables((GafferPopGraphVariables) gafferPopGraph.variables());
+ // Execute the query note this will actually run the query which we need
+ // as Gremlin will skip steps if there is no input from the previous ones
+ gremlinExecutor.eval(gremlinQuery).join();
+
+ // Get the chain and reset the variables
+ explain = getGafferPopExplanation(gafferPopGraph);
+ gafferPopGraph.setDefaultVariables((GafferPopGraphVariables) gafferPopGraph.variables());
+ } catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } catch (final Exception e) {
+ throw new GafferRuntimeException("Failed to evaluate Gremlin query: " + e.getMessage(), e);
+ }
+
+ return explain;
+ }
+
+}
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IGraphConfigurationController.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IGraphConfigurationController.java
index 6cb43224cf1..ea7732745f8 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IGraphConfigurationController.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IGraphConfigurationController.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2023 Crown Copyright
+ * Copyright 2020-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,7 +32,7 @@
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@Tag(name = "config")
-@RequestMapping("/graph/config")
+@RequestMapping("/rest/graph/config")
public interface IGraphConfigurationController {
@RequestMapping(
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IJobController.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IJobController.java
index e42c67c00b5..6d766b6ba76 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IJobController.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IJobController.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 Crown Copyright
+ * Copyright 2020-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,7 +30,7 @@
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
@Tag(name = "job")
-@RequestMapping("/graph/jobs")
+@RequestMapping("/rest/graph/jobs")
public interface IJobController {
@PostMapping(
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IOperationController.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IOperationController.java
index ec9556cb50f..669f928cd2a 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IOperationController.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IOperationController.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2023 Crown Copyright
+ * Copyright 2020-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,7 +31,7 @@
import static org.springframework.web.bind.annotation.RequestMethod.POST;
@Tag(name = "operations")
-@RequestMapping("/graph/operations")
+@RequestMapping("/rest/graph/operations")
public interface IOperationController {
@RequestMapping(
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IPropertiesController.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IPropertiesController.java
index b8ce52118e2..ed14c810b3f 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IPropertiesController.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IPropertiesController.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 Crown Copyright
+ * Copyright 2020-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,7 +27,7 @@
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@Tag(name = "properties")
-@RequestMapping("/properties")
+@RequestMapping("/rest/properties")
public interface IPropertiesController {
@RequestMapping(
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IStatusController.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IStatusController.java
index 6ca24ff7e27..24b73577f89 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IStatusController.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/IStatusController.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 Crown Copyright
+ * Copyright 2020-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
import static org.springframework.web.bind.annotation.RequestMethod.GET;
@Tag(name = "status")
-@RequestMapping("/graph/status")
+@RequestMapping("/rest/graph/status")
public interface IStatusController {
@RequestMapping(
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/VersionController.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/VersionController.java
index 3027ba64faa..b4ffe5cafc6 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/VersionController.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/VersionController.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Crown Copyright
+ * Copyright 2023-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -35,7 +35,7 @@ public class VersionController {
*
* @return Version of the graph
*/
- @GetMapping(path = "/graph/version", produces = TEXT_PLAIN_VALUE)
+ @GetMapping(path = "/rest/graph/version", produces = TEXT_PLAIN_VALUE)
@Operation(summary = "Retrieves the version of the Gaffer Graph")
public String getGafferVersion() {
// Return the preloaded version string from the common-rest library
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/factory/spring/AbstractUserFactory.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/factory/spring/AbstractUserFactory.java
new file mode 100644
index 00000000000..f61456c8576
--- /dev/null
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/factory/spring/AbstractUserFactory.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.rest.factory.spring;
+
+import org.springframework.http.HttpHeaders;
+
+import uk.gov.gchq.gaffer.rest.factory.UserFactory;
+import uk.gov.gchq.gaffer.user.User;
+
+/**
+ * The base abstract {@link UserFactory} implementation for the spring
+ * rest API. Allows setting the http headers for use in authorisation.
+ */
+public class AbstractUserFactory implements UserFactory {
+
+ HttpHeaders httpHeaders;
+
+ @Override
+ public User createUser() {
+ throw new UnsupportedOperationException("Unimplemented method 'createUser'");
+ }
+
+ /**
+ * Allow setting the {@link HttpHeaders} the user factory may use
+ * to carry out authorisation.
+ *
+ * @param httpHeaders the headers
+ */
+ public void setHttpHeaders(final HttpHeaders httpHeaders) {
+ this.httpHeaders = httpHeaders;
+ }
+
+}
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/factory/spring/UnknownUserFactory.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/factory/spring/UnknownUserFactory.java
new file mode 100644
index 00000000000..9fc77c7cb15
--- /dev/null
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/factory/spring/UnknownUserFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.rest.factory.spring;
+
+import uk.gov.gchq.gaffer.user.User;
+
+import static uk.gov.gchq.gaffer.user.User.UNKNOWN_USER_ID;
+
+/**
+ * Default implementation of the {@link AbstractUserFactory}.
+ * Always returns an empty {@link User} object (representing an unknown user).
+ */
+public class UnknownUserFactory extends AbstractUserFactory {
+
+ @Override
+ public User createUser() {
+ return new User.Builder()
+ .userId(UNKNOWN_USER_ID)
+ .opAuth("user")
+ .build();
+ }
+}
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/handler/GremlinWebSocketHandler.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/handler/GremlinWebSocketHandler.java
new file mode 100644
index 00000000000..1564a5bfd4a
--- /dev/null
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/handler/GremlinWebSocketHandler.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.rest.handler;
+
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.PooledByteBufAllocator;
+import io.netty.buffer.Unpooled;
+import io.opentelemetry.api.trace.Span;
+import io.opentelemetry.api.trace.StatusCode;
+import io.opentelemetry.context.Context;
+import io.opentelemetry.context.Scope;
+import org.apache.tinkerpop.gremlin.groovy.engine.GremlinExecutor;
+import org.apache.tinkerpop.gremlin.jsr223.ConcurrentBindings;
+import org.apache.tinkerpop.gremlin.process.remote.traversal.DefaultRemoteTraverser;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.util.MessageSerializer;
+import org.apache.tinkerpop.gremlin.util.Tokens;
+import org.apache.tinkerpop.gremlin.util.function.FunctionUtils;
+import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
+import org.apache.tinkerpop.gremlin.util.message.RequestMessage;
+import org.apache.tinkerpop.gremlin.util.message.ResponseMessage;
+import org.apache.tinkerpop.gremlin.util.message.ResponseStatusCode;
+import org.apache.tinkerpop.gremlin.util.ser.GraphBinaryMessageSerializerV1;
+import org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3;
+import org.apache.tinkerpop.gremlin.util.ser.MessageTextSerializer;
+import org.apache.tinkerpop.gremlin.util.ser.SerTokens;
+import org.json.JSONObject;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.web.socket.BinaryMessage;
+import org.springframework.web.socket.WebSocketSession;
+import org.springframework.web.socket.handler.BinaryWebSocketHandler;
+
+import uk.gov.gchq.gaffer.commonutil.otel.OtelUtil;
+import uk.gov.gchq.gaffer.rest.controller.GremlinController;
+import uk.gov.gchq.gaffer.rest.factory.spring.AbstractUserFactory;
+import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph;
+import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.AbstractMap.SimpleEntry;
+import java.util.Collections;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ * Websocket handler for accepting and responding to Gremlin queries.
+ * This enables an endpoint that acts like a Gremlin server which will
+ * run requests on the current Gaffer graph via the GafferPop library.
+ */
+public class GremlinWebSocketHandler extends BinaryWebSocketHandler {
+ private static final Logger LOGGER = LoggerFactory.getLogger(GremlinWebSocketHandler.class);
+
+ /**
+ * The default serialiser used for Gremlin queries if the serialiser for mime type is not found.
+ */
+ private static final MessageSerializer> DEFAULT_SERIALISER = new GraphBinaryMessageSerializerV1();
+
+ // Mappings of mime types and serialisers
+ private final Map> serialisers = Stream.of(
+ new SimpleEntry<>(SerTokens.MIME_GRAPHBINARY_V1, new GraphBinaryMessageSerializerV1()),
+ new SimpleEntry<>(SerTokens.MIME_GRAPHSON_V3, new GraphSONMessageSerializerV3()),
+ new SimpleEntry<>(SerTokens.MIME_JSON, new GraphSONMessageSerializerV3()))
+ .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
+
+ private final ExecutorService executorService = Context.taskWrapping(Executors.newFixedThreadPool(4));
+ private final ConcurrentBindings bindings = new ConcurrentBindings();
+ private final AbstractUserFactory userFactory;
+ private final Graph graph;
+
+ /**
+ * Constructor
+ *
+ * @param g The graph traversal source
+ * @param userFactory The user factory
+ */
+ public GremlinWebSocketHandler(final GraphTraversalSource g, final AbstractUserFactory userFactory) {
+ bindings.putIfAbsent("g", g);
+ graph = g.getGraph();
+ this.userFactory = userFactory;
+ }
+
+ @Override
+ protected void handleBinaryMessage(final WebSocketSession session, final BinaryMessage message) throws Exception {
+ ByteBuf byteBuf = convertToByteBuf(message);
+
+ // Read the start bytes to find the type to correctly deserialise
+ byte[] bytes = new byte[byteBuf.readByte()];
+ byteBuf.readBytes(bytes);
+ String mimeType = new String(bytes, StandardCharsets.UTF_8);
+
+ // Use the relevant serialiser for the mime type (cast to text is required to send response as String)
+ MessageTextSerializer> serialiser = (MessageTextSerializer>) serialisers.getOrDefault(mimeType, DEFAULT_SERIALISER);
+ LOGGER.debug("Using Tinkerpop serialiser: {}", serialiser.getClass().getSimpleName());
+
+ // Deserialise the request ensuring to discard the already read bytes
+ RequestMessage request = serialiser.deserializeRequest(byteBuf.discardReadBytes());
+
+ // Handle and respond
+ sendBinaryResponse(session, serialiser, handleGremlinRequest(session, request));
+ }
+
+ /**
+ * Extracts the relevant information from a {@link RequestMessage} and validates
+ * the Gremlin query requested before executing on the current graph. Formulates
+ * the result into a {@link ResponseMessage} to be sent back to the client.
+ *
+ * @param session The current websocket session.
+ * @param request The Gremlin request.
+ * @return The response message containing the result.
+ */
+ private ResponseMessage handleGremlinRequest(final WebSocketSession session, final RequestMessage request) {
+ final UUID requestId = request.getRequestId();
+ ResponseMessage responseMessage;
+ LOGGER.info("QUERY IS: {} ", request.getArgs().get(Tokens.ARGS_GREMLIN));
+
+ // OpenTelemetry hooks
+ Span span = OtelUtil.startSpan(this.getClass().getName(), "Gremlin Request: " + requestId.toString());
+ span.setAttribute("gaffer.gremlin.query", request.getArgs().get(Tokens.ARGS_GREMLIN).toString());
+
+ // Execute the query
+ try (Scope scope = span.makeCurrent();
+ GremlinExecutor gremlinExecutor = GremlinExecutor.build()
+ .globalBindings(bindings)
+ .executorService(executorService)
+ .create()) {
+ // Set current headers for potential authorisation then set the user
+ userFactory.setHttpHeaders(session.getHandshakeHeaders());
+ graph.variables().set(GafferPopGraphVariables.USER, userFactory.createUser());
+
+ // Run the query using the gremlin executor service
+ Object result = gremlinExecutor.eval(
+ request.getArgs().get(Tokens.ARGS_GREMLIN),
+ request.getArg(Tokens.ARGS_LANGUAGE),
+ request.getArgOrDefault(Tokens.ARGS_BINDINGS, Collections.emptyMap()),
+ request.getArgOrDefault(Tokens.ARGS_EVAL_TIMEOUT, null),
+ FunctionUtils.wrapFunction(output ->
+ // Need to replicate what TraversalOpProcessor does with a bytecode op, it converts
+ // results to Traverser so that GLVs can handle the results. Don't quite get the same
+ // benefit here as the bulk has to be 1 since we've already resolved the result
+ request.getOp().equals(Tokens.OPS_BYTECODE)
+ ? IteratorUtils.asList(output).stream().map(r -> new DefaultRemoteTraverser(r, 1))
+ .collect(Collectors.toList())
+ : IteratorUtils.asList(output)))
+ .get();
+
+ // Provide an debug explanation for the query that just ran
+ span.addEvent("Request complete");
+ if (graph instanceof GafferPopGraph) {
+ JSONObject gafferOperationChain = GremlinController.getGafferPopExplanation((GafferPopGraph) graph);
+ span.setAttribute("gaffer.gremlin.explain", gafferOperationChain.toString());
+ LOGGER.debug("{}", gafferOperationChain);
+ }
+
+ // Build the response
+ responseMessage = ResponseMessage.build(requestId)
+ .code(ResponseStatusCode.SUCCESS)
+ .result(result).create();
+
+ } catch (final InterruptedException e) {
+ Thread.currentThread().interrupt();
+ responseMessage = ResponseMessage.build(requestId)
+ .code(ResponseStatusCode.SERVER_ERROR)
+ .statusMessage(e.getMessage()).create();
+ span.setStatus(StatusCode.ERROR, e.getMessage());
+ span.recordException(e);
+ } catch (final Exception e) {
+ responseMessage = ResponseMessage.build(requestId)
+ .code(ResponseStatusCode.SERVER_ERROR)
+ .statusMessage(e.getMessage()).create();
+ span.setStatus(StatusCode.ERROR, e.getMessage());
+ span.recordException(e);
+ } finally {
+ span.end();
+ }
+
+ return responseMessage;
+
+ }
+
+ /**
+ * Serialises and sends a response using the current session.
+ *
+ * @param session Current websocket session
+ * @param serialiser The serialiser to use for the message
+ * @param response The response message
+ * @throws IOException If fail to serialise or send
+ */
+ private void sendBinaryResponse(final WebSocketSession session, final MessageSerializer> serialiser, final ResponseMessage response) throws IOException {
+ // Serialise response and read the bytes into a byte array
+ ByteBuf responseByteBuf = serialiser.serializeResponseAsBinary(response, PooledByteBufAllocator.DEFAULT);
+ byte[] responseBytes = new byte[responseByteBuf.readableBytes()];
+ responseByteBuf.readBytes(responseBytes);
+ // Send response
+ session.sendMessage(new BinaryMessage(responseBytes));
+ }
+
+ /**
+ * Simple method to help the conversion between {@link BinaryMessage}
+ * and {@link ByteBuf} type.
+ *
+ * @param message the binary web socket message.
+ * @return A netty byte buffer for the message.
+ */
+ private ByteBuf convertToByteBuf(final BinaryMessage message) {
+ ByteBuffer byteBuffer = message.getPayload();
+
+ if (byteBuffer.hasArray()) {
+ return Unpooled.wrappedBuffer(byteBuffer.array(), byteBuffer.position(), byteBuffer.remaining());
+ } else {
+ byte[] byteArray = new byte[byteBuffer.remaining()];
+ byteBuffer.get(byteArray);
+ return Unpooled.wrappedBuffer(byteArray);
+ }
+ }
+
+}
diff --git a/rest-api/spring-rest/src/main/resources/application.yml b/rest-api/spring-rest/src/main/resources/application.yml
index 06936a4979a..c495df8ccad 100644
--- a/rest-api/spring-rest/src/main/resources/application.yml
+++ b/rest-api/spring-rest/src/main/resources/application.yml
@@ -21,7 +21,7 @@ gaffer:
schemas: rest-api/spring-rest/src/main/resources/schemas
server:
- servlet.context-path: /rest
+ servlet.context-path: /
# Configuration to replicate previous Swagger UI style
springdoc:
diff --git a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/config/FactoryConfigTest.java b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/config/FactoryConfigTest.java
index 601c0e33dfc..4fbe603dfa1 100644
--- a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/config/FactoryConfigTest.java
+++ b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/config/FactoryConfigTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2021 Crown Copyright
+ * Copyright 2020-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,7 +26,7 @@
import uk.gov.gchq.gaffer.rest.factory.DefaultGraphFactory;
import uk.gov.gchq.gaffer.rest.factory.MockGraphFactory;
import uk.gov.gchq.gaffer.rest.factory.MockUserFactory;
-import uk.gov.gchq.gaffer.rest.factory.UnknownUserFactory;
+import uk.gov.gchq.gaffer.rest.factory.spring.UnknownUserFactory;
import java.util.Properties;
diff --git a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/GremlinControllerTest.java b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/GremlinControllerTest.java
new file mode 100644
index 00000000000..d5cd4e79169
--- /dev/null
+++ b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/GremlinControllerTest.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.rest.controller;
+
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.json.JSONArray;
+import org.json.JSONObject;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+import org.springframework.test.web.servlet.MockMvc;
+import org.springframework.test.web.servlet.MvcResult;
+import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+
+import uk.gov.gchq.gaffer.operation.impl.get.GetElements;
+import uk.gov.gchq.gaffer.rest.factory.spring.AbstractUserFactory;
+import uk.gov.gchq.gaffer.rest.factory.spring.UnknownUserFactory;
+import uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTestUtil.StoreType;
+import uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
+import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.MARKO;
+
+@ExtendWith(SpringExtension.class)
+@WebMvcTest(value = GremlinController.class)
+@Import(GremlinControllerTest.TestConfig.class)
+class GremlinControllerTest {
+
+ private static final String GREMLIN_EXPLAIN_ENDPOINT = "/rest/gremlin/explain";
+ private static final String CYPHER_EXPLAIN_ENDPOINT = "/rest/gremlin/cypher/explain";
+
+ @TestConfiguration
+ static class TestConfig {
+ @Bean
+ public GraphTraversalSource g() {
+ Graph graph = GafferPopModernTestUtils.createModernGraph(TestConfig.class, StoreType.MAP);
+ return graph.traversal();
+ }
+
+ @Bean
+ public AbstractUserFactory userFactory() {
+ return new UnknownUserFactory();
+ }
+ }
+
+ @Autowired
+ private MockMvc mockMvc;
+
+ @Autowired
+ private GraphTraversalSource g;
+
+ @Test
+ void shouldReturnExplainOfValidGremlinQuery() throws Exception {
+ // Given
+ String gremlinString = "g.V('" + MARKO.getId() + "').toList()";
+ List expectedOperations = Arrays.asList(GetElements.class.getName());
+
+ // When
+ MvcResult result = mockMvc
+ .perform(MockMvcRequestBuilders
+ .post(GREMLIN_EXPLAIN_ENDPOINT)
+ .content(gremlinString)
+ .contentType(TEXT_PLAIN_VALUE))
+ .andReturn();
+
+ // Then
+ // Ensure OK response
+ assertThat(result.getResponse().getStatus()).isEqualTo(200);
+
+ // Get and check response
+ JSONObject jsonResponse = new JSONObject(result.getResponse().getContentAsString());
+ assertThat(jsonResponse.has(GremlinController.EXPLAIN_OVERVIEW_KEY)).isTrue();
+ assertThat(jsonResponse.has(GremlinController.EXPLAIN_OP_CHAIN_KEY)).isTrue();
+
+ // Check the operations that ran are as expected
+ JSONArray operations = jsonResponse.getJSONObject("chain").getJSONArray("operations");
+ assertThat(operations)
+ .map(json -> ((JSONObject) json).getString("class"))
+ .containsExactlyElementsOf(expectedOperations);
+ }
+
+ @Test
+ void shouldRejectMalformedGremlinQuery() throws Exception {
+ // Given
+ String gremlinString = "g.V().stepDoesNotExist().toList()";
+
+ // When
+ MvcResult result = mockMvc
+ .perform(MockMvcRequestBuilders
+ .post(GREMLIN_EXPLAIN_ENDPOINT)
+ .content(gremlinString)
+ .contentType(TEXT_PLAIN_VALUE))
+ .andReturn();
+
+ // Then
+ // Expect a server error response
+ assertThat(result.getResponse().getStatus()).isEqualTo(500);
+ }
+
+ @Test
+ void shouldReturnExplainOfValidCypherQuery() throws Exception {
+ // Given
+ String cypherString = "MATCH (p:person) WHERE ID(p) = '" + MARKO.getId() + "' RETURN p";
+ List expectedOperations = Arrays.asList(GetElements.class.getName());
+
+ // When
+ MvcResult result = mockMvc
+ .perform(MockMvcRequestBuilders
+ .post(CYPHER_EXPLAIN_ENDPOINT)
+ .content(cypherString)
+ .contentType(TEXT_PLAIN_VALUE))
+ .andReturn();
+
+ // Then
+ // Ensure OK response
+ assertThat(result.getResponse().getStatus()).isEqualTo(200);
+
+ // Get and check response
+ JSONObject jsonResponse = new JSONObject(result.getResponse().getContentAsString());
+ assertThat(jsonResponse.has(GremlinController.EXPLAIN_OVERVIEW_KEY)).isTrue();
+ assertThat(jsonResponse.has(GremlinController.EXPLAIN_OP_CHAIN_KEY)).isTrue();
+ assertThat(jsonResponse.has(GremlinController.EXPLAIN_GREMLIN_KEY)).isTrue();
+
+ // Check the operations that ran are as expected
+ JSONArray operations = jsonResponse.getJSONObject("chain").getJSONArray("operations");
+ assertThat(operations)
+ .map(json -> ((JSONObject) json).getString("class"))
+ .containsExactlyElementsOf(expectedOperations);
+ }
+
+ @Test
+ void shouldRejectMalformedCypherQuery() throws Exception {
+ // Given
+ String cypherString = "MATCH (p:person) WHERE RETURN p";
+
+ // When
+ MvcResult result = mockMvc
+ .perform(MockMvcRequestBuilders
+ .post(CYPHER_EXPLAIN_ENDPOINT)
+ .content(cypherString)
+ .contentType(TEXT_PLAIN_VALUE))
+ .andReturn();
+
+ // Then
+ // Expect a server error response
+ assertThat(result.getResponse().getStatus()).isEqualTo(500);
+ }
+
+
+}
diff --git a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/VersionControllerTest.java b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/VersionControllerTest.java
index d50e3a137c5..5855c4c3abb 100644
--- a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/VersionControllerTest.java
+++ b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/VersionControllerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Crown Copyright
+ * Copyright 2023-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -48,7 +48,7 @@ void sendRequestAndCheckForValidVersion() throws Exception {
// Perform mock request to the endpoint
RequestBuilder requestBuilder = MockMvcRequestBuilders
- .get("/graph/version")
+ .get("/rest/graph/version")
.accept(TEXT_PLAIN_VALUE);
MvcResult result = mockMvc.perform(requestBuilder).andReturn();
diff --git a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/factory/MockUserFactory.java b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/factory/MockUserFactory.java
index a57f178acd5..b2ffc36146e 100644
--- a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/factory/MockUserFactory.java
+++ b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/factory/MockUserFactory.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020 Crown Copyright
+ * Copyright 2020-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,14 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package uk.gov.gchq.gaffer.rest.factory;
+import uk.gov.gchq.gaffer.rest.factory.spring.AbstractUserFactory;
import uk.gov.gchq.gaffer.store.Context;
import uk.gov.gchq.gaffer.user.User;
import static org.mockito.Mockito.mock;
-public class MockUserFactory implements UserFactory {
+public class MockUserFactory extends AbstractUserFactory {
final User user = mock(User.class);
diff --git a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/controller/AbstractRestApiIT.java b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/controller/AbstractRestApiIT.java
index 5b8e3640605..f8274cc8d68 100644
--- a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/controller/AbstractRestApiIT.java
+++ b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/controller/AbstractRestApiIT.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2020-2022 Crown Copyright
+ * Copyright 2020-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -82,11 +82,12 @@ protected int getPort() {
}
protected String getContextPath() {
- return contextPath;
+ // All the rest endpoints are under /rest context
+ return contextPath + "rest";
}
protected String getBaseURl() {
- return "http://localhost:" + port + "/" + contextPath;
+ return "http://localhost:" + port + "/" + getContextPath();
}
protected ResponseEntity get(final String path, final Class responseBodyClass) {
diff --git a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/handler/GremlinWebSocketIT.java b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/handler/GremlinWebSocketIT.java
new file mode 100644
index 00000000000..2dba6d0a30c
--- /dev/null
+++ b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/handler/GremlinWebSocketIT.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.rest.integration.handler;
+
+import org.apache.tinkerpop.gremlin.driver.Client;
+import org.apache.tinkerpop.gremlin.driver.Cluster;
+import org.apache.tinkerpop.gremlin.driver.Result;
+import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
+import org.apache.tinkerpop.gremlin.structure.Graph;
+import org.apache.tinkerpop.gremlin.util.ser.GraphSONMessageSerializerV3;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.boot.test.context.TestConfiguration;
+import org.springframework.boot.test.web.server.LocalServerPort;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Import;
+import org.springframework.context.annotation.Profile;
+import org.springframework.test.context.ActiveProfiles;
+import org.springframework.test.context.junit.jupiter.SpringExtension;
+
+import uk.gov.gchq.gaffer.rest.factory.spring.AbstractUserFactory;
+import uk.gov.gchq.gaffer.rest.factory.spring.UnknownUserFactory;
+import uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTestUtil.StoreType;
+import uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
+import static uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables.CYPHER_KEY;
+import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.JOSH;
+import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.MARKO;
+import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.PETER;
+import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.VADAS;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.concurrent.ExecutionException;
+import java.util.stream.Collectors;
+
+/**
+ * Integration testing for the Gremlin websocket. Testing is fairly simple as
+ * GafferPop is more heavily tested in its own module, focus of testing here is
+ * ensuring the websocket can at least accept Gremlin and GafferPop related
+ * queries via a standard tinkerpop client connection.
+ */
+@ExtendWith(SpringExtension.class)
+@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
+@Import(GremlinWebSocketIT.TestConfig.class)
+@ActiveProfiles("test")
+class GremlinWebSocketIT {
+
+ @TestConfiguration
+ static class TestConfig {
+
+ @Bean
+ @Profile("test")
+ public GraphTraversalSource g() {
+ Graph graph = GafferPopModernTestUtils.createModernGraph(TestConfig.class, StoreType.MAP);
+ return graph.traversal();
+ }
+
+ @Bean
+ @Profile("test")
+ public AbstractUserFactory userFactory() {
+ return new UnknownUserFactory();
+ }
+ }
+
+ @LocalServerPort
+ private Integer port;
+
+ @Autowired
+ private GraphTraversalSource g;
+
+ private Client client;
+
+ @BeforeEach
+ void setup() {
+ // Set up a client connection to the server
+ Cluster cluster = Cluster.build()
+ .addContactPoint("localhost")
+ .port(port)
+ .serializer(new GraphSONMessageSerializerV3()).create();
+ this.client = cluster.connect();
+ }
+
+ @Test
+ void shouldAcceptBasicGremlinQueries() {
+ // Given
+ String query = "g.V().hasLabel('person').toList()";
+
+ // When
+ List results = client.submit(query).stream().collect(Collectors.toList());
+
+ // Then
+ assertThat(results)
+ .map(result -> result.getElement().id())
+ .containsExactlyInAnyOrder(
+ MARKO.getId(),
+ VADAS.getId(),
+ PETER.getId(),
+ JOSH.getId());
+ }
+
+ @Test
+ void shouldRejectMalformedGremlinQueries() {
+ // Given
+ String query = "g.V().thisStepDoesNotExist().toList()";
+
+ // When/Then
+ assertThatExceptionOfType(ExecutionException.class)
+ .isThrownBy(() -> client.submit(query).all().get())
+ .withMessageContaining("groovy.lang.MissingMethodException");
+ }
+
+ @Test
+ void shouldAcceptQueryWithCypher() {
+ // Given
+ String cypherQuery = "MATCH (p:person) WHERE ID(p) = '" + MARKO.getId() + "' RETURN p.name";
+ String gremlinQuery = "g.with(\"" + CYPHER_KEY + "\", " + "\"" + cypherQuery + "\").call().toList()";
+
+ // When
+ List results = client.submit(gremlinQuery).stream().collect(Collectors.toList());
+
+ // Then
+ // Cypher returns each result under the project name e.g. 'p' so we need to extract
+ assertThat(results)
+ .flatMap(result -> ((LinkedHashMap) result.getObject()).values())
+ .containsExactly(MARKO.getName());
+ }
+
+}
diff --git a/rest-api/spring-rest/src/test/resources/gaffer/schema/elements.json b/rest-api/spring-rest/src/test/resources/gaffer/schema/elements.json
new file mode 100644
index 00000000000..2bf7bb63960
--- /dev/null
+++ b/rest-api/spring-rest/src/test/resources/gaffer/schema/elements.json
@@ -0,0 +1,49 @@
+{
+ "entities": {
+ "vertex": {
+ "vertex": "id"
+ },
+ "person": {
+ "vertex": "person",
+ "properties": {
+ "name": "name.string",
+ "age": "age.integer"
+ }
+ },
+ "software": {
+ "vertex": "software",
+ "properties": {
+ "name": "name.string",
+ "lang": "name.string"
+ }
+ }
+ },
+ "edges": {
+ "knows": {
+ "source": "person",
+ "destination": "person",
+ "directed": "true",
+ "properties": {
+ "weight": "weight.double"
+ }
+ },
+ "created": {
+ "source": "person",
+ "destination": "software",
+ "directed": "true",
+ "properties": {
+ "weight": "weight.double"
+ }
+ },
+ "dependsOn": {
+ "source": "software",
+ "destination": "software",
+ "directed": "true"
+ },
+ "encapsulates": {
+ "source": "software",
+ "destination": "software",
+ "directed": "true"
+ }
+ }
+}
diff --git a/rest-api/spring-rest/src/test/resources/gaffer/schema/types.json b/rest-api/spring-rest/src/test/resources/gaffer/schema/types.json
new file mode 100644
index 00000000000..601bc87a04a
--- /dev/null
+++ b/rest-api/spring-rest/src/test/resources/gaffer/schema/types.json
@@ -0,0 +1,34 @@
+{
+ "types": {
+ "id": {
+ "class": "java.lang.String"
+ },
+ "person": {
+ "class": "java.lang.String"
+ },
+ "software": {
+ "class": "java.lang.String"
+ },
+ "true": {
+ "class": "java.lang.Boolean"
+ },
+ "name.string": {
+ "class": "java.lang.String",
+ "aggregateFunction": {
+ "class": "uk.gov.gchq.koryphe.impl.binaryoperator.First"
+ }
+ },
+ "age.integer": {
+ "class": "java.lang.Integer",
+ "aggregateFunction": {
+ "class": "uk.gov.gchq.koryphe.impl.binaryoperator.First"
+ }
+ },
+ "weight.double": {
+ "class": "java.lang.Double",
+ "aggregateFunction": {
+ "class": "uk.gov.gchq.koryphe.impl.binaryoperator.First"
+ }
+ }
+ }
+}
From 0d7f7a7c7d802588621d866fe2cdfd4fa325806e Mon Sep 17 00:00:00 2001
From: cn337131 <141730190+cn337131@users.noreply.github.com>
Date: Fri, 19 Jul 2024 14:25:10 +0000
Subject: [PATCH 10/16] Gh-3250: GetElementsWithinSet bug fix (#3251)
* initial testing and changes
* tidy up, remove bug test and copyright headers
* fix commented out lines
* additional federated tests
* reduce duplicate tests
* checkstyle
* add inOutType
* copyright
* fix version variable
* address comments
---------
Co-authored-by: wb36499 <166839644+wb36499@users.noreply.github.com>
---
.../src/main/resources/version.properties | 2 +-
.../operation/impl/GetElementsWithinSet.java | 20 +-
...GetElementsWithinSetDeletedElementsIT.java | 4 +-
.../GetElementsWithinSetHandlerTest.java | 181 ++++++++++--
.../impl/GetElementsWithinSetTest.java | 35 ++-
...ederatedStoreGetElementsWithinSetTest.java | 273 ++++++++++++++++++
6 files changed, 467 insertions(+), 48 deletions(-)
create mode 100644 store-implementation/federated-store/src/test/java/uk/gov/gchq/gaffer/federatedstore/FederatedStoreGetElementsWithinSetTest.java
diff --git a/rest-api/common-rest/src/main/resources/version.properties b/rest-api/common-rest/src/main/resources/version.properties
index e26645a4bc2..8cb4d96428b 100644
--- a/rest-api/common-rest/src/main/resources/version.properties
+++ b/rest-api/common-rest/src/main/resources/version.properties
@@ -13,5 +13,5 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#
-gaffer.version=2.2.5-SNAPSHOT
+gaffer.version=${project.version}
koryphe.version=${koryphe.version}
diff --git a/store-implementation/accumulo-store/src/main/java/uk/gov/gchq/gaffer/accumulostore/operation/impl/GetElementsWithinSet.java b/store-implementation/accumulo-store/src/main/java/uk/gov/gchq/gaffer/accumulostore/operation/impl/GetElementsWithinSet.java
index 3697c7b0ca6..8c18f284e56 100644
--- a/store-implementation/accumulo-store/src/main/java/uk/gov/gchq/gaffer/accumulostore/operation/impl/GetElementsWithinSet.java
+++ b/store-implementation/accumulo-store/src/main/java/uk/gov/gchq/gaffer/accumulostore/operation/impl/GetElementsWithinSet.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2020 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@
import uk.gov.gchq.gaffer.data.element.id.EntityId;
import uk.gov.gchq.gaffer.data.elementdefinition.view.View;
import uk.gov.gchq.gaffer.operation.Operation;
-import uk.gov.gchq.gaffer.operation.graph.GraphFilters;
+import uk.gov.gchq.gaffer.operation.graph.SeededGraphFilters;
import uk.gov.gchq.gaffer.operation.io.InputOutput;
import uk.gov.gchq.gaffer.operation.io.MultiEntityIdInput;
import uk.gov.gchq.gaffer.operation.serialisation.TypeReferenceImpl;
@@ -44,11 +44,12 @@
public class GetElementsWithinSet implements
InputOutput, Iterable extends Element>>,
MultiEntityIdInput,
- GraphFilters {
+ SeededGraphFilters {
private View view;
private DirectedType directedType;
private Iterable extends EntityId> input;
private Map options;
+ private IncludeIncomingOutgoingType includeIncomingOutGoing;
@Override
public View getView() {
@@ -95,10 +96,21 @@ public void setOptions(final Map options) {
this.options = options;
}
+ @Override
+ public IncludeIncomingOutgoingType getIncludeIncomingOutGoing() {
+ return includeIncomingOutGoing;
+ }
+
+ @Override
+ public void setIncludeIncomingOutGoing(final IncludeIncomingOutgoingType inOutType) {
+ this.includeIncomingOutGoing = inOutType;
+ }
+
@Override
public GetElementsWithinSet shallowClone() {
return new GetElementsWithinSet.Builder()
.view(view)
+ .inOutType(includeIncomingOutGoing)
.directedType(directedType)
.input(input)
.options(options)
@@ -108,7 +120,7 @@ public GetElementsWithinSet shallowClone() {
public static class Builder extends Operation.BaseBuilder implements
InputOutput.Builder, Iterable extends Element>, Builder>,
MultiEntityIdInput.Builder,
- GraphFilters.Builder {
+ SeededGraphFilters.Builder {
public Builder() {
super(new GetElementsWithinSet());
}
diff --git a/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/integration/delete/GetElementsWithinSetDeletedElementsIT.java b/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/integration/delete/GetElementsWithinSetDeletedElementsIT.java
index 878ebfeacc4..24469883650 100644
--- a/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/integration/delete/GetElementsWithinSetDeletedElementsIT.java
+++ b/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/integration/delete/GetElementsWithinSetDeletedElementsIT.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2020 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,7 @@
import uk.gov.gchq.gaffer.accumulostore.operation.impl.GetElementsWithinSet;
import uk.gov.gchq.gaffer.data.element.Element;
+import uk.gov.gchq.gaffer.operation.graph.SeededGraphFilters.IncludeIncomingOutgoingType;
public class GetElementsWithinSetDeletedElementsIT extends AbstractDeletedElementsIT> {
@@ -25,6 +26,7 @@ public class GetElementsWithinSetDeletedElementsIT extends AbstractDeletedElemen
protected GetElementsWithinSet createGetOperation() {
return new GetElementsWithinSet.Builder()
.input((Object[]) VERTICES)
+ .inOutType(IncludeIncomingOutgoingType.OUTGOING)
.build();
}
}
diff --git a/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/operation/handler/GetElementsWithinSetHandlerTest.java b/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/operation/handler/GetElementsWithinSetHandlerTest.java
index 7fb10bcf8df..387d539b6f6 100644
--- a/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/operation/handler/GetElementsWithinSetHandlerTest.java
+++ b/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/operation/handler/GetElementsWithinSetHandlerTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2021 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -39,6 +39,7 @@
import uk.gov.gchq.gaffer.data.elementdefinition.view.ViewElementDefinition;
import uk.gov.gchq.gaffer.operation.OperationException;
import uk.gov.gchq.gaffer.operation.data.EntitySeed;
+import uk.gov.gchq.gaffer.operation.graph.SeededGraphFilters.IncludeIncomingOutgoingType;
import uk.gov.gchq.gaffer.operation.impl.add.AddElements;
import uk.gov.gchq.gaffer.store.Context;
import uk.gov.gchq.gaffer.store.StoreException;
@@ -69,18 +70,21 @@ public class GetElementsWithinSetHandlerTest {
.source("A0")
.dest("A23")
.directed(true)
+ .matchedVertex(EdgeId.MatchedVertex.SOURCE)
.build();
private static Edge expectedEdge2 = new Edge.Builder()
.group(TestGroups.EDGE)
.source("A0")
.dest("A23")
.directed(true)
+ .matchedVertex(EdgeId.MatchedVertex.SOURCE)
.build();
private static Edge expectedEdge3 = new Edge.Builder()
.group(TestGroups.EDGE)
.source("A0")
.dest("A23")
.directed(true)
+ .matchedVertex(EdgeId.MatchedVertex.SOURCE)
.build();
private static Entity expectedEntity1 = new Entity.Builder()
.group(TestGroups.ENTITY)
@@ -101,12 +105,14 @@ public class GetElementsWithinSetHandlerTest {
.source("A0")
.dest("A23")
.directed(true)
+ .matchedVertex(EdgeId.MatchedVertex.SOURCE)
.build();
private static Edge expectedSummarisedEdgePropertiesFiltered = new Edge.Builder()
.group(TestGroups.EDGE)
.source("A0")
.dest("A23")
.directed(true)
+ .matchedVertex(EdgeId.MatchedVertex.SOURCE)
.property(AccumuloPropertyNames.COUNT, 23 * 3)
.build();
@@ -160,12 +166,12 @@ public void reInitialise() throws StoreException, OperationException, TableExist
}
@Test
- public void shouldReturnElementsNoSummarisationByteEntityStore() throws OperationException {
+ void shouldReturnElementsNoSummarisationByteEntityStore() throws OperationException {
shouldReturnElementsNoSummarisation(BYTE_ENTITY_STORE);
}
@Test
- public void shouldReturnElementsNoSummarisationGaffer1Store() throws OperationException {
+ void shouldReturnElementsNoSummarisationGaffer1Store() throws OperationException {
shouldReturnElementsNoSummarisation(GAFFER_1_KEY_STORE);
}
@@ -187,7 +193,7 @@ private void shouldReturnElementsNoSummarisation(final AccumuloStore store) thro
}
@Test
- public void shouldSummariseByteEntityStore() throws OperationException {
+ void shouldSummariseByteEntityStore() throws OperationException {
final View view = new View.Builder(defaultView)
.entity(TestGroups.ENTITY, new ViewElementDefinition.Builder()
.groupBy()
@@ -200,11 +206,11 @@ public void shouldSummariseByteEntityStore() throws OperationException {
.build())
.build();
- runTest(BYTE_ENTITY_STORE, view, expectedSummarisedEdge, expectedEntity1, expectedEntity2);
+ runTest(BYTE_ENTITY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedSummarisedEdge, expectedEntity1, expectedEntity2);
}
@Test
- public void shouldSummariseGaffer1Store() throws OperationException {
+ void shouldSummariseGaffer1Store() throws OperationException {
final View view = new View.Builder(defaultView)
.entity(TestGroups.ENTITY, new ViewElementDefinition.Builder()
.groupBy()
@@ -217,11 +223,11 @@ public void shouldSummariseGaffer1Store() throws OperationException {
.build())
.build();
- runTest(GAFFER_1_KEY_STORE, view, expectedSummarisedEdge, expectedEntity1, expectedEntity2);
+ runTest(GAFFER_1_KEY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedSummarisedEdge, expectedEntity1, expectedEntity2);
}
@Test
- public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesByteEntityStore() throws OperationException {
+ void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesByteEntityStore() throws OperationException {
final View view = new View.Builder()
.edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
.groupBy()
@@ -231,11 +237,11 @@ public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesByteEntityStore() thr
.build())
.build();
- runTest(BYTE_ENTITY_STORE, view, expectedSummarisedEdge);
+ runTest(BYTE_ENTITY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedSummarisedEdge);
}
@Test
- public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesGaffer1Store() throws OperationException {
+ void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesGaffer1Store() throws OperationException {
final View view = new View.Builder()
.edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
.groupBy()
@@ -245,11 +251,11 @@ public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesGaffer1Store() throws
.build())
.build();
- runTest(GAFFER_1_KEY_STORE, view, expectedSummarisedEdge);
+ runTest(GAFFER_1_KEY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedSummarisedEdge);
}
@Test
- public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesFilteredByteEntityStore() throws OperationException {
+ void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesFilteredByteEntityStore() throws OperationException {
final View view = new View.Builder()
.edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
.properties(Collections.singleton(AccumuloPropertyNames.COUNT))
@@ -261,11 +267,11 @@ public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesFilteredByt
.build())
.build();
- runTest(BYTE_ENTITY_STORE, view, expectedSummarisedEdgePropertiesFiltered);
+ runTest(BYTE_ENTITY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedSummarisedEdgePropertiesFiltered);
}
@Test
- public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesFilteredGaffer1Store() throws OperationException {
+ void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesFilteredGaffer1Store() throws OperationException {
final View view = new View.Builder()
.edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
.properties(Collections.singleton(AccumuloPropertyNames.COUNT))
@@ -277,11 +283,11 @@ public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesFilteredGaf
.build())
.build();
- runTest(GAFFER_1_KEY_STORE, view, expectedSummarisedEdgePropertiesFiltered);
+ runTest(GAFFER_1_KEY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedSummarisedEdgePropertiesFiltered);
}
@Test
- public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesEmptySetByteEntityStore() throws OperationException {
+ void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesEmptySetByteEntityStore() throws OperationException {
final View view = new View.Builder()
.edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
.properties(Collections.emptySet())
@@ -293,11 +299,11 @@ public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesEmptySetByt
.build())
.build();
- runTest(BYTE_ENTITY_STORE, view, expectedSummarisedEdgePropertiesEmptySet);
+ runTest(BYTE_ENTITY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedSummarisedEdgePropertiesEmptySet);
}
@Test
- public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesEmptySetGaffer1Store() throws OperationException {
+ void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesEmptySetGaffer1Store() throws OperationException {
final View view = new View.Builder()
.edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
.properties(Collections.emptySet())
@@ -309,33 +315,152 @@ public void shouldReturnOnlyEdgesWhenViewContainsNoEntitiesPropertiesEmptySetGaf
.build())
.build();
- runTest(GAFFER_1_KEY_STORE, view, expectedSummarisedEdgePropertiesEmptySet);
+ runTest(GAFFER_1_KEY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedSummarisedEdgePropertiesEmptySet);
}
@Test
- public void shouldReturnOnlyEntitiesWhenViewContainsNoEdgesByteEntityStore() throws OperationException {
+ void shouldReturnOnlyEntitiesWhenViewContainsNoEdgesByteEntityStore() throws OperationException {
final View view = new View.Builder()
.entity(TestGroups.ENTITY, new ViewElementDefinition.Builder()
.groupBy()
.build())
.build();
- runTest(BYTE_ENTITY_STORE, view, expectedEntity1, expectedEntity2);
+ runTest(BYTE_ENTITY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedEntity1, expectedEntity2);
}
@Test
- public void shouldReturnOnlyEntitiesWhenViewContainsNoEdgesGaffer1Store() throws OperationException {
+ void shouldReturnOnlyEntitiesWhenViewContainsNoEdgesGaffer1Store() throws OperationException {
final View view = new View.Builder()
.entity(TestGroups.ENTITY, new ViewElementDefinition.Builder()
.groupBy()
.build())
.build();
- runTest(GAFFER_1_KEY_STORE, view, expectedEntity1, expectedEntity2);
+ runTest(GAFFER_1_KEY_STORE, view, IncludeIncomingOutgoingType.OUTGOING, expectedEntity1, expectedEntity2);
}
- private void runTest(final AccumuloStore store, final View view, final Element... expectedElements) throws OperationException {
+ @Test
+ void shouldGetIncomingEdgesOnlyGaffer1Store() throws OperationException {
+ final View view = new View.Builder()
+ .edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
+ .properties(Collections.emptySet())
+ .groupBy()
+ .build())
+ .build();
+
+ // Incoming edge should have matched vertex DESTINATION
+ final Edge expectedEdge = new Edge.Builder()
+ .group(TestGroups.EDGE)
+ .source("A0")
+ .dest("A23")
+ .directed(true)
+ .matchedVertex(EdgeId.MatchedVertex.DESTINATION)
+ .build();
+
+ runTest(GAFFER_1_KEY_STORE, view, IncludeIncomingOutgoingType.INCOMING, expectedEdge);
+ }
+
+ @Test
+ void shouldGetIncomingEdgesOnlyByteEntityStore() throws OperationException {
+ final View view = new View.Builder()
+ .edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
+ .properties(Collections.emptySet())
+ .groupBy()
+ .build())
+ .build();
+
+ // Incoming edge should have matched vertex DESTINATION
+ final Edge expectedEdge = new Edge.Builder()
+ .group(TestGroups.EDGE)
+ .source("A0")
+ .dest("A23")
+ .directed(true)
+ .matchedVertex(EdgeId.MatchedVertex.DESTINATION)
+ .build();
+
+ runTest(BYTE_ENTITY_STORE, view, IncludeIncomingOutgoingType.INCOMING, expectedEdge);
+ }
+
+ @Test
+ void shouldGetEdgesWhenDestAndSourceAreInDifferentBatchesGaffer1Store() throws OperationException {
+ // Given
+ // Set batch scanner entries to 20 - so edge A99 -> A1 will have its src in the final batch but its
+ // dest in the first
+ GAFFER_1_KEY_STORE.getProperties().setMaxEntriesForBatchScanner("20");
+ final Set seedSet = new HashSet<>();
+ for (int i = 1; i < 100; i++) {
+ seedSet.add(new EntitySeed("A" + i));
+ }
+
+ // When
+ final GetElementsWithinSet op = new GetElementsWithinSet.Builder()
+ .view(new View.Builder()
+ .edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
+ .properties(Collections.emptySet())
+ .groupBy()
+ .build())
+ .build())
+ .input(seedSet)
+ .build();
+ final GetElementsWithinSetHandler handler = new GetElementsWithinSetHandler();
+
+ final Iterable extends Element> results = handler.doOperation(op, user, GAFFER_1_KEY_STORE);
+
+ // Then
+ final Edge expectedEdge = new Edge.Builder()
+ .group(TestGroups.EDGE)
+ .source("A99")
+ .dest("A1")
+ .directed(true)
+ .build();
+
+ assertThat(results)
+ .asInstanceOf(InstanceOfAssertFactories.iterable(Element.class))
+ .contains(expectedEdge);
+ }
+
+ @Test
+ void shouldGetEdgesWhenDestAndSourceAreInDifferentBatchesByteEntityStore() throws OperationException {
+ // Given
+ // Set batch scanner entries to 20 - so edge A99 -> A1 will have its src in the final batch but its
+ // dest in the first
+ BYTE_ENTITY_STORE.getProperties().setMaxEntriesForBatchScanner("20");
+ final Set seedSet = new HashSet<>();
+ for (int i = 1; i < 100; i++) {
+ seedSet.add(new EntitySeed("A" + i));
+ }
+
+ // When
+ final GetElementsWithinSet op = new GetElementsWithinSet.Builder()
+ .view(new View.Builder()
+ .edge(TestGroups.EDGE, new ViewElementDefinition.Builder()
+ .properties(Collections.emptySet())
+ .groupBy()
+ .build())
+ .build())
+ .input(seedSet)
+ .build();
+ final GetElementsWithinSetHandler handler = new GetElementsWithinSetHandler();
+
+ final Iterable extends Element> results = handler.doOperation(op, user, GAFFER_1_KEY_STORE);
+
+ // Then
+ final Edge expectedEdge = new Edge.Builder()
+ .group(TestGroups.EDGE)
+ .source("A99")
+ .dest("A1")
+ .directed(true)
+ .build();
+
+ assertThat(results)
+ .asInstanceOf(InstanceOfAssertFactories.iterable(Element.class))
+ .contains(expectedEdge);
+ }
+
+ private void runTest(final AccumuloStore store, final View view, final IncludeIncomingOutgoingType type, final Element... expectedElements) throws OperationException {
final GetElementsWithinSet operation = new GetElementsWithinSet.Builder().view(view).input(seeds).build();
+ operation.setIncludeIncomingOutGoing(type);
final GetElementsWithinSetHandler handler = new GetElementsWithinSetHandler();
final Iterable extends Element> elements = handler.doOperation(operation, user, store);
@@ -398,6 +523,14 @@ private static void setupGraph(final AccumuloStore store) throws OperationExcept
.property(AccumuloPropertyNames.PROP_4, 0)
.build());
+ data.add(new Edge.Builder()
+ .group(TestGroups.EDGE)
+ .source("A99")
+ .dest("A1")
+ .directed(true)
+ .property(AccumuloPropertyNames.COLUMN_QUALIFIER, 1)
+ .build());
+
data.add(new Entity.Builder()
.group(TestGroups.ENTITY)
.vertex("A" + i)
diff --git a/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/operation/impl/GetElementsWithinSetTest.java b/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/operation/impl/GetElementsWithinSetTest.java
index bfe11a36df4..2e130cfa29d 100644
--- a/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/operation/impl/GetElementsWithinSetTest.java
+++ b/store-implementation/accumulo-store/src/test/java/uk/gov/gchq/gaffer/accumulostore/operation/impl/GetElementsWithinSetTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2021 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,18 +25,16 @@
import uk.gov.gchq.gaffer.exception.SerialisationException;
import uk.gov.gchq.gaffer.jsonserialisation.JSONSerialiser;
import uk.gov.gchq.gaffer.operation.OperationTest;
+import uk.gov.gchq.gaffer.operation.graph.SeededGraphFilters;
import java.util.Iterator;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertNotSame;
-public class GetElementsWithinSetTest extends OperationTest {
+class GetElementsWithinSetTest extends OperationTest {
@Test
- public void shouldJSONSerialiseAndDeserialise() throws SerialisationException {
+ void shouldJSONSerialiseAndDeserialise() throws SerialisationException {
// Given
final GetElementsWithinSet op = new GetElementsWithinSet.Builder()
.input(AccumuloTestData.SEED_SOURCE_1,
@@ -65,15 +63,18 @@ public void builderShouldCreatePopulatedOperation() {
final GetElementsWithinSet getElementsWithinSet = new GetElementsWithinSet.Builder()
.input(AccumuloTestData.SEED_A)
.directedType(DirectedType.DIRECTED)
+ .inOutType(SeededGraphFilters.IncludeIncomingOutgoingType.INCOMING)
.option(AccumuloTestData.TEST_OPTION_PROPERTY_KEY, "true")
.view(new View.Builder()
.edge("testEdgegroup")
.build())
.build();
- assertEquals("true", getElementsWithinSet.getOption(AccumuloTestData.TEST_OPTION_PROPERTY_KEY));
- assertEquals(DirectedType.DIRECTED, getElementsWithinSet.getDirectedType());
+
+ assertThat(getElementsWithinSet.getOption(AccumuloTestData.TEST_OPTION_PROPERTY_KEY)).isEqualTo("true");
+ assertThat(getElementsWithinSet.getDirectedType()).isEqualTo(DirectedType.DIRECTED);
+ assertThat(getElementsWithinSet.getIncludeIncomingOutGoing()).isEqualTo(SeededGraphFilters.IncludeIncomingOutgoingType.INCOMING);
assertThat(getElementsWithinSet.getInput().iterator().next()).isEqualTo(AccumuloTestData.SEED_A);
- assertNotNull(getElementsWithinSet.getView());
+ assertThat(getElementsWithinSet.getView()).isNotNull();
}
@Test
@@ -86,6 +87,7 @@ public void shouldShallowCloneOperation() {
final GetElementsWithinSet getElementsWithinSet = new GetElementsWithinSet.Builder()
.input(AccumuloTestData.SEED_A)
.directedType(DirectedType.DIRECTED)
+ .inOutType(SeededGraphFilters.IncludeIncomingOutgoingType.INCOMING)
.option(AccumuloTestData.TEST_OPTION_PROPERTY_KEY, "true")
.view(view)
.build();
@@ -94,25 +96,22 @@ public void shouldShallowCloneOperation() {
final GetElementsWithinSet clone = getElementsWithinSet.shallowClone();
// Then
- assertNotSame(getElementsWithinSet, clone);
- assertEquals("true", clone.getOption(AccumuloTestData.TEST_OPTION_PROPERTY_KEY));
- assertEquals(DirectedType.DIRECTED, clone.getDirectedType());
+ assertThat(clone).isNotSameAs(getElementsWithinSet);
+ assertThat(clone.getOption(AccumuloTestData.TEST_OPTION_PROPERTY_KEY)).isEqualTo("true");
+ assertThat(clone.getIncludeIncomingOutGoing()).isEqualTo(SeededGraphFilters.IncludeIncomingOutgoingType.INCOMING);
assertThat(clone.getInput().iterator().next()).isEqualTo(AccumuloTestData.SEED_A);
- assertEquals(view, clone.getView());
+ assertThat(clone.getView()).isEqualTo(view);
}
@Test
- public void shouldCreateInputFromVertices() {
+ void shouldCreateInputFromVertices() {
// When
final GetElementsWithinSet op = new GetElementsWithinSet.Builder()
.input(AccumuloTestData.SEED_B, AccumuloTestData.SEED_B1.getVertex())
.build();
// Then
- assertEquals(
- Lists.newArrayList(AccumuloTestData.SEED_B, AccumuloTestData.SEED_B1),
- Lists.newArrayList(op.getInput())
- );
+ assertThat(Lists.newArrayList(op.getInput())).isEqualTo(Lists.newArrayList(AccumuloTestData.SEED_B, AccumuloTestData.SEED_B1));
}
@Override
diff --git a/store-implementation/federated-store/src/test/java/uk/gov/gchq/gaffer/federatedstore/FederatedStoreGetElementsWithinSetTest.java b/store-implementation/federated-store/src/test/java/uk/gov/gchq/gaffer/federatedstore/FederatedStoreGetElementsWithinSetTest.java
new file mode 100644
index 00000000000..f0ec586b3db
--- /dev/null
+++ b/store-implementation/federated-store/src/test/java/uk/gov/gchq/gaffer/federatedstore/FederatedStoreGetElementsWithinSetTest.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.federatedstore;
+
+import org.assertj.core.api.InstanceOfAssertFactories;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+import uk.gov.gchq.gaffer.accumulostore.AccumuloProperties;
+import uk.gov.gchq.gaffer.accumulostore.operation.impl.GetElementsWithinSet;
+import uk.gov.gchq.gaffer.commonutil.StreamUtil;
+import uk.gov.gchq.gaffer.data.element.Edge;
+import uk.gov.gchq.gaffer.data.element.Element;
+import uk.gov.gchq.gaffer.data.element.Entity;
+import uk.gov.gchq.gaffer.data.element.id.EntityId;
+import uk.gov.gchq.gaffer.data.elementdefinition.view.View;
+import uk.gov.gchq.gaffer.federatedstore.operation.AddGraph;
+import uk.gov.gchq.gaffer.federatedstore.operation.FederatedOperation;
+import uk.gov.gchq.gaffer.graph.Graph;
+import uk.gov.gchq.gaffer.graph.GraphConfig;
+import uk.gov.gchq.gaffer.operation.OperationException;
+import uk.gov.gchq.gaffer.operation.data.EntitySeed;
+import uk.gov.gchq.gaffer.operation.impl.add.AddElements;
+import uk.gov.gchq.gaffer.store.schema.Schema;
+import uk.gov.gchq.gaffer.store.schema.SchemaEdgeDefinition;
+import uk.gov.gchq.gaffer.store.schema.SchemaEntityDefinition;
+import uk.gov.gchq.gaffer.user.User;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static uk.gov.gchq.gaffer.commonutil.TestPropertyNames.STRING;
+import static uk.gov.gchq.gaffer.federatedstore.FederatedStoreTestUtil.GRAPH_ID_A;
+import static uk.gov.gchq.gaffer.federatedstore.FederatedStoreTestUtil.GRAPH_ID_B;
+import static uk.gov.gchq.gaffer.federatedstore.FederatedStoreTestUtil.GRAPH_ID_TEST_FEDERATED_STORE;
+import static uk.gov.gchq.gaffer.federatedstore.FederatedStoreTestUtil.GROUP_BASIC_EDGE;
+import static uk.gov.gchq.gaffer.federatedstore.FederatedStoreTestUtil.GROUP_BASIC_ENTITY;
+import static uk.gov.gchq.gaffer.federatedstore.FederatedStoreTestUtil.getFederatedStorePropertiesWithHashMapCache;
+import static uk.gov.gchq.gaffer.federatedstore.FederatedStoreTestUtil.resetForFederatedTests;
+import static uk.gov.gchq.gaffer.store.TestTypes.BOOLEAN_TYPE;
+import static uk.gov.gchq.gaffer.store.TestTypes.DIRECTED_EITHER;
+import static uk.gov.gchq.gaffer.store.TestTypes.STRING_TYPE;
+
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+class FederatedStoreGetElementsWithinSetTest {
+ private static final AccumuloProperties ACCUMULO_PROPERTIES = AccumuloProperties.loadStoreProperties(StreamUtil.openStream(FederatedStoreGetElementsWithinSetTest.class, "properties/singleUseAccumuloStore.properties"));
+ private static final User USER = new User();
+ private static final Set SEEDS = new LinkedHashSet<>();
+ private static final String GRAPH_IDS = String.format("%s,%s", GRAPH_ID_A, GRAPH_ID_B);
+ private static final View EDGE_VIEW = new View.Builder().edge(GROUP_BASIC_EDGE).build();
+ private static final View ENTITY_VIEW = new View.Builder().entity(GROUP_BASIC_ENTITY).build();
+
+ private Graph federatedGraph;
+
+ @AfterAll
+ static void tearDownCache() {
+ resetForFederatedTests();
+ }
+
+ @BeforeEach
+ void setUp() throws Exception {
+ resetForFederatedTests();
+ FederatedStoreProperties federatedStoreProperties = getFederatedStorePropertiesWithHashMapCache();
+ for (int i = 0; i < 300; i++) {
+ SEEDS.add(new EntitySeed("A" + i));
+ }
+
+ federatedGraph = new Graph.Builder()
+ .config(new GraphConfig.Builder()
+ .graphId(GRAPH_ID_TEST_FEDERATED_STORE)
+ .build())
+ .addStoreProperties(federatedStoreProperties)
+ .build();
+
+ federatedGraph.execute(new AddGraph.Builder()
+ .graphId(GRAPH_ID_A)
+ .storeProperties(ACCUMULO_PROPERTIES)
+ .schema(getSchema())
+ .build(), USER);
+
+ federatedGraph.execute(new AddGraph.Builder()
+ .graphId(GRAPH_ID_B)
+ .storeProperties(ACCUMULO_PROPERTIES)
+ .schema(getSchema())
+ .build(), USER);
+
+ for (int i = 300; i >= 0; i--) {
+ addEntities("A" + i, GRAPH_ID_A);
+ addEntities("A" + i, GRAPH_ID_B);
+ }
+
+ // Add 6 edges total - should all have src and dest in different batches
+ addEdges("A244", "A87", GRAPH_ID_A);
+ addEdges("A168", "A110", GRAPH_ID_A);
+ addEdges("A56", "A299", GRAPH_ID_A);
+ addEdges("A297", "A193", GRAPH_ID_B);
+ addEdges("A15", "A285", GRAPH_ID_B);
+ addEdges("A1", "A52", GRAPH_ID_B);
+ }
+
+ @Test
+ void shouldReturnOnlyEdgesWhenViewContainsNoEntities() throws Exception {
+ // Given/When
+ final Iterable extends Element> results = (Iterable extends Element>) federatedGraph.execute(new FederatedOperation.Builder()
+ .op(new GetElementsWithinSet.Builder()
+ .view(EDGE_VIEW)
+ .input(SEEDS)
+ .build())
+ .graphIdsCSV(GRAPH_IDS)
+ .build(), USER);
+
+ // Then
+ assertThat(results)
+ .asInstanceOf(InstanceOfAssertFactories.iterable(Element.class))
+ .containsAll(getExpectedEdges());
+ }
+
+ @Test
+ void shouldReturnOnlyEntitiesWhenViewContainsNoEdges() throws Exception {
+ // Given/When
+ final Iterable extends Element> results = (Iterable extends Element>) federatedGraph.execute(new FederatedOperation.Builder()
+ .op(new GetElementsWithinSet.Builder()
+ .view(ENTITY_VIEW)
+ .input(SEEDS)
+ .build())
+ .graphIdsCSV(GRAPH_IDS)
+ .build(), USER);
+
+ // Then
+ assertThat(results).hasSize(600);
+ assertThat(results).extracting(r -> r.getGroup()).contains(GROUP_BASIC_ENTITY);
+ }
+
+ @Test
+ void shouldGetAllEdgesWithSmallerBatchSizeInAFederatedOperation() throws Exception {
+ // Given
+ // Set batch scanner entries to 50 - so some edges will have its src in the final batch but its
+ // dest in the first - should all still be retrieved
+ ACCUMULO_PROPERTIES.setMaxEntriesForBatchScanner("50");
+
+ // When
+ final Iterable extends Element> results = (Iterable extends Element>) federatedGraph.execute(new FederatedOperation.Builder()
+ .op(new GetElementsWithinSet.Builder()
+ .view(EDGE_VIEW)
+ .input(SEEDS)
+ .build())
+ .graphIdsCSV(GRAPH_IDS)
+ .build(), USER);
+
+ // Then
+ assertThat(results)
+ .asInstanceOf(InstanceOfAssertFactories.iterable(Element.class))
+ .containsAll(getExpectedEdges());
+ }
+
+ @Test
+ void shouldGetAllEdgesWithSmallerBatchSizeStandardOperation() throws Exception {
+ // Given
+ // Set batch scanner entries to 50 - so some edges will have its src in the final batch but its
+ // dest in the first - should all still be retrieved
+ ACCUMULO_PROPERTIES.setMaxEntriesForBatchScanner("50");
+
+ // When
+ final GetElementsWithinSet op = new GetElementsWithinSet.Builder()
+ .view(EDGE_VIEW)
+ .input(SEEDS)
+ .build();
+ final Iterable extends Element> results = federatedGraph.execute(op, USER);
+
+ // Then
+ assertThat(results)
+ .asInstanceOf(InstanceOfAssertFactories.iterable(Element.class))
+ .containsAll(getExpectedEdges());
+ }
+
+ private void addEntities(final String source, final String graphId) throws OperationException {
+ federatedGraph.execute(new FederatedOperation.Builder()
+ .op(new AddElements.Builder()
+ .input(new Entity.Builder()
+ .group(GROUP_BASIC_ENTITY)
+ .vertex(source)
+ .build())
+ .build())
+ .graphIdsCSV(graphId)
+ .build(), USER);
+ }
+
+ private void addEdges(final String source, final String dest, final String graphId) throws OperationException {
+ federatedGraph.execute(new FederatedOperation.Builder()
+ .op(new AddElements.Builder()
+ .input(new Edge.Builder()
+ .group(GROUP_BASIC_EDGE)
+ .source(source)
+ .dest(dest)
+ .directed(true)
+ .build())
+ .build())
+ .graphIdsCSV(graphId)
+ .build(), USER);
+ }
+
+ private Set getExpectedEdges() {
+ final Set expectedEdges = new HashSet<>();
+ expectedEdges.add(new Edge.Builder()
+ .group(GROUP_BASIC_EDGE)
+ .source("A244")
+ .dest("A87")
+ .directed(true)
+ .build());
+ expectedEdges.add(new Edge.Builder()
+ .group(GROUP_BASIC_EDGE)
+ .source("A168")
+ .dest("A110")
+ .directed(true)
+ .build());
+ expectedEdges.add(new Edge.Builder()
+ .group(GROUP_BASIC_EDGE)
+ .source("A56")
+ .dest("A299")
+ .directed(true)
+ .build());
+ expectedEdges.add(new Edge.Builder()
+ .group(GROUP_BASIC_EDGE)
+ .source("A297")
+ .dest("A193")
+ .directed(true)
+ .build());
+ expectedEdges.add(new Edge.Builder()
+ .group(GROUP_BASIC_EDGE)
+ .source("A15")
+ .dest("A285")
+ .directed(true)
+ .build());
+ expectedEdges.add(new Edge.Builder()
+ .group(GROUP_BASIC_EDGE)
+ .source("A1")
+ .dest("A52")
+ .directed(true)
+ .build());
+ return expectedEdges;
+ }
+
+ private Schema getSchema() {
+ return new Schema.Builder()
+ .entity(GROUP_BASIC_ENTITY, new SchemaEntityDefinition.Builder()
+ .vertex(STRING)
+ .build())
+ .edge(GROUP_BASIC_EDGE, new SchemaEdgeDefinition.Builder()
+ .source(STRING)
+ .destination(STRING)
+ .directed(DIRECTED_EITHER)
+ .build())
+ .type(STRING, STRING_TYPE)
+ .type(DIRECTED_EITHER, BOOLEAN_TYPE)
+ .build();
+ }
+}
From f4d373895b8344f4ace6d6f559c610f903787320 Mon Sep 17 00:00:00 2001
From: tb06904 <141412860+tb06904@users.noreply.github.com>
Date: Fri, 26 Jul 2024 09:42:47 +0000
Subject: [PATCH 11/16] Gh-3253 Fix custom types in gremlin (#3255)
* add conversions so gafferpop edges and vertexes are graphson compliant
* update testing
* update testing
* javadoc
* make sure cypher types can be used correctly
* typo
---
library/tinkerpop/pom.xml | 2 -
.../gchq/gaffer/tinkerpop/GafferPopGraph.java | 49 +++---
.../generator/GafferEdgeGenerator.java | 27 ++--
.../generator/GafferEntityGenerator.java | 10 +-
.../generator/GafferPopEdgeGenerator.java | 22 +--
.../generator/GafferPopVertexGenerator.java | 20 ++-
.../step/util/GafferPopHasContainer.java | 5 +-
.../util/GafferCustomTypeFactory.java | 149 ++++++++++++++++++
.../util/GafferPredicateFactory.java | 7 +-
.../util/TypeSubTypeValueFactory.java | 66 --------
.../gaffer/tinkerpop/GafferPopGraphTest.java | 3 +-
.../GafferPopElementGeneratorTest.java | 17 +-
.../traversal/step/GafferPopGraphStepIT.java | 15 +-
.../traversal/step/GafferPopHasStepIT.java | 12 +-
.../util/GafferCustomTypeFactoryTest.java | 108 +++++++++++++
.../util/TypeSubTypeValueFactoryTest.java | 66 --------
.../util/GafferPopTstvTestUtils.java | 15 +-
pom.xml | 2 +
rest-api/spring-rest/pom.xml | 5 +
.../rest/controller/GremlinController.java | 12 +-
.../rest/handler/GremlinWebSocketHandler.java | 6 +
.../controller/GremlinControllerTest.java | 35 ++++
.../handler/GremlinWebSocketIT.java | 18 +++
23 files changed, 449 insertions(+), 222 deletions(-)
create mode 100644 library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactory.java
delete mode 100644 library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/TypeSubTypeValueFactory.java
create mode 100644 library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactoryTest.java
delete mode 100644 library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/TypeSubTypeValueFactoryTest.java
diff --git a/library/tinkerpop/pom.xml b/library/tinkerpop/pom.xml
index 7612fc4c477..645ec6d4963 100644
--- a/library/tinkerpop/pom.xml
+++ b/library/tinkerpop/pom.xml
@@ -28,8 +28,6 @@
4.2.3
7.15.0
-
- 1.0.0
!GafferPopGraphStructureStandardTest,!GafferPopFeatureTest,!GafferPopGraphProcessStandardTest
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraph.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraph.java
index 42f55e88f25..89d4af3397d 100755
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraph.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraph.java
@@ -57,7 +57,7 @@
import uk.gov.gchq.gaffer.tinkerpop.generator.GafferPopElementGenerator;
import uk.gov.gchq.gaffer.tinkerpop.process.traversal.strategy.optimisation.GafferPopGraphStepStrategy;
import uk.gov.gchq.gaffer.tinkerpop.process.traversal.strategy.optimisation.GafferPopHasStepStrategy;
-import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.TypeSubTypeValueFactory;
+import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.GafferCustomTypeFactory;
import uk.gov.gchq.gaffer.tinkerpop.service.GafferPopNamedOperationServiceFactory;
import uk.gov.gchq.gaffer.user.User;
import uk.gov.gchq.koryphe.iterable.MappedIterable;
@@ -728,6 +728,32 @@ public T execute(final OperationChain opChain) {
}
}
+ /**
+ * Sets the {@link GafferPopGraphVariables} to default values for this
+ * graph
+ *
+ * @param variables The variables
+ */
+ public void setDefaultVariables(final GafferPopGraphVariables variables) {
+ LOGGER.debug("Resetting graph variables to defaults");
+ variables.set(GafferPopGraphVariables.OP_OPTIONS, Collections.unmodifiableMap(opOptions));
+ variables.set(GafferPopGraphVariables.USER, defaultUser);
+ variables.set(GafferPopGraphVariables.GET_ALL_ELEMENTS_LIMIT,
+ configuration().getInteger(GET_ALL_ELEMENTS_LIMIT, DEFAULT_GET_ALL_ELEMENTS_LIMIT));
+ variables.set(GafferPopGraphVariables.HAS_STEP_FILTER_STAGE,
+ configuration().getString(HAS_STEP_FILTER_STAGE, DEFAULT_HAS_STEP_FILTER_STAGE.toString()));
+ variables.set(GafferPopGraphVariables.LAST_OPERATION_CHAIN, new OperationChain());
+ }
+
+ /**
+ * Get the underlying Gaffer graph this GafferPop graph is connected to.
+ *
+ * @return The Gaffer Graph.
+ */
+ public Graph getGafferGraph() {
+ return graph;
+ }
+
private Iterator verticesWithSeedsAndView(final List seeds, final View view) {
final boolean getAll = null == seeds || seeds.isEmpty();
final LinkedList idVertices = new LinkedList<>();
@@ -933,9 +959,9 @@ private List getElementSeeds(final Iterable ids) {
// Check if contains label in edge ID
if (edgeIDMatcher.matches()) {
seeds.add(new EdgeSeed(edgeIDMatcher.group("src"), edgeIDMatcher.group("dest")));
+ // If not then check if a custom type e.g. TSTV
} else {
- // If not then check if a TSTV ID
- seeds.add(new EntitySeed(TypeSubTypeValueFactory.parseAsTstvIfValid(id)));
+ seeds.add(new EntitySeed(GafferCustomTypeFactory.parseAsCustomTypeIfValid(id)));
}
// Assume entity ID as a fallback
} else {
@@ -988,23 +1014,6 @@ private IncludeIncomingOutgoingType getInOutType(final Direction direction) {
return inOutType;
}
- /**
- * Sets the {@link GafferPopGraphVariables} to default values for this
- * graph
- *
- * @param variables The variables
- */
- public void setDefaultVariables(final GafferPopGraphVariables variables) {
- LOGGER.info("Resetting graph variables to defaults");
- variables.set(GafferPopGraphVariables.OP_OPTIONS, Collections.unmodifiableMap(opOptions));
- variables.set(GafferPopGraphVariables.USER, defaultUser);
- variables.set(GafferPopGraphVariables.GET_ALL_ELEMENTS_LIMIT,
- configuration().getInteger(GET_ALL_ELEMENTS_LIMIT, DEFAULT_GET_ALL_ELEMENTS_LIMIT));
- variables.set(GafferPopGraphVariables.HAS_STEP_FILTER_STAGE,
- configuration().getString(HAS_STEP_FILTER_STAGE, DEFAULT_HAS_STEP_FILTER_STAGE.toString()));
- variables.set(GafferPopGraphVariables.LAST_OPERATION_CHAIN, new OperationChain());
- }
-
/**
* Gets the next ID to assign to a supplied vertex based on the currently configured ID manager.
*
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferEdgeGenerator.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferEdgeGenerator.java
index 4ed6f1bfece..640ffb54f9a 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferEdgeGenerator.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferEdgeGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2023 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,26 +16,27 @@
package uk.gov.gchq.gaffer.tinkerpop.generator;
-import org.apache.tinkerpop.gremlin.structure.Property;
-
import uk.gov.gchq.gaffer.data.element.Edge;
import uk.gov.gchq.gaffer.data.generator.OneToOneElementGenerator;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopEdge;
-
-import java.util.Iterator;
+import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.GafferCustomTypeFactory;
public class GafferEdgeGenerator implements OneToOneElementGenerator {
@Override
public Edge _apply(final GafferPopEdge gafferPopEdge) {
- final Edge edge = new Edge(gafferPopEdge.label(), gafferPopEdge.outVertex().id(),
- gafferPopEdge.inVertex().id(), true);
- final Iterator> propItr = gafferPopEdge.properties();
- while (propItr.hasNext()) {
- final Property prop = propItr.next();
- if (null != prop.key()) {
- edge.putProperty(prop.key(), prop.value());
+ // Add edge
+ final Edge edge = new Edge(
+ gafferPopEdge.label(),
+ GafferCustomTypeFactory.parseAsCustomTypeIfValid(gafferPopEdge.outVertex().id()),
+ GafferCustomTypeFactory.parseAsCustomTypeIfValid(gafferPopEdge.inVertex().id()),
+ true);
+
+ // Add properties
+ gafferPopEdge.properties().forEachRemaining(prop -> {
+ if (prop.key() != null) {
+ edge.putProperty(prop.key(), GafferCustomTypeFactory.parseAsCustomTypeIfValid(prop.value()));
}
- }
+ });
return edge;
}
}
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferEntityGenerator.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferEntityGenerator.java
index 550bd60f9d6..b574770744f 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferEntityGenerator.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferEntityGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2023 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +19,7 @@
import uk.gov.gchq.gaffer.data.element.Entity;
import uk.gov.gchq.gaffer.data.generator.OneToOneElementGenerator;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopVertex;
+import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.GafferCustomTypeFactory;
public class GafferEntityGenerator implements OneToOneElementGenerator {
@Override
@@ -27,12 +28,13 @@ public Entity _apply(final GafferPopVertex vertex) {
throw new IllegalArgumentException("Unable to convert a null GafferPopVertex Object");
}
- final Entity entity = new Entity(vertex.label(), vertex.id());
+ final Entity entity = new Entity(vertex.label(), GafferCustomTypeFactory.parseAsCustomTypeIfValid(vertex.id()));
// Tinkerpop allows nested properties under a key for Gaffer we need to flatten these so only one property per key
vertex.properties().forEachRemaining(vertProp -> {
- entity.putProperty(vertProp.key(), vertProp.value());
- vertProp.properties().forEachRemaining(prop -> entity.putProperty(prop.key(), prop.value()));
+ entity.putProperty(vertProp.key(), GafferCustomTypeFactory.parseAsCustomTypeIfValid(vertProp.value()));
+ vertProp.properties().forEachRemaining(
+ prop -> entity.putProperty(prop.key(), GafferCustomTypeFactory.parseAsCustomTypeIfValid(prop.value())));
});
return entity;
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopEdgeGenerator.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopEdgeGenerator.java
index efc798ae581..4ca1840fde0 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopEdgeGenerator.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopEdgeGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2023 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,8 +24,7 @@
import uk.gov.gchq.gaffer.data.generator.OneToOneObjectGenerator;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopEdge;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph;
-
-import java.util.Map.Entry;
+import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.GafferCustomTypeFactory;
public class GafferPopEdgeGenerator implements OneToOneObjectGenerator {
private final GafferPopGraph graph;
@@ -48,14 +47,19 @@ public GafferPopEdge _apply(final Element element) {
}
final Edge edge = ((Edge) element);
- final GafferPopEdge gafferPopEdge = new GafferPopEdge(edge.getGroup(),
- edge.getSource(), edge.getDestination(), graph);
+ final GafferPopEdge gafferPopEdge = new GafferPopEdge(
+ edge.getGroup(),
+ GafferCustomTypeFactory.parseForGraphSONv3(edge.getSource()),
+ GafferCustomTypeFactory.parseForGraphSONv3(edge.getDestination()),
+ graph);
- for (final Entry entry : edge.getProperties().entrySet()) {
- if (null != entry.getValue()) {
- gafferPopEdge.propertyWithoutUpdate(entry.getKey(), entry.getValue());
+ // Add the properties
+ edge.getProperties().forEach((k, v) -> {
+ if (v != null) {
+ gafferPopEdge.propertyWithoutUpdate(k, GafferCustomTypeFactory.parseForGraphSONv3(v));
}
- }
+ });
+
if (gafferPopReadOnly) {
gafferPopEdge.setReadOnly();
}
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopVertexGenerator.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopVertexGenerator.java
index 18ad7aed0d2..33b5e42c6d3 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopVertexGenerator.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopVertexGenerator.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2023 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,8 +25,8 @@
import uk.gov.gchq.gaffer.data.generator.OneToOneObjectGenerator;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopVertex;
+import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.GafferCustomTypeFactory;
-import java.util.Map.Entry;
public class GafferPopVertexGenerator implements OneToOneObjectGenerator {
private final GafferPopGraph graph;
@@ -49,12 +49,18 @@ public GafferPopVertex _apply(final Element element) {
}
final Entity entity = ((Entity) element);
- final GafferPopVertex vertex = new GafferPopVertex(entity.getGroup(), entity.getVertex(), graph);
- for (final Entry entry : entity.getProperties().entrySet()) {
- if (null != entry.getValue()) {
- vertex.propertyWithoutUpdate(Cardinality.list, entry.getKey(), entry.getValue());
+ final GafferPopVertex vertex = new GafferPopVertex(
+ entity.getGroup(),
+ GafferCustomTypeFactory.parseForGraphSONv3(entity.getVertex()),
+ graph);
+
+ // Add the properties
+ entity.getProperties().forEach((k, v) -> {
+ if (v != null) {
+ vertex.propertyWithoutUpdate(Cardinality.list, k, GafferCustomTypeFactory.parseForGraphSONv3(v));
}
- }
+ });
+
if (gafferPopReadOnly) {
vertex.setReadOnly();
}
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/util/GafferPopHasContainer.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/util/GafferPopHasContainer.java
index 2d2c088b95b..1b98260cc5e 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/util/GafferPopHasContainer.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/util/GafferPopHasContainer.java
@@ -20,6 +20,7 @@
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Property;
+import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.GafferCustomTypeFactory;
import uk.gov.gchq.gaffer.tinkerpop.process.traversal.util.GafferPredicateFactory;
import java.util.function.Predicate;
@@ -45,7 +46,7 @@ public Predicate getGafferPredicate() {
@Override
protected boolean testId(final Element element) {
- return gafferPredicate.test(element.id());
+ return gafferPredicate.test(GafferCustomTypeFactory.parseAsCustomTypeIfValid(element.id()));
}
@Override
@@ -60,7 +61,7 @@ protected boolean testLabel(final Element element) {
@Override
protected boolean testValue(final Property property) {
- return gafferPredicate.test(property.value());
+ return gafferPredicate.test(GafferCustomTypeFactory.parseAsCustomTypeIfValid(property.value()));
}
@Override
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactory.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactory.java
new file mode 100644
index 00000000000..e13817615ee
--- /dev/null
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactory.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.tinkerpop.process.traversal.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import uk.gov.gchq.gaffer.types.TypeSubTypeValue;
+
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Class that helps the conversion between Gaffer custom types and
+ * standard types that Tinkerpop supports.
+ *
+ * Tinkerpop only supports a limited number of types so any custom Gaffer
+ * ones can only be passed as String representations via Gremlin so need
+ * conversion before using in Gaffer Operation chains.
+ */
+public final class GafferCustomTypeFactory {
+ /*
+ * List of supported standard types that GraphSON v3 can serialise
+ */
+ public static final List> GRAPHSONV3_TYPES = Collections.unmodifiableList(
+ Arrays.asList(
+ Boolean.class,
+ Byte[].class,
+ Date.class,
+ Double.class,
+ Float.class,
+ Integer.class,
+ List.class,
+ Long.class,
+ Map.class,
+ Set.class,
+ String.class,
+ Timestamp.class,
+ UUID.class));
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(GafferCustomTypeFactory.class);
+ private static final Pattern TSTV_REGEX = Pattern.compile(".*\\[type=(?.*),\\s*subType=(?.*),\\s*value=(?.*)\\]$");
+
+ private GafferCustomTypeFactory() {
+ // Utility class
+ }
+
+ /**
+ * Returns a the relevant Object e.g. {@link TypeSubTypeValue} from the
+ * supplied value or ID, usually by parsing a specifically formatted string.
+ * As a fallback will give back the original value if no relevant type
+ * was found.
+ *
+ *
+ * "TypeSubTypeValue[type=alpha,subType=beta,value=gamma]" // returns TypeSubTypeValue object
+ * "[type=alpha,subType=beta,value=gamma]" // returns TypeSubTypeValue object
+ * "normalvalue" // returns itself
+ *
+ *
+ * @param value The value.
+ * @return The value as its relevant type.
+ */
+ public static Object parseAsCustomTypeIfValid(final Object value) {
+ if (value instanceof String) {
+ Matcher tstvMatcher = TSTV_REGEX.matcher((String) value);
+ if (tstvMatcher.matches()) {
+ // Split into a TSTV via matcher
+ LOGGER.debug("Parsing string as a TSTV: {}", value);
+ return new TypeSubTypeValue(
+ tstvMatcher.group("type"),
+ tstvMatcher.group("stype"),
+ tstvMatcher.group("val"));
+ }
+ }
+
+ // If value is collection e.g. list or set then check the values inside it
+ if (value instanceof Collection>) {
+ List converted = new ArrayList<>();
+ ((Collection>) value).forEach(v -> converted.add(parseAsCustomTypeIfValid(v)));
+ // Return a set if needed
+ if (value instanceof Set>) {
+ return new HashSet<>(converted);
+ }
+ return converted;
+ }
+
+ return value;
+ }
+
+ /**
+ * Parses the given value to make sure it can be used with Tinkerpops
+ * GraphSONv3 types. Will convert the value to String representation
+ * if type is not compatible or if value is a collection it will convert
+ * all the values inside it.
+ *
+ * @param value The value to parse.
+ * @return The value in compatible format.
+ */
+ public static Object parseForGraphSONv3(final Object value) {
+ if (value == null) {
+ return value;
+ }
+
+ // If value is collection e.g. list or set then check the values inside it
+ if (value instanceof Collection>) {
+ List converted = new ArrayList<>();
+ ((Collection>) value).forEach(v -> converted.add(parseForGraphSONv3(v)));
+ // Return a set if needed
+ if (value instanceof Set>) {
+ return new HashSet<>(converted);
+ }
+ return converted;
+ }
+
+ // Check if the value can be used with GraphSON v3
+ if (!GRAPHSONV3_TYPES.contains(value.getClass())) {
+ LOGGER.debug("Converting value to string {}", value);
+ return value.toString();
+ }
+
+ return value;
+ }
+
+}
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferPredicateFactory.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferPredicateFactory.java
index a7c73b08e69..af22d5f5359 100644
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferPredicateFactory.java
+++ b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferPredicateFactory.java
@@ -42,7 +42,6 @@
import java.util.stream.Collectors;
public final class GafferPredicateFactory {
-
private static final String COULD_NOT_TRANSLATE_ERROR = "Could not translate Gremlin predicate: ";
private GafferPredicateFactory() {
@@ -60,7 +59,7 @@ private GafferPredicateFactory() {
* @param p the Gremlin predicate to convert
* @return the equivalent {@link KoryphePredicate}
*
- * @see TypeSubTypeValueFactory#parseAsTstvIfValid(Object)
+ * @see GafferCustomTypeFactory#parseAsCustomTypeIfValid(Object)
*/
public static Predicate> convertGremlinPredicate(final P> p) {
if (p == null) {
@@ -76,12 +75,12 @@ public static Predicate> convertGremlinPredicate(final P> p) {
BiPredicate, ?> biPredicate = p.getBiPredicate();
if (biPredicate instanceof Compare) {
- Object value = TypeSubTypeValueFactory.parseAsTstvIfValid(p.getValue());
+ Object value = GafferCustomTypeFactory.parseAsCustomTypeIfValid(p.getValue());
return getComparePredicate((Compare) biPredicate, value);
} else if (biPredicate instanceof Contains) {
Collection> value = (Collection>) p.getValue();
Collection mappedValues = value.stream()
- .map(v -> TypeSubTypeValueFactory.parseAsTstvIfValid(v))
+ .map(GafferCustomTypeFactory::parseAsCustomTypeIfValid)
.collect(Collectors.toList());
return getContainsPredicate((Contains) biPredicate, mappedValues);
} else if (biPredicate instanceof Text) {
diff --git a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/TypeSubTypeValueFactory.java b/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/TypeSubTypeValueFactory.java
deleted file mode 100644
index 4a43c13e32e..00000000000
--- a/library/tinkerpop/src/main/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/TypeSubTypeValueFactory.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2024 Crown Copyright
- *
- * Licensed 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 uk.gov.gchq.gaffer.tinkerpop.process.traversal.util;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import uk.gov.gchq.gaffer.types.TypeSubTypeValue;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-public final class TypeSubTypeValueFactory {
-
- private static final Logger LOGGER = LoggerFactory.getLogger(TypeSubTypeValueFactory.class);
- private static final Pattern TSTV_REGEX = Pattern.compile("^t:(?.*)\\|st:(?.*)\\|v:(?.*)$");
-
- private TypeSubTypeValueFactory() {
- // Utility class
- }
-
- /**
- * Returns a the relevant Object e.g. {@link TypeSubTypeValue} from the
- * supplied value or ID, usually by parsing a specifically formatted string.
- * As a fallback will give back the original value if no relevant type
- * was found.
- *
- *
- * "t:type|st:subtype|v:value" // returns TypeSubTypeValue object
- * "notATstv" // returns itself
- *
- *
- * @param value The value.
- * @return The value as its relevant type.
- */
- public static Object parseAsTstvIfValid(final Object value) {
- if (value instanceof String) {
- Matcher tstvMatcher = TSTV_REGEX.matcher((String) value);
- if (tstvMatcher.matches()) {
- // Split into a TSTV via matcher
- LOGGER.debug("Parsing string as a TSTV: {}", value);
- return new TypeSubTypeValue(
- tstvMatcher.group("type"),
- tstvMatcher.group("stype"),
- tstvMatcher.group("val"));
- }
- }
-
- return value;
- }
-
-}
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphTest.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphTest.java
index 25ef6aafc7e..29db09fc4b3 100644
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphTest.java
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/GafferPopGraphTest.java
@@ -50,7 +50,6 @@
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTestUtil.TEST_CONFIGURATION_2;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTestUtil.TEST_CONFIGURATION_3;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTestUtil.getTestUser;
-import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_ID;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_ID_STRING;
import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.CREATED;
import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.JOSH;
@@ -322,7 +321,7 @@ void shouldGetVerticesWithTSTV() {
assertThat(result).toIterable()
.hasSize(1)
.extracting(r -> r.id())
- .containsExactly(TSTV_ID);
+ .containsExactly(TSTV_ID_STRING);
}
@Test
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopElementGeneratorTest.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopElementGeneratorTest.java
index 731060cdf3a..64ee051c2b1 100644
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopElementGeneratorTest.java
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/generator/GafferPopElementGeneratorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Crown Copyright
+ * Copyright 2023-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,10 +30,10 @@
import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph;
import uk.gov.gchq.gaffer.tinkerpop.GafferPopVertex;
-public class GafferPopElementGeneratorTest {
+class GafferPopElementGeneratorTest {
@Test
- public void shouldReturnAGafferPopVertex() {
+ void shouldReturnAGafferPopVertex() {
// Given
final GafferPopGraph graph = mock(GafferPopGraph.class);
final Element element = new Entity.Builder().group(TestGroups.ENTITY).build();
@@ -49,10 +49,15 @@ public void shouldReturnAGafferPopVertex() {
}
@Test
- public void shouldReturnAGafferPopEdge() {
+ void shouldReturnAGafferPopEdge() {
// Given
final GafferPopGraph graph = mock(GafferPopGraph.class);
- final Element element = new Edge.Builder().group(TestGroups.EDGE).build();
+ final String source = "source";
+ final String dest = "dest";
+ final Element element = new Edge.Builder()
+ .group(TestGroups.EDGE)
+ .source(source)
+ .dest(dest).build();
final GafferPopElementGenerator generator = new GafferPopElementGenerator(graph, true);
@@ -66,7 +71,7 @@ public void shouldReturnAGafferPopEdge() {
}
@Test
- public void shouldThrowExceptionForInvalidElement() {
+ void shouldThrowExceptionForInvalidElement() {
// Given
final GafferPopGraph graph = mock(GafferPopGraph.class);
final Element element = mock(Element.class);
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStepIT.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStepIT.java
index 3a74cd1bd2e..c50c213c9f9 100644
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStepIT.java
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopGraphStepIT.java
@@ -34,12 +34,9 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatRuntimeException;
-import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.OTHER_TSTV_PROPERTY;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.OTHER_TSTV_PROPERTY_STRING;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV;
-import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_ID;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_ID_STRING;
-import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_PROPERTY;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_PROPERTY_STRING;
import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.AGE;
import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.JOSH;
@@ -93,7 +90,7 @@ void shouldGetVertexByTSTVSeed() {
assertThat(result)
.extracting(r -> r.id())
- .containsExactlyInAnyOrder(TSTV_ID);
+ .containsExactlyInAnyOrder(TSTV_ID_STRING);
}
@@ -141,7 +138,7 @@ void shouldFilterVerticesByLabelAndTstvProperty() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(TSTV_PROPERTY_STRING);
}
@Test
@@ -161,7 +158,7 @@ void shouldFilterVerticesByLabelAndTstvPropertyLessThan() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(TSTV_PROPERTY_STRING);
}
@Test
@@ -181,7 +178,7 @@ void shouldFilterVerticesByLabelAndTstvPropertyMoreThan() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY_STRING);
}
@Test
@@ -201,7 +198,7 @@ void shouldFilterVerticesByLabelAndTstvPropertyWithin() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY, TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY_STRING, TSTV_PROPERTY_STRING);
}
@Test
@@ -238,7 +235,7 @@ void shouldFilterVerticesByTstvPropertyWithin() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY, TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY_STRING, TSTV_PROPERTY_STRING);
}
@Test
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopHasStepIT.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopHasStepIT.java
index 0b3f0e4e0be..e817be0f64e 100644
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopHasStepIT.java
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/step/GafferPopHasStepIT.java
@@ -31,10 +31,8 @@
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
-import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.OTHER_TSTV_PROPERTY;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.OTHER_TSTV_PROPERTY_STRING;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV;
-import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_PROPERTY;
import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_PROPERTY_STRING;
import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.AGE;
import static uk.gov.gchq.gaffer.tinkerpop.util.modern.GafferPopModernTestUtils.JOSH;
@@ -80,7 +78,7 @@ void shouldFilterVerticesByLabelAndTstvProperty() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(TSTV_PROPERTY_STRING);
}
@Test
@@ -100,7 +98,7 @@ void shouldFilterVerticesByLabelAndTstvPropertyLessThan() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(TSTV_PROPERTY_STRING);
}
@Test
@@ -120,7 +118,7 @@ void shouldFilterVerticesByLabelAndTstvPropertyMoreThan() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY_STRING);
}
@Test
@@ -141,7 +139,7 @@ void shouldFilterVerticesByLabelAndTstvPropertyWithin() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY, TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY_STRING, TSTV_PROPERTY_STRING);
}
@Test
@@ -179,7 +177,7 @@ void shouldFilterVerticesByTstvPropertyWithin() {
assertThat(result)
.extracting(r -> r.value(NAME))
- .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY, TSTV_PROPERTY);
+ .containsExactlyInAnyOrder(OTHER_TSTV_PROPERTY_STRING, TSTV_PROPERTY_STRING);
}
@Test
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactoryTest.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactoryTest.java
new file mode 100644
index 00000000000..1d7ffb808c8
--- /dev/null
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/GafferCustomTypeFactoryTest.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright 2024 Crown Copyright
+ *
+ * Licensed 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 uk.gov.gchq.gaffer.tinkerpop.process.traversal.util;
+
+import org.assertj.core.api.InstanceOfAssertFactories;
+import org.junit.jupiter.api.Test;
+
+import uk.gov.gchq.gaffer.types.TypeSubTypeValue;
+
+import java.util.Collection;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.COMPLEX_TSTV_ID;
+import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.COMPLEX_TSTV_ID_STRING;
+import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_ID;
+import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_ID_STRING;
+import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_PROPERTY_SET;
+import static uk.gov.gchq.gaffer.tinkerpop.util.GafferPopTstvTestUtils.TSTV_PROPERTY_SET_STRING;
+
+class GafferCustomTypeFactoryTest {
+
+ @Test
+ void shouldConvertTypeSubTypeValues() {
+ // When
+ Object stringParsed = GafferCustomTypeFactory.parseAsCustomTypeIfValid(TSTV_ID_STRING);
+ Object tstvParsed = GafferCustomTypeFactory.parseForGraphSONv3(TSTV_ID);
+
+ // Then
+ assertThat(stringParsed)
+ .isInstanceOf(TypeSubTypeValue.class)
+ .extracting(r -> (TypeSubTypeValue) r)
+ .isEqualTo(TSTV_ID);
+
+ assertThat(tstvParsed)
+ .isInstanceOf(String.class)
+ .extracting(r -> (String) r)
+ .isEqualTo(TSTV_ID_STRING);
+ }
+
+ @Test
+ void shouldConvertComplexTypeSubTypeValues() {
+ // When
+ Object stringParsed = GafferCustomTypeFactory.parseAsCustomTypeIfValid(COMPLEX_TSTV_ID_STRING);
+ Object tstvParsed = GafferCustomTypeFactory.parseForGraphSONv3(COMPLEX_TSTV_ID);
+
+ // Then
+ assertThat(stringParsed)
+ .isInstanceOf(TypeSubTypeValue.class)
+ .extracting(r -> (TypeSubTypeValue) r)
+ .isEqualTo(COMPLEX_TSTV_ID);
+
+ assertThat(tstvParsed)
+ .isInstanceOf(String.class)
+ .extracting(r -> (String) r)
+ .isEqualTo(COMPLEX_TSTV_ID_STRING);
+ }
+
+ @Test
+ void shouldParseSetOfTypeSubTypeValues() {
+ Object stringParsed = GafferCustomTypeFactory.parseAsCustomTypeIfValid(TSTV_PROPERTY_SET_STRING);
+ Object tstvParsed = GafferCustomTypeFactory.parseForGraphSONv3(TSTV_PROPERTY_SET);
+
+ // Then
+ assertThat(stringParsed)
+ .isInstanceOf(Collection.class)
+ .asInstanceOf(InstanceOfAssertFactories.COLLECTION)
+ .containsExactlyInAnyOrderElementsOf(TSTV_PROPERTY_SET);
+
+ assertThat(tstvParsed)
+ .isInstanceOf(Collection.class)
+ .asInstanceOf(InstanceOfAssertFactories.COLLECTION)
+ .containsExactlyInAnyOrderElementsOf(TSTV_PROPERTY_SET_STRING);
+ }
+
+ @Test
+ void shouldNotParseStringAsTstv() {
+ String notATstv = "not|a|tstv";
+ Object result = GafferCustomTypeFactory.parseAsCustomTypeIfValid(notATstv);
+ assertThat(result)
+ .isInstanceOf(String.class)
+ .extracting(r -> (String) r)
+ .isEqualTo("not|a|tstv");
+ }
+
+ @Test
+ void shouldNotParseObjectAsTstv() {
+ Object notATstv = 1;
+ Object result = GafferCustomTypeFactory.parseAsCustomTypeIfValid(notATstv);
+ assertThat(result)
+ .isInstanceOf(Integer.class)
+ .extracting(r -> (Integer) r)
+ .isEqualTo(1);
+ }
+}
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/TypeSubTypeValueFactoryTest.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/TypeSubTypeValueFactoryTest.java
deleted file mode 100644
index b5b2267ec7a..00000000000
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/process/traversal/util/TypeSubTypeValueFactoryTest.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2024 Crown Copyright
- *
- * Licensed 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 uk.gov.gchq.gaffer.tinkerpop.process.traversal.util;
-
-import org.junit.jupiter.api.Test;
-
-import uk.gov.gchq.gaffer.types.TypeSubTypeValue;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-class TypeSubTypeValueFactoryTest {
-
- @Test
- void shouldParseStringAsTstv() {
- String tstv = "t:one|st:two|v:three";
- Object result = TypeSubTypeValueFactory.parseAsTstvIfValid(tstv);
- assertThat(result)
- .isInstanceOf(TypeSubTypeValue.class)
- .extracting(r -> (TypeSubTypeValue) r)
- .isEqualTo(new TypeSubTypeValue("one", "two", "three"));
- }
-
- @Test
- void shouldParseComplexStringAsTstv() {
- String tstv = "t:one|one|st:two|two|v:three|three";
- Object result = TypeSubTypeValueFactory.parseAsTstvIfValid(tstv);
- assertThat(result)
- .isInstanceOf(TypeSubTypeValue.class)
- .extracting(r -> (TypeSubTypeValue) r)
- .isEqualTo(new TypeSubTypeValue("one|one", "two|two", "three|three"));
- }
-
- @Test
- void shouldNotParseStringAsTstv() {
- String notATstv = "not|a|tstv";
- Object result = TypeSubTypeValueFactory.parseAsTstvIfValid(notATstv);
- assertThat(result)
- .isInstanceOf(String.class)
- .extracting(r -> (String) r)
- .isEqualTo("not|a|tstv");
- }
-
- @Test
- void shouldNotParseObjectAsTstv() {
- Object notATstv = 1;
- Object result = TypeSubTypeValueFactory.parseAsTstvIfValid(notATstv);
- assertThat(result)
- .isInstanceOf(Integer.class)
- .extracting(r -> (Integer) r)
- .isEqualTo(1);
- }
-}
diff --git a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/util/GafferPopTstvTestUtils.java b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/util/GafferPopTstvTestUtils.java
index 824741329ed..c3c9e342eb6 100644
--- a/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/util/GafferPopTstvTestUtils.java
+++ b/library/tinkerpop/src/test/java/uk/gov/gchq/gaffer/tinkerpop/util/GafferPopTstvTestUtils.java
@@ -24,18 +24,27 @@
import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraph;
import uk.gov.gchq.gaffer.types.TypeSubTypeValue;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
public final class GafferPopTstvTestUtils {
public static final String TSTV = "tstv";
public static final String NAME = "name";
public static final String EDGE = "test";
public static final TypeSubTypeValue TSTV_ID = new TypeSubTypeValue("alpha", "beta", "gamma");
- public static final String TSTV_ID_STRING = "t:alpha|st:beta|v:gamma";
public static final TypeSubTypeValue OTHER_TSTV_ID = new TypeSubTypeValue("delta", "epsilon", "zeta");
+ public static final TypeSubTypeValue COMPLEX_TSTV_ID = new TypeSubTypeValue("de|lt-a", "eps|i|l|o=n", "zet|09!//a");
public static final TypeSubTypeValue TSTV_PROPERTY = new TypeSubTypeValue("eta", "theta", "iota");
- public static final String TSTV_PROPERTY_STRING = "t:eta|st:theta|v:iota";
public static final TypeSubTypeValue OTHER_TSTV_PROPERTY = new TypeSubTypeValue("kappa", "lambda", "mu");
- public static final String OTHER_TSTV_PROPERTY_STRING = "t:kappa|st:lambda|v:mu";
+ public static final String TSTV_ID_STRING = "TypeSubTypeValue[type=alpha,subType=beta,value=gamma]";
+ public static final String OTHER_TSTV_ID_STRING = "TypeSubTypeValue[type=delta,subType=epsilon,value=zeta]";
+ public static final String COMPLEX_TSTV_ID_STRING = "TypeSubTypeValue[type=de|lt-a,subType=eps|i|l|o=n,value=zet|09!//a]";
+ public static final String TSTV_PROPERTY_STRING = "TypeSubTypeValue[type=eta,subType=theta,value=iota]";
+ public static final String OTHER_TSTV_PROPERTY_STRING = "TypeSubTypeValue[type=kappa,subType=lambda,value=mu]";
+ public static final Set TSTV_PROPERTY_SET = new HashSet<>(Arrays.asList(TSTV_PROPERTY, OTHER_TSTV_PROPERTY));
+ public static final Set TSTV_PROPERTY_SET_STRING = new HashSet<>(Arrays.asList(TSTV_PROPERTY_STRING, OTHER_TSTV_PROPERTY_STRING));
public static final Configuration TSTV_CONFIGURATION = new BaseConfiguration() {
{
diff --git a/pom.xml b/pom.xml
index 25626464c92..dca3aecbdfd 100644
--- a/pom.xml
+++ b/pom.xml
@@ -99,6 +99,8 @@
5.15.0
1.36.0
3.7.1
+
+ 1.0.0
2.17
diff --git a/rest-api/spring-rest/pom.xml b/rest-api/spring-rest/pom.xml
index 521ab6fb6e3..085e10e198a 100644
--- a/rest-api/spring-rest/pom.xml
+++ b/rest-api/spring-rest/pom.xml
@@ -121,6 +121,11 @@
springdoc-openapi-ui
${springdoc.version}
+
+ org.opencypher.gremlin
+ cypher-gremlin-server-plugin
+ ${cypher-gremlin.version}
+
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/GremlinController.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/GremlinController.java
index af6d5e0c449..ba763b88d22 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/GremlinController.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/controller/GremlinController.java
@@ -23,6 +23,7 @@
import org.apache.tinkerpop.gremlin.structure.Graph;
import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph;
import org.json.JSONObject;
+import org.opencypher.gremlin.server.jsr223.CypherPlugin;
import org.opencypher.gremlin.translation.CypherAst;
import org.opencypher.gremlin.translation.translator.Translator;
import org.springframework.beans.factory.annotation.Autowired;
@@ -43,7 +44,9 @@
import uk.gov.gchq.gaffer.tinkerpop.GafferPopGraphVariables;
import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
import java.util.LinkedList;
+import java.util.Map;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
import static org.springframework.http.MediaType.TEXT_PLAIN_VALUE;
@@ -61,12 +64,15 @@ public class GremlinController {
private final ConcurrentBindings bindings = new ConcurrentBindings();
private final AbstractUserFactory userFactory;
private final Graph graph;
+ private final Map> plugins = new HashMap<>();
@Autowired
public GremlinController(final GraphTraversalSource g, final AbstractUserFactory userFactory) {
bindings.putIfAbsent("g", g);
graph = g.getGraph();
this.userFactory = userFactory;
+ // Add cypher plugin so cypher functions can be used in queries
+ plugins.put(CypherPlugin.class.getName(), new HashMap<>());
}
/**
@@ -163,13 +169,15 @@ private JSONObject runGremlinAndGetExplain(final String gremlinQuery, final Http
} else {
gafferPopGraph = (GafferPopGraph) graph;
}
+ gafferPopGraph.setDefaultVariables((GafferPopGraphVariables) gafferPopGraph.variables());
// Hooks for user auth
userFactory.setHttpHeaders(httpHeaders);
graph.variables().set(GafferPopGraphVariables.USER, userFactory.createUser());
JSONObject explain = new JSONObject();
- try (GremlinExecutor gremlinExecutor = GremlinExecutor.build().globalBindings(bindings).create()) {
- gafferPopGraph.setDefaultVariables((GafferPopGraphVariables) gafferPopGraph.variables());
+ try (GremlinExecutor gremlinExecutor = GremlinExecutor.build()
+ .addPlugins("gremlin-groovy", plugins)
+ .globalBindings(bindings).create()) {
// Execute the query note this will actually run the query which we need
// as Gremlin will skip steps if there is no input from the previous ones
gremlinExecutor.eval(gremlinQuery).join();
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/handler/GremlinWebSocketHandler.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/handler/GremlinWebSocketHandler.java
index 1564a5bfd4a..abd2885bf63 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/handler/GremlinWebSocketHandler.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/handler/GremlinWebSocketHandler.java
@@ -40,6 +40,7 @@
import org.apache.tinkerpop.gremlin.util.ser.MessageTextSerializer;
import org.apache.tinkerpop.gremlin.util.ser.SerTokens;
import org.json.JSONObject;
+import org.opencypher.gremlin.server.jsr223.CypherPlugin;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.socket.BinaryMessage;
@@ -57,6 +58,7 @@
import java.nio.charset.StandardCharsets;
import java.util.AbstractMap.SimpleEntry;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
@@ -88,6 +90,7 @@ public class GremlinWebSocketHandler extends BinaryWebSocketHandler {
private final ConcurrentBindings bindings = new ConcurrentBindings();
private final AbstractUserFactory userFactory;
private final Graph graph;
+ private final Map> plugins = new HashMap<>();
/**
* Constructor
@@ -99,6 +102,8 @@ public GremlinWebSocketHandler(final GraphTraversalSource g, final AbstractUserF
bindings.putIfAbsent("g", g);
graph = g.getGraph();
this.userFactory = userFactory;
+ // Add cypher plugin so cypher functions can be used in queries
+ plugins.put(CypherPlugin.class.getName(), new HashMap<>());
}
@Override
@@ -143,6 +148,7 @@ private ResponseMessage handleGremlinRequest(final WebSocketSession session, fin
try (Scope scope = span.makeCurrent();
GremlinExecutor gremlinExecutor = GremlinExecutor.build()
.globalBindings(bindings)
+ .addPlugins("gremlin-groovy", plugins)
.executorService(executorService)
.create()) {
// Set current headers for potential authorisation then set the user
diff --git a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/GremlinControllerTest.java b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/GremlinControllerTest.java
index d5cd4e79169..92c94f4a980 100644
--- a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/GremlinControllerTest.java
+++ b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/controller/GremlinControllerTest.java
@@ -32,6 +32,8 @@
import org.springframework.test.web.servlet.MvcResult;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
+import uk.gov.gchq.gaffer.operation.impl.Limit;
+import uk.gov.gchq.gaffer.operation.impl.get.GetAllElements;
import uk.gov.gchq.gaffer.operation.impl.get.GetElements;
import uk.gov.gchq.gaffer.rest.factory.spring.AbstractUserFactory;
import uk.gov.gchq.gaffer.rest.factory.spring.UnknownUserFactory;
@@ -125,6 +127,7 @@ void shouldRejectMalformedGremlinQuery() throws Exception {
void shouldReturnExplainOfValidCypherQuery() throws Exception {
// Given
String cypherString = "MATCH (p:person) WHERE ID(p) = '" + MARKO.getId() + "' RETURN p";
+
List expectedOperations = Arrays.asList(GetElements.class.getName());
// When
@@ -152,6 +155,38 @@ void shouldReturnExplainOfValidCypherQuery() throws Exception {
.containsExactlyElementsOf(expectedOperations);
}
+ @Test
+ void shouldReturnExplainOfCypherQueryWithExtensions() throws Exception {
+ // Given (uses the toInteger custom function)
+ String cypherString = "MATCH (p:person) WHERE p.age > toInteger(22) RETURN p";
+
+ List expectedOperations = Arrays.asList(GetAllElements.class.getName(), Limit.class.getName());
+
+ // When
+ MvcResult result = mockMvc
+ .perform(MockMvcRequestBuilders
+ .post(CYPHER_EXPLAIN_ENDPOINT)
+ .content(cypherString)
+ .contentType(TEXT_PLAIN_VALUE))
+ .andReturn();
+
+ // Then
+ // Ensure OK response
+ assertThat(result.getResponse().getStatus()).isEqualTo(200);
+
+ // Get and check response
+ JSONObject jsonResponse = new JSONObject(result.getResponse().getContentAsString());
+ assertThat(jsonResponse.has(GremlinController.EXPLAIN_OVERVIEW_KEY)).isTrue();
+ assertThat(jsonResponse.has(GremlinController.EXPLAIN_OP_CHAIN_KEY)).isTrue();
+ assertThat(jsonResponse.has(GremlinController.EXPLAIN_GREMLIN_KEY)).isTrue();
+
+ // Check the operations that ran are as expected
+ JSONArray operations = jsonResponse.getJSONObject("chain").getJSONArray("operations");
+ assertThat(operations)
+ .map(json -> ((JSONObject) json).getString("class"))
+ .containsExactlyElementsOf(expectedOperations);
+ }
+
@Test
void shouldRejectMalformedCypherQuery() throws Exception {
// Given
diff --git a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/handler/GremlinWebSocketIT.java b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/handler/GremlinWebSocketIT.java
index 2dba6d0a30c..c3f72fff2f6 100644
--- a/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/handler/GremlinWebSocketIT.java
+++ b/rest-api/spring-rest/src/test/java/uk/gov/gchq/gaffer/rest/integration/handler/GremlinWebSocketIT.java
@@ -145,4 +145,22 @@ void shouldAcceptQueryWithCypher() {
.containsExactly(MARKO.getName());
}
+ @Test
+ void shouldAcceptGremlinQueryUsingCustomCypherFunctions() {
+ // Given
+ String query = "g.V().hasLabel('person').values('age').map(cypherToString()).toList()";
+
+ // When
+ List results = client.submit(query).stream().collect(Collectors.toList());
+
+ // Then
+ assertThat(results)
+ .map(result -> result.getObject())
+ .containsExactlyInAnyOrder(
+ String.valueOf(MARKO.getAge()),
+ String.valueOf(VADAS.getAge()),
+ String.valueOf(PETER.getAge()),
+ String.valueOf(JOSH.getAge()));
+ }
+
}
From dc892f1d9afaf6fd1ac115bdbb1df045a1e091b1 Mon Sep 17 00:00:00 2001
From: cn337131 <141730190+cn337131@users.noreply.github.com>
Date: Mon, 5 Aug 2024 09:06:00 +0000
Subject: [PATCH 12/16] Gh-3259: Improve release process (#3260)
* update release pipelines
* simplify
* release notes fix
* remove comment
* address comments
---
.github/release-notes.yml | 20 --
.github/release.yml | 23 +++
.github/workflows/create-release-branch.yaml | 43 ++---
.github/workflows/release.yaml | 181 ++++++++-----------
4 files changed, 116 insertions(+), 151 deletions(-)
delete mode 100644 .github/release-notes.yml
create mode 100644 .github/release.yml
diff --git a/.github/release-notes.yml b/.github/release-notes.yml
deleted file mode 100644
index 9b58699c5bc..00000000000
--- a/.github/release-notes.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-changelog:
- sections:
- - title: "Headliners"
- emoji: ":star:"
- labels: [ "headliner" ]
- - title: "New Features"
- emoji: ":gift:"
- labels: [ "feature" ]
- - title: "Enhancements"
- emoji: ":sparkles:"
- labels: [ "enhancement" ]
- - title: "Bugs Fixed"
- emoji: ":beetle:"
- labels: [ "bug" ]
- - title: "Documentation"
- emoji: ":book:"
- labels: "documentation"
- - title: "Automation"
- emoji: ":robot:"
- labels: [ "automation" ]
diff --git a/.github/release.yml b/.github/release.yml
new file mode 100644
index 00000000000..d30df62ae17
--- /dev/null
+++ b/.github/release.yml
@@ -0,0 +1,23 @@
+changelog:
+ categories:
+ - title: Headliners
+ labels:
+ - headliner
+ - title: New Features
+ labels:
+ - feature
+ - title: Enhancements
+ labels:
+ - enhancement
+ - title: Bugs Fixed
+ labels:
+ - bug
+ - title: Documentation
+ labels:
+ - documentation
+ - title: Automation
+ labels:
+ - automation
+ - title: Other changes
+ labels:
+ - "*"
diff --git a/.github/workflows/create-release-branch.yaml b/.github/workflows/create-release-branch.yaml
index 5117cc4164c..1c2d361ac09 100644
--- a/.github/workflows/create-release-branch.yaml
+++ b/.github/workflows/create-release-branch.yaml
@@ -1,11 +1,9 @@
name: Create Release Branch
on:
- workflow_dispatch:
- inputs:
- version:
- description: 'Release Branch Version'
- required: false
+ milestone:
+ types:
+ - closed
env:
artifactId: gaffer2
@@ -23,28 +21,11 @@ jobs:
token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
fetch-depth: 0
- - name: Set version from input
- if: ${{ github.event.inputs.version }}
- run: echo "RELEASE_VERSION=$(echo ${{ github.event.inputs.version }} | sed 's/^v//')" >> $GITHUB_ENV
-
- - name: Get latest tag
- if: ${{ !github.event.inputs.version }}
- uses: actions-ecosystem/action-get-latest-tag@v1
- id: get-latest-tag
-
- - name: Format latest tag
- if: ${{ !github.event.inputs.version }}
- run: echo "CURRENT_VERSION=$(echo ${{ steps.get-latest-tag.outputs.tag }} | sed 's/^gaffer2-//')" >> $GITHUB_ENV
-
- - name: Bump latest tag variable version
- if: ${{ !github.event.inputs.version }}
- run: echo "RELEASE_VERSION=$(echo ${{ env.CURRENT_VERSION }} | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+)/echo \1.$((\2+1)).0/' | sh)" >> $GITHUB_ENV
-
- - name: Verify version regex
- run: echo ${{ env.RELEASE_VERSION }} | grep -E '^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$'
+ - name: Set version from milestone
+ run: echo "RELEASE_VERSION=$(echo ${{ github.event.milestone.title }})" >> $GITHUB_ENV
- name: Set release branch
- run: echo "BRANCH_NAME=$(echo release/${{ env.RELEASE_VERSION }} )" >> $GITHUB_ENV
+ run: echo "BRANCH_NAME=$(echo release/"${{ env.RELEASE_VERSION }}" )" >> $GITHUB_ENV
- name: Set up Github credentials
run: |
@@ -54,10 +35,20 @@ jobs:
- name: Update versions
run: |
mvn versions:set-property -Dproperty=revision -DnewVersion=${RELEASE_VERSION} -DgenerateBackupPoms=false
- sed -i'' -e "s/^gaffer.version=.*/gaffer.version=$RELEASE_VERSION/" rest-api/common-rest/src/main/resources/version.properties
- name: Push to release branch
run: |
git checkout -b $BRANCH_NAME
git commit -a -m "prepare release $artifactId-$RELEASE_VERSION"
git push --set-upstream origin $BRANCH_NAME
+
+ - name: Tag release branch
+ run: |
+ git tag gaffer2-$RELEASE_VERSION
+ git push origin --tags
+
+ - name: Create PR to master
+ run: |
+ gh pr create -B master -H $BRANCH_NAME --title 'Merge release into master branch' --body 'Created by GH Action'
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 37fde540f14..70d464b9882 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -1,21 +1,22 @@
name: Full Release
on:
- milestone:
- types:
- - closed
+ pull_request:
+ branches:
+ - master
+ types: [closed]
env:
artifactId: gaffer2
MAVEN_OPTS: -Dmaven.wagon.http.retryHandler.count=3 -Dmaven.wagon.httpconnectionManager.ttlSeconds=25
-
jobs:
- create-release-tag:
+ deploy-release:
+ if: ${{ github.event.pull_request.merged }}
runs-on: ubuntu-latest
outputs:
- branch_name: ${{ steps.branch.outputs.branch_name }}
+ release_version: ${{ steps.release-version.outputs.release_version }}
steps:
- name: Setup JDK
@@ -36,106 +37,15 @@ jobs:
git config user.name github-actions[bot]
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
- - name: Set release version
- run: echo "RELEASE_VERSION=$(echo ${{ github.event.milestone.title }} | cut -c 2-)" >> $GITHUB_ENV
-
- - name: Set branch name
- run: echo "BRANCH_NAME=$(git branch -a | grep $RELEASE_VERSION | tail -n 1 | cut -c 18-)" >> $GITHUB_ENV
-
- - name: Output branch name
- id: branch
- run: echo "branch_name=$BRANCH_NAME" >> $GITHUB_OUTPUT
-
- - name: Fail if branch output missing (no branch found)
- if: ${{ !steps.branch.outputs.branch_name }}
- run: exit 1
-
- - name: Merge release into master
- run: |
- git checkout ${{ env.BRANCH_NAME }}
- git checkout master
- git merge ${{ env.BRANCH_NAME }}
-
- - name: Push changes
- run: |
- git tag $artifactId-$RELEASE_VERSION
- git push origin $artifactId-$RELEASE_VERSION
- git push
-
- update-develop:
- runs-on: ubuntu-latest
- needs:
- - create-release-tag
-
- steps:
- - name: Checkout develop
- uses: actions/checkout@v4
- with:
- ref: develop
- token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
- fetch-depth: 0
-
- - name: Set up Github credentials
- run: |
- git config user.name github-actions[bot]
- git config user.email 41898282+github-actions[bot]@users.noreply.github.com
-
- - name: Setup JDK
- uses: actions/setup-java@v4
- with:
- distribution: 'zulu'
- java-version: '8'
-
- - name: Merge release into develop
- run: |
- git checkout ${{ needs.create-release-tag.outputs.branch_name }}
- git checkout develop
- git merge ${{ needs.create-release-tag.outputs.branch_name }} --strategy-option theirs
-
- - name: Update develop branch
+ - name: Output release version
+ id: release-version
run: |
- git checkout develop
- mvn versions:set-property -Dproperty=revision -DnewVersion=$(echo ${{ github.event.milestone.title }} | cut -c 2-)-SNAPSHOT
- mvn build-helper:parse-version versions:set-property \
- -Dproperty=revision \
- -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}.\${parsedVersion.nextIncrementalVersion}-SNAPSHOT
- NEW_GAFFER_VERSION=$(mvn -q help:evaluate -DforceStdout -Dexpression=pom.version)
- sed -i'' -e "s/^gaffer.version=.*/gaffer.version=$NEW_GAFFER_VERSION/" rest-api/common-rest/src/main/resources/version.properties
- git commit -a -m "prepare for next development iteration"
- git push
-
- update-github-releases:
- runs-on: ubuntu-latest
- needs:
- - create-release-tag
-
- steps:
- - name: Checkout Master
- uses: actions/checkout@v4
- with:
- ref: master
-
- - name: Create Release Notes
- uses: docker://decathlon/release-notes-generator-action:3.1.5
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Set version
- run: echo "RELEASE_VERSION=$(echo ${{ github.event.milestone.title }} | cut -c 2-)" >> $GITHUB_ENV
-
- - name: Upload notes
- uses: softprops/action-gh-release@v2
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- tag_name: ${{ env.artifactId }}-${{ env.RELEASE_VERSION }}
- name: Gaffer ${{ env.RELEASE_VERSION }}
- body_path: release_file.md
+ echo "release_version=$(mvn -q help:evaluate -DforceStdout -Dexpression=pom.version)" >> $GITHUB_OUTPUT
generate-javadoc:
runs-on: ubuntu-latest
needs:
- - create-release-tag
+ - deploy-release
steps:
- name: Checkout Master
@@ -156,7 +66,7 @@ jobs:
java-version: '11'
- name: Set version
- run: echo "RELEASE_VERSION=$(echo ${{ github.event.milestone.title }} | cut -c 2-)" >> $GITHUB_ENV
+ run: echo "RELEASE_VERSION=$(echo ${{ needs.deploy-release.outputs.release_version }})" >> $GITHUB_ENV
- name: Upload Javadoc
run: |
@@ -176,7 +86,7 @@ jobs:
release-to-nexus:
runs-on: ubuntu-latest
needs:
- - create-release-tag
+ - deploy-release
steps:
- name: Setup JDK
@@ -188,7 +98,7 @@ jobs:
- name: Checkout release
uses: actions/checkout@v4
with:
- ref: ${{ needs.create-release-tag.outputs.branch_name }}
+ ref: master
- name: Decode CodeSigning key
env:
@@ -205,4 +115,65 @@ jobs:
GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
OSS_NEXUS_USERNAME: ${{ secrets.OSS_NEXUS_USERNAME }}
OSS_NEXUS_PASSWORD: ${{ secrets.OSS_NEXUS_PASSWORD }}
- run: mvn deploy -P quick,ossrh-release --settings cd/mvnsettings.xml -B
+ run:
+ mvn deploy -P quick,ossrh-release --settings cd/mvnsettings.xml -B
+
+ update-github-releases:
+ runs-on: ubuntu-latest
+ needs:
+ - deploy-release
+
+ steps:
+ - name: Checkout Master
+ uses: actions/checkout@v4
+ with:
+ ref: master
+
+ - name: Set version
+ run: echo "RELEASE_VERSION=$(echo ${{ needs.deploy-release.outputs.release_version }})" >> $GITHUB_ENV
+
+ - name: Create github release
+ uses: softprops/action-gh-release@v2
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ with:
+ tag_name: ${{ env.artifactId }}-${{ env.RELEASE_VERSION }}
+ name: Gaffer ${{ env.RELEASE_VERSION }}
+
+ update-develop:
+ runs-on: ubuntu-latest
+ needs:
+ - deploy-release
+
+ steps:
+ - name: Checkout develop
+ uses: actions/checkout@v4
+ with:
+ ref: develop
+ token: ${{ secrets.ADMIN_GITHUB_TOKEN }}
+ fetch-depth: 0
+
+ - name: Set up Github credentials
+ run: |
+ git config user.name github-actions[bot]
+ git config user.email 41898282+github-actions[bot]@users.noreply.github.com
+
+ - name: Setup JDK
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'zulu'
+ java-version: '8'
+
+ - name: Merge master into develop
+ run: |
+ git merge origin/master
+
+ - name: Update develop branch
+ run: |
+ git checkout develop
+ mvn versions:set-property -Dproperty=revision -DnewVersion=$(echo ${{ needs.deploy-release.outputs.release_version }})-SNAPSHOT
+ mvn build-helper:parse-version versions:set-property \
+ -Dproperty=revision \
+ -DnewVersion=\${parsedVersion.majorVersion}.\${parsedVersion.minorVersion}.\${parsedVersion.nextIncrementalVersion}-SNAPSHOT
+ git commit -a -m "prepare for next development iteration"
+ git push
From b3aaa6493be4cfcd41b495d8a8251c4806b894e5 Mon Sep 17 00:00:00 2001
From: p29876 <165825455+p29876@users.noreply.github.com>
Date: Thu, 8 Aug 2024 12:45:49 +0000
Subject: [PATCH 13/16] Make gremlin config message clearer (#3264)
---
.../main/java/uk/gov/gchq/gaffer/rest/config/GremlinConfig.java | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinConfig.java b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinConfig.java
index a13d12d5c3f..452c37dad96 100644
--- a/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinConfig.java
+++ b/rest-api/spring-rest/src/main/java/uk/gov/gchq/gaffer/rest/config/GremlinConfig.java
@@ -44,7 +44,7 @@ public GraphTraversalSource graphTraversalSource(final GraphFactory graphFactory
// Determine where to look for the GafferPop properties
String gafferPopProperties = graphFactory.getGraph().getStoreProperties().get(GafferPopGraph.GAFFERPOP_PROPERTIES);
if (gafferPopProperties == null) {
- LOGGER.warn("GafferPop properties file was not specified using default location: {}", DEFAULT_PROPERTIES);
+ LOGGER.warn("GafferPop properties file was not specified. Using default location: {}", DEFAULT_PROPERTIES);
gafferPopProperties = DEFAULT_PROPERTIES;
}
// Obtain the graph traversal
From 4a4c4b1f93f075676a1973d49bdf698fca6847e3 Mon Sep 17 00:00:00 2001
From: cn337131 <141730190+cn337131@users.noreply.github.com>
Date: Thu, 8 Aug 2024 13:53:51 +0000
Subject: [PATCH 14/16] Gh-3258: Sonarcloud Issues (#3261)
* initial improvements
* more test tidy up
* more fixes
* improve coverage
* copyright
* fix DeleteAllData test
* checkstyle
* address comments
* address comments
* checkstyle
* checkstyle
---------
Co-authored-by: wb36499 <166839644+wb36499@users.noreply.github.com>
---
.../gaffer/commonutil/ByteBufferUtilTest.java | 32 ++-
.../gaffer/commonutil/CloseableUtilTest.java | 19 +-
.../gaffer/commonutil/CollectionUtilTest.java | 94 +++-----
.../gchq/gaffer/commonutil/FieldUtilTest.java | 18 +-
.../gchq/gaffer/commonutil/GroupUtilTest.java | 8 +-
.../gchq/gaffer/commonutil/JsonAssert.java | 23 +-
.../gchq/gaffer/commonutil/JsonUtilTest.java | 85 +++----
.../gchq/gaffer/commonutil/LongUtilTest.java | 6 +-
.../gaffer/commonutil/PropertiesUtilTest.java | 25 +-
.../gaffer/commonutil/StreamUtilTest.java | 11 +-
.../gaffer/commonutil/StringUtilTest.java | 26 +--
.../commonutil/ToStringBuilderTest.java | 16 +-
.../ArrayByteSequenceTest.java | 44 ++--
.../AuthorisationsTest.java | 59 +++--
.../ElementVisibilityTest.java | 47 ++--
.../VisibilityEvaluatorTest.java | 86 +++----
...alidTrue.java => AlwaysValidTrueTest.java} | 14 +-
.../iterable/CachingIterableTest.java | 16 +-
.../iterable/ConsumableBlockingQueueTest.java | 69 +++---
.../iterable/EmptyIterableTest.java | 6 +-
.../LimitedInMemorySortedIterableTest.java | 45 +++-
.../iterable/RepeatItemIterableTest.java | 8 +-
.../iterable/StreamIterableTest.java | 8 +-
.../iterable/StreamIteratorTest.java | 6 +-
.../iterable/SuppliedIterableTest.java | 8 +-
.../iterable/TransformIterableTest.java | 41 +++-
.../TransformOneToManyIterableTest.java | 16 +-
.../gchq/gaffer/commonutil/pair/PairTest.java | 39 ++--
.../stream/GafferCollectorTest.java | 9 +-
.../gaffer/commonutil/stream/StreamsTest.java | 12 +-
.../hook/NamedOperationResolverTest.java | 216 +++++++++---------
.../store/operation/DeleteAllDataTest.java | 12 +-
.../CustomMapSerialiserTest.java | 75 +++---
.../gchq/gaffer/cache/impl/JcsCacheTest.java | 50 ++--
.../loader/AddElementsFromHdfsLoaderIT.java | 10 +-
.../RBMBackedTimestampSetSerialiserTest.java | 84 +++----
36 files changed, 709 insertions(+), 634 deletions(-)
rename core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/{AlwaysValidTrue.java => AlwaysValidTrueTest.java} (78%)
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/ByteBufferUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/ByteBufferUtilTest.java
index 9b474d6bed5..073fdd86762 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/ByteBufferUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/ByteBufferUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2023 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -30,17 +30,16 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* This test class is copied from org.apache.accumulo.core.util.ByteByfferUtilTest.
*/
-public class ByteBufferUtilTest {
+class ByteBufferUtilTest {
private static final byte[] TEST_DATA_STRING = "0123456789".getBytes(UTF_8);
@Test
- public void testNonZeroArrayOffset() {
+ void testNonZeroArrayOffset() {
final ByteBuffer bb1 = ByteBuffer.wrap(TEST_DATA_STRING, 3, 4);
// create a ByteBuffer with a non-zero array offset
@@ -49,9 +48,9 @@ public void testNonZeroArrayOffset() {
// The purpose of this test is to ensure ByteBufferUtil code works when arrayOffset is non-zero. The following asserts are not to test ByteBuffer, but
// ensure the behaviour of slice() is as expected.
- assertEquals(3, bb2.arrayOffset());
- assertEquals(0, bb2.position());
- assertEquals(4, bb2.limit());
+ assertThat(bb2.arrayOffset()).isEqualTo(3);
+ assertThat(bb2.position()).isZero();
+ assertThat(bb2.limit()).isEqualTo(4);
// start test with non zero arrayOffset
assertByteBufferEquals("3456", bb2);
@@ -62,21 +61,21 @@ public void testNonZeroArrayOffset() {
}
@Test
- public void testZeroArrayOffsetAndNonZeroPosition() {
+ void testZeroArrayOffsetAndNonZeroPosition() {
final ByteBuffer bb = ByteBuffer.wrap(TEST_DATA_STRING, 3, 4);
assertByteBufferEquals("3456", bb);
}
@Test
- public void testZeroArrayOffsetAndPosition() {
+ void testZeroArrayOffsetAndPosition() {
final ByteBuffer bb = ByteBuffer.wrap(TEST_DATA_STRING, 0, 4);
assertByteBufferEquals("0123", bb);
}
@Test
- public void testDirectByteBuffer() {
+ void testDirectByteBuffer() {
// allocate direct so it does not have a backing array
final ByteBuffer bb = ByteBuffer.allocateDirect(10);
bb.put(TEST_DATA_STRING);
@@ -90,15 +89,14 @@ public void testDirectByteBuffer() {
}
private static void assertByteBufferEquals(final String expected, final ByteBuffer bb) {
- assertEquals(expected, ByteBufferUtil.toString(bb));
- assertEquals(expected, new String(ByteBufferUtil.toBytes(bb), UTF_8));
- assertEquals(expected, ByteBufferUtil.toString(bb));
+ assertThat(ByteBufferUtil.toString(bb)).isEqualTo(expected);
+ assertThat(new String(ByteBufferUtil.toBytes(bb), UTF_8)).isEqualTo(expected);
List bal = ByteBufferUtil.toBytesList(Collections.singletonList(bb));
assertThat(bal).hasSize(1);
- assertEquals(expected, new String(bal.get(0), UTF_8));
+ assertThat(new String(bal.get(0), UTF_8)).isEqualTo(expected);
- assertEquals(new ArrayByteSequence(expected), new ArrayByteSequence(bb));
+ assertThat(new ArrayByteSequence(bb)).isEqualTo(new ArrayByteSequence(expected));
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
@@ -109,13 +107,13 @@ private static void assertByteBufferEquals(final String expected, final ByteBuff
throw new RuntimeException(e);
}
- assertEquals(expected, new String(baos.toByteArray(), UTF_8));
+ assertThat(new String(baos.toByteArray(), UTF_8)).isEqualTo(expected);
ByteArrayInputStream bais = ByteBufferUtil.toByteArrayInputStream(bb);
byte[] buffer = new byte[expected.length()];
try {
bais.read(buffer);
- assertEquals(expected, new String(buffer, UTF_8));
+ assertThat(new String(buffer, UTF_8)).isEqualTo(expected);
} catch (IOException e) {
throw new RuntimeException(e);
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/CloseableUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/CloseableUtilTest.java
index f444c1aae48..e021379e443 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/CloseableUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/CloseableUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -28,10 +28,10 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-public class CloseableUtilTest {
+class CloseableUtilTest {
@Test
- public void shouldCloseACloseable() throws IOException {
+ void shouldCloseACloseable() throws IOException {
final Closeable closeable = mock(Closeable.class);
CloseableUtil.close(closeable);
@@ -40,7 +40,7 @@ public void shouldCloseACloseable() throws IOException {
}
@Test
- public void shouldCloseAllCloseables() throws IOException {
+ void shouldCloseAllCloseables() throws IOException {
final Closeable closeable1 = mock(Closeable.class);
final Closeable closeable2 = mock(Closeable.class);
final Object nonCloseable = mock(Object.class);
@@ -51,10 +51,19 @@ public void shouldCloseAllCloseables() throws IOException {
verify(closeable2).close();
}
+ @Test
+ void shouldCloseAutoCloseables() throws Exception {
+ final AutoCloseable autoCloseable = mock(AutoCloseable.class);
+
+ CloseableUtil.close(autoCloseable);
+
+ verify(autoCloseable).close();
+ }
+
@ParameterizedTest
@NullSource
@ValueSource(strings = {"Some string"})
- public void shouldNotThrowExceptionForNullOrStringObject(Object obj) {
+ void shouldNotThrowExceptionForNullOrStringObject(Object obj) {
assertThatNoException().isThrownBy(() -> CloseableUtil.close(obj));
}
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/CollectionUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/CollectionUtilTest.java
index 962da073307..9df6e041066 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/CollectionUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/CollectionUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -27,14 +27,11 @@
import java.util.TreeSet;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-public class CollectionUtilTest {
+class CollectionUtilTest {
@Test
- public void shouldReturnTreeSetWithProvidedItem() {
+ void shouldReturnTreeSetWithProvidedItem() {
final String item = "test item";
final TreeSet treeSet = CollectionUtil.treeSet(item);
@@ -44,14 +41,14 @@ public void shouldReturnTreeSetWithProvidedItem() {
}
@Test
- public void shouldReturnTreeSetWithWithOutNullItem() {
- final TreeSet treeSet = CollectionUtil.treeSet(null);
+ void shouldReturnTreeSetWithWithOutNullItem() {
+ final TreeSet treeSet = CollectionUtil.treeSet((String) null);
assertThat(treeSet).isEmpty();
}
@Test
- public void shouldReturnTreeSetWithProvidedItems() {
+ void shouldReturnTreeSetWithProvidedItems() {
final String[] items = {"test item 1", "test item 2", null};
final TreeSet treeSet = CollectionUtil.treeSet(items);
@@ -65,14 +62,7 @@ public void shouldReturnTreeSetWithProvidedItems() {
}
@Test
- public void shouldReturnTreeSetWithNoItemsForNullArray() {
- final TreeSet treeSet = CollectionUtil.treeSet(null);
-
- assertThat(treeSet).isEmpty();
- }
-
- @Test
- public void shouldConvertMapToStringKeys() {
+ void shouldConvertMapToStringKeys() {
// Given
final Map, String> map = new HashMap<>();
populateClassKeyMap(map);
@@ -83,11 +73,11 @@ public void shouldConvertMapToStringKeys() {
// Then
final Map expectedResult = new HashMap<>();
populateStringKeyMap(expectedResult);
- assertEquals(expectedResult, result);
+ assertThat(result).isEqualTo(expectedResult);
}
@Test
- public void shouldConvertMapToStringKeysWithProvidedMap() {
+ void shouldConvertMapToStringKeysWithProvidedMap() {
// Given
final Map, String> map = new HashMap<>();
populateClassKeyMap(map);
@@ -100,11 +90,11 @@ public void shouldConvertMapToStringKeysWithProvidedMap() {
// Then
final Map expectedResult = new LinkedHashMap<>();
populateStringKeyMap(expectedResult);
- assertEquals(expectedResult, result);
+ assertThat(result).isEqualTo(expectedResult);
}
@Test
- public void shouldConvertMapToClassKeys() throws ClassNotFoundException {
+ void shouldConvertMapToClassKeys() throws ClassNotFoundException {
// Given
final Map map = new HashMap<>();
populateStringKeyMap(map);
@@ -115,11 +105,11 @@ public void shouldConvertMapToClassKeys() throws ClassNotFoundException {
// Then
final Map, String> expectedResult = new HashMap<>();
populateClassKeyMap(expectedResult);
- assertEquals(expectedResult, result);
+ assertThat(result).isEqualTo(expectedResult);
}
@Test
- public void shouldConvertMapToClassKeysWithProvidedMap() throws ClassNotFoundException {
+ void shouldConvertMapToClassKeysWithProvidedMap() throws ClassNotFoundException {
// Given
final Map map = new HashMap<>();
populateStringKeyMap(map);
@@ -132,11 +122,11 @@ public void shouldConvertMapToClassKeysWithProvidedMap() throws ClassNotFoundExc
// Then
final Map, String> expectedResult = new LinkedHashMap<>();
populateClassKeyMap(expectedResult);
- assertEquals(expectedResult, result);
+ assertThat(result).isEqualTo(expectedResult);
}
@Test
- public void shouldReturnTrueWhenCollectionContainsAProvidedValue() {
+ void shouldReturnTrueWhenCollectionContainsAProvidedValue() {
// Given
final Collection collection = Sets.newHashSet(10, 20, 2, 30);
final Object[] values = new Object[] {1, 2, 3};
@@ -145,11 +135,11 @@ public void shouldReturnTrueWhenCollectionContainsAProvidedValue() {
final boolean result = CollectionUtil.containsAny(collection, values);
// Then
- assertTrue(result);
+ assertThat(result).isTrue();
}
@Test
- public void shouldReturnFalseWhenCollectionDoesNotContainsAProvidedValue() {
+ void shouldReturnFalseWhenCollectionDoesNotContainsAProvidedValue() {
// Given
final Collection collection = Sets.newHashSet(10, 20, 30);
final Object[] values = new Object[] {1, 2, 3};
@@ -158,11 +148,11 @@ public void shouldReturnFalseWhenCollectionDoesNotContainsAProvidedValue() {
final boolean result = CollectionUtil.containsAny(collection, values);
// Then
- assertFalse(result);
+ assertThat(result).isFalse();
}
@Test
- public void shouldReturnFalseWhenContainsAnyCalledWithNullValue() {
+ void shouldReturnFalseWhenContainsAnyCalledWithNullValue() {
// Given
final Collection collection = Sets.newHashSet(10, 20, 30);
@@ -170,20 +160,20 @@ public void shouldReturnFalseWhenContainsAnyCalledWithNullValue() {
final boolean result = CollectionUtil.containsAny(collection, null);
// Then
- assertFalse(result);
+ assertThat(result).isFalse();
}
@Test
- public void shouldReturnFalseWhenContainsAnyCalledWithNullCollection() {
+ void shouldReturnFalseWhenContainsAnyCalledWithNullCollection() {
final Object[] values = new Object[] {1, 2, 3};
final boolean result = CollectionUtil.containsAny(null, values);
- assertFalse(result);
+ assertThat(result).isFalse();
}
@Test
- public void shouldReturnFalseWhenAnyMissingCalledWhenTheCollectionContainsAllValues() {
+ void shouldReturnFalseWhenAnyMissingCalledWhenTheCollectionContainsAllValues() {
// Given
final Collection collection = Sets.newHashSet(10, 20, 30);
final Object[] values = new Object[] {10, 20, 30};
@@ -192,11 +182,11 @@ public void shouldReturnFalseWhenAnyMissingCalledWhenTheCollectionContainsAllVal
final boolean result = CollectionUtil.anyMissing(collection, values);
// Then
- assertFalse(result);
+ assertThat(result).isFalse();
}
@Test
- public void shouldReturnFalseWhenAnyMissingCalledWhenNullValues() {
+ void shouldReturnFalseWhenAnyMissingCalledWhenNullValues() {
// Given
final Collection collection = Sets.newHashSet(10, 20, 30);
@@ -204,11 +194,11 @@ public void shouldReturnFalseWhenAnyMissingCalledWhenNullValues() {
final boolean result = CollectionUtil.anyMissing(collection, null);
// Then
- assertFalse(result);
+ assertThat(result).isFalse();
}
@Test
- public void shouldReturnTrueWhenAnyMissingCalledWhenTheCollectionDoesNotContainAProvidedValue() {
+ void shouldReturnTrueWhenAnyMissingCalledWhenTheCollectionDoesNotContainAProvidedValue() {
// Given
final Collection collection = Sets.newHashSet(10, 20, 30);
final Object[] values = new Object[] {1, 2, 3};
@@ -217,23 +207,11 @@ public void shouldReturnTrueWhenAnyMissingCalledWhenTheCollectionDoesNotContainA
final boolean result = CollectionUtil.anyMissing(collection, values);
// Then
- assertTrue(result);
- }
-
- @Test
- public void shouldReturnFalseWhenAnyMissingCalledWithNullValue() {
- // Given
- final Collection collection = Sets.newHashSet(10, 20, 30);
-
- // When
- final boolean result = CollectionUtil.anyMissing(collection, null);
-
- // Then
- assertFalse(result);
+ assertThat(result).isTrue();
}
@Test
- public void shouldReturnTrueWhenAnyMissingCalledWithNullCollectionAndSomeValues() {
+ void shouldReturnTrueWhenAnyMissingCalledWithNullCollectionAndSomeValues() {
// Given
final Object[] values = new Object[] {1, 2, 3};
@@ -241,20 +219,20 @@ public void shouldReturnTrueWhenAnyMissingCalledWithNullCollectionAndSomeValues(
final boolean result = CollectionUtil.anyMissing(null, values);
// Then
- assertTrue(result);
+ assertThat(result).isTrue();
}
@Test
- public void shouldReturnFalseWhenAnyMissingCalledWithNullCollectionAndValues() {
+ void shouldReturnFalseWhenAnyMissingCalledWithNullCollectionAndValues() {
// When
final boolean result = CollectionUtil.anyMissing(null, null);
// Then
- assertFalse(result);
+ assertThat(result).isFalse();
}
@Test
- public void shouldReturnTrueWhenDistinctCalledWithCollectionOfUniqueValues() {
+ void shouldReturnTrueWhenDistinctCalledWithCollectionOfUniqueValues() {
// Given
final Collection collection = Lists.newArrayList(1, 2, 3, 4, 5);
@@ -262,11 +240,11 @@ public void shouldReturnTrueWhenDistinctCalledWithCollectionOfUniqueValues() {
final boolean result = CollectionUtil.distinct(collection);
// Then
- assertTrue(result);
+ assertThat(result).isTrue();
}
@Test
- public void shouldReturnFalseWhenDistinctCalledWithCollectionOfNonUniqueValues() {
+ void shouldReturnFalseWhenDistinctCalledWithCollectionOfNonUniqueValues() {
// Given
final Collection collection = Lists.newArrayList(1, 2, 3, 1, 2);
@@ -274,7 +252,7 @@ public void shouldReturnFalseWhenDistinctCalledWithCollectionOfNonUniqueValues()
final boolean result = CollectionUtil.distinct(collection);
// Then
- assertFalse(result);
+ assertThat(result).isFalse();
}
private void populateClassKeyMap(Map, String> map) {
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/FieldUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/FieldUtilTest.java
index 0d6bfd63fb6..fe7597b0751 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/FieldUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/FieldUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2019-2020 Crown Copyright
+ * Copyright 2019-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,28 +24,28 @@
import java.util.LinkedHashSet;
import java.util.Set;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
-public class FieldUtilTest {
+class FieldUtilTest {
@Test
- public void testNullField() {
- final Pair nullPair = new Pair("Test", null);
+ void testNullField() {
+ final Pair nullPair = new Pair("Test", null);
final ValidationResult validationResult = FieldUtil.validateRequiredFields(nullPair);
final Set expected = new LinkedHashSet<>();
expected.add("Test is required.");
- assertEquals(expected, validationResult.getErrors());
+ assertThat(validationResult.getErrors()).isEqualTo(expected);
}
@Test
- public void testNotNullField() {
- final Pair nonNullPair = new Pair("Test", "Test");
+ void testNotNullField() {
+ final Pair nonNullPair = new Pair("Test", "Test");
final ValidationResult validationResult = FieldUtil.validateRequiredFields(nonNullPair);
final Set expected = new LinkedHashSet<>();
- assertEquals(expected, validationResult.getErrors());
+ assertThat(validationResult.getErrors()).isEqualTo(expected);
}
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/GroupUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/GroupUtilTest.java
index 5599ec7aefa..3eeb7084091 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/GroupUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/GroupUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,19 +21,19 @@
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatNoException;
-public class GroupUtilTest {
+class GroupUtilTest {
private static final String INVALID_STRING = "inv@l1dStr|ng&^";
private static final String VALID_STRING = "vAl1d-Str|ng";
@Test
- public void shouldThrowExceptionWithInvalidStringName() {
+ void shouldThrowExceptionWithInvalidStringName() {
assertThatIllegalArgumentException()
.isThrownBy(() -> GroupUtil.validateName(INVALID_STRING))
.withMessage("Group is invalid: inv@l1dStr|ng&^, it must match regex: [a-zA-Z0-9|-]*");
}
@Test
- public void shouldPassValidationWithValidStringName() {
+ void shouldPassValidationWithValidStringName() {
assertThatNoException().isThrownBy(() -> GroupUtil.validateName(VALID_STRING));
}
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/JsonAssert.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/JsonAssert.java
index d29353347b7..df55289db70 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/JsonAssert.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/JsonAssert.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2020 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,7 +17,8 @@
package uk.gov.gchq.gaffer.commonutil;
import com.fasterxml.jackson.databind.ObjectMapper;
-import org.junit.jupiter.api.Assertions;
+
+import static org.assertj.core.api.Assertions.assertThat;
import java.io.IOException;
import java.util.Collections;
@@ -33,18 +34,18 @@ private JsonAssert() {
public static void assertEquals(final String expectedJson, final String actualJson) {
try {
- final Map expectedSchemaMap = null != expectedJson ? OBJECT_MAPPER.readValue(expectedJson, Map.class) : Collections.emptyMap();
- final Map actualSchemaMap = null != actualJson ? OBJECT_MAPPER.readValue(actualJson, Map.class) : Collections.emptyMap();
- Assertions.assertEquals(expectedSchemaMap, actualSchemaMap);
+ final Map expectedSchemaMap = expectedJson != null ? OBJECT_MAPPER.readValue(expectedJson, Map.class) : Collections.emptyMap();
+ final Map actualSchemaMap = actualJson != null ? OBJECT_MAPPER.readValue(actualJson, Map.class) : Collections.emptyMap();
+ assertThat(actualSchemaMap).isEqualTo(expectedSchemaMap);
return;
} catch (final IOException e) {
// ignore the error and try using lists instead
}
try {
- final List expectedSchemaMap = null != expectedJson ? OBJECT_MAPPER.readValue(expectedJson, List.class) : Collections.emptyList();
- final List actualSchemaMap = null != actualJson ? OBJECT_MAPPER.readValue(actualJson, List.class) : Collections.emptyList();
- Assertions.assertEquals(expectedSchemaMap, actualSchemaMap);
+ final List expectedSchemaMap = expectedJson != null ? OBJECT_MAPPER.readValue(expectedJson, List.class) : Collections.emptyList();
+ final List actualSchemaMap = actualJson != null ? OBJECT_MAPPER.readValue(actualJson, List.class) : Collections.emptyList();
+ assertThat(actualSchemaMap).isEqualTo(expectedSchemaMap);
} catch (final IOException e) {
throw new AssertionError(expectedJson + " is not equal to " + actualJson, e);
}
@@ -56,9 +57,9 @@ public static void assertEquals(final byte[] expectedJson, final byte[] actualJs
public static void assertNotEqual(final String firstJson, final String secondJson) {
try {
- final Map firstSchemaMap = null != firstJson ? OBJECT_MAPPER.readValue(firstJson, Map.class) : Collections.emptyMap();
- final Map secondSchemaMap = null != secondJson ? OBJECT_MAPPER.readValue(secondJson, Map.class) : Collections.emptyMap();
- Assertions.assertNotEquals(firstSchemaMap, secondSchemaMap);
+ final Map firstSchemaMap = firstJson != null ? OBJECT_MAPPER.readValue(firstJson, Map.class) : Collections.emptyMap();
+ final Map secondSchemaMap = secondJson != null ? OBJECT_MAPPER.readValue(secondJson, Map.class) : Collections.emptyMap();
+ assertThat(firstSchemaMap).isNotEqualTo(secondSchemaMap);
} catch (final IOException e) {
// ignore
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/JsonUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/JsonUtilTest.java
index 8ce62ecc67a..1d1c2eacb64 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/JsonUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/JsonUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2020 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,94 +13,69 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package uk.gov.gchq.gaffer.commonutil;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+
+import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import java.util.stream.Stream;
-public class JsonUtilTest {
+class JsonUtilTest {
@Test
- public void shouldReturnTrueWhenJsonObjectsAreEqualButInADifferentOrder() {
+ void shouldReturnTrueWhenJsonObjectsAreEqualButInADifferentOrder() {
final String json1 = "{\"a\": 1, \"b\": 2}";
final String json2 = "{\"b\": 2, \"a\": 1}";
- assertTrue(JsonUtil.equals(json1, json2));
- assertTrue(JsonUtil.equals(json1.getBytes(), json2.getBytes()));
+ assertThat(JsonUtil.equals(json1, json2)).isTrue();
+ assertThat(JsonUtil.equals(json1.getBytes(), json2.getBytes())).isTrue();
JsonAssert.assertEquals(json1, json2);
JsonAssert.assertEquals(json1.getBytes(), json2.getBytes());
}
@Test
- public void shouldReturnFalseWhenJsonObjectsAreDifferentSizes() {
- final String json1 = "{\"a\": 1, \"b\": 2}";
- final String json2 = "{\"a\": 1, \"b\": 2, \"c\": 3}";
-
- assertFalse(JsonUtil.equals(json1, json2));
- assertFalse(JsonUtil.equals(json1.getBytes(), json2.getBytes()));
-
- JsonAssert.assertNotEqual(json1, json2);
- JsonAssert.assertNotEqual(json1.getBytes(), json2.getBytes());
- }
-
- @Test
- public void shouldReturnFalseWhenJsonObjectsAreNotEqual() {
- final String json1 = "{\"a\": 1, \"b\": 2}";
- final String json2 = "{\"a\": 1, \"b\": 3}";
-
- assertFalse(JsonUtil.equals(json1, json2));
- assertFalse(JsonUtil.equals(json1.getBytes(), json2.getBytes()));
-
- JsonAssert.assertNotEqual(json1, json2);
- JsonAssert.assertNotEqual(json1.getBytes(), json2.getBytes());
- }
-
- @Test
- public void shouldReturnTrueWhenJsonArraysAreEqual() {
+ void shouldReturnTrueWhenJsonArraysAreEqual() {
final String json1 = "[1,2,3]";
final String json2 = "[1,2,3]";
- assertTrue(JsonUtil.equals(json1, json2));
- assertTrue(JsonUtil.equals(json1.getBytes(), json2.getBytes()));
+ assertThat(JsonUtil.equals(json1, json2)).isTrue();
+ assertThat(JsonUtil.equals(json1.getBytes(), json2.getBytes())).isTrue();
JsonAssert.assertEquals(json1, json2);
JsonAssert.assertEquals(json1.getBytes(), json2.getBytes());
}
- @Test
- public void shouldReturnFalseWhenJsonArraysAreNotEqual() {
- // Given
- final String json1 = "[1,2,3]";
- final String json2 = "[1,2,4]";
-
- assertFalse(JsonUtil.equals(json1, json2));
- assertFalse(JsonUtil.equals(json1.getBytes(), json2.getBytes()));
+ @ParameterizedTest
+ @MethodSource("provideJSONStrings")
+ void shouldReturnFalse(String json1, String json2) {
+ assertThat(JsonUtil.equals(json1, json2)).isFalse();
+ assertThat(JsonUtil.equals(json1.getBytes(), json2.getBytes())).isFalse();
JsonAssert.assertNotEqual(json1, json2);
JsonAssert.assertNotEqual(json1.getBytes(), json2.getBytes());
}
- @Test
- public void shouldReturnFalseWhenJsonArraysAreDifferentSizes() {
- final String json1 = "[1,2,3]";
- final String json2 = "[1,2,3,4]";
-
- assertFalse(JsonUtil.equals(json1, json2));
- assertFalse(JsonUtil.equals(json1.getBytes(), json2.getBytes()));
-
- JsonAssert.assertNotEqual(json1, json2);
- JsonAssert.assertNotEqual(json1.getBytes(), json2.getBytes());
+ private static Stream provideJSONStrings() {
+ return Stream.of(
+ Arguments.of("{\"a\": 1, \"b\": 2}", "{\"a\": 1, \"b\": 2, \"c\": 3}"),
+ Arguments.of("{\"a\": 1, \"b\": 2}", "{\"a\": 1, \"b\": 3}"),
+ Arguments.of("[1,2,3]", "[1,2,4]"),
+ Arguments.of("[1,2,3]", "[1,2,3,4]")
+ );
}
@Test
- public void shouldReturnFalseWhenActualObjectIsNull() {
+ void shouldReturnFalseWhenActualObjectIsNull() {
final String json1 = "{\"a\": 1, \"b\": 2}";
- assertFalse(JsonUtil.equals(json1, null));
- assertFalse(JsonUtil.equals(json1.getBytes(), null));
+ assertThat(JsonUtil.equals(json1, null)).isFalse();
+ assertThat(JsonUtil.equals(json1.getBytes(), null)).isFalse();
JsonAssert.assertNotEqual(json1, null);
JsonAssert.assertNotEqual(json1.getBytes(), null);
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/LongUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/LongUtilTest.java
index e8d803bd5e6..6115ca5c69f 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/LongUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/LongUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,10 +23,10 @@
import static org.assertj.core.api.Assertions.assertThat;
-public class LongUtilTest {
+class LongUtilTest {
@Test
- public void shouldGetDifferentPositiveTimeBasedRandoms() {
+ void shouldGetDifferentPositiveTimeBasedRandoms() {
final int n = 1000;
final Set timestamps = new HashSet<>(n);
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/PropertiesUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/PropertiesUtilTest.java
index aef6acbf8ef..a4ffa83ee68 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/PropertiesUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/PropertiesUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,22 +18,39 @@
import org.junit.jupiter.api.Test;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatNoException;
-public class PropertiesUtilTest {
+class PropertiesUtilTest {
private static final String INVALID_STRING = "inv@l1dStr|ng&^";
private static final String VALID_STRING = "vAl1d-Str|ng";
@Test
- public void shouldThrowExceptionWithInvalidStringName() {
+ void shouldThrowExceptionWithInvalidStringName() {
assertThatIllegalArgumentException()
.isThrownBy(() -> PropertiesUtil.validateName(INVALID_STRING))
.withMessage("Property is invalid: inv@l1dStr|ng&^, it must match regex: [a-zA-Z0-9|-]*");
}
@Test
- public void shouldPassValidationWithValidStringName() {
+ void shouldPassValidationWithValidStringName() {
assertThatNoException().isThrownBy(() -> PropertiesUtil.validateName(VALID_STRING));
}
+
+ @Test
+ void shouldBeFalseWithInvalidStringName() {
+ assertThat(PropertiesUtil.isValidName(INVALID_STRING)).isFalse();
+ }
+
+ @Test
+ void shouldBeTrueWithValidStringName() {
+ assertThat(PropertiesUtil.isValidName(VALID_STRING)).isTrue();
+ }
+
+ @Test
+ void shouldStripInvalidCharacters() {
+ final String expectedString = "invl1dStr|ng";
+ assertThat(PropertiesUtil.stripInvalidCharacters(INVALID_STRING)).isEqualTo(expectedString);
+ }
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/StreamUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/StreamUtilTest.java
index 420bf7e1275..18ca1e25db6 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/StreamUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/StreamUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package uk.gov.gchq.gaffer.commonutil;
import org.junit.jupiter.api.Test;
@@ -23,12 +24,12 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.fail;
-public class StreamUtilTest {
+class StreamUtilTest {
public static final String FILE_NAME = "URLSchema.json";
@Test
- public void testOpenStreamsURLNotEmpty() throws Exception {
+ void testOpenStreamsURLNotEmpty() throws Exception {
final URI resource = getClass().getClassLoader().getResource(FILE_NAME).toURI();
if (null == resource) {
fail("Test json file not found:" + FILE_NAME);
@@ -37,8 +38,8 @@ public void testOpenStreamsURLNotEmpty() throws Exception {
final InputStream[] inputStreams = StreamUtil.openStreams(resource);
assertThat(inputStreams)
- .isNotEmpty()
- .overridingErrorMessage("InputStreams length is %s", 0);
+ .overridingErrorMessage("InputStreams length is %s", 0)
+ .isNotEmpty();
StreamUtil.closeStreams(inputStreams);
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/StringUtilTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/StringUtilTest.java
index 06cec46c3e8..f02d96edcc9 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/StringUtilTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/StringUtilTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018-2020 Crown Copyright
+ * Copyright 2018-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -25,64 +25,64 @@
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
-public class StringUtilTest {
+class StringUtilTest {
@Test
- public void unescapeCommaShouldReplaceBackslashesWithComma() {
+ void unescapeCommaShouldReplaceBackslashesWithComma() {
assertEquals("replaceBackslashesWith,Comma", StringUtil.unescapeComma("replaceBackslashesWith\\\\Comma"));
}
@Test
- public void unescapeCommaShouldNotRemoveSemicolon() {
+ void unescapeCommaShouldNotRemoveSemicolon() {
assertEquals("don'tRemove;SemiColon", StringUtil.unescapeComma("don'tRemove;SemiColon"));
}
@Test
- public void unescapeCommaShouldNotRemoveComma() {
+ void unescapeCommaShouldNotRemoveComma() {
assertEquals("don'tRemove,Comma", StringUtil.unescapeComma("don'tRemove,Comma"));
}
@Test
- public void unescapeCommaShouldRemoveBackslash() {
+ void unescapeCommaShouldRemoveBackslash() {
assertEquals("removeBackslash", StringUtil.unescapeComma("remove\\Backslash"));
}
@Test
- public void escapeCommaShouldReplaceBackslashWithSemiColon() {
+ void escapeCommaShouldReplaceBackslashWithSemiColon() {
assertEquals("replaceWith\\;semicolon", StringUtil.escapeComma("replaceWith\\semicolon"));
}
@Test
- public void escapeCommaShouldReplaceCommaWith2Backslashes() {
+ void escapeCommaShouldReplaceCommaWith2Backslashes() {
assertEquals("replaceWith\\\\comma", StringUtil.escapeComma("replaceWith,comma"));
}
@Test
- public void toBytesWhenStringIsValid() {
+ void toBytesWhenStringIsValid() {
assertArrayEquals("isValid".getBytes(), StringUtil.toBytes("isValid"));
}
@Test
- public void toBytesWhenStringIsNull() {
+ void toBytesWhenStringIsNull() {
assertArrayEquals(new byte[0], StringUtil.toBytes(null));
}
@Test
- public void toStringWhenBytesAreValid() {
+ void toStringWhenBytesAreValid() {
final byte[] validBytes = "isValid".getBytes();
assertEquals("isValid", StringUtil.toString(validBytes));
}
@Test
- public void ifEmptyStringTestReturnNull() {
+ void ifEmptyStringTestReturnNull() {
assertNull(StringUtil.nullIfEmpty(""));
}
@ParameterizedTest
@NullSource
@ValueSource(strings = {" ", "String", " string "})
- public void shouldReturnValueWhenNotEmptyString(String input) {
+ void shouldReturnValueWhenNotEmptyString(String input) {
assertEquals(input, StringUtil.nullIfEmpty(input));
}
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/ToStringBuilderTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/ToStringBuilderTest.java
index fcfc17c80c0..faf300ce53e 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/ToStringBuilderTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/ToStringBuilderTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2020 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,12 +20,12 @@
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.assertj.core.api.Assertions.assertThat;
-public class ToStringBuilderTest {
+class ToStringBuilderTest {
@BeforeEach
- public void setUp() throws Exception {
+ public void setUp() {
clearDebugModeProperty();
}
@@ -35,19 +35,19 @@ public void after() {
}
@Test
- public void testDebugOffToStringBuilder() {
+ void testDebugOffToStringBuilder() {
setDebugMode("false");
ToStringBuilder toStringBuilder = new ToStringBuilder("Test String");
- assertEquals(ToStringBuilder.SHORT_STYLE, toStringBuilder.getStyle());
+ assertThat(toStringBuilder.getStyle()).isEqualTo(ToStringBuilder.SHORT_STYLE);
}
@Test
- public void testDebugOnToStringBuilder() {
+ void testDebugOnToStringBuilder() {
setDebugMode("true");
ToStringBuilder toStringBuilder = new ToStringBuilder("Test String");
- assertEquals(ToStringBuilder.FULL_STYLE, toStringBuilder.getStyle());
+ assertThat(toStringBuilder.getStyle()).isEqualTo(ToStringBuilder.FULL_STYLE);
}
private void setDebugMode(final String value) {
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/ArrayByteSequenceTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/ArrayByteSequenceTest.java
index f3f4aa0bdef..996385f7759 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/ArrayByteSequenceTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/ArrayByteSequenceTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,13 +21,13 @@
import java.nio.ByteBuffer;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
-import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* This test class is copied from org.apache.accumulo.core.data.ArrayByteSequenceTest.
*/
-public class ArrayByteSequenceTest {
+class ArrayByteSequenceTest {
ArrayByteSequence abs;
byte[] data;
@@ -39,74 +39,76 @@ public void setUp() {
}
@Test
- public void testInvalidByteBufferBounds0ShouldThrowIAX() {
+ void testInvalidByteBufferBounds0ShouldThrowIAX() {
assertThatIllegalArgumentException().isThrownBy(() -> abs = new ArrayByteSequence(data, -1, 0));
}
@Test
- public void testInvalidByteBufferBounds1ShouldThrowIAX() {
+ void testInvalidByteBufferBounds1ShouldThrowIAX() {
assertThatIllegalArgumentException().isThrownBy(() -> abs = new ArrayByteSequence(data, data.length + 1, 0));
}
@Test
- public void testInvalidByteBufferBounds2ShouldThrowIAX() {
+ void testInvalidByteBufferBounds2ShouldThrowIAX() {
assertThatIllegalArgumentException().isThrownBy(() -> abs = new ArrayByteSequence(data, 0, -1));
}
@Test
- public void testInvalidByteBufferBounds3ShouldThrowIAX() {
+ void testInvalidByteBufferBounds3ShouldThrowIAX() {
assertThatIllegalArgumentException().isThrownBy(() -> abs = new ArrayByteSequence(data, 6, 2));
}
@Test
- public void testInvalidByteAt0ShouldThrowIAX() {
+ void testInvalidByteAt0ShouldThrowIAX() {
assertThatIllegalArgumentException().isThrownBy(() -> abs.byteAt(-1));
}
@Test
- public void testInvalidByteAt1ShouldThrowIAX() {
+ void testInvalidByteAt1ShouldThrowIAX() {
assertThatIllegalArgumentException().isThrownBy(() -> abs.byteAt(data.length));
}
@Test
- public void testSubSequence() {
- assertEquals(0, abs.subSequence(0, 0).length());
- assertEquals("mile", abs.subSequence(1, 5).toString());
+ void testSubSequence() {
+ assertThat(abs.subSequence(0, 0).length()).isZero();
+ assertThat(abs.subSequence(1, 5)).hasToString("mile");
}
@Test
- public void testInvalidSubsequence0ShouldThrowIAX() {
+ void testInvalidSubsequence0ShouldThrowIAX() {
assertThatIllegalArgumentException().isThrownBy(() -> abs.subSequence(5, 1));
}
@Test
- public void testInvalidSubsequence1ShouldThrowIAX() {
+ void testInvalidSubsequence1ShouldThrowIAX() {
assertThatIllegalArgumentException().isThrownBy(() -> abs.subSequence(-1, 1));
}
@Test
- public void testInvalidSubsequence3ShouldThrowIAX() {
+ void testInvalidSubsequence3ShouldThrowIAX() {
assertThatIllegalArgumentException().isThrownBy(() -> abs.subSequence(0, 10));
}
@Test
- public void testFromByteBuffer() {
+ void testFromByteBuffer() {
final ByteBuffer bb = ByteBuffer.wrap(data, 1, 4);
abs = new ArrayByteSequence(bb);
- assertEquals("mile", abs.toString());
+ assertThat(abs).hasToString("mile");
}
@Test
- public void testFromReadOnlyByteBuffer() {
+ void testFromReadOnlyByteBuffer() {
final ByteBuffer bb = ByteBuffer.wrap(data, 1, 4).asReadOnlyBuffer();
abs = new ArrayByteSequence(bb);
- assertEquals("mile", abs.toString());
+ assertThat(abs).hasToString("mile");
}
@Test
- public void testToString() {
- assertEquals("", new ArrayByteSequence("").toString(), "String conversion should round trip correctly");
+ void testToString() {
+ assertThat(new ArrayByteSequence(""))
+ .as("String conversion should round trip correctly")
+ .hasToString("");
}
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/AuthorisationsTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/AuthorisationsTest.java
index 2b2981311fd..7fc9fefd53e 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/AuthorisationsTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/AuthorisationsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,54 +19,54 @@
import org.junit.jupiter.api.Test;
import java.nio.ByteBuffer;
+import java.util.List;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
/**
* This test class is copied from org.apache.accumulo.core.security.AuthorizationsTest.
*/
-public class AuthorisationsTest {
+class AuthorisationsTest {
@Test
- public void testEncodeDecode() {
+ void testEncodeDecode() {
final Authorisations a = new Authorisations("a", "abcdefg", "hijklmno", ",");
final byte[] array = a.getAuthorisationsArray();
final Authorisations b = new Authorisations(array);
- assertEquals(a, b);
+ assertThat(b).isEqualTo(a);
}
@Test
- public void testEncodeEmptyAuthorisations() {
+ void testEncodeEmptyAuthorisations() {
final Authorisations a = new Authorisations();
final byte[] array = a.getAuthorisationsArray();
final Authorisations b = new Authorisations(array);
- assertEquals(a, b);
+ assertThat(b).isEqualTo(a);
}
@Test
- public void testEncodeMultiByteAuthorisations() {
+ void testEncodeMultiByteAuthorisations() {
final Authorisations a = new Authorisations("五", "b", "c", "九");
final byte[] array = a.getAuthorisationsArray();
final Authorisations b = new Authorisations(array);
- assertEquals(a, b);
+ assertThat(b).isEqualTo(a);
}
@Test
- public void testSerialization() {
+ void testSerialization() {
final Authorisations a1 = new Authorisations("a", "b");
final Authorisations a2 = new Authorisations("b", "a");
- assertEquals(a1, a2);
- assertEquals(a1.serialise(), a2.serialise());
+ assertThat(a2).isEqualTo(a1);
+ assertThat(a2.serialise()).isEqualTo(a1.serialise());
}
@Test
- public void testDefensiveAccess() {
+ void testDefensiveAccess() {
final Authorisations expected = new Authorisations("foo", "a");
final Authorisations actual = new Authorisations("foo", "a");
@@ -74,37 +74,46 @@ public void testDefensiveAccess() {
for (byte[] bytes : actual) {
bytes[0]++;
}
- assertArrayEquals(expected.getAuthorisationsArray(), actual.getAuthorisationsArray());
+ assertThat(actual.getAuthorisationsArray()).isEqualTo(expected.getAuthorisationsArray());
// test defensive getter and serializer
actual.getAuthorisations().get(0)[0]++;
- assertArrayEquals(expected.getAuthorisationsArray(), actual.getAuthorisationsArray());
- assertEquals(expected.serialise(), actual.serialise());
+ assertThat(actual.getAuthorisationsArray()).isEqualTo(expected.getAuthorisationsArray());
+ assertThat(actual.serialise()).isEqualTo(expected.serialise());
}
// This should throw ReadOnlyBufferException, but THRIFT-883 requires that the ByteBuffers themselves not be read-only
// @Test(expected = ReadOnlyBufferException.class)
@Test
- public void testReadOnlyByteBuffer() {
+ void testReadOnlyByteBuffer() {
final Authorisations expected = new Authorisations("foo");
final Authorisations actual = new Authorisations("foo");
- assertArrayEquals(expected.getAuthorisationsArray(), actual.getAuthorisationsArray());
+ assertThat(actual.getAuthorisationsArray()).isEqualTo(expected.getAuthorisationsArray());
actual.getAuthorisationsBB().get(0).array()[0]++;
- assertArrayEquals(expected.getAuthorisationsArray(), actual.getAuthorisationsArray());
+ assertThat(actual.getAuthorisationsArray()).isEqualTo(expected.getAuthorisationsArray());
}
@Test
- public void testUnmodifiableList() {
+ void testUnmodifiableList() {
final Authorisations expected = new Authorisations("foo");
final Authorisations actual = new Authorisations("foo");
- assertArrayEquals(expected.getAuthorisationsArray(), actual.getAuthorisationsArray());
+ assertThat(actual.getAuthorisationsArray()).isEqualTo(expected.getAuthorisationsArray());
- assertThatExceptionOfType(UnsupportedOperationException.class).isThrownBy(() -> {
- actual.getAuthorisationsBB().add(ByteBuffer.wrap(new byte[] {'a'}));
- });
+ final ByteBuffer byteBuffer = ByteBuffer.wrap(new byte[] {'a'});
+ final List getAuthBB = actual.getAuthorisationsBB();
+
+ assertThatExceptionOfType(UnsupportedOperationException.class)
+ .isThrownBy(() -> getAuthBB.add(byteBuffer));
+ }
+
+ @Test
+ void testToString() {
+ final Authorisations a = new Authorisations("a", "abcdefg", "hijklmno");
+
+ assertThat(a).hasToString("a,hijklmno,abcdefg");
}
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/ElementVisibilityTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/ElementVisibilityTest.java
index ff2eaf1e856..40422e88ea0 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/ElementVisibilityTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/ElementVisibilityTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -23,68 +23,67 @@
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.assertj.core.api.Assertions.assertThatNoException;
-import static org.junit.jupiter.api.Assertions.assertEquals;
import static uk.gov.gchq.gaffer.commonutil.elementvisibilityutil.ElementVisibility.quote;
/**
* This test class is copied from org.apache.accumulo.core.security.ColumnVisibilityTest.
*/
-public class ElementVisibilityTest {
+class ElementVisibilityTest {
@Test
- public void testEmptyStringIsValid() {
+ void testEmptyStringIsValid() {
final ElementVisibility a = new ElementVisibility(new byte[0]);
final ElementVisibility b = new ElementVisibility("");
- assertEquals(a, b);
+ assertThat(b).isEqualTo(a);
}
@Test
- public void testCharactersOnly() {
+ void testCharactersOnly() {
getBytesShouldNotThrowIAX("test", "words");
}
@Test
- public void testCompound() {
+ void testCompound() {
getBytesShouldNotThrowIAX("a|b", "a&b", "ab&bc", "_&-&:", "A&B&C&D&E", "A|B|C|D|E", "(A|B|C)", "(A)|B|(C)", "A&(B)&(C)", "A&B&(L)");
}
@Test
- public void testBadCharacters() {
+ void testBadCharacters() {
getBytesShouldThrowIAX("=", "*", "^", "%", "@", "a*b");
}
@Test
- public void testComplexCompound() {
+ void testComplexCompound() {
getBytesShouldNotThrowIAX("(a|b)&(x|y)", "a&(x|y)", "(a|b)&(x|y)", "A&(L|M)", "B&(L|M)", "A&B&(L|M)");
getBytesShouldNotThrowIAX("A&FOO&(L|M)", "(A|B)&FOO&(L|M)", "A&B&(L|M|FOO)", "((A|B|C)|foo)&bar");
getBytesShouldNotThrowIAX("(one&two)|(foo&bar)", "(one|foo)&three", "one|foo|bar", "(one|foo)|bar", "((one|foo)|bar)&two");
}
@Test
- public void testDanglingOperators() {
+ void testDanglingOperators() {
getBytesShouldThrowIAX("a|b&", "(|a)", "|", "a|", "|a", "|", "&");
getBytesShouldThrowIAX("&(five)", "|(five)", "(five)&", "five|", "a|(b)&", "(&five)", "(five|)");
}
@Test
- public void testMissingSeparators() {
+ void testMissingSeparators() {
getBytesShouldThrowIAX("one(five)", "(five)one", "(one)(two)", "a|(b(c))");
}
@Test
- public void testMismatchedParentheses() {
+ void testMismatchedParentheses() {
getBytesShouldThrowIAX("(", ")", "(a&b", "b|a)", "A|B)");
}
@Test
- public void testMixedOperators() {
+ void testMixedOperators() {
getBytesShouldThrowIAX("(A&B)|(C&D)&(E)", "a|b&c", "A&B&C|D", "(A&B)|(C&D)&(E)");
}
@Test
- public void testQuotes() {
+ void testQuotes() {
getBytesShouldThrowIAX("\"\"", "\"A\"A", "\"A\"\"B\"", "(A)\"B\"", "\"A\"(B)");
getBytesShouldThrowIAX("\"A", "\"", "\"B", "A&\"B", "A&\"B\\'");
@@ -92,21 +91,21 @@ public void testQuotes() {
}
@Test
- public void testToStringSimpleCharacter() {
+ void testToStringSimpleCharacter() {
final ElementVisibility cv = new ElementVisibility(quote("a"));
- assertEquals("[a]", cv.toString());
+ assertThat(cv).hasToString("[a]");
}
@Test
- public void testToStringMultiByte() {
+ void testToStringMultiByte() {
final ElementVisibility cv = new ElementVisibility(quote("五"));
- assertEquals("[\"五\"]", cv.toString());
+ assertThat(cv).hasToString("[\"五\"]");
}
@Test
- public void testParseTree() {
+ void testParseTree() {
final ElementVisibility.Node node = parse("(W)|(U&V)");
assertNode(node, ElementVisibility.NodeType.OR, 0, 9);
@@ -115,14 +114,14 @@ public void testParseTree() {
}
@Test
- public void testParseTreeWithNoChildren() {
+ void testParseTreeWithNoChildren() {
final ElementVisibility.Node node = parse("ABC");
assertNode(node, ElementVisibility.NodeType.TERM, 0, 3);
}
@Test
- public void testParseTreeWithTwoChildren() {
+ void testParseTreeWithTwoChildren() {
final ElementVisibility.Node node = parse("ABC|DEF");
assertNode(node, ElementVisibility.NodeType.OR, 0, 7);
@@ -131,7 +130,7 @@ public void testParseTreeWithTwoChildren() {
}
@Test
- public void testParseTreeWithParenthesesAndTwoChildren() {
+ void testParseTreeWithParenthesesAndTwoChildren() {
final ElementVisibility.Node node = parse("(ABC|DEF)");
assertNode(node, ElementVisibility.NodeType.OR, 1, 8);
@@ -140,7 +139,7 @@ public void testParseTreeWithParenthesesAndTwoChildren() {
}
@Test
- public void testParseTreeWithParenthesizedChildren() {
+ void testParseTreeWithParenthesizedChildren() {
final ElementVisibility.Node node = parse("ABC|(DEF&GHI)");
assertNode(node, ElementVisibility.NodeType.OR, 0, 13);
@@ -151,7 +150,7 @@ public void testParseTreeWithParenthesizedChildren() {
}
@Test
- public void testParseTreeWithMoreParentheses() {
+ void testParseTreeWithMoreParentheses() {
final ElementVisibility.Node node = parse("(W)|(U&V)");
assertNode(node, ElementVisibility.NodeType.OR, 0, 9);
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/VisibilityEvaluatorTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/VisibilityEvaluatorTest.java
index 4eec071231d..8c496ac1d16 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/VisibilityEvaluatorTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/elementvisibilityutil/VisibilityEvaluatorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -24,98 +24,106 @@
import java.util.regex.PatternSyntaxException;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
import static uk.gov.gchq.gaffer.commonutil.elementvisibilityutil.ElementVisibility.quote;
/**
* This test class is copied from org.apache.accumulo.core.security.VisibilityEvaluatorTest.
*/
-public class VisibilityEvaluatorTest {
+class VisibilityEvaluatorTest {
final VisibilityEvaluator ve = new VisibilityEvaluator(new Authorisations("one", "two", "three", "four"));
@Test
- public void testVisibilityEvaluator() throws VisibilityParseException {
+ void testVisibilityEvaluator() throws VisibilityParseException {
// test for empty vis
- assertTrue(ve.evaluate(new ElementVisibility(new byte[0])));
+ assertThat(ve.evaluate(new ElementVisibility(new byte[0]))).isTrue();
// test for and
- assertTrue(ve.evaluate(new ElementVisibility("one&two")), "'and' test");
+ assertThat(ve.evaluate(new ElementVisibility("one&two")))
+ .as("'and' test")
+ .isTrue();
// test for or
- assertTrue(ve.evaluate(new ElementVisibility("foor|four")), "'or' test");
+ assertThat(ve.evaluate(new ElementVisibility("foor|four")))
+ .as("'or' test")
+ .isTrue();
// test for and and or
- assertTrue(ve.evaluate(new ElementVisibility("(one&two)|(foo&bar)")), "'and' and 'or' test");
+ assertThat(ve.evaluate(new ElementVisibility("(one&two)|(foo&bar)")))
+ .as("'and' and 'or' test")
+ .isTrue();
}
@ParameterizedTest
@ValueSource(strings = {"one", "one|five", "five|one", "(one)", "(one&two)|(foo&bar)", "(one|foo)&three", "one|foo|bar",
"(one|foo)|bar", "((one|foo)|bar)&two"})
- public void testFalseNegatives(String marking) throws VisibilityParseException {
- assertTrue(ve.evaluate(new ElementVisibility(marking)), marking);
+ void testFalseNegatives(String marking) throws VisibilityParseException {
+ assertThat(ve.evaluate(new ElementVisibility(marking)))
+ .as(marking)
+ .isTrue();
}
@ParameterizedTest
@ValueSource(strings = {"five", "one&five", "five&one", "((one|foo)|bar)&goober"})
- public void testFalsePositives(String marking) throws VisibilityParseException {
- assertFalse(ve.evaluate(new ElementVisibility(marking)), marking);
+ void testFalsePositives(String marking) throws VisibilityParseException {
+ assertThat(ve.evaluate(new ElementVisibility(marking)))
+ .as(marking)
+ .isFalse();
}
@ParameterizedTest
@ValueSource(strings = {"one(five)", "(five)one", "(one)(two)", "a|(b(c))"})
- public void testMissingSeparatorsShouldThrowPSX(String marking) {
+ void testMissingSeparatorsShouldThrowPSX(String marking) {
assertThatExceptionOfType(PatternSyntaxException.class).isThrownBy(() -> new ElementVisibility(marking));
}
@ParameterizedTest
@ValueSource(strings = {"&(five)", "|(five)", "(five)&", "five|", "a|(b)&", "(&five)", "(five|)"})
- public void testUnexpectedSeparatorShouldThrowPSX(String marking) {
+ void testUnexpectedSeparatorShouldThrowPSX(String marking) {
assertThatExceptionOfType(PatternSyntaxException.class).isThrownBy(() -> new ElementVisibility(marking));
}
@ParameterizedTest
@ValueSource(strings = {"(", ")", "(a&b", "b|a)"})
- public void testMismatchedParenthesisShouldThrowPSX(String marking) {
+ void testMismatchedParenthesisShouldThrowPSX(String marking) {
assertThatExceptionOfType(PatternSyntaxException.class).isThrownBy(() -> new ElementVisibility(marking));
}
@Test
- public void testQuotedExpressions() throws VisibilityParseException {
+ void testQuotedExpressions() throws VisibilityParseException {
final Authorisations auths = new Authorisations("A#C", "A\"C", "A\\C", "AC");
- final VisibilityEvaluator ve = new VisibilityEvaluator(auths);
+ final VisibilityEvaluator visEv = new VisibilityEvaluator(auths);
- assertTrue(ve.evaluate(new ElementVisibility(quote("A#C") + "|" + quote("A?C"))));
+ assertThat(visEv.evaluate(new ElementVisibility(quote("A#C") + "|" + quote("A?C")))).isTrue();
- assertFalse(ve.evaluate(new ElementVisibility(quote("A#C") + "&B")));
+ assertThat(visEv.evaluate(new ElementVisibility(quote("A#C") + "&B"))).isFalse();
- assertTrue(ve.evaluate(new ElementVisibility(quote("A#C"))));
- assertTrue(ve.evaluate(new ElementVisibility("(" + quote("A#C") + ")")));
+ assertThat(visEv.evaluate(new ElementVisibility(quote("A#C")))).isTrue();
+ assertThat(visEv.evaluate(new ElementVisibility("(" + quote("A#C") + ")"))).isTrue();
}
@Test
- public void testQuote() {
- assertEquals("\"A#C\"", quote("A#C"));
- assertEquals("\"A\\\"C\"", quote("A\"C"));
- assertEquals("\"A\\\"\\\\C\"", quote("A\"\\C"));
- assertEquals("ACS", quote("ACS"));
- assertEquals("\"九\"", quote("九"));
- assertEquals("\"五十\"", quote("五十"));
+ void testQuote() {
+ assertThat(quote("A#C")).isEqualTo("\"A#C\"");
+ assertThat(quote("A\"C")).isEqualTo("\"A\\\"C\"");
+ assertThat(quote("A\"\\C")).isEqualTo("\"A\\\"\\\\C\"");
+ assertThat(quote("ACS")).isEqualTo("ACS");
+ assertThat(quote("九")).isEqualTo("\"九\"");
+ assertThat(quote("五十")).isEqualTo("\"五十\"");
}
@Test
- public void testNonAscii() throws VisibilityParseException {
- final VisibilityEvaluator ve = new VisibilityEvaluator(new Authorisations("五", "六", "八", "九", "五十"));
-
- assertTrue(ve.evaluate(new ElementVisibility(quote("五") + "|" + quote("四"))));
- assertFalse(ve.evaluate(new ElementVisibility(quote("五") + "&" + quote("四"))));
- assertTrue(ve.evaluate(new ElementVisibility(quote("五") + "&(" + quote("四") + "|" + quote("九") + ")")));
- assertTrue(ve.evaluate(new ElementVisibility("\"五\"&(\"四\"|\"五十\")")));
- assertFalse(ve.evaluate(new ElementVisibility(quote("五") + "&(" + quote("四") + "|" + quote("三") + ")")));
- assertFalse(ve.evaluate(new ElementVisibility("\"五\"&(\"四\"|\"三\")")));
+ void testNonAscii() throws VisibilityParseException {
+ final VisibilityEvaluator visEv = new VisibilityEvaluator(new Authorisations("五", "六", "八", "九", "五十"));
+
+ assertThat(visEv.evaluate(new ElementVisibility(quote("五") + "|" + quote("四")))).isTrue();
+ assertThat(visEv.evaluate(new ElementVisibility(quote("五") + "&" + quote("四")))).isFalse();
+ assertThat(visEv.evaluate(new ElementVisibility(quote("五") + "&(" + quote("四") + "|" + quote("九") + ")"))).isTrue();
+ assertThat(visEv.evaluate(new ElementVisibility("\"五\"&(\"四\"|\"五十\")"))).isTrue();
+ assertThat(visEv.evaluate(new ElementVisibility(quote("五") + "&(" + quote("四") + "|" + quote("三") + ")"))).isFalse();
+ assertThat(visEv.evaluate(new ElementVisibility("\"五\"&(\"四\"|\"三\")"))).isFalse();
}
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/AlwaysValidTrue.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/AlwaysValidTrueTest.java
similarity index 78%
rename from core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/AlwaysValidTrue.java
rename to core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/AlwaysValidTrueTest.java
index 128a54c9bdb..97d96e4e348 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/AlwaysValidTrue.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/AlwaysValidTrueTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2020 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,29 +22,29 @@
import uk.gov.gchq.koryphe.ValidationResult;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.assertj.core.api.Assertions.assertThat;
-public class AlwaysValidTrue {
+class AlwaysValidTrueTest {
@ParameterizedTest
@NullSource
@ValueSource(strings = {"test"})
- public void shouldReturnTrueForNullAndString(final String input) {
+ void shouldReturnTrueForNullAndString(final String input) {
final AlwaysValid validator = new AlwaysValid<>();
final boolean result = validator.validate(input);
- assertTrue(result);
+ assertThat(result).isTrue();
}
@ParameterizedTest
@NullSource
@ValueSource(strings = {"test"})
- public void shouldReturnValidationResultForNullAndString(final String input) {
+ void shouldReturnValidationResultForNullAndString(final String input) {
final AlwaysValid validator = new AlwaysValid<>();
final ValidationResult result = validator.validateWithValidationResult(input);
- assertTrue(result.isValid());
+ assertThat(result.isValid()).isTrue();
}
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/CachingIterableTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/CachingIterableTest.java
index 8f8bbe4386b..76c742463cf 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/CachingIterableTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/CachingIterableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2018-2023 Crown Copyright
+ * Copyright 2018-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,13 +37,13 @@
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
-public class CachingIterableTest {
+class CachingIterableTest {
private static final List SMALL_LIST = Arrays.asList(0, 1, 2, 3, 4);
private static final List LARGE_LIST = Arrays.asList(0, 1, 2, 3, 4, 5, 6, 7, 8, 9);
@Test
- public void shouldCacheSmallIterable(@Mock final Iterable mockIterable) {
+ void shouldCacheSmallIterable(@Mock final Iterable mockIterable) {
when(mockIterable.iterator())
.thenReturn(SMALL_LIST.iterator())
.thenReturn(SMALL_LIST.iterator());
@@ -57,7 +57,7 @@ public void shouldCacheSmallIterable(@Mock final Iterable mockIterable)
}
@Test
- public void shouldNotCacheALargeIterable(@Mock final Iterable mockIterable) {
+ void shouldNotCacheALargeIterable(@Mock final Iterable mockIterable) {
when(mockIterable.iterator())
.thenReturn(LARGE_LIST.iterator())
.thenReturn(LARGE_LIST.iterator());
@@ -71,14 +71,14 @@ public void shouldNotCacheALargeIterable(@Mock final Iterable mockItera
}
@Test
- public void shouldHandleNullIterable() {
+ void shouldHandleNullIterable() {
Iterable cachingIterable = new CachingIterable<>(null);
assertThat(cachingIterable).isEmpty();
}
@Test
- public void shouldCloseTheIterable() throws IOException {
+ void shouldCloseTheIterable() throws IOException {
@SuppressWarnings("unchecked")
final Iterable iterable = Mockito.mock(Iterable.class,
Mockito.withSettings().extraInterfaces(Closeable.class));
@@ -90,7 +90,7 @@ public void shouldCloseTheIterable() throws IOException {
}
@Test
- public void shouldCloseTheIterableWhenFullyCached() throws IOException {
+ void shouldCloseTheIterableWhenFullyCached() throws IOException {
@SuppressWarnings("unchecked")
final Iterable iterable = Mockito.mock(Iterable.class,
Mockito.withSettings().extraInterfaces(Closeable.class));
@@ -104,7 +104,7 @@ public void shouldCloseTheIterableWhenFullyCached() throws IOException {
@SuppressWarnings("unchecked")
@Test
- public void shouldHandleMultipleIterators() throws IOException {
+ void shouldHandleMultipleIterators() throws IOException {
// Given
final Iterable iterable = Mockito.mock(Iterable.class,
Mockito.withSettings().extraInterfaces(Closeable.class));
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/ConsumableBlockingQueueTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/ConsumableBlockingQueueTest.java
index d7c81a606d1..7a8f6bee7c0 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/ConsumableBlockingQueueTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/ConsumableBlockingQueueTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -21,28 +21,22 @@
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
+import java.util.NoSuchElementException;
import java.util.stream.Collectors;
-import java.util.stream.IntStream;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-public class ConsumableBlockingQueueTest {
+class ConsumableBlockingQueueTest {
@Test
- public void shouldConsumeResultsWhenIterating() {
+ void shouldConsumeResultsWhenIterating() throws InterruptedException {
// Given
final ConsumableBlockingQueue queue = new ConsumableBlockingQueue<>(5);
- IntStream.range(0, 4)
- .forEach(i -> {
- try {
- queue.put(i);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- });
+ for (int i = 0; i < 4; i++) {
+ queue.put(i);
+ }
// When
final List items = queue.stream().collect(Collectors.toList());
@@ -57,26 +51,25 @@ public void shouldConsumeResultsWhenIterating() {
}
@Test
- public void shouldBlockOnAdditionWhenQueueIsFull() throws InterruptedException {
+ void shouldBlockOnAdditionWhenQueueIsFull() throws InterruptedException {
// Given
final ConsumableBlockingQueue queue = new ConsumableBlockingQueue<>(5);
final boolean[] finishedAdding = new boolean[] {false};
new Thread(() -> {
- IntStream.range(0, 10)
- .forEach(i -> {
- try {
- queue.put(i);
- } catch (InterruptedException e) {
- throw new RuntimeException(e);
- }
- });
+ for (int i = 0; i < 10; i++) {
+ try {
+ queue.put(i);
+ } catch (InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
finishedAdding[0] = true;
}).start();
// Wait for some items to be added, but there isn't room for all of them
Thread.sleep(1000L);
- assertFalse(finishedAdding[0]);
+ assertThat(finishedAdding[0]).isFalse();
// Consume some results
final Iterator consumer = queue.iterator();
@@ -88,7 +81,7 @@ public void shouldBlockOnAdditionWhenQueueIsFull() throws InterruptedException {
// Now the queue has space some items should be added, but there still isn't room for all of them
Thread.sleep(1000L);
- assertFalse(finishedAdding[0]);
+ assertThat(finishedAdding[0]).isFalse();
// Consume some more results
for (int i = 0; i < 4; i++) {
@@ -98,7 +91,7 @@ public void shouldBlockOnAdditionWhenQueueIsFull() throws InterruptedException {
// Now the queue has space some items should be added and this time there is room for the rest of them
Thread.sleep(1000L);
- assertTrue(finishedAdding[0]);
+ assertThat(finishedAdding[0]).isTrue();
// Consume some rest of the results
while (consumer.hasNext()) {
@@ -110,11 +103,33 @@ public void shouldBlockOnAdditionWhenQueueIsFull() throws InterruptedException {
}
@Test
- public void shouldNotBlockWhenConsumingWhenQueueIsEmpty() {
+ void shouldNotBlockWhenConsumingWhenQueueIsEmpty() {
final ConsumableBlockingQueue queue = new ConsumableBlockingQueue<>(5);
final Iterator iterator = queue.iterator();
assertThat(iterator).isExhausted();
}
+
+ @Test
+ void shouldThrowExceptionWhenQueueIsEmpty() {
+ final ConsumableBlockingQueue queue = new ConsumableBlockingQueue<>(5);
+
+ final Iterator iterator = queue.iterator();
+
+ assertThatExceptionOfType(NoSuchElementException.class)
+ .isThrownBy(() -> iterator.next())
+ .withMessage("No more items");
+ }
+
+ @Test
+ void shouldReturnToString() throws InterruptedException {
+ final ConsumableBlockingQueue queue = new ConsumableBlockingQueue<>(5);
+
+ for (int i = 0; i < 4; i++) {
+ queue.put(i);
+ }
+
+ assertThat(queue).hasToString("ConsumableBlockingQueue[items={0,1,2,3}]");
+ }
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/EmptyIterableTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/EmptyIterableTest.java
index 80f03271365..f38352ff2b7 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/EmptyIterableTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/EmptyIterableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2015-2021 Crown Copyright
+ * Copyright 2015-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -20,10 +20,10 @@
import static org.assertj.core.api.Assertions.assertThat;
-public class EmptyIterableTest {
+class EmptyIterableTest {
@Test
- public void shouldBeEmpty() {
+ void shouldBeEmpty() {
final EmptyIterable iterable = new EmptyIterable<>();
assertThat(iterable).isEmpty();
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/LimitedInMemorySortedIterableTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/LimitedInMemorySortedIterableTest.java
index 00cc2fd759c..a5cd7b2c670 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/LimitedInMemorySortedIterableTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/LimitedInMemorySortedIterableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package uk.gov.gchq.gaffer.commonutil.iterable;
import com.google.common.collect.Lists;
@@ -26,11 +27,12 @@
import java.util.stream.IntStream;
import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
-public class LimitedInMemorySortedIterableTest {
+class LimitedInMemorySortedIterableTest {
@Test
- public void shouldLimitEntries() {
+ void shouldLimitEntries() {
final LimitedInMemorySortedIterable list = new LimitedInMemorySortedIterable(Comparator.naturalOrder(), 100);
final List expectedItems = new ArrayList<>();
IntStream.rangeClosed(1, 100).forEach(expectedItems::add);
@@ -43,7 +45,7 @@ public void shouldLimitEntries() {
}
@Test
- public void shouldLimitAndDeduplicateEntries() {
+ void shouldLimitAndDeduplicateEntries() {
final LimitedInMemorySortedIterable list = new LimitedInMemorySortedIterable(Comparator.naturalOrder(), 2, true);
list.add(1);
@@ -57,7 +59,7 @@ public void shouldLimitAndDeduplicateEntries() {
}
@Test
- public void shouldDeduplicateEntries() {
+ void shouldDeduplicateEntries() {
final LimitedInMemorySortedIterable list = new LimitedInMemorySortedIterable(Comparator.naturalOrder(), 100, true);
list.add(1);
@@ -67,7 +69,7 @@ public void shouldDeduplicateEntries() {
}
@Test
- public void shouldNotDeduplicateEntries() {
+ void shouldNotDeduplicateEntries() {
final LimitedInMemorySortedIterable list = new LimitedInMemorySortedIterable(Comparator.naturalOrder(), 100, false);
list.add(1);
@@ -77,7 +79,7 @@ public void shouldNotDeduplicateEntries() {
}
@Test
- public void shouldLimitAndNotDeduplicateEntries() {
+ void shouldLimitAndNotDeduplicateEntries() {
final LimitedInMemorySortedIterable list = new LimitedInMemorySortedIterable(Comparator.naturalOrder(), 4, false);
list.add(1);
@@ -89,7 +91,7 @@ public void shouldLimitAndNotDeduplicateEntries() {
}
@Test
- public void shouldAddAll() {
+ void shouldAddAll() {
final LimitedInMemorySortedIterable itr = new LimitedInMemorySortedIterable(Comparator.naturalOrder(), 100);
// When/Then
@@ -122,7 +124,7 @@ public void shouldAddAll() {
}
@Test
- public void shouldLimitEntriesOnAddAll() {
+ void shouldLimitEntriesOnAddAll() {
// Given
final LimitedInMemorySortedIterable itr = new LimitedInMemorySortedIterable(Comparator.naturalOrder(), 10);
@@ -156,7 +158,7 @@ public void shouldLimitEntriesOnAddAll() {
}
@Test
- public void shouldSortLargeNumberOfItems() {
+ void shouldSortLargeNumberOfItems() {
// Given
final int streamSize = 1000000;
final int resultLimit = 10000;
@@ -177,4 +179,27 @@ public void shouldSortLargeNumberOfItems() {
final List expected = Lists.newArrayList(list);
assertThat(sortedElements).isEqualTo(expected);
}
+
+ @Test
+ void shouldThrowExceptionWithNoComparator() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> new LimitedInMemorySortedIterable(null, 100))
+ .withMessage("Comparator is required");
+ }
+
+ @Test
+ void shouldThrowExceptionWithInvalidLimit() {
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> new LimitedInMemorySortedIterable(Comparator.naturalOrder(), 0))
+ .withMessage("Limit cannot be less than or equal to 0");
+ }
+
+ @Test
+ void shouldReturnToString() {
+ final LimitedInMemorySortedIterable list = new LimitedInMemorySortedIterable(Comparator.naturalOrder(), 1);
+ list.add(1);
+
+ assertThat(list)
+ .hasToString("LimitedInMemorySortedIterable[size=1,limit=1,deduplicate=false,backingMap={1=OneOrMore[deduplicate=false,singleItem=1]}]");
+ }
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/RepeatItemIterableTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/RepeatItemIterableTest.java
index b1d782f1436..1d283314947 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/RepeatItemIterableTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/RepeatItemIterableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -22,10 +22,10 @@
import static org.assertj.core.api.Assertions.assertThat;
-public class RepeatItemIterableTest {
+class RepeatItemIterableTest {
@Test
- public void shouldRepeatItem5Times() {
+ void shouldRepeatItem5Times() {
final String item = "item";
final long repeats = 5;
@@ -38,7 +38,7 @@ public void shouldRepeatItem5Times() {
@ParameterizedTest
@ValueSource(longs = {0, -1, -5})
- public void shouldRepeatItem0TimesWhenRepeatsIsEqualOrLessThanZero(long repeats) {
+ void shouldRepeatItem0TimesWhenRepeatsIsEqualOrLessThanZero(long repeats) {
final String item = "item";
final Iterable itr = new RepeatItemIterable<>(item, repeats);
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/StreamIterableTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/StreamIterableTest.java
index 7873fa784ed..aa688034042 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/StreamIterableTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/StreamIterableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2020 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -32,10 +32,10 @@
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
-public class StreamIterableTest {
+class StreamIterableTest {
@Test
- public void shouldDelegateIteratorToIterable(@Mock final StreamSupplier streamSupplier,
+ void shouldDelegateIteratorToIterable(@Mock final StreamSupplier streamSupplier,
@Mock final Stream stream,
@Mock final Iterator iterator) {
// Given
@@ -58,7 +58,7 @@ public void shouldDelegateIteratorToIterable(@Mock final StreamSupplier
}
@Test
- public void shouldDelegateCloseToStreamIterable(@Mock final StreamSupplier streamSupplier)
+ void shouldDelegateCloseToStreamIterable(@Mock final StreamSupplier streamSupplier)
throws IOException {
// Given
final StreamIterable streamIterable = new StreamIterable<>(streamSupplier);
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/StreamIteratorTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/StreamIteratorTest.java
index 757ad787bc5..2461e03d566 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/StreamIteratorTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/StreamIteratorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2020 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -26,10 +26,10 @@
import static org.mockito.Mockito.verify;
@ExtendWith(MockitoExtension.class)
-public class StreamIteratorTest {
+class StreamIteratorTest {
@Test
- public void shouldDelegateCloseToWrappedIterator(@Mock final Stream stream) {
+ void shouldDelegateCloseToWrappedIterator(@Mock final Stream stream) {
final StreamIterator streamIterator = new StreamIterator<>(stream);
streamIterator.close();
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/SuppliedIterableTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/SuppliedIterableTest.java
index 1abd6b59df8..2a8d729b4de 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/SuppliedIterableTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/SuppliedIterableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2020 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -37,10 +37,10 @@
import static org.mockito.Mockito.withSettings;
@ExtendWith(MockitoExtension.class)
-public class SuppliedIterableTest {
+class SuppliedIterableTest {
@Test
- public void shouldRequestNewIterableFromSupplierWhenIteratorInvoked(@Mock final Supplier> supplier) {
+ void shouldRequestNewIterableFromSupplierWhenIteratorInvoked(@Mock final Supplier> supplier) {
// Given
final Iterable iterable1 = Arrays.asList(1, 2, 3);
final Iterable iterable2 = Arrays.asList(4, 5, 6);
@@ -69,7 +69,7 @@ public void shouldRequestNewIterableFromSupplierWhenIteratorInvoked(@Mock final
@SuppressWarnings("unchecked")
@Test
- public void shouldCloseIterables(@Mock final Supplier> supplier) throws IOException {
+ void shouldCloseIterables(@Mock final Supplier> supplier) throws IOException {
// Given
final Iterable iterable1 = mock(Iterable.class, withSettings().extraInterfaces(Closeable.class));
final Iterable iterable2 = mock(Iterable.class, withSettings().extraInterfaces(Closeable.class));
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/TransformIterableTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/TransformIterableTest.java
index 608c07323b1..9e3f69c7cc3 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/TransformIterableTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/TransformIterableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2021 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -41,10 +41,10 @@
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
-public class TransformIterableTest {
+class TransformIterableTest {
@Test
- public void shouldCreateIteratorThatReturnsOnlyValidStrings(@Mock final Validator validator) {
+ void shouldCreateIteratorThatReturnsOnlyValidStrings(@Mock final Validator validator) {
// Given
final String item1 = "valid item 1";
final String item2 = "invalid item 2";
@@ -74,7 +74,7 @@ public void shouldCreateIteratorThatReturnsOnlyValidStrings(@Mock final Validato
}
@Test
- public void shouldThrowIAXExceptionWhenNextItemIsInvalidString(@Mock final Validator validator) {
+ void shouldThrowIAXExceptionWhenNextItemIsInvalidString(@Mock final Validator validator) {
// Given
final String item1 = "valid item 1";
final String item2 = "invalid item 2 invalid";
@@ -100,7 +100,7 @@ public void shouldThrowIAXExceptionWhenNextItemIsInvalidString(@Mock final Valid
}
@Test
- public void shouldThrowNoElementExceptionWhenNextCalledWhenNoNextString(@Mock final Validator validator) {
+ void shouldThrowNoElementExceptionWhenNextCalledWhenNoNextString(@Mock final Validator validator) {
// Given
final String item1 = "item 1";
final Iterable items = Arrays.asList(item1);
@@ -122,7 +122,7 @@ public void shouldThrowNoElementExceptionWhenNextCalledWhenNoNextString(@Mock fi
}
@Test
- public void shouldThrowExceptionIfRemoveCalled(@Mock final Validator validator) {
+ void shouldThrowExceptionIfRemoveCalled(@Mock final Validator validator) {
final String item1 = "item 1";
final String item2 = "item 2";
final Iterable items = Arrays.asList(item1, item2);
@@ -139,7 +139,7 @@ public void shouldThrowExceptionIfRemoveCalled(@Mock final Validator val
@SuppressWarnings("unchecked")
@Test
- public void shouldAutoCloseIterator() throws IOException {
+ void shouldAutoCloseIterator() throws IOException {
// Given
final boolean autoClose = true;
final Iterable items = mock(Iterable.class, Mockito.withSettings().extraInterfaces(Closeable.class));
@@ -164,7 +164,7 @@ public void shouldAutoCloseIterator() throws IOException {
@SuppressWarnings("unchecked")
@Test
- public void shouldNotAutoCloseIterator() throws IOException {
+ void shouldNotAutoCloseIterator() throws IOException {
// Given
final boolean autoClose = false;
final Iterable items = mock(Iterable.class, Mockito.withSettings().extraInterfaces(Closeable.class));
@@ -188,6 +188,31 @@ public void shouldNotAutoCloseIterator() throws IOException {
}
}
+ @Test
+ void shouldThrowExceptionWithNullInput() {
+ // Given
+ final boolean autoClose = true;
+ final AlwaysValid valid = new AlwaysValid<>();
+
+ // When/then
+ assertThatExceptionOfType(IllegalArgumentException.class)
+ .isThrownBy(() -> new TransformIterableImpl(null, valid, false, autoClose))
+ .withMessage("Input iterable is required");
+ }
+
+ @SuppressWarnings("unchecked")
+ @Test
+ void shouldGetValidator() {
+ // Given
+ final boolean autoClose = true;
+ final Iterable items = mock(Iterable.class, Mockito.withSettings().extraInterfaces(Closeable.class));
+
+ try (TransformIterable iterable = new TransformIterableImpl(items, new AlwaysValid<>(), false, autoClose)) {
+ // Then
+ assertThat(iterable.getValidator()).isInstanceOf(AlwaysValid.class);
+ }
+ }
+
private class TransformIterableImpl extends TransformIterable {
TransformIterableImpl(final Iterable input, final Validator validator) {
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/TransformOneToManyIterableTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/TransformOneToManyIterableTest.java
index e6bfc2db597..ce8673ce149 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/TransformOneToManyIterableTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/iterable/TransformOneToManyIterableTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2021 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -43,10 +43,10 @@
import static org.mockito.Mockito.when;
@ExtendWith(MockitoExtension.class)
-public class TransformOneToManyIterableTest {
+class TransformOneToManyIterableTest {
@Test
- public void shouldCreateIteratorThatReturnsOnlyValidStrings(@Mock final Validator validator) {
+ void shouldCreateIteratorThatReturnsOnlyValidStrings(@Mock final Validator validator) {
// Given
final String nullItem0 = null;
final String validItem1 = "item 1";
@@ -79,7 +79,7 @@ public void shouldCreateIteratorThatReturnsOnlyValidStrings(@Mock final Validato
}
@Test
- public void shouldCreateIteratorThatThrowsExceptionOnInvalidString(@Mock final Validator validator) {
+ void shouldCreateIteratorThatThrowsExceptionOnInvalidString(@Mock final Validator validator) {
// Given
final String item1 = "item 1";
final String item2 = "item 2a invalid,item 2b";
@@ -106,7 +106,7 @@ public void shouldCreateIteratorThatThrowsExceptionOnInvalidString(@Mock final V
}
@Test
- public void shouldThrowExceptionIfNextCalledWhenNoNextString(@Mock final Validator validator) {
+ void shouldThrowExceptionIfNextCalledWhenNoNextString(@Mock final Validator validator) {
// Given
final String item1 = "item 1";
final String items2A_B = "item 2a,item 2b";
@@ -132,7 +132,7 @@ public void shouldThrowExceptionIfNextCalledWhenNoNextString(@Mock final Validat
}
@Test
- public void shouldThrowExceptionIfRemoveCalled(@Mock final Validator validator) {
+ void shouldThrowExceptionIfRemoveCalled(@Mock final Validator validator) {
final String item1 = "item 1";
final String item2 = "item 2";
final Iterable items = Arrays.asList(item1, item2);
@@ -151,7 +151,7 @@ public void shouldThrowExceptionIfRemoveCalled(@Mock final Validator val
@SuppressWarnings("unchecked")
@Test
- public void shouldAutoCloseIterator() throws IOException {
+ void shouldAutoCloseIterator() throws IOException {
// Given
final boolean autoClose = true;
final Iterable items = mock(Iterable.class, Mockito.withSettings().extraInterfaces(Closeable.class));
@@ -176,7 +176,7 @@ public void shouldAutoCloseIterator() throws IOException {
@SuppressWarnings("unchecked")
@Test
- public void shouldNotAutoCloseIterator() throws IOException {
+ void shouldNotAutoCloseIterator() throws IOException {
// Given
final boolean autoClose = false;
final Iterable items = mock(Iterable.class, Mockito.withSettings().extraInterfaces(Closeable.class));
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/pair/PairTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/pair/PairTest.java
index 04e87d70ee8..04637ef1d48 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/pair/PairTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/pair/PairTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2020 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,48 +13,55 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package uk.gov.gchq.gaffer.commonutil.pair;
import org.junit.jupiter.api.Test;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.assertj.core.api.Assertions.assertThat;
-public class PairTest {
+class PairTest {
@Test
- public void shouldCreateMutablePair() {
+ void shouldCreateMutablePair() {
final Pair pair = new Pair<>(0, "foo");
- assertEquals(0, pair.getFirst().intValue());
- assertEquals("foo", pair.getSecond());
+ assertThat(pair.getFirst().intValue()).isZero();
+ assertThat(pair.getSecond()).isEqualTo("foo");
}
@Test
- public void shouldCreateMutablePair2() {
+ void shouldCreateMutablePair2() {
final Pair pair = new Pair<>(null, "bar");
- assertNull(pair.getFirst());
- assertEquals("bar", pair.getSecond());
+ assertThat(pair.getFirst()).isNull();
+ assertThat(pair.getSecond()).isEqualTo("bar");
}
@Test
- public void shouldBeAbleToMutateFirstInPair() {
+ void shouldBeAbleToMutateFirstInPair() {
final Pair pair = new Pair<>(0);
pair.setFirst(1);
- assertEquals(1, pair.getFirst().intValue());
- assertNull(pair.getSecond());
+ assertThat(pair.getFirst().intValue()).isEqualTo(1);
+ assertThat(pair.getSecond()).isNull();
}
@Test
- public void shouldBeAbleToMutateSecondInPair() {
+ void shouldBeAbleToMutateSecondInPair() {
final Pair pair = new Pair<>();
pair.setSecond("2nd");
- assertNull(pair.getFirst());
- assertEquals("2nd", pair.getSecond());
+ assertThat(pair.getFirst()).isNull();
+ assertThat(pair.getSecond()).isEqualTo("2nd");
+ }
+
+ @Test
+ void shouldReturnToString() {
+ final Pair pair = new Pair<>("foo", "bar");
+
+ assertThat(pair).hasToString("Pair[first=foo,second=bar]");
}
}
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/stream/GafferCollectorTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/stream/GafferCollectorTest.java
index 1cf3335da98..a0056e75e70 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/stream/GafferCollectorTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/stream/GafferCollectorTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2017-2021 Crown Copyright
+ * Copyright 2017-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -13,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
package uk.gov.gchq.gaffer.commonutil.stream;
import org.junit.jupiter.api.Test;
@@ -26,10 +27,10 @@
import static uk.gov.gchq.gaffer.commonutil.stream.GafferCollectors.toLimitedInMemorySortedIterable;
import static uk.gov.gchq.gaffer.commonutil.stream.GafferCollectors.toLinkedHashSet;
-public class GafferCollectorTest {
+class GafferCollectorTest {
@Test
- public void shouldCollectToLinkedHashSet() {
+ void shouldCollectToLinkedHashSet() {
final IntStream stream = IntStream.range(0, 100);
final Iterable iterable = stream.boxed()
@@ -41,7 +42,7 @@ public void shouldCollectToLinkedHashSet() {
}
@Test
- public void shouldCollectToLimitedSortedSet() {
+ void shouldCollectToLimitedSortedSet() {
final IntStream stream = IntStream.range(0, 100);
final int limit = 50;
final boolean deduplicate = true;
diff --git a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/stream/StreamsTest.java b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/stream/StreamsTest.java
index 1b993cf1199..ac87d0f3424 100644
--- a/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/stream/StreamsTest.java
+++ b/core/common-util/src/test/java/uk/gov/gchq/gaffer/commonutil/stream/StreamsTest.java
@@ -1,5 +1,5 @@
/*
- * Copyright 2016-2020 Crown Copyright
+ * Copyright 2016-2024 Crown Copyright
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -29,11 +29,11 @@
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
-public class StreamsTest {
+class StreamsTest {
@SuppressWarnings("unchecked")
@Test
- public void shouldCloseIteratorWhenStreamIsClosed() throws Throwable {
+ void shouldCloseIteratorWhenStreamIsClosed() throws Throwable {
// Given
final Iterator iterator = mock(Iterator.class, Mockito.withSettings().extraInterfaces(Closeable.class));
given(iterator.hasNext()).willReturn(true, false);
@@ -53,7 +53,7 @@ public void shouldCloseIteratorWhenStreamIsClosed() throws Throwable {
@SuppressWarnings("unchecked")
@Test
- public void shouldCloseIterableWhenStreamIsClosed() throws Throwable {
+ void shouldCloseIterableWhenStreamIsClosed() throws Throwable {
// Given
final Iterable iterable = mock(Iterable.class, Mockito.withSettings().extraInterfaces(Closeable.class));
final Iterator iterator = mock(Iterator.class, Mockito.withSettings().extraInterfaces(Closeable.class));
@@ -76,7 +76,7 @@ public void shouldCloseIterableWhenStreamIsClosed() throws Throwable {
@SuppressWarnings("unchecked")
@Test
- public void shouldCloseIteratorWhenParallelStreamIsClosed() throws Throwable {
+ void shouldCloseIteratorWhenParallelStreamIsClosed() throws Throwable {
// Given
final Iterator iterator = mock(Iterator.class, Mockito.withSettings().extraInterfaces(Closeable.class));
given(iterator.hasNext()).willReturn(true, false);
@@ -96,7 +96,7 @@ public void shouldCloseIteratorWhenParallelStreamIsClosed() throws Throwable {
@SuppressWarnings("unchecked")
@Test
- public void shouldCloseIterableWhenParallelStreamIsClosed() throws Throwable {
+ void shouldCloseIterableWhenParallelStreamIsClosed() throws Throwable {
// Given
final Iterable iterable = mock(Iterable.class, Mockito.withSettings().extraInterfaces(Closeable.class));
final Iterator iterator = mock(Iterator.class, Mockito.withSettings().extraInterfaces(Closeable.class));
diff --git a/core/graph/src/test/java/uk/gov/gchq/gaffer/graph/hook/NamedOperationResolverTest.java b/core/graph/src/test/java/uk/gov/gchq/gaffer/graph/hook/NamedOperationResolverTest.java
index 866e8c945b4..52ab591687a 100644
--- a/core/graph/src/test/java/uk/gov/gchq/gaffer/graph/hook/NamedOperationResolverTest.java
+++ b/core/graph/src/test/java/uk/gov/gchq/gaffer/graph/hook/NamedOperationResolverTest.java
@@ -16,6 +16,8 @@
package uk.gov.gchq.gaffer.graph.hook;
+import org.json.JSONArray;
+import org.json.JSONObject;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
@@ -29,7 +31,6 @@
import uk.gov.gchq.gaffer.named.operation.ParameterDetail;
import uk.gov.gchq.gaffer.operation.Operation;
import uk.gov.gchq.gaffer.operation.OperationChain;
-import uk.gov.gchq.gaffer.operation.OperationException;
import uk.gov.gchq.gaffer.operation.impl.Limit;
import uk.gov.gchq.gaffer.operation.impl.get.GetAdjacentIds;
import uk.gov.gchq.gaffer.operation.impl.get.GetAllElements;
@@ -54,6 +55,7 @@
class NamedOperationResolverTest extends GraphHookTest {
static final String SUFFIX_CACHE_NAME = "suffix";
+ static final String OP_NAME = "opName";
NamedOperationResolverTest() {
super(NamedOperationResolver.class);
@@ -62,6 +64,7 @@ class NamedOperationResolverTest extends GraphHookTest {
@SuppressWarnings({"unchecked", "rawtypes"})
@Test
void shouldResolveNamedOperation(@Mock final User user,
+ @Mock final Context context,
@Mock final NamedOperationCache cache,
@Mock final NamedOperationDetail namedOpDetail,
@Mock final GetAdjacentIds op1,
@@ -69,24 +72,24 @@ void shouldResolveNamedOperation(@Mock final User user,
@Mock final Iterable extends EntityId> input)
throws CacheOperationException {
// Given
- final String opName = "opName";
final NamedOperationResolver resolver = new NamedOperationResolver(cache);
final OperationChain> testChain = new OperationChain(Arrays.asList(op1, op2));
final List expectedResolvedChain = Arrays.asList(testChain);
given(op1.getInput()).willReturn(null);
- given(cache.getNamedOperation(opName, user)).willReturn(namedOpDetail);
+ given(cache.getNamedOperation(OP_NAME, user)).willReturn(namedOpDetail);
given(namedOpDetail.getOperationChain(null)).willReturn(testChain);
+ given(context.getUser()).willReturn(user);
final OperationChain opChain = new OperationChain.Builder()
.first(new NamedOperation.Builder<>()
- .name(opName)
+ .name(OP_NAME)
.input(input)
.build())
.build();
// When
- resolver.preExecute(opChain, new Context(user));
+ resolver.preExecute(opChain, context);
// Then
assertThat(opChain.getOperations()).isEqualTo(expectedResolvedChain);
@@ -98,14 +101,14 @@ void shouldResolveNamedOperation(@Mock final User user,
@SuppressWarnings({"unchecked", "rawtypes"})
@Test
void shouldResolveNestedNamedOperation(@Mock final User user,
+ @Mock final Context context,
@Mock final NamedOperationCache cache,
@Mock final NamedOperationDetail namedOpDetail,
@Mock final GetAdjacentIds op1,
@Mock final GetElements op2,
@Mock final Iterable extends EntityId> input)
- throws OperationException, CacheOperationException {
+ throws CacheOperationException {
// Given
- final String opName = "opName";
final NamedOperationResolver resolver = new NamedOperationResolver(cache);
final OperationChain namedOperationOpChain = new OperationChain(Arrays.asList(op1, op2));
@@ -113,20 +116,21 @@ void shouldResolveNestedNamedOperation(@Mock final User user,
final Map params = null;
given(op1.getInput()).willReturn(null);
- given(cache.getNamedOperation(opName, user)).willReturn(namedOpDetail);
+ given(cache.getNamedOperation(OP_NAME, user)).willReturn(namedOpDetail);
given(namedOpDetail.getOperationChain(params)).willReturn(namedOperationOpChain);
+ given(context.getUser()).willReturn(user);
final OperationChain opChain = new OperationChain.Builder()
.first(new OperationChain.Builder()
.first(new NamedOperation.Builder<>()
- .name(opName)
+ .name(OP_NAME)
.input(input)
.build())
.build())
.build();
// When
- resolver.preExecute(opChain, new Context(user));
+ resolver.preExecute(opChain, context);
// Then
assertThat(opChain.getOperations()).hasSize(1);
@@ -141,6 +145,7 @@ void shouldResolveNestedNamedOperation(@Mock final User user,
@Test
void shouldFailToResolveNestedNamedOperationsOverDefaultLimit(
@Mock final User user,
+ @Mock final Context context,
@Mock final NamedOperationCache cache,
@Mock final NamedOperationDetail namedOp1Detail,
@Mock final NamedOperationDetail namedOp2Detail,
@@ -156,6 +161,7 @@ void shouldFailToResolveNestedNamedOperationsOverDefaultLimit(
final String namedOp4Name = "namedOp4";
final NamedOperationResolver resolver = new NamedOperationResolver(cache);
+ given(context.getUser()).willReturn(user);
// Setup cache returns (we can ignore named op 4 as it wont be used due to the depth limit)
given(cache.getNamedOperation(namedOp1Name, user)).willReturn(namedOp1Detail);
given(cache.getNamedOperation(namedOp2Name, user)).willReturn(namedOp2Detail);
@@ -201,7 +207,7 @@ void shouldFailToResolveNestedNamedOperationsOverDefaultLimit(
.build();
// When
assertThatExceptionOfType(GafferRuntimeException.class)
- .isThrownBy(() -> resolver.preExecute(opChain, new Context(user)))
+ .isThrownBy(() -> resolver.preExecute(opChain, context))
.withMessageContaining("NamedOperation Resolver hit nested depth limit");
}
@@ -209,6 +215,7 @@ void shouldFailToResolveNestedNamedOperationsOverDefaultLimit(
@Test
void shouldAllowConfigurableResolverDepthLimit(
@Mock final User user,
+ @Mock final Context context,
@Mock final NamedOperationCache cache,
@Mock final NamedOperationDetail namedOp1Detail,
@Mock final NamedOperationDetail namedOp2Detail,
@@ -223,6 +230,7 @@ void shouldAllowConfigurableResolverDepthLimit(
final String namedOp3Name = "namedOp3";
// Make a resolver with a stricter depth limit
final NamedOperationResolver resolverStrict = new NamedOperationResolver(cache, 2);
+ given(context.getUser()).willReturn(user);
// Setup cache returns
given(cache.getNamedOperation(namedOp1Name, user)).willReturn(namedOp1Detail);
@@ -261,40 +269,40 @@ void shouldAllowConfigurableResolverDepthLimit(
// When resolved using the stricter limit it should fail to resolve the chain
assertThatExceptionOfType(GafferRuntimeException.class)
- .isThrownBy(() -> resolverStrict.preExecute(opChainStrict, new Context(user)))
+ .isThrownBy(() -> resolverStrict.preExecute(opChainStrict, context))
.withMessageContaining("NamedOperation Resolver hit nested depth limit");
}
-
@SuppressWarnings({"unchecked", "rawtypes"})
@Test
void shouldExecuteNamedOperationWithoutOverridingInput(@Mock final User user,
+ @Mock final Context context,
@Mock final NamedOperationCache cache,
@Mock final NamedOperationDetail namedOpDetail,
@Mock final GetAdjacentIds op1,
@Mock final GetElements op2,
@Mock final Iterable extends EntityId> input,
@Mock final Iterable mockIterable)
- throws OperationException, CacheOperationException {
+ throws CacheOperationException {
// Given
- final String opName = "opName";
final NamedOperationResolver resolver = new NamedOperationResolver(cache);
final OperationChain namedOpChain = new OperationChain(Arrays.asList(op1, op2));
final Map params = null;
given(op1.getInput()).willReturn(mockIterable);
- given(cache.getNamedOperation(opName, user)).willReturn(namedOpDetail);
+ given(cache.getNamedOperation(OP_NAME, user)).willReturn(namedOpDetail);
given(namedOpDetail.getOperationChain(params)).willReturn(namedOpChain);
+ given(context.getUser()).willReturn(user);
// When
final OperationChain opChain = new OperationChain.Builder()
.first(new NamedOperation.Builder<>()
- .name(opName)
+ .name(OP_NAME)
.input(input)
.build())
.build();
- resolver.preExecute(opChain, new Context(user));
+ resolver.preExecute(opChain, context);
// Then
assertThat(opChain.getOperations().get(0)).isSameAs(namedOpChain);
@@ -305,173 +313,128 @@ void shouldExecuteNamedOperationWithoutOverridingInput(@Mock final User user,
@SuppressWarnings({ "rawtypes" })
@Test
void shouldResolveNamedOperationWithParameter(@Mock final User user,
- @Mock final NamedOperationCache cache)
- throws OperationException, CacheOperationException {
+ @Mock final Context context,
+ @Mock final NamedOperationCache cache)
+ throws CacheOperationException {
// Given
- final String opName = "opName";
final NamedOperationResolver resolver = new NamedOperationResolver(cache);
final Map paramMap = new HashMap<>();
paramMap.put("param1", 1L);
- final ParameterDetail param = new ParameterDetail.Builder()
- .defaultValue(1L)
- .description("Limit param")
- .valueClass(Long.class)
- .build();
- final Map paramDetailMap = new HashMap<>();
- paramDetailMap.put("param1", param);
-
// Make a real NamedOperationDetail with a parameter
- final NamedOperationDetail namedOpDetail = new NamedOperationDetail.Builder()
- .operationName(opName)
- .description("standard operation")
- .operationChain("{ \"operations\": [ { \"class\":\"uk.gov.gchq.gaffer.operation.impl.get.GetAllElements\" }, "
- + "{ \"class\":\"uk.gov.gchq.gaffer.operation.impl.Limit\", \"resultLimit\": \"${param1}\" } ] }")
- .parameters(paramDetailMap)
- .build();
+ final NamedOperationDetail namedOpDetail = getValidNamedOperation();
- given(cache.getNamedOperation(opName, user)).willReturn(namedOpDetail);
+ given(cache.getNamedOperation(OP_NAME, user)).willReturn(namedOpDetail);
+ given(context.getUser()).willReturn(user);
final OperationChain opChain = new OperationChain.Builder()
.first(new NamedOperation.Builder<>()
- .name(opName)
+ .name(OP_NAME)
.parameters(paramMap)
.build())
.build();
// When
- resolver.preExecute(opChain, new Context(user));
+ resolver.preExecute(opChain, context);
// Then
- assertThat(opChain.getOperations().get(0))
- .isInstanceOf(OperationChain.class);
+ assertThat(opChain.getOperations().get(0)).isInstanceOf(OperationChain.class);
assertThat(((OperationChain) opChain.getOperations().get(0)).getOperations().get(0)).isInstanceOf(GetAllElements.class);
assertThat(((OperationChain) opChain.getOperations().get(0)).getOperations().get(1)).isInstanceOf(Limit.class);
// Check the parameter has been inserted
- assertThat(((Limit>) ((OperationChain) opChain.getOperations().get(0)).getOperations().get(1)).getResultLimit()).isEqualTo(1L);
+ assertThat(((Limit>) ((OperationChain) opChain.getOperations().get(0)).getOperations().get(1)).getResultLimit()).isEqualTo(1);
}
@Test
void shouldNotExecuteNamedOperationWithParameterOfWrongType(@Mock final User user,
+ @Mock final Context context,
@Mock final NamedOperationCache cache)
- throws OperationException, CacheOperationException {
+ throws CacheOperationException {
// Given
- final String opName = "opName";
final NamedOperationResolver resolver = new NamedOperationResolver(cache);
+
+ // Create Named Op with param of wrong type
final Map paramMap = new HashMap<>();
- // A parameter of the wrong type
paramMap.put("param1", new ArrayList<>());
- final ParameterDetail param = new ParameterDetail.Builder()
- .defaultValue(1L)
- .description("Limit param")
- .valueClass(Long.class)
- .build();
- final Map paramDetailMap = new HashMap<>();
- paramDetailMap.put("param1", param);
+ final OperationChain wrongParamTypeNamedOp = new OperationChain.Builder()
+ .first(new NamedOperation.Builder<>()
+ .name(OP_NAME)
+ .parameters(paramMap)
+ .build())
+ .build();
// Make a real NamedOperationDetail with a parameter
- final NamedOperationDetail namedOpDetail = new NamedOperationDetail.Builder()
- .operationName(opName)
- .description("standard operation")
- .operationChain("{ \"operations\": [ { \"class\":\"uk.gov.gchq.gaffer.operation.impl.get.GetAllElements\" }, "
- + "{ \"class\":\"uk.gov.gchq.gaffer.operation.impl.Limit\", \"resultLimit\": \"${param1}\" } ] }")
- .parameters(paramDetailMap)
- .build();
+ final NamedOperationDetail namedOpDetail = getValidNamedOperation();
- given(cache.getNamedOperation(opName, user)).willReturn(namedOpDetail);
+ given(cache.getNamedOperation(OP_NAME, user)).willReturn(namedOpDetail);
+ given(context.getUser()).willReturn(user);
// When
assertThatExceptionOfType(IllegalArgumentException.class)
- .isThrownBy(() -> resolver.preExecute(new OperationChain.Builder()
- .first(new NamedOperation.Builder<>()
- .name(opName)
- .parameters(paramMap)
- .build())
- .build(), new Context(user)))
+ .isThrownBy(() -> resolver.preExecute(wrongParamTypeNamedOp, context))
.withMessageContaining("Cannot deserialize value of type");
}
@Test
void shouldNotExecuteNamedOperationWithWrongParameterName(@Mock final User user,
+ @Mock final Context context,
@Mock final NamedOperationCache cache)
- throws OperationException, CacheOperationException {
+ throws CacheOperationException {
// Given
- final String opName = "opName";
final NamedOperationResolver resolver = new NamedOperationResolver(cache);
- final Map paramMap = new HashMap<>();
+
// A parameter with the wrong name
+ final Map paramMap = new HashMap<>();
paramMap.put("param2", 1L);
- final ParameterDetail param = new ParameterDetail.Builder()
- .defaultValue(1L)
- .description("Limit param")
- .valueClass(Long.class)
- .build();
- final Map paramDetailMap = new HashMap<>();
- paramDetailMap.put("param1", param);
+ final OperationChain wrongParamNameNamedOp = new OperationChain.Builder()
+ .first(new NamedOperation.Builder<>()
+ .name(OP_NAME)
+ .parameters(paramMap)
+ .build())
+ .build();
// Make a real NamedOperationDetail with a parameter
- final NamedOperationDetail namedOpDetail = new NamedOperationDetail.Builder()
- .operationName(opName)
- .description("standard operation")
- .operationChain("{ \"operations\": [ { \"class\":\"uk.gov.gchq.gaffer.operation.impl.get.GetAllElements\" }, "
- + "{ \"class\":\"uk.gov.gchq.gaffer.operation.impl.Limit\", \"resultLimit\": \"${param1}\" } ] }")
- .parameters(paramDetailMap)
- .build();
+ final NamedOperationDetail namedOpDetail = getValidNamedOperation();
- given(cache.getNamedOperation(opName, user)).willReturn(namedOpDetail);
+ given(cache.getNamedOperation(OP_NAME, user)).willReturn(namedOpDetail);
+ given(context.getUser()).willReturn(user);
// When
assertThatExceptionOfType(IllegalArgumentException.class)
- .isThrownBy(() -> resolver.preExecute(new OperationChain.Builder()
- .first(new NamedOperation.Builder<>()
- .name(opName)
- .parameters(paramMap)
- .build())
- .build(), new Context(user)))
+ .isThrownBy(() -> resolver.preExecute(wrongParamNameNamedOp, context))
.withMessageContaining("Unexpected parameter name in NamedOperation");
}
@Test
void shouldNotExecuteNamedOperationWithMissingRequiredArg(@Mock final User user,
+ @Mock final Context context,
@Mock final NamedOperationCache cache)
- throws OperationException, CacheOperationException {
+ throws CacheOperationException {
// Given
- final String opName = "opName";
final NamedOperationResolver resolver = new NamedOperationResolver(cache);
// Don't set any parameters
final Map paramMap = new HashMap<>();
- final ParameterDetail param = new ParameterDetail.Builder()
- .description("Limit param")
- .valueClass(Long.class)
- .required(true)
- .build();
- final Map paramDetailMap = new HashMap<>();
- paramDetailMap.put("param1", param);
+ final OperationChain