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

Commit 89a7004

Browse files
authored
Merge pull request #167 from pontusmelke/1.2-file-as-parameter
Pass a cypher file as a parameter
2 parents 7ecada0 + 19ab904 commit 89a7004

File tree

11 files changed

+174
-6
lines changed

11 files changed

+174
-6
lines changed

cypher-shell/src/integration-test/java/org/neo4j/shell/MainIntegrationTest.java

Lines changed: 103 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,26 @@
77

88
import java.io.ByteArrayInputStream;
99
import java.io.ByteArrayOutputStream;
10+
import java.io.FileNotFoundException;
1011
import java.io.InputStream;
1112
import java.io.PrintStream;
1213

14+
import org.neo4j.driver.exceptions.ClientException;
1315
import org.neo4j.driver.exceptions.ServiceUnavailableException;
1416
import org.neo4j.shell.cli.CliArgs;
1517
import org.neo4j.shell.log.AnsiLogger;
1618
import org.neo4j.shell.log.Logger;
19+
import org.neo4j.shell.prettyprint.LinePrinter;
1720
import org.neo4j.shell.prettyprint.PrettyConfig;
21+
import org.neo4j.shell.prettyprint.ToStringLinePrinter;
1822

23+
import static java.lang.String.format;
1924
import static org.junit.Assert.assertEquals;
2025
import static org.junit.Assert.assertTrue;
26+
import static org.mockito.Matchers.any;
27+
import static org.mockito.Mockito.mock;
28+
import static org.mockito.Mockito.verify;
29+
import static org.mockito.Mockito.verifyNoMoreInteractions;
2130

2231
public class MainIntegrationTest
2332
{
@@ -73,11 +82,10 @@ public void promptsOnWrongAuthenticationIfInteractive() throws Exception {
7382
// should be connected
7483
assertTrue(shell.isConnected());
7584
// should have prompted and set the username and password
85+
assertEquals( format( "username: neo4j%npassword: ***%n" ), baos.toString() );
7686
assertEquals("neo4j", connectionConfig.username());
7787
assertEquals("neo", connectionConfig.password());
7888

79-
String out = baos.toString();
80-
assertEquals( String.format( "username: neo4j%npassword: ***%n" ), out );
8189
}
8290

8391
@Test
@@ -133,9 +141,101 @@ public void wrongPortWithNeo4j() throws Exception
133141
main.connectMaybeInteractively( shell, connectionConfig, true, true );
134142
}
135143

