Skip to content

Commit

Permalink
[FEATURE] Enable Generic HTTP Actions in Java Client
Browse files Browse the repository at this point in the history
Signed-off-by: Andriy Redko <andriy.redko@aiven.io>
Signed-off-by: Andriy Redko <drreta@gmail.com>
Signed-off-by: Andriy Redko <andriy.redko@aiven.io>
  • Loading branch information
reta committed Apr 1, 2024
1 parent ccdb56a commit 055a3bb
Show file tree
Hide file tree
Showing 11 changed files with 632 additions and 16 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
import org.opensearch.client.opensearch.core.pit.ListAllPitResponse;
import org.opensearch.client.opensearch.dangling_indices.OpenSearchDanglingIndicesClient;
import org.opensearch.client.opensearch.features.OpenSearchFeaturesClient;
import org.opensearch.client.opensearch.generic.OpenSearchGenericClient;
import org.opensearch.client.opensearch.indices.OpenSearchIndicesClient;
import org.opensearch.client.opensearch.ingest.OpenSearchIngestClient;
import org.opensearch.client.opensearch.nodes.OpenSearchNodesClient;
Expand Down Expand Up @@ -155,6 +156,9 @@ public OpenSearchClient withTransportOptions(@Nullable TransportOptions transpor
}

// ----- Child clients
public OpenSearchGenericClient generic() {
return new OpenSearchGenericClient(this.transport, this.transportOptions);
}

public OpenSearchCatClient cat() {
return new OpenSearchCatClient(this.transport, this.transportOptions);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.opensearch.generic;

import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nullable;

/**
* Generic HTTP request / response body. It is responsibility of the caller to close the body instance
* explicitly (or through {@link GenericResponse} instance) to release all associated streams.
*/
public interface GenericBody extends AutoCloseable {
/**
* Constructs the generic response body out of {@link InputStream} with assumed content type
* @param body response body stream
* @param contentType content type
* @return generic response body instance
*/
static @Nullable GenericBody from(InputStream body, String contentType) {
if (body == null) {
return null;
} else {
return new GenericInputStreamBody(body, contentType);
}
}

/**
* Content type of this body
* @return content type
*/
String contentType();

/**
* Gets the body as {@link InputStream}
* @return
*/
InputStream body();

/**
* Releases all resources associated with this body stream.
*/
void close() throws IOException;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

package org.opensearch.client.opensearch.generic;

import java.io.InputStream;
import java.util.List;
import java.util.Map.Entry;
import org.opensearch.client.json.JsonpMapper;
import org.opensearch.client.transport.RawEndpoint;

/**
* Generic endpoint instance
*/
class GenericEndpoint implements RawEndpoint<GenericRequest, GenericResponse> {
private final GenericRequest request;
private final JsonpMapper jsonpMapper;

public GenericEndpoint(GenericRequest request, JsonpMapper jsonpMapper) {
this.request = request;
this.jsonpMapper = jsonpMapper;
}

@Override
public String method(GenericRequest request) {
return request.getMethod();
}

@Override
public String requestUrl(GenericRequest request) {
return request.getEndpoint();
}

@Override
public boolean hasRequestBody() {
return request.getBody().isPresent();
}

@Override
public GenericResponse responseDeserializer(
String uri,
String method,
String protocol,
int status,
String reason,
List<Entry<String, String>> headers,
String contentType,
InputStream body
) {
return new GenericResponse(uri, protocol, method, status, reason, headers, GenericBody.from(body, contentType), jsonpMapper);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.opensearch.client.opensearch.generic;

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

/**
* The HTTP request / response body that uses {@link InputStream}
*/
class GenericInputStreamBody implements GenericBody {
private final InputStream in;
private final String contentType;

GenericInputStreamBody(InputStream in, String contentType) {
this.in = in;
this.contentType = contentType;
}

@Override
public String contentType() {
return contentType;
}

@Override
public InputStream body() {
return in;
}

@Override
public void close() throws IOException {
in.close();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*/

/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch 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.
*/

/*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

package org.opensearch.client.opensearch.generic;

import static java.util.Collections.unmodifiableMap;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;

/**
* Generic HTTP request to OpenSearch
*/
public final class GenericRequest {
private final String method;
private final String endpoint;
private final Map<String, String> parameters = new HashMap<>();
private GenericBody body;

/**
* Create the {@linkplain GenericRequest}.
* @param method the HTTP method
* @param endpoint the path of the request (without scheme, host, port, or prefix)
*/
public GenericRequest(String method, String endpoint) {
this.method = Objects.requireNonNull(method, "method cannot be null");
this.endpoint = Objects.requireNonNull(endpoint, "endpoint cannot be null");
}

/**
* The HTTP method.
*/
public String getMethod() {
return method;
}

/**
* The path of the request (without scheme, host, port, or prefix).
*/
public String getEndpoint() {
return endpoint;
}

/**
* Add a query string parameter.
* @param name the name of the url parameter. Must not be null.
* @param value the value of the url url parameter. If {@code null} then
* the parameter is sent as {@code name} rather than {@code name=value}
* @throws IllegalArgumentException if a parameter with that name has
* already been set
*/
public void addParameter(String name, String value) {
Objects.requireNonNull(name, "url parameter name cannot be null");
if (parameters.containsKey(name)) {
throw new IllegalArgumentException("url parameter [" + name + "] has already been set to [" + parameters.get(name) + "]");
} else {
parameters.put(name, value);
}
}

/**
* Add query parameters using the provided map of key value pairs.
*
* @param paramSource a map of key value pairs where the key is the url parameter.
* @throws IllegalArgumentException if a parameter with that name has already been set.
*/
public void addParameters(Map<String, String> paramSource) {
paramSource.forEach(this::addParameter);
}

/**
* Query string parameters. The returned map is an unmodifiable view of the
* map in the request so calls to {@link #addParameter(String, String)}
* will change it.
*/
public Map<String, String> getParameters() {
return unmodifiableMap(parameters);
}

/**
* Set the body of the request. If not set or set to {@code null} then no
* body is sent with the request.
*
* @param body the {@link GenericBody} to be set as the body of the request.
*/
public void setBody(GenericBody body) {
this.body = body;
}

/**
* The body of the request. If {@code null} then no body
* is sent with the request.
*/
public Optional<GenericBody> getBody() {
return Optional.ofNullable(body);
}

/**
* Convert request to string representation
*/
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append("Request{");
b.append("method='").append(method).append('\'');
b.append(", endpoint='").append(endpoint).append('\'');
if (false == parameters.isEmpty()) {
b.append(", params=").append(parameters);
}
if (body != null) {
b.append(", body=").append(body);
}
return b.append('}').toString();
}

/**
* Compare two requests for equality
* @param obj request instance to compare with
*/
@Override
public boolean equals(Object obj) {
if (obj == null || (obj.getClass() != getClass())) {
return false;
}
if (obj == this) {
return true;
}

GenericRequest other = (GenericRequest) obj;
return method.equals(other.method)
&& endpoint.equals(other.endpoint)
&& parameters.equals(other.parameters)
&& Objects.equals(body, other.body);
}

/**
* Calculate the hash code of the request
*/
@Override
public int hashCode() {
return Objects.hash(method, endpoint, parameters, body);
}
}
Loading

0 comments on commit 055a3bb

Please sign in to comment.