Skip to content

Commit ea34d25

Browse files
committed
Basic user agent functionality
1 parent c5ec406 commit ea34d25

File tree

4 files changed

+201
-12
lines changed

4 files changed

+201
-12
lines changed

java-client/src/main/java/co/elastic/clients/base/Transport.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,4 +41,6 @@ <RequestT, ResponseT, ErrorT> CompletableFuture<ResponseT> performRequestAsync(
4141
);
4242

4343
JsonpMapper jsonpMapper();
44+
45+
String userAgent();
4446
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package co.elastic.clients.base;
21+
22+
import java.util.Collections;
23+
import java.util.Map;
24+
25+
/**
26+
* Models a user agent, consisting of a name, version,
27+
* and optional key-value metadata.
28+
*/
29+
public class UserAgent {
30+
31+
// TODO: move this to a resource file
32+
static final String DEFAULT_NAME = "elasticsearch-java";
33+
34+
// TODO: move this to a resource file
35+
static final String DEFAULT_VERSION = "1.2.3";
36+
37+
// Default user agent, constructed from default repo name and version
38+
public static final UserAgent DEFAULT = new UserAgent(DEFAULT_NAME, DEFAULT_VERSION);
39+
40+
private final String name;
41+
private final String version;
42+
private final Map<String, String> metadata;
43+
44+
public UserAgent(String name, String version, Map<String, String> metadata) {
45+
this.name = name;
46+
this.version = version;
47+
this.metadata = metadata;
48+
}
49+
50+
public UserAgent(String repoName, String version) {
51+
this(repoName, version, Collections.emptyMap());
52+
}
53+
54+
@Override
55+
public String toString() {
56+
if (metadata.isEmpty()) {
57+
return String.format("%s/%s", name, version);
58+
}
59+
else {
60+
StringBuilder metadataString = new StringBuilder();
61+
for (Map.Entry<String, String> entry : metadata.entrySet()) {
62+
if (metadataString.length() > 0) {
63+
metadataString.append("; ");
64+
}
65+
metadataString.append(entry.getKey());
66+
metadataString.append(' ');
67+
metadataString.append(entry.getValue());
68+
}
69+
return String.format("%s/%s (%s)", name, version, metadataString);
70+
}
71+
}
72+
73+
}

java-client/src/main/java/co/elastic/clients/base/rest_client/RestClientTransport.java

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,13 @@
1919

2020
package co.elastic.clients.base.rest_client;
2121