144+
@Test
145+
public void shouldAskForCredentialsWhenConnectingWithAFile() throws Exception {
146+
//given
147+
148+
assertEquals("", connectionConfig.username());
149+
assertEquals("", connectionConfig.password());
150+
151+
//when
152+
CliArgs cliArgs = new CliArgs();
153+
cliArgs.setInputFilename(fileFromResource("single.cypher"));
154+
ShellAndConnection sac = getShell(cliArgs);
155+
CypherShell shell = sac.shell;
156+
ConnectionConfig connectionConfig = sac.connectionConfig;
157+
main.connectMaybeInteractively( shell, connectionConfig, true, true );
158+
159+
// then we should have prompted and set the username and password
160+
assertEquals( format( "username: neo4j%npassword: ***%n" ), baos.toString() );
161+
assertEquals("neo4j", connectionConfig.username());
162+
assertEquals("neo", connectionConfig.password());
163+
}
164+
165+
@Test
166+
public void shouldReadSingleCypherStatementsFromFile() throws Exception {
167+
assertEquals(format( "result%n42%n" ), executeFileNonInteractively(fileFromResource("single.cypher")));
168+
}
169+
170+
@Test
171+
public void shouldReadEmptyCypherStatementsFile() throws Exception {
172+
assertEquals("", executeFileNonInteractively(fileFromResource("empty.cypher")));
173+
}
174+
175+
@Test
176+
public void shouldReadMultipleCypherStatementsFromFile() throws Exception {
177+
assertEquals(format( "result%n42%n" +
178+
"result%n1337%n" +
179+
"result%n\"done\"%n"), executeFileNonInteractively(fileFromResource("multiple.cypher")));
180+
}
181+
182+
@Test
183+
public void shouldFailIfInputFileDoesntExist() throws Exception {
184+
// expect
185+
exception.expect( FileNotFoundException.class);
186+
exception.expectMessage( "what.cypher (No such file or directory)" );
187+
executeFileNonInteractively("what.cypher");
188+
}
189+
190+
@Test
191+
public void shouldHandleInvalidCypherFromFile() throws Exception {
192+
//given
193+
Logger logger = mock(Logger.class);
194+
195+
196+
// when
197+
String actual = executeFileNonInteractively( fileFromResource( "invalid.cypher" ), logger);
198+
199+
//then we print the first valid row
200+
assertEquals( format( "result%n42%n" ), actual );
201+
//and print errors to the error log
202+
verify(logger).printError(any( ClientException.class ));
203+
verifyNoMoreInteractions(logger);
204+
}
205+
206+
private String executeFileNonInteractively(String filename) throws Exception {
207+
return executeFileNonInteractively(filename, mock(Logger.class));
208+
}
209+
210+
private String executeFileNonInteractively(String filename, Logger logger) throws Exception
211+
{
212+
CliArgs cliArgs = new CliArgs();
213+
cliArgs.setInputFilename(filename);
214+
215+
ToStringLinePrinter linePrinter = new ToStringLinePrinter();
216+
ShellAndConnection sac = getShell( cliArgs, linePrinter );
217+
CypherShell shell = sac.shell;
218+
ConnectionConfig connectionConfig = sac.connectionConfig;
219+
main.connectMaybeInteractively( shell, connectionConfig, true, true );
220+
ShellRunner shellRunner = ShellRunner.getShellRunner(cliArgs, shell, logger, connectionConfig);
221+
shellRunner.runUntilEnd();
222+
223+
return linePrinter.result();
224+
}
225+
226+
private String fileFromResource(String filename)
227+
{
228+
return getClass().getClassLoader().getResource(filename).getFile();
229+
}
230+
136231
private ShellAndConnection getShell( CliArgs cliArgs )
137232
{
138233
Logger logger = new AnsiLogger( cliArgs.getDebugMode() );
234+
return getShell( cliArgs, logger );
235+
}
236+
237+
private ShellAndConnection getShell( CliArgs cliArgs, LinePrinter linePrinter )
238+
{
139239
PrettyConfig prettyConfig = new PrettyConfig( cliArgs );
140240
ConnectionConfig connectionConfig = new ConnectionConfig(
141241
cliArgs.getScheme(),
@@ -146,6 +246,6 @@ private ShellAndConnection getShell( CliArgs cliArgs )
146246
cliArgs.getEncryption(),
147247
cliArgs.getDatabase() );
148248

149-
return new ShellAndConnection( new CypherShell( logger, prettyConfig, true, new ShellParameterMap() ), connectionConfig );
249+
return new ShellAndConnection( new CypherShell( linePrinter, prettyConfig, true, new ShellParameterMap() ), connectionConfig );
150250
}
151251
}

cypher-shell/src/integration-test/resources/empty.cypher

Whitespace-only changes.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
RETURN 42 AS result;
2+
THIS IS NOT CYPHER;
3+
RETURN 1337 AS result;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
//do this first
2+
RETURN 42 AS result;
3+
//then we do
4+
RETURN 1337 AS result;
5+
//and finally we do
6+
RETURN "done" AS result;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
RETURN 42 AS result;

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

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@
99
import org.neo4j.shell.parser.ShellStatementParser;
1010

1111
import javax.annotation.Nonnull;
12+
import java.io.BufferedInputStream;
13+
import java.io.File;
14+
import java.io.FileInputStream;
15+
import java.io.FileNotFoundException;
1216
import java.io.IOException;
17+
import java.io.InputStream;
1318

1419
import static org.fusesource.jansi.internal.CLibrary.STDIN_FILENO;
1520
import static org.fusesource.jansi.internal.CLibrary.STDOUT_FILENO;
@@ -54,8 +59,9 @@ static ShellRunner getShellRunner(@Nonnull CliArgs cliArgs,
5459
return new InteractiveShellRunner(cypherShell, cypherShell, cypherShell, logger, new ShellStatementParser(),
5560
System.in, FileHistorian.getDefaultHistoryFile(), userMessagesHandler, connectionConfig);
5661
} else {
62+
5763
return new NonInteractiveShellRunner(cliArgs.getFailBehavior(), cypherShell, logger,
58-
new ShellStatementParser(), System.in);
64+
new ShellStatementParser(), getInputStream(cliArgs));
5965
}
6066
}
6167

