Questions about combining Hystrix with Feign - spring-cloud

I am trying to use the new HystrixFeign support in Feign. Here is what my code looks like
route66Client =
HystrixFeign.builder()
.logger(new Slf4jLogger())
.encoder(new GsonEncoder())
.target(Route66Client.class, "http://route66/");
The Route66Client is defined as
public interface Route66Client {
#RequestLine("POST /route")
ApiResponse getRoute(
RouteRequest request);
}
When i try to run the code. I get UnknownHostException. As it is not able to resolve route66 for its hostname. Anyone knows what i could be missing ?
Further i had this working with regular Feign ( not HystrixFeign ). All i did was to annotate my Route66Client class with #FeignClient("...") and then injecting Route66Client in the calling class ( So no Feign.builder() was used )
But i couldn't find some #HystrixFeignClient annotation. So i went ahead and started using the HystrixFeign.builder(). But then when i did that the serviceName->Address resolution stopped working.

If you want the benefits of eureka, don't use the builder directly. Put #EnableFeignClients on an #Configuration class (usually your application). Then label Route66Client with #FeignClient("route66") and inject Route66Client. This is only available in Brixton's 2nd Milestone. See the documentation.

Related

is there a way to mock internal service calls while writing Component testcases

I'm writing my component level test-cases for my repository. My API calls internally a third party API which I need to mock. I don't have direct access to that API neither I can directly call it, it needs to be called from within the API calls. I need to mock in order to run the component test-cases successfully. I tried wiremock but looks like it is not working and my API is still calling the 3rd party URL. Is there any way to solve this problem. Here is my code -
Annotation at class level
#Rule
public WireMockRule wireMockRule = new WireMockRule(wireMockConfig().port(8888));
WireMockServer wm;
Started server
#BeforeEach
void setUp() {
wm = new WireMockServer(WireMockConfiguration.options().port(8888));
wm.start();
}
In the tests.
wireMockServer.start();
wm.stubFor(get("https://someurl")
.willReturn(badRequest()
.withHeader("Content-Type", "application/json")
.withBody("<response>SUCCESS</response>")));
MvcResult mvcResult = this.mockMvc.perform(MockMvcRequestBuilders
.post(apiContextPath)
.contentType(MediaType.APPLICATION_JSON)
.accept(MediaType.APPLICATION_JSON)
.content(gson.toJson(offerParams)))
.andExpect(status().isOk()).andReturn();
wireMockServer.stop();
Sorry can't paste whole code due to security reasons.
It looks like you're trying to use the JUnit 4 rule with JUnit 5, meaning the WireMock server won't be started.
Try the JUnit Jupiter extension instead: https://wiremock.org/docs/junit-jupiter/

How and Where to use failOnUndocumentedParams in Spring Auto REST Docs?

I am working with Spring Auto REST Docs, and want to know the use of failOnUndocumentedParams, and how to use it. I want that documentation should not be generated if I miss a field from the POJO. I believe using failOnUndocumentedParams is my solution. However, I don't know how and where to use it.
#Before
public void setUp() throws IOException {
mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext)
.alwaysDo(JacksonResultHandlers.prepareJackson(objectMapper))
.alwaysDo(MockMvcRestDocumentation.document("{class-name}/{method-name}",
Preprocessors.preprocessRequest(),
Preprocessors.preprocessResponse(
ResponseModifyingPreprocessors.replaceBinaryContent(),
ResponseModifyingPreprocessors.limitJsonArrayLength(objectMapper),
Preprocessors.prettyPrint())))
.apply(MockMvcRestDocumentation.documentationConfiguration(this.restDocumentation)
.uris()
.withScheme("http")
.withHost("localhost")
.withPort(8080)
.and().snippets()
.withDefaults(CliDocumentation.curlRequest(),
HttpDocumentation.httpRequest(),
HttpDocumentation.httpResponse(),
AutoDocumentation.requestFields(),
AutoDocumentation.responseFields(),
AutoDocumentation.pathParameters(),
AutoDocumentation.requestParameters(),
AutoDocumentation.description(),
AutoDocumentation.methodAndPath(),
AutoDocumentation.section()))
.build();
}
Here's how my mockMvc looks.
You can configure the feature when creating the request or response field snippets. In your case you want to enable the feature for all tests and thus when setting up Mock MVC:
.withDefaults(
...
AutoDocumentation.requestFields().failOnUndocumentedFields(true),
AutoDocumentation.responseFields().failOnUndocumentedFields(true),
...)
Alternatively one can enable the feature for a single test:
document("some-path",
AutoDocumentation.requestFields().failOnUndocumentedFields(true),
AutoDocumentation.responseFields().failOnUndocumentedFields(true))

