Skip to content

Commit

Permalink
Add support for endpoints with a binary response (#434) (#435)
Browse files Browse the repository at this point in the history
Co-authored-by: Sylvain Wallez <sylvain@elastic.co>
  • Loading branch information
github-actions[bot] and swallez committed Oct 28, 2022
1 parent 5c4fd61 commit 03b74fc
Show file tree
Hide file tree
Showing 8 changed files with 374 additions and 66 deletions.
@@ -0,0 +1,45 @@
/*
* 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.transport.endpoints;

import java.util.Map;
import java.util.function.Function;

public class BinaryEndpoint<RequestT> extends EndpointBase<RequestT, BinaryResponse> {

public BinaryEndpoint(
String id,
Function<RequestT, String> method,
Function<RequestT, String> requestUrl,
Function<RequestT,
Map<String, String>> queryParameters,
Function<RequestT, Map<String, String>> headers,
boolean hasRequestBody,
Object ignored // same number of arguments as SimpleEndpoint
) {
super(id, method, requestUrl, queryParameters, headers, hasRequestBody);
}

@Override
public boolean isError(int statusCode) {
return statusCode >= 400;
}
}

@@ -0,0 +1,58 @@
/*
* 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.transport.endpoints;

import java.io.IOException;
import java.io.InputStream;

/**
* Response for API endpoints that return non-JSON content.
* <p>
* <b>Note</b>: binary responses hold a reference to the transport layer's response body. As such, they must be closed
* to ensure that any associated resources are released. Alternatively you can also close the {@link #content()} stream.
*/
public interface BinaryResponse extends AutoCloseable {

/**
* The response content type. If not known, defaults to {@code application/octet-stream}.
*/
String contentType();

/**
* The content length, or {@code -1} if not known.
*/
long contentLength();

/**
* The response body. This method can be called only once and will throw an {@link IllegalStateException} on subsequent calls.
* <p>
* Calling {@link InputStream#close()} on the result has the same effect as calling {@link #close()} on this object.
*
* @throws IOException if the stream could not be created
* @throws IllegalStateException if this method has already been called
*/
InputStream content() throws IOException;

/**
* Releases any resources associated with this response.
*/
@Override
void close() throws IOException;
}
Expand Up @@ -19,12 +19,10 @@

package co.elastic.clients.transport.endpoints;

import co.elastic.clients.json.JsonpDeserializer;

import java.util.Map;
import java.util.function.Function;

public class BooleanEndpoint<RequestT> extends SimpleEndpoint<RequestT, BooleanResponse> {
public class BooleanEndpoint<RequestT> extends EndpointBase<RequestT, BooleanResponse> {

public BooleanEndpoint(
String id,
Expand All @@ -34,9 +32,9 @@ public BooleanEndpoint(
Map<String, String>> queryParameters,
Function<RequestT, Map<String, String>> headers,
boolean hasRequestBody,
JsonpDeserializer<?> responseParser // always null
Object ignored // same number of arguments as SimpleEndpoint
) {
super(id, method, requestUrl, queryParameters, headers, hasRequestBody, null);
super(id, method, requestUrl, queryParameters, headers, hasRequestBody);
}

@Override
Expand Down
@@ -0,0 +1,131 @@
/*
* 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.transport.endpoints;

import co.elastic.clients.elasticsearch._types.ErrorResponse;
import co.elastic.clients.json.JsonpDeserializer;
import co.elastic.clients.transport.Endpoint;
import org.apache.http.client.utils.URLEncodedUtils;

import java.util.Collections;
import java.util.Map;
import java.util.function.Function;

public class EndpointBase<RequestT, ResponseT> implements Endpoint<RequestT, ResponseT, ErrorResponse> {

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

/**
* Returns a function that always returns an empty String to String map. Useful to avoid creating lots of
* duplicate lambdas in endpoints that don't have headers or parameters.
*/
@SuppressWarnings("unchecked")
public static <T> Function<T, Map<String, String>> emptyMap() {
return (Function<T, Map<String, String>>) EMPTY_MAP;
}

protected final String id;
protected final Function<RequestT, String> method;
protected final Function<RequestT, String> requestUrl;
protected final Function<RequestT, Map<String, String>> queryParameters;
protected final Function<RequestT, Map<String, String>> headers;
protected final boolean hasRequestBody;

public EndpointBase(
String id,
Function<RequestT, String> method,
Function<RequestT, String> requestUrl,
Function<RequestT, Map<String, String>> queryParameters,
Function<RequestT, Map<String, String>> headers,
boolean hasRequestBody
) {
this.id = id;
this.method = method;
this.requestUrl = requestUrl;
this.queryParameters = queryParameters;
this.headers = headers;
this.hasRequestBody = hasRequestBody;
}

@Override
public String id() {
return this.id;
}

@Override
public String method(RequestT request) {
return this.method.apply(request);
}

@Override
public String requestUrl(RequestT request) {
return this.requestUrl.apply(request);
}

@Override
public Map<String, String> queryParameters(RequestT request) {
return this.queryParameters.apply(request);
}

@Override
public Map<String, String> headers(RequestT request) {
return this.headers.apply(request);
}

@Override
public boolean hasRequestBody() {
return this.hasRequestBody;
}

// ES-specific
@Override
public boolean isError(int statusCode) {
return statusCode >= 400;
}

@Override
public JsonpDeserializer<ErrorResponse> errorDeserializer(int statusCode) {
return ErrorResponse._DESERIALIZER;
}

public <NewResponseT> SimpleEndpoint<RequestT, NewResponseT> withResponseDeserializer(
JsonpDeserializer<NewResponseT> newResponseParser
) {
return new SimpleEndpoint<>(
id,
method,
requestUrl,
queryParameters,
headers,
hasRequestBody,
newResponseParser
);
}

public static RuntimeException noPathTemplateFound(String what) {
return new RuntimeException("Could not find a request " + what + " with this set of properties. " +
"Please check the API documentation, or raise an issue if this should be a valid request.");
}

public static void pathEncode(String src, StringBuilder dest) {
// TODO: avoid dependency on HttpClient here (and use something more efficient)
dest.append(URLEncodedUtils.formatSegments(src).substring(1));
}
}
Expand Up @@ -28,7 +28,8 @@
import java.util.Map;
import java.util.function.Function;

public class SimpleEndpoint<RequestT, ResponseT> implements JsonEndpoint<RequestT, ResponseT, ErrorResponse> {
public class SimpleEndpoint<RequestT, ResponseT> extends EndpointBase<RequestT, ResponseT>
implements JsonEndpoint<RequestT, ResponseT, ErrorResponse> {

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

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

private final String id;
private final Function<RequestT, String> method;
private final Function<RequestT, String> requestUrl;
private final Function<RequestT, Map<String, String>> queryParameters;
private final Function<RequestT, Map<String, String>> headers;
private final boolean hasRequestBody;
private final JsonpDeserializer<ResponseT> responseParser;

public SimpleEndpoint(
Expand All @@ -58,56 +53,15 @@ public SimpleEndpoint(
boolean hasRequestBody,
JsonpDeserializer<ResponseT> responseParser
) {
this.id = id;
this.method = method;
this.requestUrl = requestUrl;
this.queryParameters = queryParameters;
this.headers = headers;
this.hasRequestBody = hasRequestBody;
super(id, method, requestUrl, queryParameters, headers, hasRequestBody);
this.responseParser = responseParser;
}

@Override
public String id() {
return this.id;
}

@Override
public String method(RequestT request) {
return this.method.apply(request);
}

@Override
public String requestUrl(RequestT request) {
return this.requestUrl.apply(request);
}

@Override
public Map<String, String> queryParameters(RequestT request) {
return this.queryParameters.apply(request);
}

@Override
public Map<String, String> headers(RequestT request) {
return this.headers.apply(request);
}

@Override
public boolean hasRequestBody() {
return this.hasRequestBody;
}

@Override
public JsonpDeserializer<ResponseT> responseDeserializer() {
return this.responseParser;
}

// ES-specific
@Override
public boolean isError(int statusCode) {
return statusCode >= 400;
}

@Override
public JsonpDeserializer<ErrorResponse> errorDeserializer(int statusCode) {
return ErrorResponse._DESERIALIZER;
Expand Down
@@ -0,0 +1,44 @@
/*
* 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.transport.endpoints;

import co.elastic.clients.elasticsearch._types.ErrorResponse;
import co.elastic.clients.json.JsonpDeserializer;
import co.elastic.clients.transport.JsonEndpoint;

import java.util.Map;
import java.util.function.Function;

public class SimpleJsonEndpoint<RequestT, ResponseT> extends SimpleEndpoint<RequestT, ResponseT>
implements JsonEndpoint<RequestT, ResponseT, ErrorResponse> {

public SimpleJsonEndpoint(
String id,
Function<RequestT, String> method,
Function<RequestT, String> requestUrl,
Function<RequestT,
Map<String, String>> queryParameters,
Function<RequestT, Map<String, String>> headers,
boolean hasRequestBody,
JsonpDeserializer<ResponseT> responseParser
) {
super(id, method, requestUrl, queryParameters, headers, hasRequestBody, responseParser);
}
}

0 comments on commit 03b74fc

Please sign in to comment.