@@ -64,7 +70,8 @@ static ShellRunner getShellRunner(@Nonnull CliArgs cliArgs,
6470
* @return true if an interactive shellrunner should be used, false otherwise
6571
*/
6672
static boolean shouldBeInteractive(@Nonnull CliArgs cliArgs) {
67-
if (cliArgs.getNonInteractive()) {
73+
if ( cliArgs.getNonInteractive() || cliArgs.getInputFilename() != null )
74+
{
6875
return false;
6976
}
7077

@@ -114,4 +121,18 @@ static boolean isOutputInteractive() {
114121
return System.console() != null;
115122
}
116123
}
124+
125+
/**
126+
* If an input file has been defined use that, otherwise use STDIN
127+
* @throws FileNotFoundException if the provided input file doesn't exist
128+
*/
129+
static InputStream getInputStream(CliArgs cliArgs) throws FileNotFoundException
130+
{
131+
if ( cliArgs.getInputFilename() == null)
132+
{
133+
return System.in;
134+
} else {
135+
return new BufferedInputStream( new FileInputStream( new File(cliArgs.getInputFilename()) ));
136+
}
137+
}
117138
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ private static CliArgs getCliArgs( CliArgs cliArgs, ArgumentParser parser, Names
9898
}
9999
cliArgs.setEncryption(ns.getBoolean("encryption"));
100100
cliArgs.setDatabase(ns.getString("database"));
101+
cliArgs.setInputFilename(ns.getString( "file" ) );
101102

102103
//----------------
103104
// Other arguments
@@ -167,6 +168,8 @@ private static ArgumentParser setupParser(ParameterMap parameterMap)
167168
connGroup.addArgument("-d", "--database")
168169
.help("database to connect to. Can also be specified using environment variable " + ConnectionConfig.DATABASE_ENV_VAR)
169170
.setDefault("");
171+
connGroup.addArgument("-f", "--file")
172+
.help("Pass a file with cypher statements to be executed. After the statements have been executed cypher-shell will be shutdown");
170173

171174
MutuallyExclusiveGroup failGroup = parser.addMutuallyExclusiveGroup();
172175
failGroup.addArgument("--fail-fast")

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class CliArgs {
3232
private boolean driverVersion = false;
3333
private int numSampleRows = DEFAULT_NUM_SAMPLE_ROWS;
3434
private boolean wrap = true;
35+
private String inputFilename = null;
3536
private ParameterMap parameters = new ShellParameterMap();
3637

3738
/**
@@ -111,6 +112,14 @@ public void setNonInteractive(boolean nonInteractive) {
111112
this.nonInteractive = nonInteractive;
112113
}
113114

115+
/**
116+
* Sets a filename where to read Cypher statements from, much like piping statements from a file.
117+
*/
118+
public void setInputFilename(String inputFilename)
119+
{
120+
this.inputFilename = inputFilename;
121+
}
122+
114123
/**
115124
* Enable/disable debug mode
116125
*/
@@ -174,6 +183,11 @@ public boolean getNonInteractive() {
174183
return nonInteractive;
175184
}
176185

186+
public String getInputFilename()
187+
{
188+
return inputFilename;
189+
}
190+
177191
public boolean getVersion() {
178192
return version;
179193
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,7 @@ public void promptsForPassIfUserExistsIfInteractive() throws Exception {
199199
}
200200

201201
@Test
202-
public void promptsSielntlyForPassIfUserExistsIfOutputRedirected() throws Exception {
202+
public void promptsSilentlyForPassIfUserExistsIfOutputRedirected() throws Exception {
203203
doThrow(authException).doNothing().when(shell).connect(connectionConfig);
204204
doReturn("bob").when(connectionConfig).username();
205205

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,4 +240,17 @@ public void shouldFailForInvalidSyntaxForArg() throws Exception {
240240
CliArgHelper.parseAndThrow( "-P", "foo: => 'nanana'");
241241
}
242242

243+
@Test
244+
public void testDefaultInputFileName() {
245+
CliArgs arguments = CliArgHelper.parse();
246+
assertNotNull( arguments );
247+
assertNull( arguments.getInputFilename() );
248+
}
249+
250+
@Test
251+
public void testSetInputFileName() {
252+
CliArgs arguments = CliArgHelper.parse("--file", "foo");
253+
assertNotNull( arguments );
254+
assertEquals( "foo", arguments.getInputFilename() );
255+
}
243256
}

0 commit comments

Comments
 (0)