Why Swagger created a systemId field in example? - java

I have a REST POST function that has the following header:
#POST
#Consumes(value = { MediaType.APPLICATION_JSON + ";charset=utf-8" })
#Produces(value = { MediaType.APPLICATION_JSON + ";charset=utf-8" })
#ApiOperation(value = "Create a document type", notes = "creates a document type from Json and returns the created type", response = Response.class)
#Session(roles = { Role.ROLE_ADMINISTRATOR })
#PublicApi
public Response create(
#ApiParam(value = "Created DocumentType", required = true)
#SwaggerDataType(type =
com.infor.daf.icp.internal.rest.models.DocumentType.class)
com.infor.daf.icp.internal.rest.models.DocumentType documentType) {
When I look at it in Swagger UI, the Swagger creates an example request body. That body has
systemId (string, optional),
in Model view and
systemId : "string"
in the JSON view. But in the whole project there is not a field named systemId. I had checked the request class and its ancestors one by one, and the whole project by search Java. That symbol sequence systemId does not appear even as a substring of another name.
Where does Swagger gets that name and how can I stop it? For I want it to create a valid example, of course.
Edit: The API function itself takes JSON input without problems and correctly composes an object of the declared class.
Imports :
package com....documentarchive.rest.v1
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
The swagger UI looks so:
Edit 2.
I have removed #SwaggerDataType, or replaced it with #RequestBody, but the strange behaviour remains.
I have set the example to be shown as a concrete string with real data:
#ApiParam(example = DOC_TYPE_EXAMPLE, value = "Created DocumentType", required = true) #RequestBody com.infor.daf.icp.internal.rest.models.DocumentType documentType) {
....
static final private String DOC_TYPE_EXAMPLE = "{'entityModel':\n" +
" {'name':'Anatemplate',\n" +
" 'desc':'Ana-template',\n" +
And even that didn't help! Swagger still generates some senseless string from some distant file (thanks to #xpa1492 for the reference) somewhere on the internet, instead of simply showing out the prepared string.
More edit:
The pom file: https://drive.google.com/file/d/16fOCq5EFZYVBJRPg0HeiP102eRzEaH6W/view?usp=sharing

Seems to have been answered here: https://github.com/kongchen/swagger-maven-plugin/issues/608
Swagger configuration was not loading the Jackson annotation module, ignoring all annotations used. Therefore ApiReader was reading wrong class (https://docs.oracle.com/javase/8/docs/api/org/w3c/dom/DocumentType.html).

Related

How to hide "Schema" from "response" and "Request body" using OpenAPI 3 in Spring Boot?

Is there any way to hide Schema from the Responses and Request body parts? We only need to show Example Value. We use OpenAPI 3.
Dependency:
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.6.9</version>
</dependency>
We can hide listed schema part by using springdoc.swagger-ui.defaultModelsExpandDepth=-1 in application.properties file.
but we want to remove the API schema part from Request Body and Responses.
I tried content= #Content(schema = #Schema(hidden = true )) but it hides whole request body/Response.
Code for Response:
#ApiResponses({
#ApiResponse(responseCode = "200", content = #Content(schema = #Schema(name = "Success response", example = "JsonResponse..."),
mediaType = MediaType.APPLICATION_JSON_VALUE)),
#ApiResponse(responseCode = "400", description = "BAD REQUEST", content = #Content(schema = #Schema(hidden = true)))
})
Code for Request Body:
#io.swagger.v3.oas.annotations.parameters.RequestBody(
content= #Content(schema = #Schema(example="JsonRequestBody...")))
Can anyone please suggest how we can do that?
UPDATE:
We can hide the Schema part from the response like below.
#ApiResponse(responseCode = IConstants.R_str_200, content = #Content(examples=
#ExampleObject(name="SUCCESS RESPONSE",value="Json response..."),
mediaType = IConstants.MEDIA_JSONVALUE))
but still can't able to hide Schema part from Request Body.
I don't think this can be solved using annotations.
You can predefine swagger css to hide the element you want.
To achieve that, first check which version of swagger-ui are you using.
In my case it's 3.25.0.
You can check which version you are using by going to External Libraries folder (if you use InteliJ) and you should find it there ( see picture below)
Then, write controller class like this :
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.stream.Collectors;
#RestController
#RequestMapping(path = "/swagger-ui")
public class SwaggerController {
#GetMapping(path = "/swagger-ui.css", produces = "text/css")
public String getCss() {
String orig = toText(getClass().getResourceAsStream("/META-INF/resources/webjars/swagger-ui/3.25.0/swagger-ui.css"));
String customCss = "li.tabitem.active {\n" +
" display:block !important;\n" +
"}\n" +
"li.tabitem {\n" +
" display:none !important;\n" +
"}}";
return orig+customCss;
}
static String toText(InputStream in) {
return new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8))
.lines().collect(Collectors.joining("\n"));
}
}
The endpoint of this controller will be called when the css is loaded.
In essence, the loading css is intercepted here and a custom css is added to hide the element you want.
With this change, when you start the application and go to the endpoint to view the swagger documentation you should see UI as in the picture below :

