Skip to content
Merged
5 changes: 5 additions & 0 deletions docs/changelog/138965.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 138965
summary: Add `project_routing` to CLI
area: SQL
type: enhancement
issues: []
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.sql.qa.multi_node;

import org.elasticsearch.test.cluster.ElasticsearchCluster;
import org.elasticsearch.xpack.sql.qa.cli.ProjectRoutingTestCase;
import org.junit.ClassRule;

public class CliProjectRoutingIT extends ProjectRoutingTestCase {
@ClassRule
public static final ElasticsearchCluster cluster = SqlTestCluster.getCluster();

@Override
protected String getTestRestCluster() {
return cluster.getHttpAddresses();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.sql.qa.security;

import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.test.cluster.ElasticsearchCluster;
import org.elasticsearch.xpack.sql.qa.cli.EmbeddedCli.SecurityConfig;
import org.elasticsearch.xpack.sql.qa.cli.ProjectRoutingTestCase;
import org.junit.ClassRule;

public class CliProjectRoutingIT extends ProjectRoutingTestCase {
@ClassRule
public static ElasticsearchCluster cluster = SqlSecurityTestCluster.getCluster();

@Override
protected String getTestRestCluster() {
return cluster.getHttpAddresses();
}

@Override
protected Settings restClientSettings() {
return RestSqlIT.securitySettings();
}

@Override
protected String getProtocol() {
return RestSqlIT.SSL_ENABLED ? "https" : "http";
}

@Override
protected SecurityConfig securityConfig() {
return CliSecurityIT.adminSecurityConfig();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.sql.qa.single_node;

import org.elasticsearch.test.cluster.ElasticsearchCluster;
import org.elasticsearch.xpack.sql.qa.cli.ProjectRoutingTestCase;
import org.junit.ClassRule;

public class CliProjectRoutingIT extends ProjectRoutingTestCase {
@ClassRule
public static final ElasticsearchCluster cluster = SqlTestCluster.getCluster();

@Override
protected String getTestRestCluster() {
return cluster.getHttpAddresses();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.sql.qa.cli;

import java.io.IOException;

import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.matchesRegex;

public abstract class ProjectRoutingTestCase extends CliIntegrationTestCase {

public void testProjectRoutingCommand() throws IOException {
index("test", body -> body.field("name", "foo"));
assertEquals("[?1l>[?1000l[?2004lproject_routing set to [90m_alias:foo[0m", command("project_routing = _alias:foo"));
assertThat(
command("SELECT * FROM test"),
matchesRegex(".*\\s*\\[project_routing\\] is only allowed when cross-project search is enabled\\s*.*")
);
}

public void testProjectRoutingResetCommand() throws IOException {
index("test", body -> body.field("name", "foo"));
assertEquals("[?1l>[?1000l[?2004lproject_routing set to [90m_alias:foo[0m", command("project_routing = _alias:foo"));
assertEquals("[?1l>[?1000l[?2004lproject_routing set to [90mnull[0m", command("project_routing = null"));
assertThat(command("SELECT * FROM test"), matchesRegex(".*\\s*name\\s*\\.*"));
assertThat(readLine(), containsString("----------"));
assertThat(readLine(), matchesRegex(".*\\s*foo\\s*\\.*"));
assertEquals("", readLine());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import org.elasticsearch.xpack.sql.cli.command.FetchSizeCliCommand;
import org.elasticsearch.xpack.sql.cli.command.LenientCliCommand;
import org.elasticsearch.xpack.sql.cli.command.PrintLogoCommand;
import org.elasticsearch.xpack.sql.cli.command.ProjectRoutingCliCommand;
import org.elasticsearch.xpack.sql.cli.command.ServerInfoCliCommand;
import org.elasticsearch.xpack.sql.cli.command.ServerQueryCliCommand;
import org.elasticsearch.xpack.sql.client.ClientException;
Expand Down Expand Up @@ -146,6 +147,7 @@ private void execute(String uri, boolean debug, boolean binary, String keystoreL
new FetchSizeCliCommand(),
new LenientCliCommand(),
new AllowPartialResultsCliCommand(),
new ProjectRoutingCliCommand(),
new FetchSeparatorCliCommand(),
new ServerInfoCliCommand(),
new ServerQueryCliCommand()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public class CliSessionConfiguration {
private boolean debug;
private boolean lenient;
private boolean allowPartialResults;
private String projectRouting;

public CliSessionConfiguration() {
this.fetchSize = CoreProtocol.FETCH_SIZE;
Expand Down Expand Up @@ -67,4 +68,12 @@ public boolean allowPartialResults() {
public void setAllowPartialResults(boolean allowPartialResults) {
this.allowPartialResults = allowPartialResults;
}

public String projectRouting() {
return projectRouting;
}

public void setProjectRouting(String projectRouting) {
this.projectRouting = projectRouting;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
package org.elasticsearch.xpack.sql.cli.command;

import org.elasticsearch.xpack.sql.cli.CliTerminal;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* allows to set project routing for cross-project search
*/
public class ProjectRoutingCliCommand extends AbstractCliCommand {

public ProjectRoutingCliCommand() {
super(Pattern.compile("project(?: |_)routing *= *(.+)", Pattern.CASE_INSENSITIVE));
}

@Override
protected boolean doHandle(CliTerminal terminal, CliSession cliSession, Matcher m, String line) {
String val = m.group(1);
if (val == null || val.equals("null") || val.trim().isEmpty()) {
cliSession.cfg().setProjectRouting(null);
} else {
cliSession.cfg().setProjectRouting(val.trim());
}
terminal.line().text("project_routing set to ").em("" + cliSession.cfg().projectRouting()).end();
return true;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ protected boolean doHandle(CliTerminal terminal, CliSession cliSession, String l
String data;
try {
CliSessionConfiguration cfg = cliSession.cfg();
response = cliClient.basicQuery(line, cfg.getFetchSize(), cfg.isLenient(), cfg.allowPartialResults());
response = cliClient.basicQuery(line, cfg.getFetchSize(), cfg.isLenient(), cfg.allowPartialResults(), cfg.projectRouting());
formatter = new SimpleFormatter(response.columns(), response.rows(), CLI);
data = formatter.formatWithHeader(response.columns(), response.rows());
while (true) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,4 +132,17 @@ public void testAllowPartialSearchResults() {
testTerminal.clear();
}

public void testProjectRouting() {
TestTerminal testTerminal = new TestTerminal();
HttpClient httpClient = mock(HttpClient.class);
CliSession cliSession = new CliSession(httpClient);
ProjectRoutingCliCommand cliCommand = new ProjectRoutingCliCommand();
assertFalse(cliCommand.handle(testTerminal, cliSession, "project_routing"));
assertNull(cliSession.cfg().projectRouting());
assertTrue(cliCommand.handle(testTerminal, cliSession, "project_routing = _alias:_origin"));
assertEquals("_alias:_origin", cliSession.cfg().projectRouting());
assertEquals("project_routing set to <em>_alias:_origin</em><flush/>", testTerminal.toString());
testTerminal.clear();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,11 @@ public void testExceptionHandling() throws Exception {
TestTerminal testTerminal = new TestTerminal();
HttpClient client = mock(HttpClient.class);
CliSession cliSession = new CliSession(client);
when(client.basicQuery("blah", 1000, false, false)).thenThrow(new SQLException("test exception"));
when(client.basicQuery("blah", 1000, false, false, null)).thenThrow(new SQLException("test exception"));
ServerQueryCliCommand cliCommand = new ServerQueryCliCommand();
assertTrue(cliCommand.handle(testTerminal, cliSession, "blah"));
assertEquals("<b>Bad request [</b><i>test exception</i><b>]</b>\n", testTerminal.toString());
verify(client, times(1)).basicQuery(eq("blah"), eq(1000), eq(false), eq(false));
verify(client, times(1)).basicQuery(eq("blah"), eq(1000), eq(false), eq(false), any());
verifyNoMoreInteractions(client);
}

Expand All @@ -45,15 +45,15 @@ public void testOnePageQuery() throws Exception {
HttpClient client = mock(HttpClient.class);
CliSession cliSession = new CliSession(client);
cliSession.cfg().setFetchSize(10);
when(client.basicQuery("test query", 10, false, false)).thenReturn(fakeResponse("", true, "foo"));
when(client.basicQuery("test query", 10, false, false, null)).thenReturn(fakeResponse("", true, "foo"));
ServerQueryCliCommand cliCommand = new ServerQueryCliCommand();
assertTrue(cliCommand.handle(testTerminal, cliSession, "test query"));
assertEquals("""
field \s
---------------
foo \s
<flush/>""", testTerminal.toString());
verify(client, times(1)).basicQuery(eq("test query"), eq(10), eq(false), eq(false));
verify(client, times(1)).basicQuery(eq("test query"), eq(10), eq(false), eq(false), any());
verifyNoMoreInteractions(client);
}

Expand All @@ -62,7 +62,7 @@ public void testThreePageQuery() throws Exception {
HttpClient client = mock(HttpClient.class);
CliSession cliSession = new CliSession(client);
cliSession.cfg().setFetchSize(10);
when(client.basicQuery("test query", 10, false, false)).thenReturn(fakeResponse("my_cursor1", true, "first"));
when(client.basicQuery("test query", 10, false, false, null)).thenReturn(fakeResponse("my_cursor1", true, "first"));
when(client.nextPage("my_cursor1")).thenReturn(fakeResponse("my_cursor2", false, "second"));
when(client.nextPage("my_cursor2")).thenReturn(fakeResponse("", false, "third"));
ServerQueryCliCommand cliCommand = new ServerQueryCliCommand();
Expand All @@ -74,7 +74,7 @@ public void testThreePageQuery() throws Exception {
second \s
third \s
<flush/>""", testTerminal.toString());
verify(client, times(1)).basicQuery(eq("test query"), eq(10), eq(false), eq(false));
verify(client, times(1)).basicQuery(eq("test query"), eq(10), eq(false), eq(false), any());
verify(client, times(2)).nextPage(any());
verifyNoMoreInteractions(client);
}
Expand All @@ -86,7 +86,7 @@ public void testTwoPageQueryWithSeparator() throws Exception {
cliSession.cfg().setFetchSize(15);
// Set a separator
cliSession.cfg().setFetchSeparator("-----");
when(client.basicQuery("test query", 15, false, false)).thenReturn(fakeResponse("my_cursor1", true, "first"));
when(client.basicQuery("test query", 15, false, false, null)).thenReturn(fakeResponse("my_cursor1", true, "first"));
when(client.nextPage("my_cursor1")).thenReturn(fakeResponse("", false, "second"));
ServerQueryCliCommand cliCommand = new ServerQueryCliCommand();
assertTrue(cliCommand.handle(testTerminal, cliSession, "test query"));
Expand All @@ -97,7 +97,7 @@ public void testTwoPageQueryWithSeparator() throws Exception {
-----
second \s
<flush/>""", testTerminal.toString());
verify(client, times(1)).basicQuery(eq("test query"), eq(15), eq(false), eq(false));
verify(client, times(1)).basicQuery(eq("test query"), eq(15), eq(false), eq(false), any());
verify(client, times(1)).nextPage(any());
verifyNoMoreInteractions(client);
}
Expand All @@ -107,7 +107,7 @@ public void testCursorCleanupOnError() throws Exception {
HttpClient client = mock(HttpClient.class);
CliSession cliSession = new CliSession(client);
cliSession.cfg().setFetchSize(15);
when(client.basicQuery("test query", 15, false, false)).thenReturn(fakeResponse("my_cursor1", true, "first"));
when(client.basicQuery("test query", 15, false, false, null)).thenReturn(fakeResponse("my_cursor1", true, "first"));
when(client.nextPage("my_cursor1")).thenThrow(new SQLException("test exception"));
when(client.queryClose("my_cursor1", Mode.CLI)).thenReturn(true);
ServerQueryCliCommand cliCommand = new ServerQueryCliCommand();
Expand All @@ -118,7 +118,7 @@ public void testCursorCleanupOnError() throws Exception {
first \s
<b>Bad request [</b><i>test exception</i><b>]</b>
""", testTerminal.toString());
verify(client, times(1)).basicQuery(eq("test query"), eq(15), eq(false), eq(false));
verify(client, times(1)).basicQuery(eq("test query"), eq(15), eq(false), eq(false), any());
verify(client, times(1)).nextPage(any());
verify(client, times(1)).queryClose(eq("my_cursor1"), eq(Mode.CLI));
verifyNoMoreInteractions(client);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,12 +85,12 @@ public SqlQueryResponse basicQuery(String query, int fetchSize) throws SQLExcept
}

public SqlQueryResponse basicQuery(String query, int fetchSize, boolean fieldMultiValueLeniency) throws SQLException {
return basicQuery(query, fetchSize, fieldMultiValueLeniency, cfg.allowPartialSearchResults(), cfg.projectRouting());
return basicQuery(query, fetchSize, fieldMultiValueLeniency, cfg.allowPartialSearchResults());
}

public SqlQueryResponse basicQuery(String query, int fetchSize, boolean fieldMultiValueLeniency, boolean allowPartialSearchResults)
throws SQLException {
return basicQuery(query, fetchSize, fieldMultiValueLeniency, allowPartialSearchResults, cfg.projectRouting());
return basicQuery(query, fetchSize, fieldMultiValueLeniency, allowPartialSearchResults, null);
}

public SqlQueryResponse basicQuery(
Expand All @@ -102,6 +102,10 @@ public SqlQueryResponse basicQuery(
) throws SQLException {
// TODO allow customizing the time zone - this is what session set/reset/get should be about
// method called only from CLI

if (projectRouting == null) {
projectRouting = cfg.projectRouting();
}
SqlQueryRequest sqlRequest = new SqlQueryRequest(
query,
emptyList(),
Expand All @@ -119,6 +123,7 @@ public SqlQueryResponse basicQuery(
allowPartialSearchResults,
projectRouting
);

return query(sqlRequest).response();
}

Expand Down