Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion geequel-shell/src/main/java/org/neo4j/shell/Main.java
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ void startShell(@Nonnull CliArgs cliArgs) {
try {
CypherShell shell = new CypherShell(logger, prettyConfig);
// Can only prompt for password if input has not been redirected
connectMaybeInteractively(shell, connectionConfig, isInputInteractive(), isOutputInteractive());
connectMaybeInteractively(shell, connectionConfig, isInputInteractive(cliArgs), isOutputInteractive());

// Construct shell runner after connecting, due to interrupt handling
ShellRunner shellRunner = ShellRunner.getShellRunner(cliArgs, shell, logger, connectionConfig);
Expand Down
18 changes: 9 additions & 9 deletions geequel-shell/src/main/java/org/neo4j/shell/ShellRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,10 @@
import org.neo4j.shell.parser.ShellStatementParser;

import javax.annotation.Nonnull;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.*;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;

import static org.fusesource.jansi.internal.CLibrary.STDIN_FILENO;
import static org.fusesource.jansi.internal.CLibrary.STDOUT_FILENO;
Expand Down Expand Up @@ -80,8 +79,9 @@ static ShellRunner getShellRunner(@Nonnull CliArgs cliArgs,
return new InteractiveShellRunner(cypherShell, cypherShell, logger, new ShellStatementParser(),
System.in, FileHistorian.getDefaultHistoryFile(), userMessagesHandler);
} else {
InputStream inputStream = cliArgs.getFile().isPresent() ? Files.newInputStream(Paths.get(cliArgs.getFile().get())) : System.in;
return new NonInteractiveShellRunner(cliArgs.getFailBehavior(), cypherShell, logger,
new ShellStatementParser(), System.in);
new ShellStatementParser(), inputStream);
}
}

Expand All @@ -94,7 +94,7 @@ static boolean shouldBeInteractive(@Nonnull CliArgs cliArgs) {
return false;
}

return isInputInteractive();
return isInputInteractive(cliArgs);
}

