From f470bf37fba470b5b623b2b768aebbd02724f259 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Tue, 12 Oct 2021 16:38:32 +0100 Subject: [PATCH 1/4] Basic user agent functionality --- .../co/elastic/clients/base/Transport.java | 2 + .../co/elastic/clients/base/UserAgent.java | 73 ++++++++++++++++++ .../base/rest_client/RestClientTransport.java | 61 ++++++++++++--- .../elastic/clients/base/UserAgentTest.java | 77 +++++++++++++++++++ 4 files changed, 201 insertions(+), 12 deletions(-) create mode 100644 java-client/src/main/java/co/elastic/clients/base/UserAgent.java create mode 100644 java-client/src/test/java/co/elastic/clients/base/UserAgentTest.java diff --git a/java-client/src/main/java/co/elastic/clients/base/Transport.java b/java-client/src/main/java/co/elastic/clients/base/Transport.java index 6685e8b5e..8cc3bba30 100644 --- a/java-client/src/main/java/co/elastic/clients/base/Transport.java +++ b/java-client/src/main/java/co/elastic/clients/base/Transport.java @@ -41,4 +41,6 @@ CompletableFuture performRequestAsync( ); JsonpMapper jsonpMapper(); + + String userAgent(); } diff --git a/java-client/src/main/java/co/elastic/clients/base/UserAgent.java b/java-client/src/main/java/co/elastic/clients/base/UserAgent.java new file mode 100644 index 000000000..011ac7c3e --- /dev/null +++ b/java-client/src/main/java/co/elastic/clients/base/UserAgent.java @@ -0,0 +1,73 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 co.elastic.clients.base; + +import java.util.Collections; +import java.util.Map; + +/** + * Models a user agent, consisting of a name, version, + * and optional key-value metadata. + */ +public class UserAgent { + + // TODO: move this to a resource file + static final String DEFAULT_NAME = "elasticsearch-java"; + + // TODO: move this to a resource file + static final String DEFAULT_VERSION = "1.2.3"; + + // Default user agent, constructed from default repo name and version + public static final UserAgent DEFAULT = new UserAgent(DEFAULT_NAME, DEFAULT_VERSION); + + private final String name; + private final String version; + private final Map metadata; + + public UserAgent(String name, String version, Map metadata) { + this.name = name; + this.version = version; + this.metadata = metadata; + } + + public UserAgent(String repoName, String version) { + this(repoName, version, Collections.emptyMap()); + } + + @Override + public String toString() { + if (metadata.isEmpty()) { + return String.format("%s/%s", name, version); + } + else { + StringBuilder metadataString = new StringBuilder(); + for (Map.Entry entry : metadata.entrySet()) { + if (metadataString.length() > 0) { + metadataString.append("; "); + } + metadataString.append(entry.getKey()); + metadataString.append(' '); + metadataString.append(entry.getValue()); + } + return String.format("%s/%s (%s)", name, version, metadataString); + } + } + +} diff --git a/java-client/src/main/java/co/elastic/clients/base/rest_client/RestClientTransport.java b/java-client/src/main/java/co/elastic/clients/base/rest_client/RestClientTransport.java index 8d15fec38..11d88f3a0 100644 --- a/java-client/src/main/java/co/elastic/clients/base/rest_client/RestClientTransport.java +++ b/java-client/src/main/java/co/elastic/clients/base/rest_client/RestClientTransport.java @@ -19,17 +19,13 @@ package co.elastic.clients.base.rest_client; -import co.elastic.clients.base.ApiException; -import co.elastic.clients.base.BooleanEndpoint; -import co.elastic.clients.base.BooleanResponse; -import co.elastic.clients.base.ElasticsearchCatRequest; -import co.elastic.clients.base.Endpoint; -import co.elastic.clients.base.Transport; +import co.elastic.clients.base.*; import co.elastic.clients.json.JsonpDeserializer; import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.NdJsonpSerializable; import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; +import org.apache.http.Header; import org.apache.http.entity.ByteArrayEntity; import org.apache.http.entity.ContentType; import org.elasticsearch.client.Cancellable; @@ -51,12 +47,30 @@ public class RestClientTransport implements Transport { private final RestClient restClient; private final JsonpMapper mapper; - private RequestOptions requestOptions; + private final RequestOptions requestOptions; - public RestClientTransport(RestClient restClient, JsonpMapper mapper, @Nullable RequestOptions options) { + public RestClientTransport(RestClient restClient, JsonpMapper mapper, @Nullable RequestOptions options, + @Nullable UserAgent userAgent) { this.restClient = restClient; this.mapper = mapper; - this.requestOptions = options; + RequestOptions baseOptions = options == null ? RequestOptions.DEFAULT : options; + String manualUserAgent = findUserAgentIn(baseOptions); + if (manualUserAgent == null && userAgent == null) { + this.requestOptions = baseOptions.toBuilder().addHeader("User-Agent", UserAgent.DEFAULT.toString()).build(); + } + else if (manualUserAgent == null) { + this.requestOptions = baseOptions.toBuilder().addHeader("User-Agent", userAgent.toString()).build(); + } + else if (userAgent == null) { + this.requestOptions = baseOptions; + } + else { + throw new IllegalArgumentException("Multiple user agents specified"); + } + } + + public RestClientTransport(RestClient restClient, JsonpMapper mapper, @Nullable RequestOptions options) { + this(restClient, mapper, options, null); } public RestClientTransport(RestClient restClient, JsonpMapper mapper) { @@ -84,16 +98,41 @@ public RestClientTransport withRequestOptions(Function ResponseT performRequest( RequestT request, Endpoint endpoint @@ -154,9 +193,7 @@ private org.elasticsearch.client.Request prepareLowLevelRequest( org.elasticsearch.client.Request clientReq = new org.elasticsearch.client.Request(method, path); clientReq.addParameters(params); - if (requestOptions != null) { - clientReq.setOptions(requestOptions); - } + clientReq.setOptions(requestOptions); // Request-type specific parameters. if (request instanceof ElasticsearchCatRequest) { diff --git a/java-client/src/test/java/co/elastic/clients/base/UserAgentTest.java b/java-client/src/test/java/co/elastic/clients/base/UserAgentTest.java new file mode 100644 index 000000000..a9acd47f2 --- /dev/null +++ b/java-client/src/test/java/co/elastic/clients/base/UserAgentTest.java @@ -0,0 +1,77 @@ +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you 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 co.elastic.clients.base; + +import co.elastic.clients.base.rest_client.RestClientTransport; +import org.apache.http.HttpHost; +import org.elasticsearch.client.RequestOptions; +import org.elasticsearch.client.RestClient; +import org.junit.Test; + +import static co.elastic.clients.base.UserAgent.DEFAULT_NAME; +import static co.elastic.clients.base.UserAgent.DEFAULT_VERSION; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +public class UserAgentTest { + + private static final String DEFAULT_USER_AGENT = String.format("%s/%s", DEFAULT_NAME, DEFAULT_VERSION); + + private static final String CUSTOM_NAME = "ÜberClient"; + private static final String CUSTOM_VERSION = "1.0.13"; + private static final String CUSTOM_USER_AGENT = String.format("%s/%s", CUSTOM_NAME, CUSTOM_VERSION); + + private static final String CUSTOM_NAME_2 = "MegaClient"; + private static final String CUSTOM_VERSION_2 = "6.7.8"; + + @Test + public void testDefaultUserAgent() throws Exception { + RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); + Transport transport = new RestClientTransport(restClient, null); + assertEquals(DEFAULT_USER_AGENT, transport.userAgent()); + } + + @Test + public void testCustomUserAgent() throws Exception { + RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); + Transport transport = new RestClientTransport(restClient, null, null, + new UserAgent(CUSTOM_NAME, CUSTOM_VERSION)); + assertEquals(CUSTOM_USER_AGENT, transport.userAgent()); + } + + @Test + public void testManualUserAgent() throws Exception { + RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); + Transport transport = new RestClientTransport(restClient, null, + RequestOptions.DEFAULT.toBuilder().addHeader("User-Agent", CUSTOM_USER_AGENT).build()); + assertEquals(CUSTOM_USER_AGENT, transport.userAgent()); + } + + @Test + public void testMultipleUserAgentsThrowsException() throws Exception { + RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); + Exception exception = assertThrows(IllegalArgumentException.class, () -> { + Transport transport = new RestClientTransport(restClient, null, + RequestOptions.DEFAULT.toBuilder().addHeader("User-Agent", CUSTOM_USER_AGENT).build(), + new UserAgent(CUSTOM_NAME_2, CUSTOM_VERSION_2)); + }); + } + +} From 4be8a6195bc6731cc4f59bad498879d336bd4875 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Wed, 13 Oct 2021 09:44:09 +0100 Subject: [PATCH 2/4] Read client version from version.properties file --- .../co/elastic/clients/base/UserAgent.java | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/java-client/src/main/java/co/elastic/clients/base/UserAgent.java b/java-client/src/main/java/co/elastic/clients/base/UserAgent.java index 011ac7c3e..f26ebc070 100644 --- a/java-client/src/main/java/co/elastic/clients/base/UserAgent.java +++ b/java-client/src/main/java/co/elastic/clients/base/UserAgent.java @@ -19,8 +19,11 @@ package co.elastic.clients.base; +import java.io.IOException; +import java.io.InputStream; import java.util.Collections; import java.util.Map; +import java.util.Properties; /** * Models a user agent, consisting of a name, version, @@ -28,11 +31,30 @@ */ public class UserAgent { - // TODO: move this to a resource file static final String DEFAULT_NAME = "elasticsearch-java"; - // TODO: move this to a resource file - static final String DEFAULT_VERSION = "1.2.3"; + // The client version is loaded from the 'version.properties' file + static final String DEFAULT_VERSION; + static { + InputStream in = UserAgent.class.getResourceAsStream("/co.elastic.clients.elasticsearch/version.properties"); + if (in != null) { + Properties prop = new Properties(); + String version; + try { + prop.load(in); + version = prop.getProperty("version", "?"); + } catch (IOException e) { + // Unable to read properties file + version = "?"; + } + DEFAULT_VERSION = version; + } + else { + // Unable to locate properties file + DEFAULT_VERSION = "?"; + } + // TODO: log error if DEFAULT_VERSION now equals "?" + } // Default user agent, constructed from default repo name and version public static final UserAgent DEFAULT = new UserAgent(DEFAULT_NAME, DEFAULT_VERSION); From cb05c29a788dfccf3977dc28f3f51f8418304947 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Wed, 13 Oct 2021 09:47:58 +0100 Subject: [PATCH 3/4] Removed star import --- .../clients/base/rest_client/RestClientTransport.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/java-client/src/main/java/co/elastic/clients/base/rest_client/RestClientTransport.java b/java-client/src/main/java/co/elastic/clients/base/rest_client/RestClientTransport.java index 11d88f3a0..bd22c6ff9 100644 --- a/java-client/src/main/java/co/elastic/clients/base/rest_client/RestClientTransport.java +++ b/java-client/src/main/java/co/elastic/clients/base/rest_client/RestClientTransport.java @@ -19,7 +19,13 @@ package co.elastic.clients.base.rest_client; -import co.elastic.clients.base.*; +import co.elastic.clients.base.ApiException; +import co.elastic.clients.base.BooleanEndpoint; +import co.elastic.clients.base.BooleanResponse; +import co.elastic.clients.base.ElasticsearchCatRequest; +import co.elastic.clients.base.Endpoint; +import co.elastic.clients.base.Transport; +import co.elastic.clients.base.UserAgent; import co.elastic.clients.json.JsonpDeserializer; import co.elastic.clients.json.JsonpMapper; import co.elastic.clients.json.NdJsonpSerializable; From 2e3d7bff201b579642cdde66a9158c33414c51c1 Mon Sep 17 00:00:00 2001 From: Nigel Small Date: Wed, 13 Oct 2021 13:38:57 +0100 Subject: [PATCH 4/4] Set up and tear down tests per class --- .../elastic/clients/base/UserAgentTest.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/java-client/src/test/java/co/elastic/clients/base/UserAgentTest.java b/java-client/src/test/java/co/elastic/clients/base/UserAgentTest.java index a9acd47f2..1875052d9 100644 --- a/java-client/src/test/java/co/elastic/clients/base/UserAgentTest.java +++ b/java-client/src/test/java/co/elastic/clients/base/UserAgentTest.java @@ -23,8 +23,12 @@ import org.apache.http.HttpHost; import org.elasticsearch.client.RequestOptions; import org.elasticsearch.client.RestClient; +import org.junit.AfterClass; +import org.junit.BeforeClass; import org.junit.Test; +import java.io.IOException; + import static co.elastic.clients.base.UserAgent.DEFAULT_NAME; import static co.elastic.clients.base.UserAgent.DEFAULT_VERSION; import static org.junit.Assert.assertEquals; @@ -41,16 +45,26 @@ public class UserAgentTest { private static final String CUSTOM_NAME_2 = "MegaClient"; private static final String CUSTOM_VERSION_2 = "6.7.8"; + public static RestClient restClient; + + @BeforeClass + public static void setUp() { + restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); + } + + @AfterClass + public static void tearDown() throws IOException { + restClient.close(); + } + @Test public void testDefaultUserAgent() throws Exception { - RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); Transport transport = new RestClientTransport(restClient, null); assertEquals(DEFAULT_USER_AGENT, transport.userAgent()); } @Test public void testCustomUserAgent() throws Exception { - RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); Transport transport = new RestClientTransport(restClient, null, null, new UserAgent(CUSTOM_NAME, CUSTOM_VERSION)); assertEquals(CUSTOM_USER_AGENT, transport.userAgent()); @@ -58,7 +72,6 @@ public void testCustomUserAgent() throws Exception { @Test public void testManualUserAgent() throws Exception { - RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); Transport transport = new RestClientTransport(restClient, null, RequestOptions.DEFAULT.toBuilder().addHeader("User-Agent", CUSTOM_USER_AGENT).build()); assertEquals(CUSTOM_USER_AGENT, transport.userAgent()); @@ -66,7 +79,6 @@ public void testManualUserAgent() throws Exception { @Test public void testMultipleUserAgentsThrowsException() throws Exception { - RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build(); Exception exception = assertThrows(IllegalArgumentException.class, () -> { Transport transport = new RestClientTransport(restClient, null, RequestOptions.DEFAULT.toBuilder().addHeader("User-Agent", CUSTOM_USER_AGENT).build(),