Skip to content
This repository was archived by the owner on Jul 6, 2023. It is now read-only.

Commit 9463bed

Browse files
authored
Merge pull request #69 from spacecowboy/addressschema
Fallback to plain `bolt://` when url scheme is `bolt+routing://`
2 parents 4d26858 + 6a44b82 commit 9463bed

File tree

12 files changed

+101
-36
lines changed

12 files changed

+101
-36
lines changed

cypher-shell/src/integration-test/java/org/neo4j/shell/commands/CypherShellIntegrationTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ public void setUp() throws Exception {
4444
commitCommand = new Commit(shell);
4545
beginCommand = new Begin(shell);
4646

47-
shell.connect(new ConnectionConfig("localhost", 7687, "neo4j", "neo", true));
47+
shell.connect(new ConnectionConfig(logger, "bolt://", "localhost", 7687, "neo4j", "neo", true));
4848
}
4949

5050
@After
@@ -84,7 +84,7 @@ public void connectTwiceThrows() throws CommandException {
8484
thrown.expect(CommandException.class);
8585
thrown.expectMessage("Already connected");
8686

87-
ConnectionConfig config = new ConnectionConfig("localhost", 7687, "neo4j", "neo", true);
87+
ConnectionConfig config = new ConnectionConfig(logger, "bolt://", "localhost", 7687, "neo4j", "neo", true);
8888
assertTrue("Shell should already be connected", shell.isConnected());
8989
shell.connect(config);
9090
}

cypher-shell/src/main/java/org/neo4j/shell/ConnectionConfig.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,36 @@
11
package org.neo4j.shell;
22

33
import org.neo4j.driver.v1.Config;
4+
import org.neo4j.shell.log.AnsiFormattedText;
5+
import org.neo4j.shell.log.Logger;
46

57
import javax.annotation.Nonnull;
68

