Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for binary endpoints #434

Merged
merged 2 commits into from Oct 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
@@ -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);
}
}