Skip to content

Commit b4e46a0

Browse files
swallezgithub-actions[bot]
authored andcommitted
Add support for endpoints with a binary response (#434)
1 parent 5c4fd61 commit b4e46a0

File tree

8 files changed

+374
-66
lines changed

8 files changed

+374
-66
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
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.transport.endpoints;
21+
22+
import java.util.Map;
23+
import java.util.function.Function;
24+
25+
public class BinaryEndpoint<RequestT> extends EndpointBase<RequestT, BinaryResponse> {
26+
27+
public BinaryEndpoint(
28+
String id,
29+
Function<RequestT, String> method,
30+
Function<RequestT, String> requestUrl,
31+
Function<RequestT,
32+
Map<String, String>> queryParameters,
33+
Function<RequestT, Map<String, String>> headers,
34+
boolean hasRequestBody,
35+
Object ignored // same number of arguments as SimpleEndpoint
36+
) {
37+
super(id, method, requestUrl, queryParameters, headers, hasRequestBody);
38+
}
39+
40+
@Override
41+
public boolean isError(int statusCode) {
42+
return statusCode >= 400;
43+
}
44+
}
45+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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.transport.endpoints;
21+
22+
import java.io.IOException;
23+
import java.io.InputStream;
24+
25+
/**
26+
* Response for API endpoints that return non-JSON content.
27+
* <p>
28+
* <b>Note</b>: binary responses hold a reference to the transport layer's response body. As such, they must be closed
29+
* to ensure that any associated resources are released. Alternatively you can also close the {@link #content()} stream.
30+
*/
31+
public interface BinaryResponse extends AutoCloseable {
32+
33+
/**
34+
* The response content type. If not known, defaults to {@code application/octet-stream}.
35+
*/
36+
String contentType();
37+
38+
/**
39+
* The content length, or {@code -1} if not known.
40+
*/
41+
long contentLength();
42+
43+
/**
44+
* The response body. This method can be called only once and will throw an {@link IllegalStateException} on subsequent calls.
45+
* <p>
46+
* Calling {@link InputStream#close()} on the result has the same effect as calling {@link #close()} on this object.
47+
*
48+
* @throws IOException if the stream could not be created
49+
* @throws IllegalStateException if this method has already been called
50+
*/
51+
InputStream content() throws IOException;
52+
53+
/**
54+
* Releases any resources associated with this response.
55+
*/
56+
@Override
57+
void close() throws IOException;
58+
}

java-client/src/main/java/co/elastic/clients/transport/endpoints/BooleanEndpoint.java

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,10 @@
1919

2020
package co.elastic.clients.transport.endpoints;
2121

22-
import co.elastic.clients.json.JsonpDeserializer;
23-
2422
import java.util.Map;
2523
import java.util.function.Function;
2624

27-
public class BooleanEndpoint<RequestT> extends SimpleEndpoint<RequestT, BooleanResponse> {
25+
public class BooleanEndpoint<RequestT> extends EndpointBase<RequestT, BooleanResponse> {
2826

2927
public BooleanEndpoint(
3028
String id,
@@ -34,9 +32,9 @@ public BooleanEndpoint(
3432
Map<String, String>> queryParameters,
3533
Function<RequestT, Map<String, String>> headers,
3634
boolean hasRequestBody,
37-
JsonpDeserializer<?> responseParser // always null
35+
Object ignored // same number of arguments as SimpleEndpoint
3836
) {
39-
super(id, method, requestUrl, queryParameters, headers, hasRequestBody, null);
37+
super(id, method, requestUrl, queryParameters, headers, hasRequestBody);
4038
}
4139

4240
@Override
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
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.transport.endpoints;
21+
22+
import co.elastic.clients.elasticsearch._types.ErrorResponse;
23+
import co.elastic.clients.json.JsonpDeserializer;
24+
import co.elastic.clients.transport.Endpoint;
25+
import org.apache.http.client.utils.URLEncodedUtils;
26+
27+
import java.util.Collections;
28+
import java.util.Map;
29+
import java.util.function.Function;
30+
31+
public class EndpointBase<RequestT, ResponseT> implements Endpoint<RequestT, ResponseT, ErrorResponse> {
32+
33+
private static final Function<?, Map<String, String>> EMPTY_MAP = x -> Collections.emptyMap();
34+
35+
/**
36+
* Returns a function that always returns an empty String to String map. Useful to avoid creating lots of
37+
* duplicate lambdas in endpoints that don't have headers or parameters.
38+
*/
39+
@SuppressWarnings("unchecked")
40+
public static <T> Function<T, Map<String, String>> emptyMap() {
41+
return (Function<T, Map<String, String>>) EMPTY_MAP;
42+
}
43+
44+
protected final String id;
45+
protected final Function<RequestT, String> method;
46+
protected final Function<RequestT, String> requestUrl;
47+
protected final Function<RequestT, Map<String, String>> queryParameters;
48+
protected final Function<RequestT, Map<String, String>> headers;
49+
protected final boolean hasRequestBody;
50+
51+
public EndpointBase(
52+
String id,
53+
Function<RequestT, String> method,
54+
Function<RequestT, String> requestUrl,
55+
Function<RequestT, Map<String, String>> queryParameters,
56+
Function<RequestT, Map<String, String>> headers,
57+
boolean hasRequestBody
58+
) {
59+
this.id = id;
60+
this.method = method;
61+
this.requestUrl = requestUrl;
62+
this.queryParameters = queryParameters;
63+
this.headers = headers;
64+
this.hasRequestBody = hasRequestBody;
65+
}
66+
67+
@Override
68+
public String id() {
69+
return this.id;
70+
}
71+
72+
@Override
73+
public String method(RequestT request) {
74+
return this.method.apply(request);
75+
}
76+
77+
@Override
78+
public String requestUrl(RequestT request) {
79+
return this.requestUrl.apply(request);
80+
}
81+
82+
@Override
83+
public Map<String, String> queryParameters(RequestT request) {
84+
return this.queryParameters.apply(request);
85+
}
86+
87+
@Override
88+
public Map<String, String> headers(RequestT request) {
89+
return this.headers.apply(request);
90+
}
91+
92+
@Override
93+
public boolean hasRequestBody() {
94+
return this.hasRequestBody;
95+
}
96+
97+
// ES-specific
98+
@Override
99+
public boolean isError(int statusCode) {
100+
return statusCode >= 400;
101+
}
102+
103+
@Override
104+
public JsonpDeserializer<ErrorResponse> errorDeserializer(int statusCode) {
105+
return ErrorResponse._DESERIALIZER;
106+
}
107+
108+
public <NewResponseT> SimpleEndpoint<RequestT, NewResponseT> withResponseDeserializer(
109+
JsonpDeserializer<NewResponseT> newResponseParser
110+
) {
111+
return new SimpleEndpoint<>(
112+
id,
113+
method,
114+
requestUrl,
115+
queryParameters,
116+
headers,
117+
hasRequestBody,
118+
newResponseParser
119+
);
120+
}
121+
122+
public static RuntimeException noPathTemplateFound(String what) {
123+
return new RuntimeException("Could not find a request " + what + " with this set of properties. " +
124+
"Please check the API documentation, or raise an issue if this should be a valid request.");
125+
}
126+
127+
public static void pathEncode(String src, StringBuilder dest) {
128+
// TODO: avoid dependency on HttpClient here (and use something more efficient)
129+
dest.append(URLEncodedUtils.formatSegments(src).substring(1));
130+
}
131+
}

java-client/src/main/java/co/elastic/clients/transport/endpoints/SimpleEndpoint.java

Lines changed: 3 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@
2828
import java.util.Map;
2929
import java.util.function.Function;
3030

31-
public class SimpleEndpoint<RequestT, ResponseT> implements JsonEndpoint<RequestT, ResponseT, ErrorResponse> {
31+
public class SimpleEndpoint<RequestT, ResponseT> extends EndpointBase<RequestT, ResponseT>
32+
implements JsonEndpoint<RequestT, ResponseT, ErrorResponse> {
3233

3334
private static final Function<?, Map<String, String>> EMPTY_MAP = x -> Collections.emptyMap();
3435

@@ -41,12 +42,6 @@ public static <T> Function<T, Map<String, String>> emptyMap() {
4142
return (Function<T, Map<String, String>>) EMPTY_MAP;
4243
}
4344

44-
private final String id;
45-
private final Function<RequestT, String> method;
46-
private final Function<RequestT, String> requestUrl;
47-
private final Function<RequestT, Map<String, String>> queryParameters;
48-
private final Function<RequestT, Map<String, String>> headers;
49-
private final boolean hasRequestBody;
5045
private final JsonpDeserializer<ResponseT> responseParser;
5146

5247
public SimpleEndpoint(
@@ -58,56 +53,15 @@ public SimpleEndpoint(
5853
boolean hasRequestBody,
5954
JsonpDeserializer<ResponseT> responseParser
6055
) {
61-
this.id = id;
62-
this.method = method;
63-
this.requestUrl = requestUrl;
64-
this.queryParameters = queryParameters;
65-
this.headers = headers;
66-
this.hasRequestBody = hasRequestBody;
56+
super(id, method, requestUrl, queryParameters, headers, hasRequestBody);
6757
this.responseParser = responseParser;
6858
}
6959

70-
@Override
71-
public String id() {
72-
return this.id;
73-
}
74-
75-
@Override
76-
public String method(RequestT request) {
77-
return this.method.apply(request);
78-
}
79-
80-
@Override
81-
public String requestUrl(RequestT request) {
82-
return this.requestUrl.apply(request);
83-
}
84-
85-
@Override
86-
public Map<String, String> queryParameters(RequestT request) {
87-
return this.queryParameters.apply(request);
88-
}
89-
90-
@Override
91-
public Map<String, String> headers(RequestT request) {
92-
return this.headers.apply(request);
93-
}
94-
95-
@Override
96-
public boolean hasRequestBody() {
97-
return this.hasRequestBody;
98-
}
99-
10060
@Override
10161
public JsonpDeserializer<ResponseT> responseDeserializer() {
10262
return this.responseParser;
10363
}
10464

105-
// ES-specific
106-
@Override
107-
public boolean isError(int statusCode) {
108-
return statusCode >= 400;
109-
}
110-
11165
@Override
11266
public JsonpDeserializer<ErrorResponse> errorDeserializer(int statusCode) {
11367
return ErrorResponse._DESERIALIZER;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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.transport.endpoints;
21+
22+
import co.elastic.clients.elasticsearch._types.ErrorResponse;
23+
import co.elastic.clients.json.JsonpDeserializer;
24+
import co.elastic.clients.transport.JsonEndpoint;
25+
26+
import java.util.Map;
27+
import java.util.function.Function;
28+
29+
public class SimpleJsonEndpoint<RequestT, ResponseT> extends SimpleEndpoint<RequestT, ResponseT>
30+
implements JsonEndpoint<RequestT, ResponseT, ErrorResponse> {
31+
32+
public SimpleJsonEndpoint(
33+
String id,
34+
Function<RequestT, String> method,
35+
Function<RequestT, String> requestUrl,
36+
Function<RequestT,
37+
Map<String, String>> queryParameters,
38+
Function<RequestT, Map<String, String>> headers,
39+
boolean hasRequestBody,
40+
JsonpDeserializer<ResponseT> responseParser
41+
) {
42+
super(id, method, requestUrl, queryParameters, headers, hasRequestBody, responseParser);
43+
}
44+
}

0 commit comments

Comments
 (0)