Swagger - Wrong Example Value

I have an older JAX-RS Webservice which is documented with Swagger 1.5.12.
To keep the webservice backwards compatible I duplicated the controller and the model and put it in two different packages.
de.kembytes.webservice.rest.v1_0
de.kembytes.webservice.rest.v1_0.model
de.kembytes.webservice.rest.v2_0
de.kembytes.webservice.rest.v2_0.model
The controllers look like this:
Version 1.0:
package de.kembytes.webservice.rest.v1_0;
import de.kembytes.webservice.rest.v1_0.model.WebserviceRequest;
import de.kembytes.webservice.rest.v1_0.model.WebserviceResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
#Api(value = "Webservice")
#Path("/webservice")
public class WebserviceController {
#POST
#Path("/calculate")
#ApiOperation(value = "Version 1.0")
public WebserviceResponse calculate(WebserviceRequest request){
WebserviceResponse response = new WebserviceResponse();
// do logic
return response;
}
}
Version 2.0:
package de.kembytes.webservice.rest.v2_0;
import de.kembytes.webservice.rest.v2_0.model.WebserviceRequest;
import de.kembytes.webservice.rest.v2_0.model.WebserviceResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
#Api(value = "Webservice")
#Path("/webservice/2.0")
public class WebserviceController {
#POST
#Path("/calculate")
#ApiOperation(value = "Version 2.0")
public WebserviceResponse calculate(WebserviceRequest request){
WebserviceResponse response = new WebserviceResponse();
// do logic
return response;
}
}
In the Swagger UI the two versions are displayed correctly.
The problem is that the example value in the Swagger UI is always generated from the WebserviceRequest of version 2.0.
How can I configure the example value to be generated from the request of the corresponding version?
The Swagger-Config look like this:
beanConfig = new BeanConfig();
beanConfig.setSchemes(new String[] {"http", "https"});
beanConfig.setBasePath(contextPath);
beanConfig.setResourcePackage("de.kembytes.webservice.rest");
beanConfig.setScan(true);

Spring WebFlux File Upload: Unsupported Media Type 415 with Multipart upload

