Spring Data Rest Content Type - spring-data-rest

I am writing unit tests for my application with Spring Data Rest MongoDB. Based on Josh's "Building REST services with Spring" get start guide, I have the following test code:
#Test
public void readSingleAccount() throws Exception {
mockMvc.perform(get("/accounts/"
+ this.account.getId()))
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.id", is(this.account.getId())))
.andExpect(jsonPath("$.email", is(this.account.getEmail())))
.andExpect(jsonPath("$.password", is(this.account.getPassword())));
}
And this test fails on the content type.
Content type expected:<application/json;charset=UTF-8> but was: <application/hal+json>
Expected :application/json;charset=UTF-8
Actual :application/hal+json
I don't see MediaType come with HAL. Is the content type defined in another class?

Had the same Problem when not using tomcat (which is configured to return utf-8 using Spring Boot). The solution is to set the accept header in your GET request so the response gets the correct content type:
private MediaType contentType = new MediaType("application", "hal+json", Charset.forName("UTF-8"));
and in your request, do
#Test
public void readSingleAccount() throws Exception {
mockMvc.perform(get("/accounts/"
+ this.account.getId()).**accept(contentType)**)
.andExpect(status().isOk())
.andExpect(content().contentType(contentType))
.andExpect(jsonPath("$.id", is(this.account.getId())))
.andExpect(jsonPath("$.email", is(this.account.getEmail())))
.andExpect(jsonPath("$.password", is(this.account.getPassword())));
}

Related

Apache CXF doesn't select by http method when #DefaultMethod annotation is used

I have two POST methods and one DELETE in my resource. They have same path.
I annotated one of POSTs with #DefaultMethod, so when someone doesn't send correct Accept header, correct method will be selected. But this causes that when DELETE is called, cxf selects POST instead of correct delete method. Is there any workaround for this?
CXF version: 3.1.17
#DefaultMethod
#POST
#Consumes(MeasurementMediaType.MEASUREMENT_TYPE)
#Produces(MeasurementMediaType.MEASUREMENT_TYPE)
public Response post(MeasurementRepresentation measurementRepresentation, #HeaderParam(value = HttpHeaders.ACCEPT) String acceptHeader) URISyntaxException {
...
}
#POST
#Consumes(MEASUREMENT_COLLECTION_TYPE)
#Produces(MEASUREMENT_COLLECTION_TYPE)
public Response post(MeasurementCollectionRepresentation measurementCollectionRepresentation, #HeaderParam(value = HttpHeaders.ACCEPT) String acceptHeader) {
...
}
#DELETE
public Response delete(
#QueryParam("fragmentType") String fragmentType,
#QueryParam("source") String source,
#QueryParam("dateFrom") DateTime dateFrom,
#QueryParam("dateTo") DateTime dateTo,
#QueryParam("type") String type) {
...
}
java.lang.NullPointerException
at com.cumulocity.measurement.rest.resources.MeasurementCollectionResource.post(MeasurementCollectionResource.java:280)
Two things:
1) DefaultMethod appears not to refer to the default method to select, but the default HTTPMethod. So it's essentially overriding the httpmethod of your call. This is a CXF extension to JAX-RS, so you may be able to ask the CXF team to update the functionality or create a new annotation for your use case.
2) If I understand you correctly, you would like the first method to be called if someone sends the body of {"Hello" : "World"}? Wouldn't you then get errors when trying to construct your MeasurementRepresentation? If they send a bad request, why not let CXF respond with an appropriate HTTP error code?

How to implement an integration test to check if my circuit breaker fallback is called?

