public final class GettingStarted
extends io.sphere.sdk.models.Base
The client communicates asynchronously with the SPHERE.IO backend via HTTPS.
The client has a method execute
which takes a request as parameter and returns a future of the response type.
There are different clients for different future interfaces:
Client | Future implementation |
---|---|
JavaClient | java.util.concurrent.CompletableFuture |
ScalaClient | scala.concurrent.Future |
PlayJavaClient | play.libs.F.Promise |
Config defaultValuesFromClasspath = ConfigFactory.load();
Map<String, Object> values = new HashMap<>();
values.put("sphere.project", "your project key");
values.put("sphere.clientId", "your client id");
values.put("sphere.clientSecret", "your client secret");
Config config = ConfigFactory.parseMap(values).withFallback(defaultValuesFromClasspath);
JavaClient client = new JavaClientImpl(config);
/* sphere.project, sphere.clientId, sphere.clientSecret should be set in application.conf */
Configuration configuration = Play.application().configuration();
PlayJavaClient client = new PlayJavaClientImpl(configuration);
For integration tests you can also use directly Typesafe Config to create a client:
Config defaultValuesFromClasspath = ConfigFactory.load();
Map<String, Object> values = new HashMap<>();
values.put("sphere.project", "your project key");
values.put("sphere.clientId", "your client id");
values.put("sphere.clientSecret", "your client secret");
Config config = ConfigFactory.parseMap(values).withFallback(defaultValuesFromClasspath);
PlayJavaClient client = new PlayJavaClientImpl(config);
A client works on the abstraction level of one HTTP request for one project. With one client you can start multiple requests in parallel, it is thread-safe.
The clients have a method PlayJavaClient.execute(io.sphere.sdk.http.ClientRequest)
, which takes a ClientRequest
as parameter.
You can create ClientRequest
yourself or use the given ones.
To find the given ones navigate to DefaultModel
and look for all known subinterfaces,
these should include Product
, Category
,
TaxCategory
and some more.
The package of these models contain subpackages queries
and commands
which include typical requests like TaxCategoryQuery
. Example:
F.Promise<PagedQueryResult<TaxCategory>> promise = client.execute(new TaxCategoryQuery().byName("de19"));
For Play Framework F.Promise
s will be mapped into a Result
or other types as described in the
Play Framework documentation:
Query<Category> query = new CategoryQuery().byName(Locale.ENGLISH, "demo cat");
F.Promise<PagedQueryResult<Category>> promise = client.execute(query);
F.Promise<Result> result = promise.map(pagedQueryResult -> {
List<Category> categories = pagedQueryResult.getResults();
return ok(categoriesTemplate.render(categories));
});
The clients are interfaces which have a default implementation (add "Impl" to the interface name).
This enables you to use the decorator pattern to configure the cross concern behaviour of the client:
The following listing shows a pimped client which updates metrics on responses, retries commands and sets default values:
package io.sphere.sdk.client;
import io.sphere.sdk.queries.PagedQueryResult;
import io.sphere.sdk.queries.Query;
import io.sphere.sdk.http.ClientRequest;
import io.sphere.sdk.commands.Command;
import play.libs.F;
/**
* This is just a demo how powerful the decorator pattern for the client can be.
* Don't use this example in production as it is.
*/
public class WrappedClientDemo implements PlayJavaClient {
private final PlayJavaClient client;
private final MetricComponent metricComponent;
public WrappedClientDemo(PlayJavaClient client, MetricComponent metricComponent) {
this.client = client;
this.metricComponent = metricComponent;
}
@SuppressWarnings("unchecked")
@Override
public <T> F.Promise<T> execute(ClientRequest<T> clientRequest) {
final F.Promise<T> result;
final F.Promise<T> intermediateResult = filtered(client.execute(clientRequest));
if (clientRequest instanceof Query) {
final F.Function<Throwable, T> defaultEmpty = exception -> (T) PagedQueryResult.empty();
result = intermediateResult.recover(defaultEmpty);
} else if (clientRequest instanceof Command) {
final F.Function<Throwable, T> retry = exception -> (T) client.execute(clientRequest);
result = intermediateResult.recover(retry);
} else {
result = intermediateResult;
}
return result;
}
//this method will be called for every request
private <T> F.Promise<T> filtered(final F.Promise<T> promise) {
promise.onFailure(Email::writeEmailToDevelopers);
promise.onFailure(metricComponent::incrementFailureRequests);
promise.onRedeem(result -> metricComponent.incrementSuccessfulRequests());
return promise;
}
@Override
public void close() {
client.close();
}
}
Since the clients are interfaces you can implement them to provide test doubles.
Here are some example to provide fake client responses in tests:
//provide directly a model instance or more as result, sadly needs unchecked castings
PlayJavaClient client = new PlayJavaClientImpl(ConfigFactory.load(), new SphereRequestExecutorTestDouble() {
@Override
protected <T> T result(final ClientRequest<T> requestable) {
final T res;
if(requestable.httpRequest().getPath().contains("/categories")){
final LocalizedString name = LocalizedString.of(Locale.ENGLISH, "cat name");
final LocalizedString slug = LocalizedString.of(Locale.ENGLISH, "cat-slug");
final Category category = CategoryBuilder.of("cat-id", name, slug).build();
res = (T) PagedQueryResult.of(category);
} else {
res = super.result(requestable);
}
return res;
}
});
//provide model instances by parsing json, sadly needs unchecked castings
PlayJavaClient client = new PlayJavaClientImpl(ConfigFactory.load(), new SphereRequestExecutorTestDouble() {
@Override
protected <T> T result(final ClientRequest<T> requestable) {
final T res;
if(requestable.httpRequest().getPath().contains("/categories")) {
//in Play projects the file is in "test/resources/categories.json"
res = (T) JsonUtils.readObjectFromJsonFileInClasspath("categories.json", CategoryQuery.resultTypeReference());
} else {
res = super.result(requestable);
}
return res;
}
});
//just return JSON
PlayJavaClient client = new PlayJavaClientImpl(ConfigFactory.load(), new HttpClientTestDouble() {
@Override
public HttpResponse testDouble(Requestable requestable) {
final HttpResponse response;
if (requestable.httpRequest().getPath().contains("/categories")) {
//JSON representation is often useful to deal with errors, but this time again a happy path example
//alternatively you can provide the String from a file in the classpath
response = HttpResponse.of(200, "{\n" +
" \"offset\" : 0,\n" +
" \"count\" : 1,\n" +
" \"total\" : 1,\n" +
" \"results\" : [ {\n" +
" \"id\" : \"5ebe6dc9-ba32-4030-9f3e-eee0137a1274\",\n" +
" \"version\" : 1,\n" +
" \"name\" : {\n" +
" \"en\" : \"TestSnowboard equipment\"\n" +
" },\n" +
" \"slug\" : {\n" +
" \"en\" : \"snowboard-equipment\"\n" +
" },\n" +
" \"ancestors\" : [ ],\n" +
" \"orderHint\" : \"0.000014020459255201865700631\",\n" +
" \"createdAt\" : \"2014-06-06T09:12:05.520Z\",\n" +
" \"lastModifiedAt\" : \"2014-06-06T09:12:05.520Z\"\n" +
" }]\n" +
"}");
} else {
//here you can put if else blocks for further preconfigured responses
throw new RuntimeException("I'm not prepared for this request: " + requestable);
}
return response;
}
});
Builders are mutable and use setters like AddressBuilder.streetName(String)
and by calling
them it changes the internal state of the builders.
Some immutable models contain methods starting with with
such as Address.withEmail(String)
. By calling this method a copy the address will be returned which has the same values of the original address but it has another email address.