I'm running into some issues handling a file upload using spring's reactive framework. I think I'm following the docs, but can't get away from this 415 / Unsupported Media Type issue.
My controller looks like below (as per the example here: https://docs.spring.io/spring/docs/current/spring-framework-reference/web-reactive.html#webflux-multipart-forms)
package com.test.controllers;
import reactor.core.publisher.Flux;
import org.springframework.http.MediaType;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
#RestController
public class TestController {
#RequestMapping(value = "/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
public Flux<String> uploadHandler(#RequestBody Flux<Part> parts) {
return parts
.filter(part -> part instanceof FilePart)
.ofType(FilePart.class)
.log()
.flatMap(p -> Flux.just(p.filename()));
}
}
POSTing to this endpoint though, always gives me the same output:
curl -X POST -F "data=#basic.ppt" http://localhost:8080/upload
---
"Unsupported Media Type","message":"Content type 'multipart/form-data;boundary=------------------------537139718d79303c;charset=UTF-8' not supported"
I've attempted to use #RequestPart("data") too, but get a similar Unsupported Media Type error, albeit with the content type of the file.
It seems that Spring is having issues converting these to a Part..? I'm stuck - any help is apprecitated!
Well, it's not a direct answer for your question, because I use functional endpoints, but I hope it will help you somehow.
import org.springframework.context.annotation.Bean;
import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.stereotype.Controller;
import org.springframework.web.reactive.function.BodyExtractors;
import org.springframework.web.reactive.function.server.HandlerFunction;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.ServerResponse;
import java.io.File;
import java.util.Map;
import static org.springframework.web.reactive.function.BodyInserters.fromObject;
import static org.springframework.web.reactive.function.server.RequestPredicates.POST;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.nest;
import static org.springframework.web.reactive.function.server.RouterFunctions.route;
#Controller
public class FileUploadController {
#Bean
RouterFunction<ServerResponse> apiRoutes() {
return nest(path("/api"),
route(POST("/upload"), fileUpload()));
}
private HandlerFunction<ServerResponse> fileUpload() {
return request -> {
return request.body(BodyExtractors.toMultipartData()).flatMap(parts -> {
Map<String, Part> map = parts.toSingleValueMap();
final FilePart filePart = (FilePart) map.get("file");
final String dir = "C:\\JDeveloper\\mywork\\Spring\\SpringTest\\webflux-file-upload\\uploaded";
filePart.transferTo(new File(dir + "/" + filePart.filename()));
return ServerResponse.ok().body(fromObject("ok, file uploaded"));
}
);
};
}
}
You can upload a file with curl like this:
curl -F "file=#C:\Users\Wojtek\Desktop\img-5081775796112008742.jpg" localhost:8080/api/fileupload
Thanks #kojot for your answer, but in this case I discovered the issue was my inclusion of spring-webmvc transiently in addition to spring-webflux. Your solution would likely have worked too, but I wanted to stick with the Controller style so ended up forcibly excluding spring-webmvc from my build.gradle:
configurations {
implementation {
exclude group: 'org.springframework', module: 'spring-webmvc'
}
}
After that it worked as documented.

Uploading a file using RESTful API based on Grails JAX-RS Plugin

I'm trying to get a file with a RESTful API based with JAX-RS on Grails.The file is sent from a regular POST multi-part form with file input tag. ( For sending the file I'm using postman google extention )
But after sending the request I get "HTTP Status 400 - Bad Request" response.
I checked many tutorials and followed exactly their steps but it's not working.
Here is the the code in REST service to handle the request :
import com.sun.jersey.multipart.FormDataParam
import com.sun.jersey.core.header.FormDataContentDisposition;
import org.json.simple.JSONObject
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
import javax.ws.rs.Consumes
import javax.ws.rs.FormParam
import javax.ws.rs.GET
import javax.ws.rs.POST
import javax.ws.rs.Path
import javax.ws.rs.PathParam
import javax.ws.rs.Produces
import javax.ws.rs.QueryParam
import javax.ws.rs.core.Context
import javax.ws.rs.core.Response;
import javax.ws.rs.core.MediaType
import javax.ws.rs.core.MultivaluedMap
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
#Path('/api/upload/')
class UploadResource {
#POST
#Path("/tst")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces('application/json')
public String uploadFile(#FormDataParam("file") InputStream is, #FormDataParam("file") FormDataContentDisposition fileDetail){
String uploadedFileLocation = "Some Location";
// save it
saveToFile(is, uploadedFileLocation);
JSONObject JObject = new JSONObject();
JObject.put("Message", "Aha")
JObject.put("Response", "200")
JObject.put("Status", "OK")
return JObject.toJSONString()
}
}
And here is the way I send the file :
http://postimg.org/image/x3wfrs6h5/
Instead of disabling Grails' multipart resolver completely for the whole application (see: https://code.google.com/p/grails-jaxrs/issues/detail?id=52#c11) you could get the file by accessing the multipart file from Grails' WebUtils Holder.
#POST
#Path("/tst")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces('application/json')
public String uploadFile() {
String uploadedFileLocation = "Some Location";
// here is the workaround for issue: https://code.google.com/p/grails-jaxrs/issues/detail?id=52
GrailsWebRequest request = WebUtils.retrieveGrailsWebRequest()
MultipartFile multipartFile = request.getRequest().getFile('file')
def is = multipartFile.inputStream
// save it
saveToFile(is, uploadedFileLocation);
JSONObject JObject = new JSONObject();
JObject.put("Message", "Aha")
JObject.put("Response", "200")
JObject.put("Status", "OK")
return JObject.toJSONString()
}
The problem occures because the controller initiated by the jaxrs plugin parses the request upfront and this leads to the error.
This problem is known and there is a workaround for this: https://code.google.com/p/grails-jaxrs/issues/detail?id=52#c11
But it is still a known bug on jaxrs plugin.
By default Grails defines a bean named 'multipartResolver' defined for
CommonsMultipartResolver, but this does not work with jax-rs as per the reasons detailed by Denny.
The only issue I see with his proposal to use GrailsWebRequest is that if you go to YOUR_APP_URL/application.wadl, you will not see the file param there and therefore auto-generated client code will not work.
What I have done is override this in resources.groovy to:
multipartResolver(org.springframework.web.multipart.support.StandardServletMultipartResolver) { bean ->
bean.autowire = 'byName'
}

How to catch isHexDigit Exception in WeldListener?

From somewhere I got some stupid and invalid URL Request to a Webservice, like
http://localhost:8080/WebApplication1/webresources/testme?stupidparameter=dyndcvgz/&%$\xa7edns
My sample Application looks like following, build with Netbeans on glassfish 4.0 and jdk1.7u40
package WebApplication1;
import javax.ejb.Stateless;
import javax.ws.rs.Path;
import javax.ws.rs.GET;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
#Stateless
#Path("/testme")
public class NewSessionBean {
/**
* http://localhost:8080/WebApplication1/webresources/testme?stupidparameter=dyndcvgz/&%$\xa7edns
*
* #param stupidparameter
* #return
*/
#GET
#Produces("text/plain")
public String getXml(
#QueryParam("stupidparameter") String stupidparameter) {
System.out.println("stupidparameter = " + stupidparameter);
return "got " + stupidparameter;
}
}
and with the given test url I always got IllegalStateException: isHexDigit from org.jboss.weld.servlet.WeldListener. How can I avoid or handle this exception?
This seems like a problem with Glassfish, the way it's processing parameters with special characters.
https://java.net/jira/browse/GRIZZLY-1538
Until the issue is resolved in a future release, you could try to encode the parameter as base64 and decode it back when the paramter is received inside getXml(). This would avoid having related special characters from passing through logic inside glassfish where the exception is thrown.

Categories