In my application, I need to call an external endpoint and if it is too slow a fallback is activated.
The following code is an example of how my app looks like:
#FeignClient(name = "${config.name}", url = "${config.url:}", fallback = ExampleFallback.class)
public interface Example {
#RequestMapping(method = RequestMethod.GET, value = "/endpoint", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE)
MyReturnObject find(#RequestParam("myParam") String myParam);
}
And its fallback implementation:
#Component
public Class ExampleFallback implements Example {
private final FallbackService fallback;
#Autowired
public ExampleFallback(final FallbackService fallback) {
this.fallback = fallback;
}
#Override
public MyReturnObject find(final String myParam) {
return fallback.find(myParam);
}
Also, a configured timeout for circuit breaker:
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 5000
How can I implement an integration test to check if my circuit break is working, i.e, if my endpoint (mocked in that case) is slow or if it returns an error like 4xx or 5xx?
I'm using Spring Boot 1.5.3 with Spring Cloud (Feign + Hystrix)
Note i donot know Feign or Hystrix.
In my opinion it is problematic to implement an automated integrationtest that simulates different implementatondetails of Feign+Hystrix - this implementation detail can change at any time. There are many different types of failure: primary-Endpoint not reachable, illegal data (i.e. receiving a html-errormessage, when exprecting xml data in a special format), disk-full, .....
if you mock an endpoint you make an assumption of implementationdetail of Feign+Hystrix how the endpoint behaves in a errorsituation (i.e. return null, return some specific errorcode, throw an exception of type Xyz....)
i would create only one automated integration test with a real primary-enpoint that has a never reachable url and a mocked-fallback-endpoint where you verify that the processed data comes from the mock.
This automated test assumes that handling of "networkconnection too slow" is the same as "url-notfound" from your app-s point of view.
For all other tests i would create a thin wrapper interface around Feign+Hystrix where you mock Feign+Hystrix. This way you can automatically test for example what happens if you receive 200bytes from primary interface and then get an expetion.
For details about hiding external dependencies see onion-architecture

Can't upload files in spring boot

I've been struggling with this for the past 3 days now, I keep getting the following exception when I try upload a file in my spring boot project.
org.springframework.web.multipart.support.MissingServletRequestPartException: Required request part 'file' is not present
I'm not sure if it makes a differance but I am deploying my application as a war onto weblogic,
here is my controller
#PostMapping
public AttachmentDto createAttachment(#RequestParam(value = "file") MultipartFile file) {
logger.info("createAttachment - {}", file.getOriginalFilename());
AttachmentDto attachmentDto = null;
try {
attachmentDto = attachmentService.createAttachment(new AttachmentDto(file, 1088708753L));
} catch (IOException e) {
e.printStackTrace();
}
return attachmentDto;
}
multi part beans I can see in spring boot actuator
payload seen in chrome
Name attribute is required for #RequestParm 'file'
<input type="file" class="file" name="file"/>
You can try use #RequestPart, because it uses HttpMessageConverter, that takes into consideration the 'Content-Type' header of the request part.
Note that #RequestParam annotation can also be used to associate the part of a "multipart/form-data" request with a method argument supporting the same method argument types. The main difference is that when the method argument is not a String, #RequestParam relies on type conversion via a registered Converter or PropertyEditor while #RequestPart relies on HttpMessageConverters taking into consideration the 'Content-Type' header of the request part. #RequestParam is likely to be used with name-value form fields while #RequestPart is likely to be used with parts containing more complex content (e.g. JSON, XML).
Spring Documentation
Code:
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public AttachmentDto createAttachment(#RequestPart("file") MultipartFile file) {
logger.info("Attachment - {}", file.getOriginalFilename());
try {
return attachmentService.createAttachment(new AttachmentDto(file, 1088708753L));
} catch (final IOException e) {
logger.e("Error creating attachment", e);
}
return null;
}
You are using multi part to send files so there is nothing much configuration to do to get desired result.
I m having the same requirement and my code just run fine :
#RestController
#RequestMapping("/api/v2")
public class DocumentController {
private static String bucketName = "pharmerz-chat";
// private static String keyName = "Pharmerz"+ UUID.randomUUID();
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA)
public URL uploadFileHandler(#RequestParam("name") String name,
#RequestParam("file") MultipartFile file) throws IOException {
/******* Printing all the possible parameter from #RequestParam *************/
System.out.println("*****************************");
System.out.println("file.getOriginalFilename() " + file.getOriginalFilename());
System.out.println("file.getContentType()" + file.getContentType());
System.out.println("file.getInputStream() " + file.getInputStream());
System.out.println("file.toString() " + file.toString());
System.out.println("file.getSize() " + file.getSize());
System.out.println("name " + name);
System.out.println("file.getBytes() " + file.getBytes());
System.out.println("file.hashCode() " + file.hashCode());
System.out.println("file.getClass() " + file.getClass());
System.out.println("file.isEmpty() " + file.isEmpty());
/**
BUSINESS LOGIC
Write code to upload file where you want
*****/
return "File uploaded";
}
None of the above solutions worked for me, but when I digged deeper i found that spring security was the main culprit. Even if i was sending the CSRF token, I repeatedly faced the issue POST not supported. I came to know that i was receiving forbidden 403 when i inspected using developer tools in google chrome and saw the status code in the network tab. I added the mapping to ignoredCsrfMapping in Spring Security configuration and then it worked absolutely without any other flaw. Don't know why i was not allowed to post multipart data by security. Some of the mandatory setting that needs to be stated in application.properties file are as follows:
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=10MB
spring.http.multipart.max-file-size=10MB
spring.http.multipart.max-request-size=10MB
spring.http.multipart.enabled=true

Retrieve WS request in CXF Web service

I got a CXF OSGi Web service (based on the example demo in servicemix: https://github.com/apache/servicemix/tree/master/examples/cxf/cxf-jaxws-blueprint)
The Web service works fine and i call all the available implemented methods of the service.
My question is how can i retrieve the request inside a WS method and parse in a string XML format.
I have found that this is possible inside interceptors for logging, but i want also to the WS-Request inside my methods.
For storing the request in the database I suggest to extend the new CXF message logging.
You can implement a custom LogEventSender that writes into the database.
I had similar requirement where I need to save data into DB once method is invoked. I had used ThreadLocal with LoggingInInterceptor and LoggingOutInterceptor. For example in LoggingInInterceptor I used to set the message into ThreadContext and in webservice method get the message using LoggingContext.getMessage() and in LoggingOutInterceptor I used to removed the message(NOTE: Need to be careful here you need to explictly remove the message from thread context else you will end up with memory leak, and also incase of client side code interceptors get reversed.
public class LoggingContext {
private static ThreadLocal<String> message;
public static Optional<String> getMessage() {
return Optional.ofNullable(message.get());
}
public static void setMessage(final String message) {
LoggingContext.message = new ThreadLocal<>();
LoggingContext.message.set(message);
}
}
Not an answer to this question but i achieved to do my task by using JAXB in the end and do some manipulations there.

Possible to do custom method processing with ResteasyClient (Proxy Framework)?

Is it possible to register a DynamicFeature with an ResteasyClient (Proxy Framework) similar to what can be done on server side?
So something similar to this:
final ResteasyClient client = new ResteasyClientBuilder().build();
client.register(new MyDynamicFeature());
Where MyDynamicFeature implements DynamicFeature
I'm trying to figure out how to have a ClientResponseFilter check the http return status depending on the annotation that is present on the resource method, and the DynamicFeature appeared to be the most promising lead to get access to the ResourceInfo.
So essentially, I want to do something like this:
#POST
#Path("some/path/user")
#ExpectedHttpStatus(201) // <- this would have to be passed on somehow as expectedStatus
User createUser(User request);
And then in the ClientResponseFilter (or any other solution) something like this:
#Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
if (responseContext.getStatus() != expectedStatus) {
// explode
}
}
Cause in the ClientResponseFilter, I don't see any way to know what the resource method is that defined the REST call that the filter is currently analyzing.
And the problem is that the framework right now only checks whether the response status is success, it doesn't check whether it's 200 or 201 and we'd like to refine that.
Here are some articles that seems to explain something very similar, yet this doesn't seem to be working with the ClientResponseFilter / ResteasyClient:
Match Filter with specific Method through NameBinding on RESTeasy
What is the proper replacement of the Resteasy 3.X PreProcessInterceptor?
First of all, I can't take credit for the solution really, but I'm going to paste the answer here.
Also, you could ask why the heck we're doing this? Because we need / want to test that the service returns the right http status, but unfortunately the service we are testing does not always return the same http status for the same http method.
E.g. in the example below, the post returns HttpStatus.OK, and another post method of the same service could return HttpStatus.CREATED.
Here's the solution we ended up with, a combination of ClientResponseFilter:
import java.io.IOException;
import java.util.UUID;
import javax.ws.rs.client.ClientRequestContext;
import javax.ws.rs.client.ClientResponseContext;
import javax.ws.rs.client.ClientResponseFilter;
/**
* {#link ClientResponseFilter} which will handle setting the HTTP StatusCode property for use with
* {#link HttpStatusResponseInterceptor}
*/
public class HttpStatusResponseFilter implements ClientResponseFilter {
public static final String STATUS_CODE = "StatusCode-" + UUID.randomUUID();
#Override
public void filter(ClientRequestContext requestContext, ClientResponseContext responseContext) throws IOException {
requestContext.setProperty(STATUS_CODE, responseContext.getStatusInfo());
}
}
And ReaderInterceptor:
import java.io.IOException;
import java.lang.annotation.Annotation;
import javax.ws.rs.ServerErrorException;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ReaderInterceptor;
import javax.ws.rs.ext.ReaderInterceptorContext;
/**
* {#link ReaderInterceptor} which will verify the success HTTP status code returned from the server against the
* expected successful HTTP status code {#link SuccessStatus}
*
* #see HttpStatusResponseFilter
*/
public class HttpStatusResponseInterceptor implements ReaderInterceptor {
#Override
public Object aroundReadFrom(ReaderInterceptorContext interceptorContext) throws ServerErrorException, IOException {
Status actualStatus = (Status) interceptorContext.getProperty(HttpStatusResponseFilter.STATUS_CODE);
if (actualStatus == null) {
throw new IllegalStateException("Property " + HttpStatusResponseFilter.STATUS_CODE + " does not exist!");
}
Status expectedStatus = null;
for (Annotation annotation : interceptorContext.getAnnotations()) {
if (annotation.annotationType() == SuccessStatus.class) {
expectedStatus = ((SuccessStatus) annotation).value();
break;
}
}
if (expectedStatus != null && expectedStatus != actualStatus) {
throw new ServerErrorException(String.format("Invalid status code returned. Expected %d, but got %d.",
expectedStatus.getStatusCode(), actualStatus.getStatusCode()), actualStatus);
}
return interceptorContext.proceed();
}
}
We register both those when we create the client:
final ResteasyClient client = new ResteasyClientBuilder().disableTrustManager().build();
client.register(new HttpStatusResponseFilter());
client.register(new HttpStatusResponseInterceptor());
And the SuccessStatus is an annotation that we use to annotate the methods that we want to specifically check, e.g. like that:
#POST
#Path("some/foobar")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
#SuccessStatus(Status.OK)
Foobar createFoobar(Foobar foobar);
It's not possible to register a DynamicFeature in your client.
See the DynamicFeature documentation:
A JAX-RS meta-provider for dynamic registration of post-matching
providers during a JAX-RS application setup at deployment time.
Dynamic feature is used by JAX-RS runtime to register providers that
shall be applied to a particular resource class and method and
overrides any annotation-based binding definitions defined on any
registered resource filter or interceptor instance.
Providers implementing this interface MAY be annotated with #Provider
annotation in order to be discovered by JAX-RS runtime when scanning
for resources and providers. This provider types is supported only as
part of the Server API.
The JAX-RS Client API can be utilized to consume any Web service exposed on top of a HTTP protocol, and is not restricted to services implemented using JAX-RS.
Please note the JAX-RS Client API does not invoke the resource classes directly. Instead, it generates HTTP requests to the server. Consequently, you won't be able to read the annotations from your resource classes.
Update 1
I'm not sure if this will be useful for you, but since you would like to access the server resource classes from your client, it would be interesting to mention that Jersey provides a proxy-based client API (org.glassfish.jersey.client.proxy package).
The basic idea is you can attach the standard JAX-RS annotations to an interface, and then implement that interface by a resource class on the server side while reusing the same interface on the client side by dynamically generating an implementation of that using java.lang.reflect.Proxy calling the right low-level client API methods.
This example was extracted from Jersey documentation:
Consider a server which exposes a resource at http://localhost:8080. The resource can be described by the following interface:
#Path("myresource")
public interface MyResourceIfc {
#GET
#Produces("text/plain")
String get();
#POST
#Consumes("application/xml")
#Produces("application/xml")
MyBean postEcho(MyBean bean);
#GET
#Path("{id}")
#Produces("text/plain")
String getById(#PathParam("id") String id);
}
You can use WebResourceFactory class defined in this package to access the server-side resource using this interface. Here is an example:
Client client = ClientBuilder.newClient();
WebTarget target = client.target("http://localhost:8080/");
MyResourceIfc resource = WebResourceFactory.newResource(MyResourceIfc.class, target);
String responseFromGet = resource.get();
MyBean responseFromPost = resource.postEcho(myBeanInstance);
String responseFromGetById = resource.getById("abc");
I'm not sure if RESTEasy provides something similar to it.
Update 2
RESTEasy also provides a proxy framework. See the documentation:
RESTEasy has a client proxy framework that allows you to use JAX-RS annotations to invoke on a remote HTTP resource. The way it works is that you write a Java interface and use JAX-RS annotations on methods and the interface. For example:
public interface SimpleClient {
#GET
#Path("basic")
#Produces("text/plain")
String getBasic();
#PUT
#Path("basic")
#Consumes("text/plain")
void putBasic(String body);
#GET
#Path("queryParam")
#Produces("text/plain")
String getQueryParam(#QueryParam("param") String param);
#GET
#Path("matrixParam")
#Produces("text/plain")
String getMatrixParam(#MatrixParam("param") String param);
#GET
#Path("uriParam/{param}")
#Produces("text/plain")
int getUriParam(#PathParam("param") int param);
}
RESTEasy has a simple API based on Apache HttpClient. You generate a proxy then you can invoke methods on the proxy. The invoked method gets translated to an HTTP request based on how you annotated the method and posted to the server. Here's how you would set this up:
Client client = ClientFactory.newClient();
WebTarget target = client.target("http://example.com/base/uri");
ResteasyWebTarget rtarget = (ResteasyWebTarget) target;
SimpleClient simple = rtarget.proxy(SimpleClient.class);
simple.putBasic("hello world");
Alternatively you can use the RESTEasy client extension interfaces directly:
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://example.com/base/uri");
SimpleClient simple = target.proxy(SimpleClient.class);
simple.putBasic("hello world");
[...]
The framework also supports the JAX-RS locator pattern, but on the client side. So, if you have a method annotated only with #Path, that proxy method will return a new proxy of the interface returned by that method.
[...]
It is generally possible to share an interface between the client and server. In this scenario, you just have your JAX-RS services implement an annotated interface and then reuse that same interface to create client proxies to invoke on the client-side.
Update 3
Since you are already using RESTEasy Proxy Framework and assuming your server resources implement the same interfaces you are using to create your client proxies, the following solution should work.
A ProxyFactory from Spring AOP, which is already packed with RESTEasy Client will do trick. This solution, basically, creates a proxy of the proxy to intercept the method that is being invoked.
The following class stores the Method instance:
public class MethodWrapper {
private Method method;
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
}
And the following code makes the magic:
ResteasyClient client = new ResteasyClientBuilder().build();
ResteasyWebTarget target = client.target("http://example.com/api");
ExampleResource resource = target.proxy(ExampleResource.class);
MethodWrapper wrapper = new MethodWrapper();
ProxyFactory proxyFactory = new ProxyFactory(resource);
proxyFactory.addAdvice(new MethodInterceptor() {
#Override
public Object invoke(MethodInvocation invocation) throws Throwable {
wrapper.setMethod(invocation.getMethod());
return invocation.proceed();
}
});
ExampleResource resourceProxy = (ExampleResource) proxyFactory.getProxy();
Response response = resourceProxy.doSomething("Hello World!");
Method method = wrapper.getMethod();
ExpectedHttpStatus expectedHttpStatus = method.getAnnotation(ExpectedHttpStatus.class);
int status = response.getStatus();
int expectedStatus = annotation.status();
For more information, have a look at the documentation:
MethodInterceptor
ProxyFactory
MethodInvocation