22-
import co.elastic.clients.base.ApiException;
23-
import co.elastic.clients.base.BooleanEndpoint;
24-
import co.elastic.clients.base.BooleanResponse;
25-
import co.elastic.clients.base.ElasticsearchCatRequest;
26-
import co.elastic.clients.base.Endpoint;
27-
import co.elastic.clients.base.Transport;
22+
import co.elastic.clients.base.*;
2823
import co.elastic.clients.json.JsonpDeserializer;
2924
import co.elastic.clients.json.JsonpMapper;
3025
import co.elastic.clients.json.NdJsonpSerializable;
3126
import jakarta.json.stream.JsonGenerator;
3227
import jakarta.json.stream.JsonParser;
28+
import org.apache.http.Header;
3329
import org.apache.http.entity.ByteArrayEntity;
3430
import org.apache.http.entity.ContentType;
3531
import org.elasticsearch.client.Cancellable;
@@ -51,12 +47,30 @@ public class RestClientTransport implements Transport {
5147

5248
private final RestClient restClient;
5349
private final JsonpMapper mapper;
54-
private RequestOptions requestOptions;
50+
private final RequestOptions requestOptions;
5551

56-
public RestClientTransport(RestClient restClient, JsonpMapper mapper, @Nullable RequestOptions options) {
52+
public RestClientTransport(RestClient restClient, JsonpMapper mapper, @Nullable RequestOptions options,
53+
@Nullable UserAgent userAgent) {
5754
this.restClient = restClient;
5855
this.mapper = mapper;
59-
this.requestOptions = options;
56+
RequestOptions baseOptions = options == null ? RequestOptions.DEFAULT : options;
57+
String manualUserAgent = findUserAgentIn(baseOptions);
58+
if (manualUserAgent == null && userAgent == null) {
59+
this.requestOptions = baseOptions.toBuilder().addHeader("User-Agent", UserAgent.DEFAULT.toString()).build();
60+
}
61+
else if (manualUserAgent == null) {
62+
this.requestOptions = baseOptions.toBuilder().addHeader("User-Agent", userAgent.toString()).build();
63+
}
64+
else if (userAgent == null) {
65+
this.requestOptions = baseOptions;
66+
}
67+
else {
68+
throw new IllegalArgumentException("Multiple user agents specified");
69+
}
70+
}
71+
72+
public RestClientTransport(RestClient restClient, JsonpMapper mapper, @Nullable RequestOptions options) {
73+
this(restClient, mapper, options, null);
6074
}
6175

6276
public RestClientTransport(RestClient restClient, JsonpMapper mapper) {
@@ -84,16 +98,41 @@ public RestClientTransport withRequestOptions(Function<RequestOptions.Builder, R
8498
return withRequestOptions(fn.apply(builder).build());
8599
}
86100

101+
public RestClientTransport withUserAgent(UserAgent userAgent) {
102+
return new RestClientTransport(this.restClient, this.mapper, this.requestOptions, userAgent);
103+
}
104+
87105
@Override
88106
public JsonpMapper jsonpMapper() {
89107
return mapper;
90108
}
91109

110+
/**
111+
* Find and return the first header value which is keyed under any
112+
* case-insensitive variant of "User-Agent".
113+
*
114+
* @return user agent string
115+
*/
116+
@Override
117+
public String userAgent() {
118+
return findUserAgentIn(requestOptions);
119+
}
120+
92121
@Override
93122
public void close() throws IOException {
94123
this.restClient.close();
95124
}
96125

126+
private static String findUserAgentIn(RequestOptions options) {
127+
// TODO: move this into RequestOptions?
128+
for (Header header : options.getHeaders()) {
129+
if (header.getName().equalsIgnoreCase("User-Agent")) {
130+
return header.getValue();
131+
}
132+
}
133+
return null;
134+
}
135+
97136
public <RequestT, ResponseT, ErrorT> ResponseT performRequest(
98137
RequestT request,
99138
Endpoint<RequestT, ResponseT, ErrorT> endpoint
@@ -154,9 +193,7 @@ private <RequestT> org.elasticsearch.client.Request prepareLowLevelRequest(
154193

155194
org.elasticsearch.client.Request clientReq = new org.elasticsearch.client.Request(method, path);
156195
clientReq.addParameters(params);
157-
if (requestOptions != null) {
158-
clientReq.setOptions(requestOptions);
159-
}
196+
clientReq.setOptions(requestOptions);
160197

161198
// Request-type specific parameters.
162199
if (request instanceof ElasticsearchCatRequest) {
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Licensed to Elasticsearch B.V. under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch B.V. licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package co.elastic.clients.base;
21+
22+
import co.elastic.clients.base.rest_client.RestClientTransport;
23+
import org.apache.http.HttpHost;
24+
import org.elasticsearch.client.RequestOptions;
25+
import org.elasticsearch.client.RestClient;
26+
import org.junit.Test;
27+
28+
import static co.elastic.clients.base.UserAgent.DEFAULT_NAME;
29+
import static co.elastic.clients.base.UserAgent.DEFAULT_VERSION;
30+
import static org.junit.Assert.assertEquals;
31+
import static org.junit.Assert.assertThrows;
32+
33+
public class UserAgentTest {
34+
35+
private static final String DEFAULT_USER_AGENT = String.format("%s/%s", DEFAULT_NAME, DEFAULT_VERSION);
36+
37+
private static final String CUSTOM_NAME = "ÜberClient";
38+
private static final String CUSTOM_VERSION = "1.0.13";
39+
private static final String CUSTOM_USER_AGENT = String.format("%s/%s", CUSTOM_NAME, CUSTOM_VERSION);
40+
41+
private static final String CUSTOM_NAME_2 = "MegaClient";
42+
private static final String CUSTOM_VERSION_2 = "6.7.8";
43+
44+
@Test
45+
public void testDefaultUserAgent() throws Exception {
46+
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
47+
Transport transport = new RestClientTransport(restClient, null);
48+
assertEquals(DEFAULT_USER_AGENT, transport.userAgent());
49+
}
50+
51+
@Test
52+
public void testCustomUserAgent() throws Exception {
53+
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
54+
Transport transport = new RestClientTransport(restClient, null, null,
55+
new UserAgent(CUSTOM_NAME, CUSTOM_VERSION));
56+
assertEquals(CUSTOM_USER_AGENT, transport.userAgent());
57+
}
58+
59+
@Test
60+
public void testManualUserAgent() throws Exception {
61+
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
62+
Transport transport = new RestClientTransport(restClient, null,
63+
RequestOptions.DEFAULT.toBuilder().addHeader("User-Agent", CUSTOM_USER_AGENT).build());
64+
assertEquals(CUSTOM_USER_AGENT, transport.userAgent());
65+
}
66+
67+
@Test
68+
public void testMultipleUserAgentsThrowsException() throws Exception {
69+
RestClient restClient = RestClient.builder(new HttpHost("localhost", 9200)).build();
70+
Exception exception = assertThrows(IllegalArgumentException.class, () -> {
71+
Transport transport = new RestClientTransport(restClient, null,
72+
RequestOptions.DEFAULT.toBuilder().addHeader("User-Agent", CUSTOM_USER_AGENT).build(),
73+
new UserAgent(CUSTOM_NAME_2, CUSTOM_VERSION_2));
74+
});
75+
}
76+
77+
}

0 commit comments

Comments
 (0)