com.couchbase.client.java.cluster.api.AsyncRestBuilder Maven / Gradle / Ivy
/*
* Copyright (c) 2016 Couchbase, Inc.
*
* Licensed 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 com.couchbase.client.java.cluster.api;
import java.util.LinkedHashMap;
import java.util.Map;
import com.couchbase.client.core.ClusterFacade;
import com.couchbase.client.core.annotations.InterfaceAudience;
import com.couchbase.client.core.annotations.InterfaceStability;
import com.couchbase.client.core.logging.CouchbaseLogger;
import com.couchbase.client.core.logging.CouchbaseLoggerFactory;
import com.couchbase.client.core.message.config.RestApiRequest;
import com.couchbase.client.core.message.config.RestApiResponse;
import com.couchbase.client.deps.io.netty.handler.codec.http.HttpHeaders;
import com.couchbase.client.deps.io.netty.handler.codec.http.HttpMethod;
import com.couchbase.client.java.document.json.JsonObject;
import com.couchbase.client.java.document.json.JsonValue;
import rx.Observable;
import rx.functions.Func0;
/**
* A builder class to incrementally construct REST API requests and execute
* them asynchronously
*
* @author Simon Baslé
* @since 2.3.2
*/
@InterfaceAudience.Public
@InterfaceStability.Experimental
public class AsyncRestBuilder implements RestBuilderMarker {
private static final CouchbaseLogger LOGGER = CouchbaseLoggerFactory.getInstance(AsyncRestBuilder.class);
//parameters from the RestApiClient
private final String username;
private final String password;
private final ClusterFacade core;
//internals of the builder
private final HttpMethod method;
private final String path;
private final Map params;
private final Map headers;
private String body;
/**
* @param username the username to authenticate the request with.
* @param password the password to authenticate the request with.
* @param core the {@link ClusterFacade core} through which to send the request.
* @param method the {@link HttpMethod} for the request.
* @param path the full URL path for the request.
*/
public AsyncRestBuilder(String username, String password, ClusterFacade core, HttpMethod method, String path) {
this.username = username;
this.password = password;
this.core = core;
this.method = method;
this.path = path;
this.body = "";
this.params = new LinkedHashMap();
this.headers = new LinkedHashMap();
}
/**
* Adds an URL query parameter to the request. Using a key twice will
* result in the last call being taken into account.
*
* @param key the parameter key.
* @param value the parameter value.
*/
public AsyncRestBuilder withParam(String key, String value) {
this.params.put(key, value);
return this;
}
/**
* Adds an HTTP header to the request. Using a key twice will result
* in the last value being used for a given header.
*
* @param key the header name (see {@link HttpHeaders.Names} for standard names).
* @param value the header value (see {@link HttpHeaders.Values} for standard values).
*/
public AsyncRestBuilder withHeader(String key, Object value) {
this.headers.put(key, value);
return this;
}
/**
* Sets the body for the request without assuming a Content-Type or Accept header.
* Note that you should avoid calling this for HTTP methods where it makes no sense
* (eg. GET, DELETE), as it won't be ignored for these types of requests.
*
* @param body the raw body value to use, as a String.
*/
public AsyncRestBuilder bodyRaw(String body) {
this.body = body;
return this;
}
/**
* Sets the "Content-Type" standard header's value. This is a convenience
* method equivalent to calling
* {@link #withHeader(String, Object) withHeader("Content-Type", type)}.
*
* @param type the "Content-Type" to use.
*/
public AsyncRestBuilder contentType(String type) {
return withHeader(HttpHeaders.Names.CONTENT_TYPE, type);
}
/**
* Sets the "Accept" standard header's value. This is a convenience
* method equivalent to calling {@link #withHeader(String, Object) withHeader("Accept", type)}.
*
* @param type the "Accept" type to use.
*/
public AsyncRestBuilder accept(String type) {
return withHeader(HttpHeaders.Names.ACCEPT, type);
}
/**
* Sets the body for the request, assuming it is JSON. This is equivalent to setting
* the {@link #contentType(String) "Content-Type"} to "application/json"
* and then setting the body via {@link #bodyRaw(String)}.
*
* Note that you should avoid calling this for HTTP methods where it makes no sense
* (eg. GET, DELETE), as it won't be ignored for these types of requests.
*
* @param jsonBody the JSON body to use, as a String.
*/
public AsyncRestBuilder body(String jsonBody) {
accept(HttpHeaders.Values.APPLICATION_JSON);
contentType(HttpHeaders.Values.APPLICATION_JSON);
bodyRaw(jsonBody);
return this;
}
/**
* Sets the body for the request, assuming it is JSON. This is equivalent to setting
* the {@link #contentType(String) "Content-Type"} to "application/json"
* and then setting the body via {@link #bodyRaw(String)}.
*
* Note that you should avoid calling this for HTTP methods where it makes no sense
* (eg. GET, DELETE), as it won't be ignored for these types of requests.
*
* @param jsonBody the JSON body to use, as a {@link JsonObject}.
*/
public AsyncRestBuilder body(JsonValue jsonBody) {
return body(jsonBody.toString());
}
/**
* Sets the body for the request to be an url-encoded form. This is equivalent to setting
* the {@link #contentType(String) "Content-Type"} to "application/x-www-form-urlencoded"
* and then setting the body via {@link #bodyRaw(String)}.
*
* @param form the {@link Form} builder object used to set form parameters.
*/
public AsyncRestBuilder bodyForm(Form form) {
contentType(HttpHeaders.Values.APPLICATION_X_WWW_FORM_URLENCODED);
return bodyRaw(form.toUrlEncodedString());
}
//==== Getters ====
/**
* @return the {@link HttpMethod} used for this request.
*/
public HttpMethod method() {
return method;
}
/**
* @return the full HTTP path (minus query parameters) used for this request.
*/
public String path() {
return this.path;
}
/**
* @return the body used for this request.
*/
public String body() {
return this.body;
}
/**
* @return a copy of the query parameters used for this request.
*/
public Map params() {
return new LinkedHashMap(params);
}
/**
* @return a copy of the HTTP headers used for this request.
*/
public Map headers() {
return new LinkedHashMap(headers);
}
//==== RestApiRequest and execution ====
/**
* @return the {@link RestApiRequest} message sent through the {@link ClusterFacade}
* when executing this request.
*/
public RestApiRequest asRequest() {
return new RestApiRequest(this.username, this.password,
this.method, this.path, this.params, this.headers, this.body);
}
/**
* Executes the API request in an asynchronous fashion.
*
* The return type is an {@link Observable} that will only emit the result of executing the request.
* It is a cold Observable (and the request is only sent when it is subscribed to).
*
* @return an {@link Observable} of the result of the API call, which is a {@link RestApiResponse}.
*/
public Observable execute() {
return Observable.defer(new Func0>() {
@Override
public Observable call() {
RestApiRequest apiRequest = asRequest();
LOGGER.debug("Executing Cluster API request {} on {}", apiRequest.method(), apiRequest.pathWithParameters());
return core.send(asRequest());
}
});
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy