Skip to content
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 76 additions & 1 deletion src/it/java/org/seedstack/feign/FeignIT.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,12 @@
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.junit.Test;
import org.seedstack.feign.fixtures.Message;
import org.seedstack.feign.fixtures.TestAPI;
import org.seedstack.feign.fixtures.TestContract;
import org.seedstack.feign.fixtures.apis.HystrixDisabledAPI;
import org.seedstack.feign.fixtures.apis.HystrixEnabledAPI;
import org.seedstack.feign.fixtures.apis.TargetableAPI;
import org.seedstack.feign.fixtures.apis.TestAPI;
import org.seedstack.feign.fixtures.apis.TestContractAPI;
import org.seedstack.seed.it.AbstractSeedWebIT;

import javax.inject.Inject;
Expand All @@ -29,6 +34,18 @@ public class FeignIT extends AbstractSeedWebIT {
@Inject
private TestAPI testAPI;

@Inject
private TestContractAPI contractAPI;

@Inject
private HystrixEnabledAPI hystrixEnabledAPI;

@Inject
private HystrixDisabledAPI hystrixDisabledAPI;

@Inject
private TargetableAPI targetableAPI;

@Deployment
public static WebArchive createDeployment() {
return ShrinkWrap.create(WebArchive.class, "feign.war");
Expand All @@ -40,6 +57,30 @@ public void feignClientIsInjectable() throws Exception {
assertThat(testAPI).isNotNull();
}

@Test
@RunAsClient
public void feignContractClientIsInjectable() throws Exception {
assertThat(contractAPI).isNotNull();
}

@Test
@RunAsClient
public void feignHystrixEnabledClientIsInjectable() throws Exception {
assertThat(hystrixEnabledAPI).isNotNull();
}

@Test
@RunAsClient
public void feignHystrixDisabledClientIsInjectable() throws Exception {
assertThat(hystrixDisabledAPI).isNotNull();
}

@Test
@RunAsClient
public void feignTargetableClientIsInjectable() throws Exception {
assertThat(targetableAPI).isNotNull();
}

@Test
@RunAsClient
public void testNominalCall() {
Expand All @@ -55,4 +96,38 @@ public void testFallback() {
assertThat(message.getBody()).isEqualTo("Error code: 404 !");
assertThat(message.getAuthor()).isEqualTo("fallback");
}

@Test
@RunAsClient
public void testContractNominalCall() {
Message message = contractAPI.getMessage();
assertThat(message.getBody()).isEqualTo("Hello World !");
assertThat(message.getAuthor()).isEqualTo("computer");
assertThat(TestContract.hasBeenUsed()).isTrue();
}

@Test
@RunAsClient
public void testHystrixEnabledNominalCall() {
Message message = hystrixEnabledAPI.getMessage();
assertThat(message.getBody()).isEqualTo("Hello World !");
assertThat(message.getAuthor()).isEqualTo("computer");
}

@Test
@RunAsClient
public void testHystrixDisabledNominalCall() {
Message message = hystrixDisabledAPI.getMessage();
assertThat(message.getBody()).isEqualTo("Hello World !");
assertThat(message.getAuthor()).isEqualTo("computer");
}

@Test
@RunAsClient
public void testTargetableNominalCall() {
Message message = targetableAPI.getMessage();
assertThat(message.getBody()).isEqualTo("I was routed trough a custom target");
assertThat(message.getAuthor()).isEqualTo("or i thought so");
}

}
175 changes: 175 additions & 0 deletions src/it/java/org/seedstack/feign/fixtures/TestContract.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.feign.fixtures;

import static feign.Util.checkState;
import static feign.Util.emptyToNull;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;

import feign.Body;
import feign.Contract;
import feign.HeaderMap;
import feign.Headers;
import feign.MethodMetadata;
import feign.Param;
import feign.QueryMap;
import feign.RequestLine;

public class TestContract extends Contract.BaseContract {

private static boolean used = false;

public static boolean hasBeenUsed() {
return used;
}
private static void use() {
used = true;
}

// Copycat of DefaultContract with test-flag
@Override
protected void processAnnotationOnClass(MethodMetadata data, Class<?> targetType) {
use();
if (targetType.isAnnotationPresent(Headers.class)) {
String[] headersOnType = targetType.getAnnotation(Headers.class).value();
checkState(headersOnType.length > 0, "Headers annotation was empty on type %s.",
targetType.getName());
Map<String, Collection<String>> headers = toMap(headersOnType);
headers.putAll(data.template().headers());
data.template().headers(null); // to clear
data.template().headers(headers);
}
}

@Override
protected void processAnnotationOnMethod(MethodMetadata data, Annotation methodAnnotation,
Method method) {
use();
Class<? extends Annotation> annotationType = methodAnnotation.annotationType();
if (annotationType == RequestLine.class) {
String requestLine = RequestLine.class.cast(methodAnnotation).value();
checkState(emptyToNull(requestLine) != null,
"RequestLine annotation was empty on method %s.", method.getName());
if (requestLine.indexOf(' ') == -1) {
checkState(requestLine.indexOf('/') == -1,
"RequestLine annotation didn't start with an HTTP verb on method %s.",
method.getName());
data.template().method(requestLine);
return;
}
data.template().method(requestLine.substring(0, requestLine.indexOf(' ')));
if (requestLine.indexOf(' ') == requestLine.lastIndexOf(' ')) {
// no HTTP version is ok
data.template().append(requestLine.substring(requestLine.indexOf(' ') + 1));
} else {
// skip HTTP version
data.template().append(
requestLine.substring(requestLine.indexOf(' ') + 1,
requestLine.lastIndexOf(' ')));
}

data.template().decodeSlash(RequestLine.class.cast(methodAnnotation).decodeSlash());

} else if (annotationType == Body.class) {
String body = Body.class.cast(methodAnnotation).value();
checkState(emptyToNull(body) != null, "Body annotation was empty on method %s.",
method.getName());
if (body.indexOf('{') == -1) {
data.template().body(body);
} else {
data.template().bodyTemplate(body);
}
} else if (annotationType == Headers.class) {
String[] headersOnMethod = Headers.class.cast(methodAnnotation).value();
checkState(headersOnMethod.length > 0, "Headers annotation was empty on method %s.",
method.getName());
data.template().headers(toMap(headersOnMethod));
}

}

@Override
protected boolean processAnnotationsOnParameter(MethodMetadata data, Annotation[] annotations,
int paramIndex) {
use();
boolean isHttpAnnotation = false;
for (Annotation annotation : annotations) {
Class<? extends Annotation> annotationType = annotation.annotationType();
if (annotationType == Param.class) {
String name = ((Param) annotation).value();
checkState(emptyToNull(name) != null, "Param annotation was empty on param %s.",
paramIndex);
nameParam(data, name, paramIndex);
if (annotationType == Param.class) {
Class<? extends Param.Expander> expander = ((Param) annotation).expander();
if (expander != Param.ToStringExpander.class) {
data.indexToExpanderClass().put(paramIndex, expander);
}
}
isHttpAnnotation = true;
String varName = '{' + name + '}';
if (data.template().url().indexOf(varName) == -1 &&
!searchMapValuesContainsSubstring(data.template().queries(), varName) &&
!searchMapValuesContainsSubstring(data.template().headers(), varName)) {
data.formParams().add(name);
}
} else if (annotationType == QueryMap.class) {
checkState(data.queryMapIndex() == null,
"QueryMap annotation was present on multiple parameters.");
data.queryMapIndex(paramIndex);
data.queryMapEncoded(QueryMap.class.cast(annotation).encoded());
isHttpAnnotation = true;
} else if (annotationType == HeaderMap.class) {
checkState(data.headerMapIndex() == null,
"HeaderMap annotation was present on multiple parameters.");
data.headerMapIndex(paramIndex);
isHttpAnnotation = true;
}
}
return isHttpAnnotation;
}

private static <K, V> boolean searchMapValuesContainsSubstring(Map<K, Collection<String>> map,
String search) {
Collection<Collection<String>> values = map.values();
if (values == null) {
return false;
}

for (Collection<String> entry : values) {
for (String value : entry) {
if (value.indexOf(search) != -1) {
return true;
}
}
}

return false;
}

private static Map<String, Collection<String>> toMap(String[] input) {
Map<String, Collection<String>> result = new LinkedHashMap<String, Collection<String>>(
input.length);
for (String header : input) {
int colon = header.indexOf(':');
String name = header.substring(0, colon);
if (!result.containsKey(name)) {
result.put(name, new ArrayList<String>(1));
}
result.get(name).add(header.substring(colon + 2));
}
return result;
}

}
2 changes: 2 additions & 0 deletions src/it/java/org/seedstack/feign/fixtures/TestFallback.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
*/
package org.seedstack.feign.fixtures;

import org.seedstack.feign.fixtures.apis.TestAPI;

public class TestFallback implements TestAPI {
@Override
public Message getMessage() {
Expand Down
10 changes: 9 additions & 1 deletion src/it/java/org/seedstack/feign/fixtures/TestResource.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/message")
@Path("/")
public class TestResource {

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/message")
public Message say() {
return new Message("Hello World !", "computer");
}

@GET
@Produces(MediaType.APPLICATION_JSON)
@Path("/target/message")
public Message targetSay() {
return new Message("I was routed trough a custom target","or i thought so");
}
}
34 changes: 34 additions & 0 deletions src/it/java/org/seedstack/feign/fixtures/TestTarget.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
/**
*
*/
package org.seedstack.feign.fixtures;

import javax.inject.Inject;

import org.seedstack.feign.fixtures.apis.TargetableAPI;
import org.seedstack.seed.Application;

import feign.Target.HardCodedTarget;

public class TestTarget extends HardCodedTarget<TargetableAPI> {

@Inject
public TestTarget(Application application) {
super(TargetableAPI.class, String.format("http://localhost:%s/feign/target/",
application.getConfiguration().getMandatory(String.class,
"integrationTest.reservedPort")));
}

@Override
public String toString() {
return "TestTarget []" + super.toString();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.feign.fixtures.apis;

import org.seedstack.feign.FeignApi;
import org.seedstack.feign.fixtures.Message;

import feign.RequestLine;


@FeignApi
public interface HystrixDisabledAPI {

@RequestLine("GET /message")
Message getMessage();

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/**
* Copyright (c) 2013-2016, The SeedStack authors <http://seedstack.org>
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package org.seedstack.feign.fixtures.apis;

import org.seedstack.feign.FeignApi;
import org.seedstack.feign.fixtures.Message;

import feign.RequestLine;


@FeignApi
public interface HystrixEnabledAPI {

@RequestLine("GET /message")
Message getMessage();

}
Loading