From 4dabf241b3df55c1749fb0414b1fcd5274f3cd7b Mon Sep 17 00:00:00 2001 From: Gregory Woods Date: Fri, 23 Oct 2020 14:09:27 +0100 Subject: [PATCH] Refactor Testkit-backend to use jackson for databinding The objective of this commit is to support the current enabled tests on the Java Testkit Suite in more extensible and maintainable structure. The commands were separated in different objects which are mapped by Jackson and ran by the CommandProcessor. Jackson is also responsible for serializing the response using custom serializers to map values and records to CypherValues, structure used by teskit to return the values with the type information. --- testkit-backend/pom.xml | 20 +- .../src/main/java/CommandProcessor.java | 347 ------------------ .../src/main/java/TestkitTypes.java | 123 ------- .../org/testkit/backend/CommandProcessor.java | 216 +++++++++++ .../org/testkit/backend}/Runner.java | 39 +- .../org/testkit/backend}/SessionState.java | 6 + .../org/testkit/backend/TestkitState.java | 56 +++ .../TestkitCypherTypeDeserializer.java | 122 ++++++ .../backend/messages/TestkitModule.java | 44 +++ .../messages/requests/AuthorizationToken.java | 38 ++ .../messages/requests/DriverClose.java | 49 +++ .../backend/messages/requests/NewDriver.java | 76 ++++ .../backend/messages/requests/NewSession.java | 77 ++++ .../backend/messages/requests/ResultNext.java | 61 +++ .../messages/requests/RetryableNegative.java | 56 +++ .../messages/requests/RetryablePositive.java | 54 +++ .../requests/SessionBeginTransaction.java | 73 ++++ .../messages/requests/SessionClose.java | 49 +++ .../requests/SessionReadTransaction.java | 110 ++++++ .../backend/messages/requests/SessionRun.java | 70 ++++ .../messages/requests/TestkitRequest.java | 38 ++ .../messages/requests/TransactionCommit.java | 61 +++ .../messages/requests/TransactionRun.java | 64 ++++ .../backend/messages/responses/Driver.java} | 39 +- .../messages/responses/DriverError.java | 45 +++ .../messages/responses/NullRecord.java | 35 ++ .../backend/messages/responses/Record.java | 45 +++ .../backend/messages/responses/Result.java | 45 +++ .../messages/responses/RetryableDone.java | 35 ++ .../messages/responses/RetryableTry.java | 45 +++ .../backend/messages/responses/Session.java | 45 +++ .../responses/TeskitBackendErrorResponse.java | 35 ++ .../responses/TestkitErrorResponse.java | 39 ++ .../messages/responses/TestkitResponse.java | 27 ++ .../messages/responses/Transaction.java | 45 +++ .../responses/serializer/GenUtils.java | 67 ++++ .../TestkitListValueSerializer.java | 49 +++ .../serializer/TestkitMapValueSerializer.java | 57 +++ .../TestkitNodeValueSerializer.java | 62 ++++ .../serializer/TestkitRecordSerializer.java | 44 +++ .../serializer/TestkitValueSerializer.java | 56 +++ .../backend/MessageDeserializerTest.java | 96 +++++ .../backend/MessageSerializerTest.java | 55 +++ 43 files changed, 2315 insertions(+), 500 deletions(-) delete mode 100644 testkit-backend/src/main/java/CommandProcessor.java delete mode 100644 testkit-backend/src/main/java/TestkitTypes.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java rename testkit-backend/src/main/java/{ => neo4j/org/testkit/backend}/Runner.java (67%) rename testkit-backend/src/main/java/{ => neo4j/org/testkit/backend}/SessionState.java (91%) create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitCypherTypeDeserializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/AuthorizationToken.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DriverClose.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewSession.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultNext.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryableNegative.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryablePositive.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionBeginTransaction.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionClose.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionRun.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionCommit.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionRun.java rename testkit-backend/src/main/java/{Testkit.java => neo4j/org/testkit/backend/messages/responses/Driver.java} (58%) create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DriverError.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/NullRecord.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Record.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Result.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableDone.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableTry.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Session.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TeskitBackendErrorResponse.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitErrorResponse.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitResponse.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Transaction.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitListValueSerializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitMapValueSerializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitNodeValueSerializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitRecordSerializer.java create mode 100644 testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java create mode 100644 testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageDeserializerTest.java create mode 100644 testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageSerializerTest.java diff --git a/testkit-backend/pom.xml b/testkit-backend/pom.xml index 28d40371fe..df538d1e08 100644 --- a/testkit-backend/pom.xml +++ b/testkit-backend/pom.xml @@ -32,6 +32,24 @@ com.fasterxml.jackson.core jackson-databind + + org.projectlombok + lombok + 1.18.12 + provided + + + + + org.hamcrest + hamcrest-junit + test + + + org.junit.jupiter + junit-jupiter + test + @@ -49,7 +67,7 @@ - Runner + neo4j.org.testkit.backend.Runner testkit-backend diff --git a/testkit-backend/src/main/java/CommandProcessor.java b/testkit-backend/src/main/java/CommandProcessor.java deleted file mode 100644 index a594d1e764..0000000000 --- a/testkit-backend/src/main/java/CommandProcessor.java +++ /dev/null @@ -1,347 +0,0 @@ -/* - * Copyright (c) 2002-2020 "Neo4j," - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * 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. - */ -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; - -import java.io.BufferedReader; -import java.io.BufferedWriter; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; - -import org.neo4j.driver.AccessMode; -import org.neo4j.driver.AuthTokens; -import org.neo4j.driver.AuthToken; -import org.neo4j.driver.Bookmark; -import org.neo4j.driver.Driver; -import org.neo4j.driver.GraphDatabase; -import org.neo4j.driver.Query; -import org.neo4j.driver.Record; -import org.neo4j.driver.Result; -import org.neo4j.driver.Session; -import org.neo4j.driver.SessionConfig; -import org.neo4j.driver.Transaction; -import org.neo4j.driver.TransactionWork; -import org.neo4j.driver.exceptions.Neo4jException; -import org.neo4j.driver.exceptions.ClientException; -import org.neo4j.driver.internal.InternalBookmark; - -public class CommandProcessor -{ - private final Map drivers = new HashMap<>(); - private final Map sessionStates = new HashMap<>(); - private final Map results = new HashMap<>(); - private final Map transactions = new HashMap<>(); - private final Map errors = new HashMap<>(); - - private int idGenerator = 0; - - private final ObjectMapper objectMapper = new ObjectMapper(); - - private final BufferedReader in; - private final BufferedWriter out; - - public CommandProcessor(BufferedReader in, BufferedWriter out) { - this.in = in; - this.out = out; - } - - private String readLine() { - try { - return this.in.readLine(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - private void write(String s) { - try { - this.out.write(s); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - // Logs to frontend - private void log(String s) { - try { - this.out.write(s + "\n"); - this.out.flush(); - } catch (IOException e) { } - System.out.println(s); - } - - private void flush() { - try { - this.out.flush(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } - - // Reads one request and writes the response. Returns false when not able to read anymore. - public boolean process() { - boolean inRequest = false; - StringBuilder request = new StringBuilder(); - - log("Waiting for request"); - - while (true) { - String currentLine = readLine(); - // End of stream - if ( currentLine == null) { - return false; - } - - if ( currentLine.equals( "#request begin" )) - { - inRequest = true; - } else if (currentLine.equals( "#request end" )) - { - if ( !inRequest ) - { - throw new RuntimeException( "Request end not expected" ); - } - try - { - processRequest( request.toString()); - } catch ( Exception e ){ - if (e instanceof Neo4jException) { - // Error to track - String id = newId(); - errors.put(id, (Neo4jException)e); - writeResponse(Testkit.wrap("DriverError", Testkit.id(id))); - System.out.println("Neo4jException: " + e); - } else { - // Unknown error, interpret this as a backend error. - // Report to frontend and rethrow, note that if socket been - // closed the writing will throw itself... - writeResponse(Testkit.wrap("BackendError", Testkit.msg(e.toString()))); - // This won't print if there was an IO exception since line above will rethrow - e.printStackTrace(); - throw e; - } - } - return true; - } else - { - if ( !inRequest ) - { - throw new RuntimeException( "Command Received whilst not in request"); - } - request.append( currentLine ); - } - } - } - - public void processRequest( String request) - { - System.out.println( "request = " + request + ", in = " + in + ", out = " + out ); - - JsonNode jsonRequest; - try { - jsonRequest = objectMapper.readTree( request ); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - - String requestType = jsonRequest.get( "name" ).asText(); - JsonNode requestData = jsonRequest.get("data"); - log("Received request: " + requestType); - - if (requestType.equals( "NewDriver" )) - { - String id = newId(); - String response = Testkit.wrap("Driver", Testkit.id(id)); - - String uri = requestData.get("uri").asText(); - JsonNode requestAuth = requestData.get("authorizationToken").get("data"); - AuthToken authToken; - switch (requestAuth.get("scheme").asText()) { - case "basic": - authToken = AuthTokens.basic(requestAuth.get("principal").asText(), requestAuth.get("credentials").asText(), requestAuth.get("realm").asText()); - break; - default: - writeResponse(Testkit.wrap("BackendError", Testkit.msg("Unsupported auth scheme"))); - return; - } - - drivers.putIfAbsent( id, GraphDatabase.driver(uri, authToken)); - writeResponse( response); - } else if ( requestType.equals( "NewSession" )) - { - String id = requestData.get( "driverId" ).asText(); - Driver driver = drivers.get( id ); - AccessMode accessMode = requestData.get( "accessMode" ).asText().equals( "r" ) ? AccessMode.READ : AccessMode.WRITE; - //Bookmark bookmark = InternalBookmark.parse( requestData.get("bookmarks").asText() ); - Session session = driver.session( SessionConfig.builder() - .withDefaultAccessMode( accessMode ).build()); - String newId = newId(); - sessionStates.put( newId, new SessionState(session) ); - String response = Testkit.wrap("Session", Testkit.id(newId)); - - writeResponse( response); - } else if ( requestType.equals( "SessionRun" )) - { - String id = requestData.get( "sessionId" ).asText(); - Session session = sessionStates.get( id ).session; - JsonNode jsonParams = (JsonNode)requestData.get("params"); - Map params = new HashMap(); - Iterator> iter = jsonParams.fields(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - params.put(entry.getKey(), TestkitTypes.toDriver(entry.getValue())); - } - Query query = new Query(requestData.get("cypher").asText(), params); - Result result = session.run(query); - String newId = newId(); - - results.put( newId, result ); - String response = Testkit.wrap("Result", Testkit.id(newId)); - writeResponse( response); - } else if ( requestType.equals( "TransactionRun" )) - { - String txId = requestData.get("txId").asText(); - String cypher = requestData.get("cypher").asText(); - Transaction tx = transactions.get(txId); - Result result = tx.run(cypher); - String newId = newId(); - results.put(newId, result); - writeResponse(Testkit.wrap("Result", Testkit.id(newId))); - } else if ( requestType.equals( "RetryablePositive" )) - { - String id = requestData.get( "sessionId" ).asText(); - SessionState sessState = sessionStates.get( id ); - sessState.retryableState = 1; - } else if ( requestType.equals( "RetryableNegative" )) - { - String id = requestData.get( "sessionId" ).asText(); - SessionState sessState = sessionStates.get( id ); - sessState.retryableState = -1; - sessState.retryableErrorId = requestData.get("errorId").asText(); - } else if ( requestType.equals( "SessionReadTransaction" )) - { - String id = requestData.get( "sessionId" ).asText(); - SessionState sessState = sessionStates.get( id ); - sessState.session.readTransaction((Transaction tx) -> { - // Reset session state - sessState.retryableState = 0; - // Stash this transaction as there will be commands using it - String txId = newId(); - transactions.put(txId, tx); - // Instruct testkit client to send it's commands within the transaction - try { - writeResponse(Testkit.wrap("RetryableTry", Testkit.id(txId))); - } catch ( Exception e) { - e.printStackTrace(); - } - while ( true ) { - // Process commands as usual but blocking in here - process(); - // Check if state changed on session - switch (sessState.retryableState) { - case 0: - // Nothing happened to session state while processing command - break; - case 1: - // Client is happy to commit - return 0; - case -1: - // Client wants to rollback - if (sessState.retryableErrorId != "") { - Neo4jException err = errors.get(sessState.retryableErrorId); - throw err; - } else { - throw new RuntimeException("Error from client in retryable tx"); - } - } - } - }); - // Assume that exception is thrown by readTransaction when retry fails so this is - // never reached. - writeResponse(Testkit.wrap("RetryableDone", "{}")); - } else if ( requestType.equals( "ResultNext" )) - { - String id = requestData.get( "resultId" ).asText(); - Result result = results.get( id ); - - // TODO: Should we call next here and catch the exception instead? - if (!result.hasNext()) { - writeResponse(Testkit.wrap("NullRecord", "{}")); - return; - } - - Record record = result.next(); - String newId = newId(); - - results.put( newId, result ); - String response = Testkit.wrap("Record", Testkit.values(TestkitTypes.fromRecord(record))); - - writeResponse( response); - } else if ( requestType.equals( "SessionClose" )) - { - String id = requestData.get( "sessionId" ).asText(); - Session session = sessionStates.get( id ).session; - session.close(); - sessionStates.remove( id ); - - String response = Testkit.wrap("Session", Testkit.id(id)); - - writeResponse( response); - } - else if ( requestType.equals( "DriverClose" )) - { - String id = requestData.get( "driverId" ).asText(); - Driver driver = drivers.get( id ); - driver.close(); - drivers.remove( id ); - - String response = Testkit.wrap("Driver", Testkit.id(id)); - - writeResponse( response); - } - else - { - throw new RuntimeException("Request " + requestType + " not handled"); - } - } - - private void writeResponse(String response) - { - System.out.println( "response = " + response ); - - write( "#response begin\n" ); - write( response + "\n" ); - write( "#response end\n" ); - flush(); - } - - private String newId() - { - int nextNumber = idGenerator++; - return String.valueOf( nextNumber ); - } - - private SessionState getSessionState(JsonNode req) { - String id = req.get( "data" ).get( "sessionId" ).asText(); - return sessionStates.get( id ); - } -} diff --git a/testkit-backend/src/main/java/TestkitTypes.java b/testkit-backend/src/main/java/TestkitTypes.java deleted file mode 100644 index 479f3e4069..0000000000 --- a/testkit-backend/src/main/java/TestkitTypes.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (c) 2002-2020 "Neo4j," - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * 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. - */ -import com.fasterxml.jackson.databind.JsonNode; - -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.lang.reflect.Array; - -import org.neo4j.driver.Record; -import org.neo4j.driver.Value; -import org.neo4j.driver.types.Node; -import org.neo4j.driver.internal.value.IntegerValue; -import org.neo4j.driver.internal.value.ListValue; -import org.neo4j.driver.internal.value.MapValue; -import org.neo4j.driver.internal.value.NodeValue; -import org.neo4j.driver.internal.value.NullValue; -import org.neo4j.driver.internal.value.StringValue; - - -public class TestkitTypes -{ - - public static String fromRecord( Record record ) - { - String v = ""; - - for ( Value value : record.values() ) - { - if (v != "") { - v += ","; - } - v += toTestkitType(value); - } - - return v; - } - - - private static String toTestkitType( Object obj ) - { - if ( obj instanceof Value) { - Value value = (Value) obj; - if ( value instanceof NullValue ) - { - return "{\"data\" : null, \"name\" : \"CypherNull\"}"; - } else if ( value instanceof IntegerValue ) - { - return toTestkitType(value.asInt()); - } else if ( value instanceof StringValue ) - { - return toTestkitType(value.asString()); - } else if ( value instanceof ListValue ) - { - return toTestkitType(((ListValue)value).asList()); - } else if ( value instanceof NodeValue ) - { - Node node = ((NodeValue)value).asNode(); - String v = String.format("{\"id\":%s,\"labels\":%s,\"props\":%s}", - toTestkitType(node.id()), toTestkitType(node.labels()), toTestkitType(node.asMap())); - return Testkit.wrap("Node", v); - } else if ( value instanceof MapValue ) - { - return "{\"data\" : {\"value\": {}, \"name\" : \"CypherMap\"}"; - } - } else { - if (obj instanceof Integer || obj instanceof Long) { - return Testkit.wrap("CypherInt", Testkit.value(obj.toString())); - } else if (obj instanceof String) { - return Testkit.wrap("CypherString", Testkit.value("\""+obj.toString()+"\"")); - } else if (obj instanceof List) { - List list = (List)obj; - String v = ""; - for (int i = 0; i < list.size(); i++) { - if (i > 0) { - v += ","; - } - v += toTestkitType(list.get(i)); - } - return Testkit.wrap("CypherList", Testkit.value("["+v+"]")); - } else if (obj instanceof Map) { - Map map = (Map)obj; - String v = ""; - - for (Map.Entry entry : map.entrySet()) { - if (v != "") { - v += ","; - } - v += String.format("\"%s\":%s", entry.getKey(), toTestkitType(entry.getValue())); - } - return Testkit.wrap("CypherMap", Testkit.value("{"+v+"}")); - } - } - - throw new RuntimeException("Can not convert to testkit type:"+obj.getClass()); - } - - public static Object toDriver(JsonNode node) - { - String name = node.get("name").asText(); - JsonNode data = node.get("data"); - if (name.equals("CypherInt")) { - return data.get("value").asInt(); - } - throw new RuntimeException("Can not convert from " + node + " to driver type"); - } -} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java new file mode 100644 index 0000000000..18371081d4 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/CommandProcessor.java @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import neo4j.org.testkit.backend.messages.TestkitModule; +import neo4j.org.testkit.backend.messages.requests.TestkitRequest; +import neo4j.org.testkit.backend.messages.responses.DriverError; +import neo4j.org.testkit.backend.messages.responses.TestkitErrorResponse; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.UncheckedIOException; + +import org.neo4j.driver.exceptions.Neo4jException; + +public class CommandProcessor +{ + private final TestkitState testkitState; + + private final ObjectMapper objectMapper = new ObjectMapper(); + + private final BufferedReader in; + private final BufferedWriter out; + + public CommandProcessor( BufferedReader in, BufferedWriter out ) + { + this.in = in; + this.out = out; + configureObjectMapper(); + this.testkitState = new TestkitState( this::writeResponse, this::process ); + } + + private void configureObjectMapper() + { + TestkitModule testkitModule = new TestkitModule(); + this.objectMapper.registerModule( testkitModule ); + this.objectMapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES ); + } + + private String readLine() + { + try + { + return this.in.readLine(); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + private void write( String s ) + { + try + { + this.out.write( s ); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + // Logs to frontend + private void log( String s ) + { + try + { + this.out.write( s + "\n" ); + this.out.flush(); + } + catch ( IOException e ) + { + } + System.out.println( s ); + } + + private void flush() + { + try + { + this.out.flush(); + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + // Reads one request and writes the response. Returns false when not able to read anymore. + public boolean process() + { + boolean inRequest = false; + StringBuilder request = new StringBuilder(); + + log( "Waiting for request" ); + + while ( true ) + { + String currentLine = readLine(); + // End of stream + if ( currentLine == null ) + { + return false; + } + + if ( currentLine.equals( "#request begin" ) ) + { + inRequest = true; + } + else if ( currentLine.equals( "#request end" ) ) + { + if ( !inRequest ) + { + throw new RuntimeException( "Request end not expected" ); + } + try + { + processRequest( request.toString() ); + } + catch ( Exception e ) + { + if ( e instanceof Neo4jException ) + { + // Error to track + String id = testkitState.newId(); + testkitState.getErrors().put( id, (Neo4jException) e ); + writeResponse( driverError( id ) ); + System.out.println( "Neo4jException: " + e ); + } + else + { + // Unknown error, interpret this as a backend error. + // Report to frontend and rethrow, note that if socket been + // closed the writing will throw itself... + writeResponse( TestkitErrorResponse.builder().errorMessage( e.toString() ).build() ); + // This won't print if there was an IO exception since line above will rethrow + e.printStackTrace(); + throw e; + } + } + return true; + } + else + { + if ( !inRequest ) + { + throw new RuntimeException( "Command Received whilst not in request" ); + } + request.append( currentLine ); + } + } + } + + private DriverError driverError( String id ) + { + return DriverError.builder().data( DriverError.DriverErrorBody.builder().id( id ).build() ).build(); + } + + public void processRequest( String request ) + { + System.out.println( "request = " + request + ", in = " + in + ", out = " + out ); + try + { + TestkitRequest testkitMessage = objectMapper.readValue( request, TestkitRequest.class ); + TestkitResponse response = testkitMessage.process( testkitState ); + if ( response != null ) + { + writeResponse( response ); + } + } + catch ( IOException e ) + { + throw new UncheckedIOException( e ); + } + } + + private void writeResponse( TestkitResponse response ) + { + try + { + String responseStr = objectMapper.writeValueAsString( response ); + System.out.println("response = " + responseStr + ", in = " + in + ", out = " + out); + write( "#response begin\n" ); + write( responseStr + "\n" ); + write( "#response end\n" ); + flush(); + } + catch ( JsonProcessingException ex ) + { + throw new RuntimeException( "Error writing response", ex ); + } + } +} diff --git a/testkit-backend/src/main/java/Runner.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/Runner.java similarity index 67% rename from testkit-backend/src/main/java/Runner.java rename to testkit-backend/src/main/java/neo4j/org/testkit/backend/Runner.java index f0580e68a4..27b3a3963f 100644 --- a/testkit-backend/src/main/java/Runner.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/Runner.java @@ -16,41 +16,60 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package neo4j.org.testkit.backend; + import java.io.BufferedReader; import java.io.BufferedWriter; -import java.io.UncheckedIOException; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; +import java.io.UncheckedIOException; import java.net.ServerSocket; import java.net.Socket; +import java.util.concurrent.CompletableFuture; public class Runner { public static void main( String[] args ) throws IOException { - ServerSocket serverSocket = new ServerSocket(9876); - System.out.println("Starting Java Testkit Backend Started"); + ServerSocket serverSocket = new ServerSocket( 9876 ); - while (true) { - System.out.println("Listening on socket"); - Socket clientSocket = serverSocket.accept(); + System.out.println( "Java TestKit Backend Started on port: " + serverSocket.getLocalPort() ); + while ( true ) + { + final Socket clientSocket = serverSocket.accept(); + CompletableFuture.runAsync( () -> handleClient( clientSocket ) ); + } + } + + private static void handleClient( Socket clientSocket ) + { + try + { System.out.println( "Handling connection from: " + clientSocket.getRemoteSocketAddress() ); BufferedReader in = new BufferedReader( new InputStreamReader( clientSocket.getInputStream() ) ); BufferedWriter out = new BufferedWriter( new OutputStreamWriter( clientSocket.getOutputStream() ) ); - CommandProcessor commandProcessor = new CommandProcessor(in, out); + CommandProcessor commandProcessor = new CommandProcessor( in, out ); boolean cont = true; - while (cont) { - try { + while ( cont ) + { + try + { cont = commandProcessor.process(); - } catch (Exception e) { + } + catch ( Exception e ) + { e.printStackTrace(); clientSocket.close(); cont = false; } } } + catch ( IOException ex ) + { + throw new UncheckedIOException( ex ); + } } } diff --git a/testkit-backend/src/main/java/SessionState.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/SessionState.java similarity index 91% rename from testkit-backend/src/main/java/SessionState.java rename to testkit-backend/src/main/java/neo4j/org/testkit/backend/SessionState.java index e3487a25d2..8afd8a83a6 100644 --- a/testkit-backend/src/main/java/SessionState.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/SessionState.java @@ -16,9 +16,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package neo4j.org.testkit.backend; + +import lombok.Getter; +import lombok.Setter; import org.neo4j.driver.Session; +@Getter +@Setter public class SessionState { public Session session; diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java new file mode 100644 index 0000000000..2bb6e98808 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/TestkitState.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend; + +import lombok.Getter; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.neo4j.driver.Driver; +import org.neo4j.driver.Result; +import org.neo4j.driver.Transaction; +import org.neo4j.driver.exceptions.Neo4jException; + +@Getter +public class TestkitState +{ + private final Map drivers = new HashMap<>(); + private final Map sessionStates = new HashMap<>(); + private final Map results = new HashMap<>(); + private final Map transactions = new HashMap<>(); + private final Map errors = new HashMap<>(); + private int idGenerator = 0; + private final Consumer responseWriter; + private final Supplier processor; + + public TestkitState( Consumer responseWriter, Supplier processor ) + { + this.responseWriter = responseWriter; + this.processor = processor; + } + + public String newId() + { + return String.valueOf( idGenerator++ ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitCypherTypeDeserializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitCypherTypeDeserializer.java new file mode 100644 index 0000000000..a4b725c6cf --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitCypherTypeDeserializer.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; + +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class TestkitCypherTypeDeserializer extends StdDeserializer> +{ + public TestkitCypherTypeDeserializer() + { + super( Map.class ); + } + + public TestkitCypherTypeDeserializer( Class typeClass ) + { + super( typeClass ); + } + + @Override + public Map deserialize( JsonParser p, DeserializationContext ctxt ) throws IOException, JsonProcessingException + { + Map result = new HashMap<>(); + + String key; + if ( p.isExpectedStartObjectToken() ) + { + key = p.nextFieldName(); + } + else + { + JsonToken t = p.getCurrentToken(); + if ( t == JsonToken.END_OBJECT ) + { + return Collections.emptyMap(); + } + if ( t != JsonToken.FIELD_NAME ) + { + ctxt.reportWrongTokenException( this, JsonToken.FIELD_NAME, null ); + } + key = p.getCurrentName(); + } + + for ( ; key != null; key = p.nextFieldName() ) + { + String paramType = null; + + if ( p.nextToken() == JsonToken.START_OBJECT ) + { + String fieldName = p.nextFieldName(); + if ( fieldName.equals( "name" ) ) + { + paramType = p.nextTextValue(); + Class mapValueType = cypherTypeToJavaType( paramType ); + p.nextFieldName(); // next is data which we can drop + p.nextToken(); + p.nextToken(); + p.nextToken(); + if ( mapValueType == null ) + { + result.put( key, null ); + } else + { + result.put( key, p.readValueAs( mapValueType ) ); + } + + } + } + + } + return result; + } + + private Class cypherTypeToJavaType( String typeString ) + { + switch ( typeString ) + { + case "CypherBool": + return Boolean.class; + case "CypherInt": + return Integer.class; + case "CypherFloat": + return Double.class; + case "CypherString": + return String.class; + case "CypherList": + return List.class; + case "CypherMap": + return Map.class; + case "CypherNull": + return null; + default: + return null; + } + } +} + diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java new file mode 100644 index 0000000000..588d7949c9 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/TestkitModule.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages; + +import com.fasterxml.jackson.databind.module.SimpleModule; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitListValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitMapValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitNodeValueSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitRecordSerializer; +import neo4j.org.testkit.backend.messages.responses.serializer.TestkitValueSerializer; + +import org.neo4j.driver.Record; +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.value.ListValue; +import org.neo4j.driver.internal.value.MapValue; +import org.neo4j.driver.internal.value.NodeValue; + +public class TestkitModule extends SimpleModule +{ + public TestkitModule() + { + this.addSerializer( Value.class, new TestkitValueSerializer() ); + this.addSerializer( NodeValue.class, new TestkitNodeValueSerializer() ); + this.addSerializer( ListValue.class, new TestkitListValueSerializer() ); + this.addSerializer( Record.class, new TestkitRecordSerializer() ); + this.addSerializer( MapValue.class, new TestkitMapValueSerializer() ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/AuthorizationToken.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/AuthorizationToken.java new file mode 100644 index 0000000000..3b870dc3be --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/AuthorizationToken.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.Map; + +@Setter +@Getter +@NoArgsConstructor +@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="name") +public class AuthorizationToken +{ + @JsonProperty("data") + private Map tokens; + +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DriverClose.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DriverClose.java new file mode 100644 index 0000000000..b081f0176f --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/DriverClose.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Driver; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@Setter +@Getter +@NoArgsConstructor +public class DriverClose implements TestkitRequest +{ + private DriverCloseBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + testkitState.getDrivers().get( data.getDriverId() ).close(); + return Driver.builder().data( Driver.DriverBody.builder().id( data.getDriverId() ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + private static class DriverCloseBody + { + private String driverId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java new file mode 100644 index 0000000000..c9a77274d5 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewDriver.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Driver; +import neo4j.org.testkit.backend.messages.responses.TestkitErrorResponse; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.Optional; + +import org.neo4j.driver.AuthToken; +import org.neo4j.driver.AuthTokens; +import org.neo4j.driver.Config; +import org.neo4j.driver.GraphDatabase; + +@Setter +@Getter +@NoArgsConstructor +public class NewDriver implements TestkitRequest +{ + private NewDriverBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + String id = testkitState.newId(); + + AuthToken authToken; + switch ( data.getAuthorizationToken().getTokens().get( "scheme" ) ) + { + case "basic": + authToken = AuthTokens.basic( data.authorizationToken.getTokens().get( "principal" ), + data.authorizationToken.getTokens().get( "credentials" ), + data.authorizationToken.getTokens().get( "realm" ) ); + break; + default: + return TestkitErrorResponse.builder().errorMessage( "Auth scheme " + data.authorizationToken.getTokens().get( "scheme" ) + "not implemented" ) + .build(); + } + + Config.ConfigBuilder configBuilder = Config.builder(); + Optional.ofNullable( data.userAgent ).ifPresent( configBuilder::withUserAgent ); + testkitState.getDrivers().putIfAbsent( id, GraphDatabase.driver( data.uri, authToken, configBuilder.build() ) ); + return Driver.builder().data( Driver.DriverBody.builder().id( id ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class NewDriverBody + { + private String uri; + private AuthorizationToken authorizationToken; + private String userAgent; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewSession.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewSession.java new file mode 100644 index 0000000000..98e9688cb5 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/NewSession.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Session; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import org.neo4j.driver.AccessMode; +import org.neo4j.driver.Driver; +import org.neo4j.driver.SessionConfig; +import org.neo4j.driver.internal.InternalBookmark; + +@Setter +@Getter +@NoArgsConstructor +public class NewSession implements TestkitRequest +{ + private NewSessionBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + Driver driver = testkitState.getDrivers().get( data.getDriverId() ); + AccessMode formattedAccessMode = data.getAccessMode().equals( "r" ) ? AccessMode.READ : AccessMode.WRITE; + SessionConfig.Builder builder = SessionConfig.builder() + .withDefaultAccessMode( formattedAccessMode ); + + Optional.ofNullable( data.bookmarks ) + .map( bookmarks -> bookmarks.stream().map( InternalBookmark::parse ).collect( Collectors.toList() ) ) + .ifPresent( builder::withBookmarks ); + + Optional.ofNullable( data.database ).ifPresent( builder::withDatabase ); + + org.neo4j.driver.Session session = driver.session( builder.build() ); + String newId = testkitState.newId(); + testkitState.getSessionStates().put( newId, new SessionState( session ) ); + + return Session.builder().data( Session.SessionBody.builder().id( newId ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class NewSessionBody + { + private String driverId; + private String accessMode; + private List bookmarks; + private String database; + private int fetchSize; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultNext.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultNext.java new file mode 100644 index 0000000000..4105802170 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/ResultNext.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.NullRecord; +import neo4j.org.testkit.backend.messages.responses.Record; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import org.neo4j.driver.Result; +import org.neo4j.driver.exceptions.NoSuchRecordException; + +@Setter +@Getter +@NoArgsConstructor +public class ResultNext implements TestkitRequest +{ + private ResultNextBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + try + { + Result result = testkitState.getResults().get( data.getResultId() ); + org.neo4j.driver.Record record = result.next(); + return Record.builder().data( Record.RecordBody.builder().values( record ).build() ).build(); + } + catch ( NoSuchRecordException ignored ) + { + return NullRecord.builder().build(); + } + } + + @Setter + @Getter + @NoArgsConstructor + public static class ResultNextBody + { + private String resultId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryableNegative.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryableNegative.java new file mode 100644 index 0000000000..3c438b0f91 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryableNegative.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@Setter +@Getter +@NoArgsConstructor +public class RetryableNegative implements TestkitRequest +{ + private RetryableNegativeBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + SessionState sessionState = testkitState.getSessionStates().getOrDefault( data.sessionId, null ); + if ( sessionState == null ) + { + throw new RuntimeException( "Could not find session" ); + } + sessionState.setRetryableState( -1 ); + sessionState.setRetryableErrorId( data.errorId ); + return null; + } + + @Setter + @Getter + @NoArgsConstructor + public static class RetryableNegativeBody + { + private String sessionId; + private String errorId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryablePositive.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryablePositive.java new file mode 100644 index 0000000000..a8e8179c80 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/RetryablePositive.java @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@Setter +@Getter +@NoArgsConstructor +public class RetryablePositive implements TestkitRequest +{ + private RetryablePositiveBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + SessionState sessionState = testkitState.getSessionStates().get( data.sessionId ); + if ( sessionState == null ) + { + throw new RuntimeException( "Could not find session" ); + } + sessionState.setRetryableState( 1 ); + return null; + } + + @Setter + @Getter + @NoArgsConstructor + public static class RetryablePositiveBody + { + private String sessionId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionBeginTransaction.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionBeginTransaction.java new file mode 100644 index 0000000000..8f91cf45e3 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionBeginTransaction.java @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; +import neo4j.org.testkit.backend.messages.responses.Transaction; + +import java.util.Map; +import java.util.Optional; + +import org.neo4j.driver.TransactionConfig; + +@Setter +@Getter +@NoArgsConstructor +public class SessionBeginTransaction implements TestkitRequest +{ + private SessionBeginTransactionBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + return Optional.ofNullable( testkitState.getSessionStates().getOrDefault( data.sessionId, null ) ) + .map( SessionState::getSession ) + .map( session -> + { + TransactionConfig.Builder builder = TransactionConfig.builder(); + Optional.ofNullable( data.txMeta ).ifPresent( builder::withMetadata ); + // Optional.ofNullable( data.timeout ).ifPresent( builder::withTimeout ); + String txId = testkitState.newId(); + org.neo4j.driver.Transaction tx = session.beginTransaction( builder.build() ); + testkitState.getTransactions().put( txId, tx ); + return transaction( txId ); + } ) + .orElseThrow( () -> new RuntimeException( "Could not find session" ) ); + } + + private Transaction transaction( String txId ) + { + return Transaction.builder().data( Transaction.TransactionBody.builder().id( txId ).build() ).build(); + } + + @Getter + @NoArgsConstructor + @Setter + public static class SessionBeginTransactionBody + { + private String sessionId; + private Map txMeta; + private Integer timeout; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionClose.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionClose.java new file mode 100644 index 0000000000..b4b5f2feca --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionClose.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Session; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@Setter +@Getter +@NoArgsConstructor +public class SessionClose implements TestkitRequest +{ + private SessionCloseBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + testkitState.getSessionStates().get( data.getSessionId() ).getSession().close(); + return Session.builder().data( Session.SessionBody.builder().id( data.getSessionId() ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + private static class SessionCloseBody + { + private String sessionId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java new file mode 100644 index 0000000000..956b9a6629 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionReadTransaction.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.SessionState; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.RetryableDone; +import neo4j.org.testkit.backend.messages.responses.RetryableTry; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.Optional; + +import org.neo4j.driver.Session; +import org.neo4j.driver.TransactionWork; + +@Setter +@Getter +@NoArgsConstructor +public class SessionReadTransaction implements TestkitRequest +{ + private SessionReadTransactionBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + return Optional.ofNullable( testkitState.getSessionStates().getOrDefault( data.sessionId, null ) ) + .map( sessionState -> + { + Session session = sessionState.getSession(); + session.readTransaction( handle( testkitState, sessionState ) ); + return retryableDone(); + } ).orElseThrow( () -> new RuntimeException( "Could not find session" ) ); + } + + private TransactionWork handle( TestkitState testkitState, SessionState sessionState ) + { + return tx -> + { + System.out.println( "Start" ); + sessionState.setRetryableState( 0 ); + String txId = testkitState.newId(); + testkitState.getTransactions().put( txId, tx ); + testkitState.getResponseWriter().accept( retryableTry( txId ) ); + + while ( true ) + { + // Process commands as usual but blocking in here + testkitState.getProcessor().get(); + + // Check if state changed on session + switch ( sessionState.retryableState ) + { + case 0: + // Nothing happened to session state while processing command + break; + case 1: + // Client is happy to commit + return 0; + case -1: + // Client wants to rollback + if ( !"".equals( sessionState.retryableErrorId ) ) + { + throw testkitState.getErrors().get( sessionState.retryableErrorId ); + } + else + { + throw new RuntimeException( "Error from client in retryable tx" ); + } + } + } + }; + } + + private RetryableTry retryableTry( String txId ) + { + return RetryableTry.builder().data( RetryableTry.RetryableTryBody.builder().id( txId ).build() ).build(); + } + + private RetryableDone retryableDone() + { + return RetryableDone.builder().build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class SessionReadTransactionBody + { + private String sessionId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionRun.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionRun.java new file mode 100644 index 0000000000..0432c16748 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/SessionRun.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.TestkitCypherTypeDeserializer; +import neo4j.org.testkit.backend.messages.responses.Result; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.Map; +import java.util.Optional; + +import org.neo4j.driver.Query; +import org.neo4j.driver.Session; + +@Setter +@Getter +@NoArgsConstructor +public class SessionRun implements TestkitRequest +{ + private SessionRunBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + Session session = testkitState.getSessionStates().get( data.getSessionId() ).getSession(); + Query query = Optional.ofNullable( data.params ) + .map( params -> new Query( data.cypher, data.params ) ) + .orElseGet( () -> new Query( data.cypher ) ); + org.neo4j.driver.Result result = session.run( query ); + String newId = testkitState.newId(); + testkitState.getResults().put( newId, result ); + + return Result.builder().data( Result.ResultBody.builder().id( newId ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class SessionRunBody + { + private String sessionId; + private String cypher; + + @JsonDeserialize( using = TestkitCypherTypeDeserializer.class ) + private Map params; + private String txMeta; + private int timeout; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java new file mode 100644 index 0000000000..6157c38086 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TestkitRequest.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +@JsonTypeInfo( use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "name" ) +@JsonSubTypes( { + @JsonSubTypes.Type( NewDriver.class ), @JsonSubTypes.Type( NewSession.class ), + @JsonSubTypes.Type( SessionRun.class ), @JsonSubTypes.Type( ResultNext.class ), + @JsonSubTypes.Type( SessionClose.class ), @JsonSubTypes.Type( DriverClose.class ), + @JsonSubTypes.Type( RetryableNegative.class ), @JsonSubTypes.Type( SessionReadTransaction.class ), + @JsonSubTypes.Type( TransactionRun.class ), @JsonSubTypes.Type( RetryablePositive.class ), + @JsonSubTypes.Type( SessionBeginTransaction.class ), @JsonSubTypes.Type( TransactionCommit.class ) +} ) +public interface TestkitRequest +{ + TestkitResponse process( TestkitState testkitState ); +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionCommit.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionCommit.java new file mode 100644 index 0000000000..be08777a59 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionCommit.java @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; +import neo4j.org.testkit.backend.messages.responses.Transaction; + +import java.util.Optional; + +@Getter +@NoArgsConstructor +@Setter +public class TransactionCommit implements TestkitRequest +{ + private TransactionCommitBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + return Optional.ofNullable( testkitState.getTransactions().get( data.txId ) ) + .map( tx -> + { + tx.commit(); + return transaction( data.txId ); + } ) + .orElseThrow( () -> new RuntimeException( "Could not find transaction" ) ); + } + + private Transaction transaction( String txId ) + { + return Transaction.builder().data( Transaction.TransactionBody.builder().id( txId ).build() ).build(); + } + + @Getter + @NoArgsConstructor + @Setter + public static class TransactionCommitBody + { + private String txId; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionRun.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionRun.java new file mode 100644 index 0000000000..eb18d403ee --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/requests/TransactionRun.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.requests; + +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import neo4j.org.testkit.backend.TestkitState; +import neo4j.org.testkit.backend.messages.responses.Result; +import neo4j.org.testkit.backend.messages.responses.TestkitResponse; + +import java.util.Optional; + +@Setter +@Getter +@NoArgsConstructor +public class TransactionRun implements TestkitRequest +{ + private TransactionRunBody data; + + @Override + public TestkitResponse process( TestkitState testkitState ) + { + return Optional.ofNullable( testkitState.getTransactions().get( data.txId ) ) + .map( tx -> tx.run( data.cypher ) ) + .map( result -> + { + String resultId = testkitState.newId(); + testkitState.getResults().put( resultId, result ); + return result( resultId ); + } ) + .orElseThrow( () -> new RuntimeException( "Could not find transaction" ) ); + } + + private Result result( String resultId ) + { + return Result.builder().data( Result.ResultBody.builder().id( resultId ).build() ).build(); + } + + @Setter + @Getter + @NoArgsConstructor + public static class TransactionRunBody + { + private String txId; + private String cypher; + } +} diff --git a/testkit-backend/src/main/java/Testkit.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Driver.java similarity index 58% rename from testkit-backend/src/main/java/Testkit.java rename to testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Driver.java index ea2d4fda55..9a6c0e2d2b 100644 --- a/testkit-backend/src/main/java/Testkit.java +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Driver.java @@ -16,29 +16,30 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +package neo4j.org.testkit.backend.messages.responses; -public class Testkit -{ - public static String wrap(String name, String data) - { - return String.format("{\"name\": \"%s\", \"data\":%s}", name, data); - } +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; - public static String value(String v) { - return "{\"value\":"+v+"}"; - } - - public static String values(String v) { - return "{\"values\":["+v+"]}"; - } +@Setter +@Getter +@Builder +public class Driver implements TestkitResponse +{ + private final DriverBody data; - public static String id(String v) { - return "{\"id\":"+v+"}"; + @Override + public String testkitName() + { + return "Driver"; } - public static String msg(String msg) { - return "{\"msg\":\""+msg+"\"}"; + @Setter + @Getter + @Builder + public static class DriverBody + { + private final String id; } } - - diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DriverError.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DriverError.java new file mode 100644 index 0000000000..e5fcf72c4b --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/DriverError.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class DriverError implements TestkitResponse +{ + private DriverErrorBody data; + + @Override + public String testkitName() + { + return "DriverError"; + } + + @Setter + @Getter + @Builder + public static class DriverErrorBody + { + private String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/NullRecord.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/NullRecord.java new file mode 100644 index 0000000000..716600ca28 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/NullRecord.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class NullRecord implements TestkitResponse +{ + @Override + public String testkitName() + { + return "NullRecord"; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Record.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Record.java new file mode 100644 index 0000000000..e132b7d9f9 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Record.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class Record implements TestkitResponse +{ + private RecordBody data; + + @Override + public String testkitName() + { + return "Record"; + } + + @Setter + @Getter + @Builder + public static class RecordBody + { + private org.neo4j.driver.Record values; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Result.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Result.java new file mode 100644 index 0000000000..9ed51048f8 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Result.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class Result implements TestkitResponse +{ + private ResultBody data; + + @Override + public String testkitName() + { + return "Result"; + } + + @Setter + @Getter + @Builder + public static class ResultBody + { + private String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableDone.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableDone.java new file mode 100644 index 0000000000..a13543f035 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableDone.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class RetryableDone implements TestkitResponse +{ + @Override + public String testkitName() + { + return "RetryableDone"; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableTry.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableTry.java new file mode 100644 index 0000000000..0fc93a4da8 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/RetryableTry.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class RetryableTry implements TestkitResponse +{ + private RetryableTryBody data; + + @Override + public String testkitName() + { + return "RetryableTry"; + } + + @Setter + @Getter + @Builder + public static class RetryableTryBody + { + private String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Session.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Session.java new file mode 100644 index 0000000000..772d71045d --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Session.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class Session implements TestkitResponse +{ + private SessionBody data; + + @Override + public String testkitName() + { + return "Session"; + } + + @Setter + @Getter + @Builder + public static class SessionBody + { + private String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TeskitBackendErrorResponse.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TeskitBackendErrorResponse.java new file mode 100644 index 0000000000..13a7e5a1ae --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TeskitBackendErrorResponse.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class TeskitBackendErrorResponse implements TestkitResponse +{ + @Override + public String testkitName() + { + return "BackendError"; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitErrorResponse.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitErrorResponse.java new file mode 100644 index 0000000000..eadf0c4ef3 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitErrorResponse.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class TestkitErrorResponse implements TestkitResponse +{ + @JsonProperty("msg") + private String errorMessage; + + @Override + public String testkitName() + { + return "Error"; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitResponse.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitResponse.java new file mode 100644 index 0000000000..b945e4307e --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/TestkitResponse.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +@JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="name") +public interface TestkitResponse +{ + String testkitName(); +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Transaction.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Transaction.java new file mode 100644 index 0000000000..9f491a78ed --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/Transaction.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses; + +import lombok.Builder; +import lombok.Getter; +import lombok.Setter; + +@Setter +@Getter +@Builder +public class Transaction implements TestkitResponse +{ + private final TransactionBody data; + + @Override + public String testkitName() + { + return "Transaction"; + } + + @Setter + @Getter + @Builder + public static class TransactionBody + { + private final String id; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java new file mode 100644 index 0000000000..fa29189226 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/GenUtils.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; + +import java.io.IOException; +import java.util.List; + +@AllArgsConstructor( access = AccessLevel.PRIVATE ) +public final class GenUtils +{ + public static void object( JsonGenerator gen, RunnableWithIOException runnable ) throws IOException + { + gen.writeStartObject(); + runnable.run(); + gen.writeEndObject(); + } + + public static void list( JsonGenerator gen, List list ) throws IOException + { + gen.writeStartArray(); + for ( T element : list ) + { + gen.writeObject( element ); + } + gen.writeEndArray(); + } + + public static void cypherObject( JsonGenerator gen, String name, T value ) throws IOException + { + cypherObject( gen, name, () -> gen.writeObjectField("value", value ) ); + } + + public static void cypherObject( JsonGenerator gen, String name, RunnableWithIOException runnable ) throws IOException + { + object( gen, () -> + { + gen.writeStringField( "name", name ); + gen.writeFieldName( "data" ); + object( gen, runnable ); + } ); + } + + interface RunnableWithIOException + { + void run() throws IOException; + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitListValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitListValueSerializer.java new file mode 100644 index 0000000000..54d661a640 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitListValueSerializer.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.function.Function; + +import org.neo4j.driver.internal.value.ListValue; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.list; + +public class TestkitListValueSerializer extends StdSerializer +{ + public TestkitListValueSerializer() + { + super( ListValue.class ); + } + + @Override + public void serialize( ListValue listValue, JsonGenerator gen, SerializerProvider serializerProvider ) throws IOException + { + cypherObject( gen, "CypherList", () -> + { + gen.writeFieldName( "value" ); + list( gen, listValue.asList( Function.identity() ) ); + }); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitMapValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitMapValueSerializer.java new file mode 100644 index 0000000000..fa626d1281 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitMapValueSerializer.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.Map; +import java.util.function.Function; + +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.value.MapValue; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.object; + +public class TestkitMapValueSerializer extends StdSerializer +{ + public TestkitMapValueSerializer() + { + super( MapValue.class ); + } + + @Override + public void serialize( MapValue mapValue, JsonGenerator gen, SerializerProvider serializerProvider ) throws IOException + { + cypherObject( gen, "CypherMap", () -> + { + gen.writeFieldName( "value" ); + object( gen, () -> + { + for ( Map.Entry entry : mapValue.asMap( Function.identity() ).entrySet() ) + { + gen.writeObjectField( entry.getKey(), entry.getValue() ); + } + } ); + } ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitNodeValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitNodeValueSerializer.java new file mode 100644 index 0000000000..c452fd1be8 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitNodeValueSerializer.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +import java.util.function.Function; +import java.util.stream.StreamSupport; + +import org.neo4j.driver.internal.value.ListValue; +import org.neo4j.driver.internal.value.MapValue; +import org.neo4j.driver.internal.value.NodeValue; +import org.neo4j.driver.internal.value.StringValue; +import org.neo4j.driver.types.Node; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +public class TestkitNodeValueSerializer extends StdSerializer +{ + public TestkitNodeValueSerializer() + { + super( NodeValue.class ); + } + + @Override + public void serialize( NodeValue nodeValue, JsonGenerator gen, SerializerProvider serializerProvider ) throws IOException + { + + cypherObject( gen, "Node", () -> + { + Node node = nodeValue.asNode(); + gen.writeObjectField( "id", node.id() ); + + StringValue[] labels = StreamSupport.stream( node.labels().spliterator(), false ) + .map( StringValue::new ) + .toArray( StringValue[]::new ); + + gen.writeObjectField( "labels", new ListValue( labels ) ); + gen.writeObjectField( "props", new MapValue( node.asMap( Function.identity() ) ) ); + + } ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitRecordSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitRecordSerializer.java new file mode 100644 index 0000000000..997c19a990 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitRecordSerializer.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; + +import org.neo4j.driver.Record; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.list; + +public class TestkitRecordSerializer extends StdSerializer +{ + + public TestkitRecordSerializer() + { + super( org.neo4j.driver.Record.class ); + } + + @Override + public void serialize( Record record, JsonGenerator gen, SerializerProvider provider ) throws IOException + { + list( gen, record.values() ); + } +} diff --git a/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java new file mode 100644 index 0000000000..6ffbecebf2 --- /dev/null +++ b/testkit-backend/src/main/java/neo4j/org/testkit/backend/messages/responses/serializer/TestkitValueSerializer.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend.messages.responses.serializer; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; + +import org.neo4j.driver.Value; +import org.neo4j.driver.internal.types.InternalTypeSystem; + +import static neo4j.org.testkit.backend.messages.responses.serializer.GenUtils.cypherObject; + +public class TestkitValueSerializer extends StdSerializer +{ + public TestkitValueSerializer( ) + { + super( Value.class ); + } + + @Override + public void serialize( Value value, JsonGenerator gen, SerializerProvider provider ) throws IOException + { + if ( InternalTypeSystem.TYPE_SYSTEM.BOOLEAN().isTypeOf( value ) ) + { + cypherObject( gen, "CypherBool", value.asBoolean() ); + } else if ( InternalTypeSystem.TYPE_SYSTEM.NULL().isTypeOf( value ) ) { + cypherObject( gen, "CypherNull", () -> gen.writeNullField( "value" ) ); + } else if ( InternalTypeSystem.TYPE_SYSTEM.INTEGER().isTypeOf( value ) ) { + cypherObject( gen, "CypherInt", value.asInt() ); + } else if ( InternalTypeSystem.TYPE_SYSTEM.FLOAT().isTypeOf( value ) ) { + cypherObject( gen, "CypherFloat", value.asDouble() ); + } else if ( InternalTypeSystem.TYPE_SYSTEM.STRING().isTypeOf( value ) ) { + cypherObject( gen, "CypherString", value.asString() ); + } + + } +} diff --git a/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageDeserializerTest.java b/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageDeserializerTest.java new file mode 100644 index 0000000000..2bf55b1231 --- /dev/null +++ b/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageDeserializerTest.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import neo4j.org.testkit.backend.messages.TestkitModule; +import neo4j.org.testkit.backend.messages.requests.NewDriver; +import neo4j.org.testkit.backend.messages.requests.NewSession; +import neo4j.org.testkit.backend.messages.requests.TestkitRequest; +import neo4j.org.testkit.backend.messages.requests.SessionRun; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.instanceOf; + +class MessageDeserializerTest +{ + private static final ObjectMapper mapper = new ObjectMapper(); + + @BeforeAll + static void setUp() + { + TestkitModule tkm = new TestkitModule(); + + //mapper.registerModule( tkm ); + mapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES ); + } + + @Test + void testDeserializeNewDriver() throws JsonProcessingException + { + Object message = mapper.readValue( + "{\"name\": \"NewDriver\", \"data\": {\"uri\": \"bolt://localhost:7687\", " + + "\"authorizationToken\": {\"name\": \"AuthorizationToken\", \"data\": {\"scheme\": \"basic\", \"principal\": \"neo4j\", " + + "\"credentials\": \"pass\", \"realm\": \"\", \"ticket\": \"\"}}, \"userAgent\": null}}", + TestkitRequest.class ); + + assertThat( message, instanceOf( NewDriver.class ) ); + + NewDriver newDriver = (NewDriver) message; + assertThat( newDriver.getData().getUri(), equalTo( "bolt://localhost:7687" ) ); + assertThat( newDriver.getData().getAuthorizationToken().getTokens().get( "scheme" ), equalTo( "basic" ) ); + assertThat( newDriver.getData().getAuthorizationToken().getTokens().get( "principal" ), equalTo( "neo4j" ) ); + } + + @Test + void testDeserializerNewSession() throws JsonProcessingException + { + Object message = mapper.readValue( + "{\"name\": \"NewSession\", " + + "\"data\": {\"driverId\": \"0\", \"accessMode\": \"w\", \"bookmarks\": null, \"database\": null, \"fetchSize\": null}}", + TestkitRequest.class ); + + assertThat( message, instanceOf( NewSession.class ) ); + + NewSession sessionRequest = (NewSession) message; + assertThat( sessionRequest.getData().getDriverId(), equalTo( "0" ) ); + assertThat( sessionRequest.getData().getAccessMode(), equalTo( "w" ) ); + } + + @Test + void testDeserializerNewSessionRun() throws JsonProcessingException + { + Object message = mapper.readValue( + "{\"name\": \"SessionRun\", \"data\": {\"sessionId\": \"1\", \"cypher\": \"RETURN $x as y\", " + + "\"params\": {\"x\": {\"name\": \"CypherBool\", \"data\": {\"value\": true}}}, \"txMeta\": null, \"timeout\": null}}", + TestkitRequest.class ); + + assertThat( message, instanceOf( SessionRun.class ) ); + + SessionRun sessionRun = (SessionRun) message; + assertThat( sessionRun.getData().getSessionId(), equalTo( "1" ) ); + assertThat( sessionRun.getData().getCypher(), equalTo( "RETURN $x as y" ) ); + } + +} \ No newline at end of file diff --git a/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageSerializerTest.java b/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageSerializerTest.java new file mode 100644 index 0000000000..494ff07d39 --- /dev/null +++ b/testkit-backend/src/test/java/neo4j/org/testkit/backend/MessageSerializerTest.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2002-2020 "Neo4j," + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * 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 neo4j.org.testkit.backend; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import neo4j.org.testkit.backend.messages.TestkitModule; +import neo4j.org.testkit.backend.messages.responses.Driver; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.equalTo; + +public class MessageSerializerTest +{ + private static final ObjectMapper mapper = new ObjectMapper(); + + @BeforeAll + static void setUp() + { + TestkitModule tkm = new TestkitModule(); + + mapper.registerModule( tkm ); + mapper.disable( DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES ); + } + + @Test + void shouldSerializerNewDriverResponse() throws JsonProcessingException + { + Driver response = Driver.builder().data( Driver.DriverBody.builder().id( "1" ).build() ).build(); + String expectedOutput = "{\"name\":\"Driver\",\"data\":{\"id\":\"1\"}}"; + + String serializedResponse = mapper.writeValueAsString( response ); + + assertThat( serializedResponse, equalTo( expectedOutput ) ); + } +}