79
public class ConnectionConfig {
10+
private final String scheme;
811
private final String host;
912
private final int port;
1013
private final Config.EncryptionLevel encryption;
1114
private String username;
1215
private String password;
1316

14-
public ConnectionConfig(@Nonnull String host, int port, @Nonnull String username, @Nonnull String password,
15-
boolean encryption) {
17+
public ConnectionConfig(@Nonnull Logger logger, @Nonnull String scheme, @Nonnull String host, int port,
18+
@Nonnull String username, @Nonnull String password, boolean encryption) {
1619
this.host = host;
1720
this.port = port;
1821
this.username = fallbackToEnvVariable(username, "NEO4J_USERNAME");
1922
this.password = fallbackToEnvVariable(password, "NEO4J_PASSWORD");
2023
this.encryption = encryption ? Config.EncryptionLevel.REQUIRED : Config.EncryptionLevel.NONE;
24+
25+
if ("bolt+routing://".equalsIgnoreCase(scheme)) {
26+
logger.printError(
27+
AnsiFormattedText.s()
28+
.colorRed()
29+
.append("Routing is not supported by cypher-shell. Falling back to direct connection.")
30+
.formattedString());
31+
scheme = "bolt://";
32+
}
33+
this.scheme = scheme;
2134
}
2235

2336
/**
@@ -32,6 +45,11 @@ static String fallbackToEnvVariable(@Nonnull String preferredValue, @Nonnull Str
3245
return result;
3346
}
3447

48+
@Nonnull
49+
public String scheme() {
50+
return scheme;
51+
}
52+
3553
@Nonnull
3654
public String host() {
3755
return host;
@@ -53,7 +71,7 @@ public String password() {
5371

5472
@Nonnull
5573
public String driverUrl() {
56-
return String.format("bolt://%s:%d", host(), port());
74+
return String.format("%s%s:%d", scheme(), host(), port());
5775
}
5876

5977
@Nonnull

cypher-shell/src/main/java/org/neo4j/shell/Main.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static void main(String[] args) {
4949
}
5050

5151
static String getWelcomeMessage(@Nonnull ConnectionConfig connectionConfig,
52-
@Nonnull String serverVersion) {
52+
@Nonnull String serverVersion) {
5353
String neo4j = "Neo4j";
5454
if (!serverVersion.isEmpty()) {
5555
neo4j += " " + serverVersion;
@@ -80,17 +80,19 @@ void startShell(@Nonnull CliArgs cliArgs) {
8080
out.println("Cypher-Shell " + Build.version());
8181
return;
8282
}
83+
Logger logger = new AnsiLogger(cliArgs.getDebugMode());
84+
logger.setFormat(cliArgs.getFormat());
8385

84-
ConnectionConfig connectionConfig = new ConnectionConfig(cliArgs.getHost(),
86+
ConnectionConfig connectionConfig = new ConnectionConfig(
87+
logger,
88+
cliArgs.getScheme(),
89+
cliArgs.getHost(),
8590
cliArgs.getPort(),
8691
cliArgs.getUsername(),
8792
cliArgs.getPassword(),
8893
cliArgs.getEncryption());
8994

90-
Logger logger = new AnsiLogger(cliArgs.getDebugMode());
9195
try {
92-
logger.setFormat(cliArgs.getFormat());
93-
9496
CypherShell shell = new CypherShell(logger);
9597
connectInteractively(shell, connectionConfig);
9698

cypher-shell/src/main/java/org/neo4j/shell/cli/CliArgHelper.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
public class CliArgHelper {
2727

2828
static final Pattern ADDRESS_ARG_PATTERN =
29-
Pattern.compile("\\s*(?<protocol>[a-zA-Z]+://)?((?<username>\\w+):(?<password>[^\\s]+)@)?(?<host>[a-zA-Z\\d\\-\\.]+)?(:(?<port>\\d+))?\\s*");
29+
Pattern.compile("\\s*(?<scheme>[a-zA-Z0-9+\\-.]+://)?((?<username>\\w+):(?<password>[^\\s]+)@)?(?<host>[a-zA-Z\\d\\-.]+)?(:(?<port>\\d+))?\\s*");
3030

3131
/**
3232
* @param args to parse
@@ -53,6 +53,7 @@ public static CliArgs parse(@Nonnull String... args) {
5353

5454
CliArgs cliArgs = new CliArgs();
5555

56+
cliArgs.setScheme(addressMatcher.group("scheme"), "bolt://");
5657
cliArgs.setHost(addressMatcher.group("host"), "localhost");
5758
// Safe, regex only matches integers
5859
String portString = addressMatcher.group("port");
@@ -99,7 +100,7 @@ private static Matcher parseAddressMatcher(ArgumentParser parser, String address
99100
PrintWriter printWriter = new PrintWriter(System.err);
100101
parser.printUsage(printWriter);
101102
printWriter.println("cypher-shell: error: Failed to parse address: '" + address + "'");
102-
printWriter.println("\n Address should be of the form: [username:password@][host][:port]");
103+
printWriter.println("\n Address should be of the form: [scheme://][username:password@][host][:port]");
103104
printWriter.flush();
104105
return null;
105106
}
@@ -114,7 +115,7 @@ private static ArgumentParser setupParser() {
114115
ArgumentGroup connGroup = parser.addArgumentGroup("connection arguments");
115116
connGroup.addArgument("-a", "--address")
116117
.help("address and port to connect to")
117-
.setDefault("localhost:7687");
118+
.setDefault("bolt://localhost:7687");
118119
connGroup.addArgument("-u", "--username")
119120
.setDefault("")
120121
.help("username to connect as. Can also be specified using environment variable NEO4J_USERNAME");

cypher-shell/src/main/java/org/neo4j/shell/cli/CliArgs.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.Optional;
66

77
public class CliArgs {
8+
private String scheme = "bolt://";
89
private String host = "localhost";
910
private int port = 7687;
1011
private String username = "";
@@ -17,6 +18,13 @@ public class CliArgs {
1718
private boolean nonInteractive = false;
1819
private boolean version = false;
1920

21+
/**
22+
* Set the scheme to the primary value, or if null, the fallback value.
23+
*/
24+
void setScheme(@Nullable String primary, @Nonnull String fallback) {
25+
scheme = primary == null ? fallback : primary;
26+
}
27+
2028
/**
2129
* Set the host to the primary value, or if null, the fallback value.
2230
*/
@@ -87,6 +95,11 @@ void setDebugMode(boolean enabled) {
8795
this.debugMode = enabled;
8896
}
8997

98+
@Nonnull
99+
public String getScheme() {
100+
return scheme;
101+
}
102+
90103
@Nonnull
91104
public String getHost() {
92105
return host;

cypher-shell/src/main/java/org/neo4j/shell/state/BoltResult.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,12 @@ public BoltResult(@Nonnull List<Record> records, @Nonnull ResultSummary summary)
1919
this.summary = summary;
2020
}
2121

22+
@Nonnull
2223
public List<Record> getRecords() {
2324
return records;
2425
}
2526

27+
@Nonnull
2628
public ResultSummary getSummary() {
2729
return summary;
2830
}

cypher-shell/src/test/java/org/neo4j/shell/ConnectionConfigTest.java

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,21 @@
22

33
import org.junit.Test;
44
import org.neo4j.driver.v1.Config;
5+
import org.neo4j.shell.log.Logger;
56

67
import static org.junit.Assert.assertEquals;
8+
import static org.mockito.Mockito.mock;
9+
import static org.mockito.Mockito.verify;
710

811
public class ConnectionConfigTest {
9-
ConnectionConfig config = new ConnectionConfig("localhost", 1, "bob", "pass", false);
12+
private Logger logger = mock(Logger.class);
13+
private ConnectionConfig config = new ConnectionConfig(logger, "bolt://", "localhost", 1, "bob",
14+
"pass", false);
15+
16+
@Test
17+
public void scheme() throws Exception {
18+
assertEquals("bolt://", config.scheme());
19+
}
1020

1121
@Test
1222
public void host() throws Exception {
@@ -29,13 +39,23 @@ public void password() throws Exception {
2939
}
3040

3141
@Test
32-
public void driverUrl() throws Exception {
42+
public void driverUrlDefaultScheme() throws Exception {
43+
assertEquals("bolt://localhost:1", config.driverUrl());
44+
}
45+
46+
@Test
47+
public void driverUrlRoutingScheme() throws Exception {
48+
ConnectionConfig config = new ConnectionConfig(logger, "bolt+routing://", "localhost", 1, "bob",
49+
"pass", false);
50+
verify(logger).printError("@|RED Routing is not supported by cypher-shell. Falling back to direct connection.|@");
3351
assertEquals("bolt://localhost:1", config.driverUrl());
3452
}
3553

3654
@Test
3755
public void encryption() {
38-
assertEquals(Config.EncryptionLevel.REQUIRED, new ConnectionConfig("", -1, "", "", true).encryption());
39-
assertEquals(Config.EncryptionLevel.NONE, new ConnectionConfig("", -1, "", "", false).encryption());
56+
assertEquals(Config.EncryptionLevel.REQUIRED,
57+
new ConnectionConfig(logger, "bolt://", "", -1, "", "", true).encryption());
58+
assertEquals(Config.EncryptionLevel.NONE,
59+
new ConnectionConfig(logger, "bolt://", "", -1, "", "", false).encryption());
4060
}
4161
}

cypher-shell/src/test/java/org/neo4j/shell/CypherShellTest.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ public class CypherShellTest {
4646

4747
@Before
4848
public void setup() {
49+
when(mockedBoltStateHandler.getServerVersion()).thenReturn("");
50+
4951
doReturn(System.out).when(logger).getOutputStream();
5052
offlineTestShell = new OfflineTestShell(logger, mockedBoltStateHandler, mockedPrettyPrinter);
5153

@@ -56,7 +58,7 @@ public void setup() {
5658

5759
@Test
5860
public void verifyDelegationOfConnectionMethods() throws CommandException {
59-
ConnectionConfig cc = new ConnectionConfig("", 1, "", "", false);
61+
ConnectionConfig cc = new ConnectionConfig(logger, "bolt://", "", 1, "", "", false);
6062
CypherShell shell = new CypherShell(logger, mockedBoltStateHandler, mockedPrettyPrinter);
6163

6264
shell.connect(cc);

cypher-shell/src/test/java/org/neo4j/shell/cli/AddressArgPatternTest.java

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,23 @@
1616
@RunWith(Parameterized.class)
1717
public class AddressArgPatternTest {
1818

19-
private final String protocol;
19+
private final String scheme;
2020
private final String host;
2121
private final String port;
2222
private final String username;
2323
private final String password;
2424
private final String connString;
2525

26-
public AddressArgPatternTest(String protocol, String host, String port,
26+
public AddressArgPatternTest(String scheme, String host, String port,
2727
String username, String password) {
2828

29-
this.protocol = protocol;
29+
this.scheme = scheme;
3030
this.host = host;
3131
this.port = port;
3232
this.username = username;
3333
this.password = password;
3434

35-
StringBuilder connString = new StringBuilder().append(protocol);
35+
StringBuilder connString = new StringBuilder().append(scheme);
3636
// Only expect username/pass in case host is present
3737
if (!host.isEmpty() && !username.isEmpty() && !password.isEmpty()) {
3838
connString.append(username).append(":").append(password).append("@");
@@ -49,12 +49,12 @@ public AddressArgPatternTest(String protocol, String host, String port,
4949
@Parameterized.Parameters
5050
public static Collection<Object[]> parameters() {
5151
Collection<Object[]> data = new ArrayList<>();
52-
for (final String protocol : getProtocols()) {
52+
for (final String scheme : getSchemes()) {
5353
for (final String username : getUsernames()) {
5454
for (final String password : getPasswords()) {
5555
for (final String host : getHosts()) {
5656
for (final String port : getPorts()) {
57-
data.add(new String[]{protocol, host, port, username, password});
57+
data.add(new String[]{scheme, host, port, username, password});
5858
}
5959
}
6060
}
@@ -79,12 +79,12 @@ private static String[] getUsernames() {
7979
return new String[]{"", "bob1"};
8080
}
8181

82-
private static String[] getProtocols() {
83-
return new String[]{"", "bolt://"};
82+
private static String[] getSchemes() {
83+
return new String[]{"", "bolt://", "bolt+routing://", "w31rd.-+://"};
8484
}
8585

8686
@Test
87-
public void testUserPassProtocolHostPort() {
87+
public void testUserPassschemeHostPort() {
8888
Matcher m = CliArgHelper.ADDRESS_ARG_PATTERN.matcher(" " + connString + " ");
8989
assertTrue("Expected a match: " + connString, m.matches());
9090
if (host.isEmpty()) {
@@ -104,10 +104,10 @@ public void testUserPassProtocolHostPort() {
104104
assertEquals("Mismatched username", username, m.group("username"));
105105
assertEquals("Mismatched password", password, m.group("password"));
106106
}
107-
if (protocol.isEmpty()) {
108-
assertNull("Protocol should have been null", m.group("protocol"));
107+
if (scheme.isEmpty()) {
108+
assertNull("scheme should have been null", m.group("scheme"));
109109
} else {
110-
assertEquals("Mismatched protocol", protocol, m.group("protocol"));
110+
assertEquals("Mismatched scheme", scheme, m.group("scheme"));
111111
}
112112
}
113113
}

cypher-shell/src/test/java/org/neo4j/shell/cli/CliArgHelperTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ public void parseUserName() throws Exception {
103103

104104
@Test
105105
public void parseFullAddress() throws Exception {
106-
CliArgs cliArgs = CliArgHelper.parse("--address", "alice:foo@bar:69");
106+
CliArgs cliArgs = CliArgHelper.parse("--address", "bolt+routing://alice:foo@bar:69");
107107
assertNotNull(cliArgs);
108108
assertEquals("alice", cliArgs.getUsername());
109109
assertEquals("foo", cliArgs.getPassword());

0 commit comments

Comments
 (0)