
com.github.davidmoten.odata.client.TestingService Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of odata-client-runtime Show documentation
Show all versions of odata-client-runtime Show documentation
OData client runtime for use with generated code
The newest version!
package com.github.davidmoten.odata.client;
import static java.util.Arrays.asList;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.core.JsonParseException;
import com.github.davidmoten.guavamini.Preconditions;
public final class TestingService {
public static Builder baseUrl(String url) {
if (url.endsWith("/")) {
url = url.substring(0, url.length() - 1);
}
return new Builder().baseUrl(url);
}
public static Builder pathStyle(PathStyle pathStyle) {
return new Builder().pathStyle(pathStyle);
}
public static Builder builder() {
return new Builder();
}
public static final class Response {
public final String resource;
public final int statusCode;
public Response(String resource, int statusCode) {
this.resource = resource;
this.statusCode = statusCode;
}
public Response(String text) {
this(text, HttpURLConnection.HTTP_OK);
}
@Override
public String toString() {
return "Response [resource=" + resource + ", statusCode=" + statusCode + "]";
}
}
public static abstract class BuilderBase, R> {
final Map responses = new HashMap<>();
final Map requests = new HashMap<>();
String baseUrl = "https://testing.com";
PathStyle pathStyle = PathStyle.IDENTIFIERS_AS_SEGMENTS;
@SuppressWarnings("unchecked")
public T baseUrl(String baseUrl) {
this.baseUrl = baseUrl;
return (T) this;
}
@SuppressWarnings("unchecked")
public T pathStyle(PathStyle pathStyle) {
this.pathStyle = pathStyle;
return (T) this;
}
private String toUrl(String path) {
final String url;
if (path.startsWith("https://")) {
url = path;
} else {
url = baseUrl + path;
}
return url;
}
public Builder expectRequest(String path) {
return new Builder(this, path);
}
public static final class Builder, R> {
private final List requestHeaders = new ArrayList<>();
private final BuilderBase base;
private String payloadResourcePath = "/empty.txt";
private HttpMethod method = HttpMethod.GET;
private final String path;
private String responseResourcePath;
private Integer statusCode;
public Builder(BuilderBase base, String path) {
this.base = base;
this.path = path;
}
public Builder withPayload(String resourcePath) {
this.payloadResourcePath = resourcePath;
return this;
}
public Builder withRequestHeaders(RequestHeader... requestHeaders) {
this.requestHeaders.addAll(Arrays.asList(requestHeaders));
return this;
}
public Builder withMethod(HttpMethod method) {
this.method = method;
return this;
}
public Builder withResponse(String resourcePath) {
this.responseResourcePath = resourcePath;
return this;
}
public Builder withResponseStatusCode(int statusCode) {
this.statusCode = statusCode;
return this;
}
public Builder withRequestHeadersStandard() {
return withRequestHeaders(RequestHeader.ACCEPT_JSON_METADATA_MINIMAL,
RequestHeader.ODATA_VERSION);
}
private T add() {
return base.expectRequestAndResponse(path, payloadResourcePath,
responseResourcePath, method, applyDefaultIfMissing(statusCode, method),
requestHeaders.toArray(new RequestHeader[] {}));
}
public R build() {
return add().build();
}
@SuppressWarnings("unchecked")
public Builder expectRequest(String path) {
return (Builder) add().expectRequest(path);
}
}
private static int applyDefaultIfMissing(Integer statusCode, HttpMethod method) {
if (statusCode != null) {
return statusCode;
} else if (method == HttpMethod.PUT || method == HttpMethod.PATCH
|| method == HttpMethod.DELETE) {
return HttpURLConnection.HTTP_NO_CONTENT;
} else {
return HttpURLConnection.HTTP_OK;
}
}
@SuppressWarnings("unchecked")
private T expectRequestAndResponse(String path, String requestResourceName,
String responseResourceName, HttpMethod method, int statusCode,
RequestHeader... requestHeaders) {
String url = toUrl(path);
requests.put(toKey(method, url, asList(requestHeaders)), requestResourceName);
responses.put(toKey(method, url, asList(requestHeaders)),
new Response(responseResourceName, statusCode));
return (T) this;
}
@SuppressWarnings("unchecked")
public T expectDelete(String path, int responseStatusCode,
RequestHeader... requestHeaders) {
String key = toKey(HttpMethod.DELETE, baseUrl + path, asList(requestHeaders));
requests.put(key, "/empty.txt");
responses.put(key, new Response(null, responseStatusCode));
return (T) this;
}
public T expectDelete(String path, RequestHeader... requestHeaders) {
return expectDelete(path, HttpURLConnection.HTTP_NO_CONTENT, requestHeaders);
}
private static String toKey(HttpMethod method, String url,
Collection requestHeaders) {
return method + "\n " + url + "\n " + requestHeaders.stream()
.map(x -> x.name() + "=" + x.value()).sorted().collect(Collectors.joining("|"));
}
private static void log(Object o) {
System.out.println(o);
}
protected HttpService createService() {
return new HttpService() {
@Override
public HttpResponse submit(HttpMethod method, String url,
List requestHeaders, InputStream content, int length,
HttpRequestOptions options) {
if (method == HttpMethod.GET) {
Preconditions.checkArgument(content == null);
return _get(url, requestHeaders, options);
} else {
return postPatchPutOrDelete(url, requestHeaders, content, length, options,
method);
}
}
private HttpResponse _get(String url, List requestHeaders,
HttpRequestOptions options) {
logExpected();
String key = BuilderBase.toKey(HttpMethod.GET, url, requestHeaders);
log("Getting:\n" + key);
Response response = responses.get(key);
String resourceName = response == null ? null : response.resource;
if (resourceName == null) {
throw new RuntimeException("GET response not found for url=" + url
+ ", headers=" + requestHeaders);
}
try {
URL resource = TestingService.class.getResource(resourceName);
if (resource == null) {
throw new RuntimeException(
"resource not found on classpath: " + resourceName);
}
byte[] bytes = Files.readAllBytes(Paths.get(resource.toURI()));
return new HttpResponse(response.statusCode, bytes);
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
}
}
private void logExpected() {
log("Expected responses:");
responses.entrySet().forEach(r -> log(r.getKey() + "\n=>" + r.getValue()));
}
private HttpResponse postPatchPutOrDelete(String url,
List requestHeaders, InputStream content, int length,
HttpRequestOptions options, HttpMethod method) {
log(method + " called at " + url);
final String text;
if (content == null) {
text = "";
} else {
text = Util.utf8(content);
}
log("content=" + text);
logExpected();
log("Calling:");
String key = BuilderBase.toKey(method, url, requestHeaders);
log(key);
String requestResourceName = requests.get(key);
if (requestResourceName == null) {
throw new RuntimeException(method + " request not expected for url=" + url
+ ", headers=" + requestHeaders);
}
try {
System.out.println("requestResourceName=" + requestResourceName);
byte[] requestExpected = readResource(url, requestResourceName);
String expectedText = new String(requestExpected, StandardCharsets.UTF_8);
if (expectedText.equals(text) || jsonEquals(text, expectedText)) {
Response resp = responses
.get(BuilderBase.toKey(method, url, requestHeaders));
String responseResourceName = resp.resource;
final byte[] responseExpected;
if (responseResourceName == null) {
responseExpected = null;
} else {
responseExpected = readResource(url, responseResourceName);
}
// final int responseCode;
// if (resp.statusCode != HttpURLConnection.HTTP_OK) {
// responseCode = resp.statusCode;
// } else {
// responseCode = url.contains("delta") ? HttpURLConnection.HTTP_OK
// : HttpURLConnection.HTTP_CREATED;
// }
return new HttpResponse(resp.statusCode, responseExpected);
} else {
throw new RuntimeException(
"request does not match expected.\n==== Received ====\n" + text
+ "\n==== Expected =====\n" + expectedText);
}
} catch (IOException | URISyntaxException e) {
throw new RuntimeException(e);
}
}
@Override
public Path getBasePath() {
return new Path(baseUrl, pathStyle);
}
@Override
public void close() throws Exception {
// do nothing
}
@Override
public InputStream getStream(String url, List requestHeaders,
HttpRequestOptions options) {
HttpResponse h = get(url, requestHeaders, options);
return new ByteArrayInputStream(h.getBytes());
}
@Override
public InputStream getStream(HttpMethod method, String url,
List requestHeaders, HttpRequestOptions options) {
throw new UnsupportedOperationException();
}
};
}
public abstract R build();
}
private static boolean jsonEquals(final String text, String expectedText) throws IOException {
try {
return Serializer.INSTANCE.matches(expectedText, text);
} catch (JsonParseException e) {
return false;
}
}
public static abstract class ContainerBuilder extends BuilderBase, T> {
public abstract T _create(Context context);
private final Map properties = new HashMap<>();
private final List schemas = new ArrayList<>();
@Override
public T build() {
return _create(new Context(Serializer.INSTANCE, createService(), properties, schemas));
}
public ContainerBuilder addSchema(SchemaInfo schema) {
this.schemas.add(schema);
return this;
}
public ContainerBuilder addProperty(String name, Object value) {
properties.put(name, value);
return this;
}
public ContainerBuilder addProperties(Map p) {
properties.putAll(p);
return this;
}
}
public static final class Builder extends BuilderBase {
@Override
public HttpService build() {
return createService();
}
}
private static byte[] readResource(String url, String resourceName)
throws IOException, URISyntaxException {
if (resourceName == null) {
throw new RuntimeException("resource not found for url=" + url);
}
try (InputStream in = TestingService.class.getResourceAsStream(resourceName)) {
return Util.toByteArray(in);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy