how to write spring-restdocs snippets with MockMultipartFile - java

I use spring unit test with spring-restdocs.
this is my mockmvc code:
mockMvc.perform(fileUpload("/api/enterprise/uploadImage")
.file(imageFile)
.with(csrf().asHeader())
.params(params)
).andExpect(status().isOk());
but when use spring-restdocs I don't how to write the snippet of file filed.
this is my snippets create code:
document.snippets(
requestParameters(
parameterWithName("file").description("upload file"),
parameterWithName("imageType").description("image type")
)
);
in this way I get an error:
org.springframework.restdocs.snippet.SnippetException: Request parameters with the following names were not found in the request: [file]
at org.springframework.restdocs.request.RequestParametersSnippet.verificationFailed(RequestParametersSnippet.java:79)
at org.springframework.restdocs.request.AbstractParametersSnippet.verifyParameterDescriptors(AbstractParametersSnippet.java:93)
at org.springframework.restdocs.request.AbstractParametersSnippet.createModel(AbstractParametersSnippet.java:70)
at org.springframework.restdocs.snippet.TemplatedSnippet.document(TemplatedSnippet.java:64)
at org.springframework.restdocs.mockmvc.RestDocumentationResultHandler.handle(RestDocumentationResultHandler.java:101)
at org.springframework.test.web.servlet.MockMvc.applyDefaultResultActions(MockMvc.java:195)
at org.springframework.test.web.servlet.MockMvc.perform(MockMvc.java:163)
at com.athena.edge.enterprise.controller.UploadImageTest.uploadImage(UploadImageTest.java:108)

You're sending a multi-part request so the file that's being uploaded isn't a request parameter. Instead, it's one of the parts in the request and
your test is failing because you're trying to document a request parameter that doesn't exist.
Spring REST Docs doesn't have support for documenting parts in a multipart request at the moment. There is an open issue for it. I haven't implemented anything yet as request parts can be quite complex. For example, in some cases the part name and a description may be sufficient but in others it may be useful to document the part's headers, the structure of its content, etc.
Please comment on the issue linked to above, particularly if support for the simplest case would be useful.

Since the release of version 1.1.0.RELEASE of spring-restdocs you can use RequestPartsSnippet.
You can now write spring-restdocs snippets with MockMultipartFile as the following:
mockMvc.perform(multipart("/upload").file("file", "example".getBytes()))
.andExpect(status().isOk())
.andDo(document("upload", RequestPartsSnippet.requestParts(
RequestPartDescriptor.partWithName("file").description("The file to upload"))
));
This example is taken from the official documentation here.

Related

Swagger ui is not loading swagger api json

I have made api documention with the help of swagger-editor. Now I want see it graphically with the help of swagger-ui. I have deployed my json here https://powerful-escarpment-92284.herokuapp.com/swagger.json
Now When I am trying to access it then I am getting an error
Failed to load spec.
I solved this by setting this header in the response of swagger.json file.
app.get('/:file(*)', function(req, res, next){
var file = req.params.file, path = __dirname + '/' + file;
console.log('.');
res.header('Access-Control-Allow-Origin', '*');
res.download(path);
});
I was getting this error in a c# project, due to a problem with a controller I added.
I could get more information by trying to browse to the swagger.json
It turned out I was missing the routing attribute on a controller action.
After many exercises for me apparently, it helped just to serve a file with flask server [How to serve static files in Flask. and (important) add "?" to the end of the url, i.e. http://127.0.0.1/js/swagger.json?
The URL is not shown, so I assume if you access the swagger directly via "index.html" or any other way, the URL format must be like this.
http://<host>:<port>/<project_name>/_swagger-ui/index.html?url=http://<host>:<port>/<project_name>/<path>/swagger.json
like:
http://localhost:8080/counter/_swagger-ui/index.html?url=http://localhost:8080/counter/webapi/swagger.json
I hope, it helps :)

How to log request payload when using Camel Rest?

I'd like to log the original 'raw' request body (e.g. JSON) while using Camel Rest endpoints. What's the proper way to do this?
My setup (RouteBuilder) looks like this:
restConfiguration().component("jetty")
.host(this.host)
.port(this.port)
.contextPath(this.contextPath)
.bindingMode(RestBindingMode.json);
rest("myService/").post()
.produces("application/json; charset=UTF-8")
.type(MyServiceRequest.class)
.outType(MyServiceResponse.class)
.to(SERVICE_CONTEXT_IN);
from(SERVICE_CONTEXT_IN).process(this.serviceProcessor);
My problem here is that the mechanics such as storing the request as an Exchange property are 'too late' in terms of using this approach, any processors are too late in the route, i.e., the binding already took place and consumed the Request. Also the CamelHttpServletRequest's InputStream has already been read and contains no data.
The first place to use the log EIP is directly before the single processor:
from(SERVICE_CONTEXT_IN).log(LoggingLevel.INFO, "Request: ${in.body}")
.process(this.serviceProcessor);
but at that point the ${in.body} is already an instance of MyServiceRequest. The added log above simply yields Request: x.y.z.MyServiceRequest#12345678. What I'd like to log is the original JSON prior to being bound to a POJO.
There seems to be no built-in way of enabling logging of the 'raw' request in RestConfigurationDefinition nor RestDefinition.
I could get rid of the automatic JSON binding and manually read the HTTP Post request's InputStream, log and perform manual unmarshalling etc. in a dedicated processor but I would like to keep the built-in binding.
I agree there is no way to log the raw request (I assume you mean the payload going through the wire before any automatic binding) using Camel Rest endpoints.
But taking Roman Vottner into account, you may change your restConfiguration() as follows:
restConfiguration().component("jetty")
.host(this.host)
.port(this.port)
.componentProperty("handlers", "#yourLoggingHandler")
.contextPath(this.contextPath)
.bindingMode(RestBindingMode.json);
where your #yourLoggingHandler needs to be registered in your registry and implement org.eclipse.jetty.server.Handler. Please take a look at writing custom handlers at Jetty documentation http://www.eclipse.org/jetty/documentation/current/jetty-handlers.html#writing-custom-handlers.
In the end I 'solved' this by not using the REST DSL binding with a highly sophisticated processor for logging the payload:
restConfiguration().component("jetty")
.host(this.host)
.port(this.port)
.contextPath(this.contextPath);
rest("myService/").post()
.produces("application/json; charset=UTF-8")
.to(SERVICE_CONTEXT_IN);
from(SERVICE_CONTEXT_IN).process(this.requestLogProcessor)
.unmarshal()
.json(JsonLibrary.Jackson, MyServiceRequest.class)
.process(this.serviceProcessor)
.marshal()
.json(JsonLibrary.Jackson);
All the requestLogProcessor does is to read the in body as InputStream, get and log the String, and eventually pass it on.
You can solve this by:
Turning the RestBindingMode to off on your specific route and logging the incoming request string as is.
After which you can convert the JSON string to your IN type object using ObjectMapper.
At the end of the route convert the java object to JSON and put it in the exchange out body, as we turned off the RestBindingMode.
rest("myService/").post()
.bindingMode(RestBindingMode.off)
.to(SERVICE_CONTEXT_IN);
In my case, streamCaching did the trick because the Stream was readable only once. Thefore I was able log but was not able to forward the body any more. I hope this might be of help to someone

Sending dynamic custom headers in swagger UI try outs

I am using swagger in java.
I am reading a header called callerId through requestAttributes in the code. I am not using the header through the annotation #HeaderParam.
because of this reason, the header section is not showing up in the swagger UI for try outs.
How can I make this header show up in the try out form.
Is there any way I can achieve this without hard coding the header value.
Thanks in advance.
You can add parameters to an operation using #ApiImplicitParams and #ApiImplicitParam.
The following wiki documentation should provide you with an explanation on how to use these annotations - https://github.com/swagger-api/swagger-core/wiki/Annotations#apiimplicitparam-apiimplicitparams
Keep in mind I assume here that this is not a security header but actually part of the API (at least based on the description you gave). If you're looking for a security header, there's an alternative method. Please leave a comment and I'll edit this response.

Spring RestTemplate forward large file to another service

I am trying to forward a large file pulled as an input stream to another service using spring's resttemplate. I have followed the answer given by #artbristol in this topic: How to forward large files with RestTemplate?
And it looks like it is setting the body of the request properly (grabbing the request with charlesproxy). The problem is that I have not set the headers correctly since I believe I need to set the content-type as multipart/formdata which I tried by adding this in the callback:
request.getHeaders().setContentType(
new MediaType("multipart", "form-data"));
But in the http headers I am still missing the boundary, not sure how to set that and I am sure I am probably missing some other settings.
So I was able to figure this out. Basically I needed to create a Spring message converter that will take in the input stream and write out to the body. I also basically have to use the Form Message Converter to write out the response body as well.
So in the restTemplate I call an add message converter to add new input stream message converter. In the call back I create a multivaluemap that takes in a string and inputstream and wrap that around an HttpEntity. Then I create a new instance of the Form Message converter and call write, passing in request, and the mutlivaluemap.
It looks like the issue is that I did not include the path to htrace-core.jar in the spark class path:
spark-shell --driver-class-path /opt/cloudera/parcels/CDH/lib/hbase/hbase-server.jar:/opt/cloudera/parcels/CDH/lib/hbase/hbase-protocol.jar:/opt/cloudera/parcels/CDH/lib/hbase/hbase-hadoop2-compat.jar:/opt/cloudera/parcels/CDH/lib/hbase/hbase-client.jar:/opt/cloudera/parcels/CDH/lib/hbase/hbase-common.jar:/opt/cloudera/parcels/CDH/lib/hbase/lib/htrace-core.jar:/etc/hbase/conf
Seems like this is new for spark 1.x

POST curl request returns syantactic error on Spring tc server

I'm new to Spring, STS, MVC, and web development.
I have a working Spring REST-based web application. I also have a java client which works. I followed these 2 tutorials:
RESTful webservices with Spring
Get started with Spring MVC
What I was able to do is use a java client within the same project and use the RestTemplate postForLocation method. It works! However, now I would like the client to be an iPhone and I don't know how to do that. In the java client, the RestTemplate did a post using the com.project.Transaction class.
Please correct me if I am wrong here. In the XML file, the restTemplate "messageConverter" attribute gets set to jaxbMarshaller which is a Jaxb2Marshaller class with "Transaction" as one of the classes bounded. I don't understand the details of it but I am assuming the RestTemplate takes the Transaction object and marshalls it into XML which gets sent to the server as a POST request.
First question:
Is there any way I can see what the marshalled object (ie: the output) looks like? I'm using STS and please be specific as I am new. For example, maybe the data sent is like <xml ...?
Second question:
I am trying to use curl to make a similar POST request as the java client. This is my curl request:
curl -X POST -H 'Accept:application/xml' -H 'Content-Type: application/xml' http://localhost:8080/BarcodePayment/transactions/ --data "<?xml version="1.0" encoding="UTF-8"?><transaction><amount>3.1</amount><id>5</id><paid>true</id></transaction>"
When I do that, I receive a STATUS 400: syntactically error message.
Third queston:
I would love to be able to understand the details a little bit better. I looked into the source code for RestTemplate from here. In it, the postForLocation method uses HttpEntityRequestCallback which I cannot find any info in Google. I found HttpHeaders in java API doc. However, in RestTemplate, they call getLocation() method which does not exist in the Java API doc under javax.ws.rs.core -> HttpHeaders. How can I understand this stuff better?
I know it's a lot of question. Thanks for the help! Let me know if you need more code snippets and I'll be happy to provide!
Details:
TransactionController
#RequestMapping(method = RequestMethod.POST)
public View addTransaction(#RequestBody Transaction transaction)
{
transactionService.saveTransaction(transaction);
return new RedirectView("/transactionsView/"+ transaction.getId());
}
Your xml is incorrect:
<transaction><amount>3.1</amount><id>5</id><paid>true</id></transaction>
^^^ should be /paid
It turns out I have to use single quotes for the data. After --data "< xml .... ", I change it to ' < xml ... ' and it works.

Categories