Skip to content

Commit 8aca246

Browse files
swallezl-trotta
authored andcommitted
Randomize client implementation, remove cloud-id on Rest5, add tests for overloaded methods
1 parent ec293c0 commit 8aca246

File tree

17 files changed

+293
-281
lines changed

17 files changed

+293
-281
lines changed

java-client/src/main/java/co/elastic/clients/transport/ElasticsearchTransportBase.java

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,15 +77,9 @@ public abstract class ElasticsearchTransportBase implements ElasticsearchTranspo
7777
}
7878
}
7979

80-
private final TransportHttpClient httpClient;
81-
private final Instrumentation instrumentation;
82-
83-
@Override
84-
public void close() throws IOException {
85-
httpClient.close();
86-
}
87-
88-
private final JsonpMapper mapper;
80+
protected final TransportHttpClient httpClient;
81+
protected final Instrumentation instrumentation;
82+
protected final JsonpMapper mapper;
8983
protected final TransportOptions transportOptions;
9084

9185
public ElasticsearchTransportBase(TransportHttpClient httpClient, TransportOptions options,
@@ -113,6 +107,20 @@ public ElasticsearchTransportBase(
113107
this.instrumentation = instrumentation;
114108
}
115109

110+
/** INTERNAL, used only for tests. */
111+
protected ElasticsearchTransportBase cloneWith(
112+
@Nullable TransportOptions options,
113+
@Nullable JsonpMapper mapper,
114+
@Nullable Instrumentation instrumentation
115+
) {
116+
throw new UnsupportedOperationException();
117+
}
118+
119+
@Override
120+
public void close() throws IOException {
121+
httpClient.close();
122+
}
123+
116124
@Override
117125
public final JsonpMapper jsonpMapper() {
118126
return mapper;
@@ -123,6 +131,10 @@ public final TransportOptions options() {
123131
return transportOptions;
124132
}
125133

134+
public TransportHttpClient httpClient() {
135+
return httpClient;
136+
}
137+
126138
@Override
127139
public final <RequestT, ResponseT, ErrorT> ResponseT performRequest(
128140
RequestT request,

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import java.io.Closeable;
2727
import java.io.IOException;
2828
import java.util.concurrent.CompletableFuture;
29+
import java.util.function.Function;
2930

3031
/**
3132
* The transport layer that allows {@link ApiClient}s to send requests.
@@ -51,4 +52,24 @@ <RequestT, ResponseT, ErrorT> CompletableFuture<ResponseT> performRequestAsync(
5152
* {@link #performRequestAsync(Object, Endpoint, TransportOptions)};
5253
*/
5354
TransportOptions options();
55+
56+
/**
57+
* Clone this transport with new options.
58+
*
59+
* @throws UnsupportedOperationException
60+
*/
61+
default Transport withOptions(@Nullable TransportOptions options) {
62+
throw new UnsupportedOperationException();
63+
}
64+
65+
/**
66+
* Clone this transport with additional options. The lambda expression is provided an options builder
67+
* initialized with the transport's current options.
68+
*
69+
* @param fn a lambda expression that takes the current options as input
70+
* @throws UnsupportedOperationException
71+
*/
72+
default Transport withOptions(Function<TransportOptions.Builder, TransportOptions.Builder> fn) {
73+
return withOptions(fn.apply(options().toBuilder()).build());
74+
}
5475
}

java-client/src/main/java/co/elastic/clients/transport/rest5_client/Rest5ClientTransport.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@
2121

2222
import co.elastic.clients.json.JsonpMapper;
2323
import co.elastic.clients.transport.ElasticsearchTransportBase;
24+
import co.elastic.clients.transport.Transport;
25+
import co.elastic.clients.transport.TransportOptions;
2426
import co.elastic.clients.transport.instrumentation.Instrumentation;
2527
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
2628

29+
import javax.annotation.Nullable;
30+
2731
public class Rest5ClientTransport extends ElasticsearchTransportBase {
2832

2933
private final Rest5Client restClient;
@@ -45,4 +49,20 @@ public Rest5ClientTransport(Rest5Client restClient, JsonpMapper jsonpMapper, Res
4549
public Rest5Client restClient() {
4650
return this.restClient;
4751
}
52+
53+
@Override
54+
public Transport withOptions(@Nullable TransportOptions options) {
55+
return new Rest5ClientTransport(restClient, mapper, Rest5ClientOptions.of(options), instrumentation);
56+
}
57+
58+
/** INTERNAL, used only for tests. */
59+
@Override
60+
protected ElasticsearchTransportBase cloneWith(TransportOptions options, JsonpMapper mapper, Instrumentation instrumentation) {
61+
return new Rest5ClientTransport(
62+
restClient,
63+
mapper != null ? mapper : this.mapper,
64+
Rest5ClientOptions.of(options != null ? options : this.transportOptions),
65+
instrumentation != null ? instrumentation : this.instrumentation
66+
);
67+
}
4868
}

java-client/src/main/java/co/elastic/clients/transport/rest5_client/low_level/Rest5Client.java

Lines changed: 6 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -154,50 +154,14 @@ public class Rest5Client implements Closeable {
154154

155155
/**
156156
* Returns a new {@link Rest5ClientBuilder} to help with {@link Rest5Client} creation.
157-
* Creates a new builder instance and sets the nodes that the client will send requests to.
158-
*
159-
* @param cloudId a valid elastic cloud cloudId that will route to a cluster. The cloudId is located in
160-
* the user console https://cloud.elastic.co and will resemble a string like the following
161-
* optionalHumanReadableName:dXMtZWFzdC0xLmF3cy5mb3VuZC5pbyRlbGFzdGljc2VhcmNoJGtpYmFuYQ==
157+
* Creates a new builder instance and sets the hosts that the client will send requests to.
162158
*/
163-
public static Rest5ClientBuilder builder(String cloudId) {
164-
// there is an optional first portion of the cloudId that is a human readable string, but it is not
165-
// used.
166-
if (cloudId.contains(":")) {
167-
if (cloudId.indexOf(':') == cloudId.length() - 1) {
168-
throw new IllegalStateException("cloudId " + cloudId + " must begin with a human readable " +
169-
"identifier followed by a colon");
170-
}
171-
cloudId = cloudId.substring(cloudId.indexOf(':') + 1);
172-
}
173-
174-
String decoded = new String(Base64.getDecoder().decode(cloudId), UTF_8);
175-
// once decoded the parts are separated by a $ character.
176-
// they are respectively domain name and optional port, elasticsearch id, kibana id
177-
String[] decodedParts = decoded.split("\\$");
178-
if (decodedParts.length != 3) {
179-
throw new IllegalStateException("cloudId " + cloudId + " did not decode to a cluster identifier" +
180-
" correctly");
181-
}
182-
183-
// domain name and optional port
184-
String[] domainAndMaybePort = decodedParts[0].split(":", 2);
185-
String domain = domainAndMaybePort[0];
186-
int port;
187-
188-
if (domainAndMaybePort.length == 2) {
189-
try {
190-
port = Integer.parseInt(domainAndMaybePort[1]);
191-
} catch (NumberFormatException nfe) {
192-
throw new IllegalStateException("cloudId " + cloudId + " does not contain a valid port " +
193-
"number");
194-
}
195-
} else {
196-
port = 443;
159+
public static Rest5ClientBuilder builder(URI... uris) {
160+
if (uris == null || uris.length == 0) {
161+
throw new IllegalArgumentException("uris must not be null nor empty");
197162
}
198-
199-
String url = decodedParts[1] + "." + domain;
200-
return builder(new HttpHost("https", url, port));
163+
List<Node> nodes = Arrays.stream(uris).map(u -> new Node(HttpHost.create(u))).toList();
164+
return new Rest5ClientBuilder(nodes);
201165
}
202166

203167
/**

java-client/src/main/java/co/elastic/clients/transport/rest_client/RestClientHttpClient.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,7 @@ public int size() {
227227
@Override
228228
public BinaryData body() throws IOException {
229229
HttpEntity entity = restResponse.getEntity();
230-
return entity == null ? null : new HttpEntityBinaryData(restResponse.getEntity());
230+
return entity == null ? null : new HttpEntityBinaryData(entity);
231231
}
232232

233233
@Nullable

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

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,13 @@
2121

2222
import co.elastic.clients.json.JsonpMapper;
2323
import co.elastic.clients.transport.ElasticsearchTransportBase;
24+
import co.elastic.clients.transport.Transport;
25+
import co.elastic.clients.transport.TransportOptions;
2426
import co.elastic.clients.transport.instrumentation.Instrumentation;
2527
import org.elasticsearch.client.RestClient;
2628

29+
import javax.annotation.Nullable;
30+
2731
public class RestClientTransport extends ElasticsearchTransportBase {
2832

2933
private final RestClient restClient;
@@ -45,4 +49,21 @@ public RestClientTransport(RestClient restClient, JsonpMapper jsonpMapper, RestC
4549
public RestClient restClient() {
4650
return this.restClient;
4751
}
52+
53+
54+
@Override
55+
public Transport withOptions(@Nullable TransportOptions options) {
56+
return new RestClientTransport(restClient, mapper, RestClientOptions.of(options), instrumentation);
57+
}
58+
59+
/** INTERNAL, used only for tests. */
60+
@Override
61+
protected ElasticsearchTransportBase cloneWith(TransportOptions options, JsonpMapper mapper, Instrumentation instrumentation) {
62+
return new RestClientTransport(
63+
restClient,
64+
mapper != null ? mapper : this.mapper,
65+
RestClientOptions.of(options != null ? options : this.transportOptions),
66+
instrumentation != null ? instrumentation : this.instrumentation
67+
);
68+
}
4869
}

java-client/src/test/java/co/elastic/clients/documentation/DocTestsTransport.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
import java.util.function.Function;
3838

3939
/**
40-
* A transport implementation that always returns the same result. Used for doc snippets where we can to check
40+
* A transport implementation that always returns the same result. Used for doc snippets where we can check
4141
* compilation and do very simple tests.
4242
*/
4343
public class DocTestsTransport implements ElasticsearchTransport {

java-client/src/test/java/co/elastic/clients/elasticsearch/ElasticsearchTestClient.java

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import co.elastic.clients.transport.rest5_client.Rest5ClientTransport;
2525
import co.elastic.clients.transport.rest5_client.low_level.Rest5Client;
2626
import co.elastic.clients.transport.rest_client.RestClientTransport;
27+
import com.sun.net.httpserver.HttpServer;
2728
import org.apache.hc.core5.http.Header;
2829
import org.apache.hc.core5.http.message.BasicHeader;
2930
import org.apache.http.HttpHost;
@@ -35,26 +36,35 @@
3536
import javax.annotation.Nullable;
3637
import javax.net.ssl.SSLContext;
3738
import java.net.URI;
39+
import java.security.SecureRandom;
3840
import java.util.Base64;
39-
import java.util.Random;
4041

4142
public class ElasticsearchTestClient {
4243

44+
protected enum ClientImpl { Rest4, Rest5 }
45+
4346
// Same value for all tests in a test run
44-
private static final int RAND = new Random().nextInt(100);
47+
private static final ClientImpl flavor;
48+
static {
49+
var flavors = ClientImpl.values();
50+
flavor = flavors[new SecureRandom().nextInt(flavors.length)];
51+
}
4552

4653
private static JsonpMapper mapper(JsonpMapper mapper) {
4754
return mapper != null ? mapper : new JsonbJsonpMapper();
4855
}
4956

5057
public static ElasticsearchClient createClient(String url, @Nullable JsonpMapper mapper, @Nullable SSLContext sslContext) {
51-
if(RAND % 2 == 0) {
52-
System.out.println("Using a Rest4 client");
53-
return createRest4Client(url, mapper, sslContext);
54-
} else {
55-
System.out.println("Using a Rest5 client");
56-
return createRest5Client(url, mapper, sslContext);
57-
}
58+
System.out.println("Using a " + flavor + " client");
59+
return switch (flavor) {
60+
case Rest4 -> createRest4Client(url, mapper, sslContext);
61+
case Rest5 -> createRest5Client(url, mapper, sslContext);
62+
};
63+
}
64+
65+
public static ElasticsearchClient createClient(HttpServer server, @Nullable JsonpMapper mapper) {
66+
var address = server.getAddress();
67+
return createClient("http://" + address.getHostString() + ":" + address.getPort(), mapper, null);
5868
}
5969

6070
public static ElasticsearchClient createRest4Client(String url, @Nullable JsonpMapper mapper, @Nullable SSLContext sslContext) {

java-client/src/test/java/co/elastic/clients/elasticsearch/ElasticsearchTestServer.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public class ElasticsearchTestServer implements AutoCloseable {
4848

4949
private final String[] plugins;
5050
private volatile ElasticsearchContainer container;
51+
private String url;
52+
private SSLContext sslContext;
5153
private ElasticsearchClient client;
5254

5355
private static ElasticsearchTestServer global;
@@ -95,6 +97,8 @@ public ElasticsearchTestServer(String... plugins) {
9597
private static final int RAND = new Random().nextInt(100);
9698

9799
protected void setup(String url, SSLContext sslContext) {
100+
this.url = url;
101+
this.sslContext = sslContext;
98102
this.client = ElasticsearchTestClient.createClient(url, null, sslContext);
99103
}
100104

@@ -244,6 +248,14 @@ public ElasticsearchContainer container() {
244248
return this.container;
245249
}
246250

251+
public String url() {
252+
return url;
253+
}
254+
255+
public SSLContext sslContext() {
256+
return sslContext;
257+
}
258+
247259
public ElasticsearchClient client() {
248260
return client;
249261
}

0 commit comments

Comments
 (0)