Autofac named service is not found in Web API Controller using attribute (Autofac.Extras.Attributed)

(Note: This is a simplified example intended to highlight the issue I'm seeing.)
I have a service I'm trying to register as a named service as follows:
builder.Register(new MyService()).Named<IMyService>("Test").SingleInstance();
I would have expected to be able to use this service in the constructor of my API Controller:
public TestController([WithKey("Test")] IMyService myService)
{
}
However, an exception gets thrown:
None of the constructors found with
'Autofac.Core.Activators.Reflection.DefaultConstructorFinder' on type
'TestController' can be invoked with the available services and parameters:
Cannot resolve parameter 'IMyService myService' of constructor 'Void
.ctor(IMyService)'.
The same code as above works when I replace the .Named() call with a .As():
builder.Register(new MyService()).As<IMyService>().SingleInstance();
public TestController(IMyService myService)
{
}
It also seems to work when I keep the .Named() call, but add the .As() call to it first:
builder.Register(new MyService()).As<IMyService>().Named<IMyService>("Test")
.SingleInstance();
public TestController([WithKey("Test")] IMyService myService)
{
}
Any ideas on why this behaves as it does? Am I doing something wrong in how I register named services?
From the Autofac wiki:
That component will require you to register a keyed service with the specified name. You'll also need to register the component with the filter so the container knows to look for it.
var builder = new ContainerBuilder();
// Register the keyed service to consume
builder.RegisterType<MyArtwork>().Keyed<IArtwork>("Painting");
// Specify WithAttributeFilter for the consumer
builder.RegisterType<ArtDisplay>().As<IDisplay>().WithAttributeFilter();
...
var container = builder.Build();
Notice the WithAttributeFilter(). Try to add this to your RegisterControllers() call.
You need to use the [WithName] attribute not [WithKey]

Performing Explicit Route Mapping based upon Web Api v2 Attributes

I'm upgrading a custom solution where I can dynamically register and unregister Web Api controllers to use the new attribute routing mechanism. However, it seems to recent update to RTM break my solution.
My solution exposes a couple of Web Api controllers for administration purposes. These are registered using the new HttpConfigurationExtensions.MapHttpAttributeRoutes method call.
The solution also allows Web Api controllers to be hosted in third-party assemblies and registered dynamically. At this stage, calling HttpConfigurationExtensions.MapHttAttributeRoutes a second time once the third-party controller is loaded would raise an exception. Therefore, my solution uses reflection to inspect the RoutePrefix and Route attributes and register corresponding routes on the HttpConfiguration object.
Unfortunately, calling the Web Api results in the following error:
"No HTTP resource was found that matches the request URI".
Here is a simple controller that I want to use:
[RoutePrefix("api/ze")]
public sealed class ZeController : ApiController
{
[HttpGet]
[Route("one")]
public string GetOne()
{
return "One";
}
[HttpGet]
[Route("two")]
public string GetTwo()
{
return "Two";
}
[HttpPost]
[Route("one")]
public string SetOne(string value)
{
return String.Empty;
}
}
Here is the first solution I tried:
configuration.Routes.MapHttpRoute("ZeApi", "api/ze/{action}");
Here is the second solution I tried:
var type = typeof(ZeController);
var routeMembers = type.GetMethods().Where(m => m.IsPublic);
foreach (MethodInfo method in routeMembers)
{
var routeAttribute = method.GetCustomAttributes(false).OfType<RouteAttribute>().FirstOrDefault();
if (routeAttribute != null)
{
string controllerName = type.Name.Substring(0, type.Name.LastIndexOf("Controller"));
string routeTemplate = string.Join("/", "api/Ze", routeAttribute.Template);
configuration.Routes.MapHttpRoute(method.Name, routeTemplate);
}
}
I also have tried a third solution, whereby I create custom classes that implement IHttpRoute and trying to register them with the configuration to no avail.
Is it possible to use legacy-style route mapping based upon the information contained in the new routing attributes ?
Update
I have installed my controller in a Web Application in order to troubleshoot the routing selection process with the Web Api Route Debugger. Here is the result of the screenshot:
As you can see, the correct action seems to be selected, but I still get a 404 error.
Update2
After further analysis, and per Kiran Challa's comment below, it seems that the design of Web Api prevents mixing attribute routing and conventional routing, and that what I want to do is not possible using this approach.
I have created a custom attribute [RouteEx] that serves the same purpose of the Web Api [Route] attribute, and now my code works perfectly.
I guess, since this is not possible using the conventional attribute routing, none of the answers on this question could legitimately be consisered valid. So I'm not nominating an answer just yet.
You shouldn't be required to use reflection and inspect the attribute-routing based attributes yourself. Attribute routing uses existing Web API features to get list of controllers to scan through.
Question: Before the switch to attribute routing, how were you loading these assemblies having the
controllers?
If you were doing this by IAssembliesResolver service, then this solution should work even with attribute routing and you should not be needing to do anything extra.
Regarding your Update: are you calling MapHttpAttributeRoutes?

Complex (non string) return type for Jersey REST method

I'm having trouble setting something up that I'm pretty sure /should/ be easy, so I thought I'd throw it to the crowd. I can't seem to find what I'm looking for elsewhere on the web or on SE.
I am simplifying my project of course, but basically I have a JAX-WS annontated Jersey resource class that looks something like this:
#Path("myresource")
public class MyResource {
#Autowired
MyComplexObjectDAO daoInstance;
#Path("findObject/{id}")
#GET
public MyComplexObject findObject( #PathParam(value="id") String id ) {
return daoInstance.findObject( id );
}
#Path("saveObject")
#PUT
public MyComplexObject saveObject( MyComplexObject objectToSave ) {
MyComplexObject savedObject = daoInstance.saveObject( objectToSave );
return savedObject;
}
}
So you can see I'm autowiring a DAO object using spring, and then I use the DAO methods in the REST handlers.
The 'findObject' call seems to work fine - so far it works exactly as I expect it to.
The 'saveObject' call is not working the way I want and that's what I need some advice on.
You can see that I'm trying to directly take an instance of my complex object as a parameter to the REST method. Additionally I would like to return an instance of the complex object after it's been saved.
I put together some 'client' code for testing this out.
#Test
public void saveTest() {
WebResource wsClient = createWebServiceClient();
MyComplexObject unsavedInstance = createMyComplexObject();
MyComplexObject savedInstance =
wsClient
.path("saveObject")
.accept(MediaType.APPLICATION_XML)
.put(MyComplexObject.class, unsavedInstance);
assertNotNull(savedIntent);
}
Which is returning the following error:
com.sun.jersey.api.client.UniformInterfaceException: PUT http://localhost:8081/rest/myresource/save returned a response status of 400 Bad Request
I don't see why this isn't working and I think I've tried just about everything I can think of. Any help or direction would be very much appreciated.
Thanks so much!
I see that you call the accept() method in your test client (which means that a "Accept:" header is added to the request, indicating the server what type of representation you would like). However, you don't call the type() method to add a "Content-type:" header and inform the server that you are sending XML data. See http://jersey.java.net/nonav/documentation/latest/client-api.html#d4e644 for examples.
Side remark: your URLs are not RESTful - you should avoid verbs in your path:
So, instead of:
/api/findObject/{id}
/api/saveObject
You should use:
/api/objects/{id}
/api/objects
Last note: to create an object on calling /api/objects, you should do a POST and not a PUT to adhere to REST best practices and widely adopted patterns.
switching to the 'concrete class' solution I alluded to in my earlier comment is what fixed things up for me.