/**
Expand All @@ -104,18 +104,18 @@ static boolean shouldBeInteractive(@Nonnull CliArgs cliArgs) {
* @return true if the shell is reading from an interactive terminal, false otherwise (e.g., we are reading from a
* file).
*/
static boolean isInputInteractive() {
static boolean isInputInteractive(@Nonnull CliArgs cliArgs) {
if (isWindows()) {
// Input will never be a TTY on windows and it isatty seems to be able to block forever on Windows so avoid
// calling it.
return System.console() != null;
return System.console() != null && !cliArgs.getFile().isPresent();
}
try {
return 1 == isatty(STDIN_FILENO);
} catch (Throwable ignored) {
// system is not using libc (like Alpine Linux)
// Fallback to checking stdin OR stdout
return System.console() != null;
return System.console() != null && !cliArgs.getFile().isPresent();
}
}

Expand Down
13 changes: 10 additions & 3 deletions geequel-shell/src/main/java/org/neo4j/shell/cli/CliArgHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,11 @@ public static CliArgs parse(@Nonnull String... args) {
// Other arguments
// geequel string might not be given, represented by null
cliArgs.setCypher(ns.getString("geequel"));
// Fail behavior as sensible default and returns a proper type
cliArgs.setFailBehavior(ns.get("fail-behavior"));

cliArgs.setFile(ns.getString("file"));

//Set Output format
cliArgs.setFormat(Format.parse(ns.get("format")));
cliArgs.setFormat(Format.parse(ns.get("format"), cliArgs));

cliArgs.setEncryption(ns.getBoolean("encryption"));

Expand All @@ -111,6 +111,9 @@ public static CliArgs parse(@Nonnull String... args) {

cliArgs.setDriverVersion(ns.getBoolean("driver-version"));

// Fail behavior as sensible default and returns a proper type
cliArgs.setFailBehavior(ns.get("fail-behavior"));

return cliArgs;
}

Expand Down Expand Up @@ -181,6 +184,10 @@ private static ArgumentParser setupParser()
.help("print additional debug information")
.action(new StoreTrueArgumentAction());

parser.addArgument("-f", "--file")
.help("specify a file to run as a cypher script")
.setDefault("");

parser.addArgument("--non-interactive")
.help("force non-interactive mode, only useful if auto-detection fails (like on Windows)")
.dest("force-non-interactive")
Expand Down
13 changes: 13 additions & 0 deletions geequel-shell/src/main/java/org/neo4j/shell/cli/CliArgs.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ public class CliArgs {
private boolean driverVersion = false;
private int numSampleRows = DEFAULT_NUM_SAMPLE_ROWS;
private boolean wrap = true;
private Optional<String> file = Optional.empty();

/**
* Set the scheme to the primary value, or if null, the fallback value.
Expand Down Expand Up @@ -101,6 +102,13 @@ public void setCypher(@Nullable String cypher) {
this.cypher = Optional.ofNullable(cypher);
}

/**
* Set the file path to the cypher script to execute
*/
public void setFile (@Nullable String file) {
this.file = Optional.ofNullable(file);
}

/**
* Set whether the connection should be encrypted
*/
Expand Down Expand Up @@ -156,6 +164,11 @@ public Optional<String> getCypher() {
return cypher;
}

@Nonnull
public Optional<String> getFile() {
return file;
}

@Nonnull
public Format getFormat() {
return format;
Expand Down
4 changes: 2 additions & 2 deletions geequel-shell/src/main/java/org/neo4j/shell/cli/Format.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,13 @@ public enum Format {
PLAIN;
// TODO JSON, strictly intended for machine consumption with data formatted in JSON

public static Format parse(@Nonnull String format) {
public static Format parse(@Nonnull String format, @Nonnull CliArgs cliArgs) {
if (format.equalsIgnoreCase(PLAIN.name())) {
return PLAIN;
} else if (format.equalsIgnoreCase( VERBOSE.name() )) {
return VERBOSE;
} else {
return isInputInteractive() && isOutputInteractive() ? VERBOSE : PLAIN;
return isInputInteractive(cliArgs) && isOutputInteractive() ? VERBOSE : PLAIN;
}
}
}
59 changes: 59 additions & 0 deletions geequel-shell/src/test/java/org/neo4j/shell/CypherShellTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.neo4j.driver.v1.summary.ResultSummary;
import org.neo4j.shell.cli.CliArgHelper;
import org.neo4j.shell.cli.CliArgs;
import org.neo4j.shell.cli.NonInteractiveShellRunner;
import org.neo4j.shell.cli.StringShellRunner;
import org.neo4j.shell.commands.CommandExecutable;
import org.neo4j.shell.commands.CommandHelper;
Expand All @@ -41,6 +42,8 @@
import org.neo4j.shell.state.BoltStateHandler;
import org.neo4j.shell.state.ListBoltResult;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Optional;

Expand Down Expand Up @@ -281,6 +284,62 @@ public void specifyingACypherStringShouldGiveAStringRunner() throws IOException
}
}


@Test
public void specifyingACypherStringShouldAlwaysGiveAStringRunner() throws IOException {
CliArgs cliArgs = CliArgHelper.parse("-f", "test-file", "MATCH (n) RETURN n ");

ConnectionConfig connectionConfig = mock(ConnectionConfig.class);

ShellRunner shellRunner = ShellRunner.getShellRunner(cliArgs, offlineTestShell, logger, connectionConfig);

if (!(shellRunner instanceof StringShellRunner)) {
fail("Expected a different runner than: " + shellRunner.getClass().getSimpleName());
}

cliArgs = CliArgHelper.parse("MATCH (n) RETURN n ", "-f", "test-file");

shellRunner = ShellRunner.getShellRunner(cliArgs, offlineTestShell, logger, connectionConfig);

if (!(shellRunner instanceof StringShellRunner)) {
fail("Expected a different runner than: " + shellRunner.getClass().getSimpleName());
}
}


@Test
public void specifyingAFilePathShouldGiveANonInteractiveRunner() throws IOException {
File file = File.createTempFile("test-file", ".cypher");
file.deleteOnExit();
FileOutputStream fos = new FileOutputStream(file);
fos.write("RETURN 1;".getBytes());
fos.close();
CliArgs cliArgs = CliArgHelper.parse("-f", file.getAbsolutePath());

ConnectionConfig connectionConfig = mock(ConnectionConfig.class);

ShellRunner shellRunner = ShellRunner.getShellRunner(cliArgs, offlineTestShell, logger, connectionConfig);

if (!(shellRunner instanceof NonInteractiveShellRunner)) {
fail("Expected a different runner than: " + shellRunner.getClass().getSimpleName());
}
}

@Test
public void specifyingANonexistentFilePathShouldThrowAnError() throws IOException {
CliArgs cliArgs = CliArgHelper.parse("-f", "test-file");

ConnectionConfig connectionConfig = mock(ConnectionConfig.class);

try {
ShellRunner.getShellRunner(cliArgs, offlineTestShell, logger, connectionConfig);
} catch (IOException e) {
assertEquals("java.nio.file.NoSuchFileException: test-file", e.toString());
return;
}
fail("Expected an exception to be thrown");
}

@Test
public void setParameterDoesNotTriggerByBoltError() throws CommandException {
// given
Expand Down
14 changes: 14 additions & 0 deletions geequel-shell/src/test/java/org/neo4j/shell/cli/CliArgsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -117,4 +117,18 @@ public void setCypher() throws Exception {
cliArgs.setCypher(null);
assertFalse(cliArgs.getCypher().isPresent());
}

@Test
public void setFile() throws Exception {
// default
assertFalse(cliArgs.getFile().isPresent());

cliArgs.setFile("foo");
assertTrue(cliArgs.getFile().isPresent());
//noinspection OptionalGetWithoutIsPresent
assertEquals("foo", cliArgs.getFile().get());

cliArgs.setFile(null);
assertFalse(cliArgs.getFile().isPresent());
}
}