I am using swagger 3.0.0-Snapshot to create documentation for my Spring Boot application.
My maven dependencies are
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger2</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-spring-webmvc</artifactId>
<version>3.0.0-SNAPSHOT</version>
</dependency>
My swagger config class is as simple as possible:
#Configuration
#EnableSwagger2WebMvc
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.useDefaultResponseMessages(false)
.select()
.apis(RequestHandlerSelectors.basePackage("com.mycompany.cs"))
.paths(PathSelectors.any())
.build()
.pathMapping("/")
.useDefaultResponseMessages(false);
}
And my controller method has the following annotation:
#ApiOperation(value = "Hello world", httpMethod = "POST")
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK",
examples = #Example(value = #ExampleProperty(mediaType = "application/json",
value = exampleValue)))
})
It is working and shows in Swagger UI "Example Value" field value that has constant string exampleValue that is private static String.
The question is how to pass the content of json file that is in resources folder to #ExampleProperty value?
I tried to read file content in static block and pass it to initialize final String with it, but then the compiler says that "Attribute value has to be constant".
The content of json file must be shown in example field in Swagger UI.
Good news is that Swagger is using Spring and it is possible to use the power of DI.
For instance, you want to add new functionality to ServiceModelToSwagger2MapperImpl. Create your own component that extends it and mark it primary. Spring will autowire your implementation of ServiceModelToSwagger2Mapper abstract class.
#Component
#Primary
#Slf4j
public class ServiceModelToSwagger2MapperExtensionImpl extends ServiceModelToSwagger2MapperImpl {
For instance, you want it to read the content of the file and put it to the example field:
#Override
protected Map<String, Response> mapResponseMessages(Set<ResponseMessage> from) {
Map<String, Response> responses = super.mapResponseMessages(from);
responses.forEach((key, response)-> {
Map<String, Object> examples = response.getExamples();
examples.entrySet().forEach(example -> {
Object exampleObject = example.getValue();
if (exampleObject instanceof String) {
String exampleValue = (String) exampleObject;
if (exampleValue.startsWith("file:")) {
String fileContent = readFileContent(exampleValue);
example.setValue(fileContent);
}
}});
});
return responses;
}
private String readFileContent(String example) {
String fileContent = "";
try {
String fileName = example.replace("file:", "");
File resource = new ClassPathResource(fileName).getFile();
if(resource.exists()) {
fileContent
= new String(Files.readAllBytes(resource.toPath()));
}
} catch (
IOException e) {
log.error("Cannot read swagger documentation from file {}", example);
}
return fileContent;
}
And here is an example of usage in your controller:
#ApiResponses(value = {
#ApiResponse(code = 200, message = "OK",
examples = #Example(value = #ExampleProperty(mediaType = "application/vnd.siren+json",
value = "file:/data/controller-responses/reponse.json")))
})
Related
I'm using SpringBoot with the following dependency
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.12</version>
</dependency>
The controller class (#RestController) has one entry point (#GetMapping), and this entry point should return a List of the object : MyClass.java. I added Swagger annotations above the method in order to create API documentation via a swagger UI page.
The swagger documentation should indicate that the return object is of type
List< MyClass>
But how should I do that ? If I do
"#Schema(implementation = List< MyClass >.class)"
there is a compile error.
Swagger annotations:
#Operation(....)
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "successful operation",
content = { #Content(mediaType = "application/json",
schema = #Schema(implementation = ????)) }),
#ApiResponse(...),
#ApiResponse(...)
#GetMapping(value = "/aaa", produces = MediaType.APPLICATION_JSON_VALUE)
public List<MyClass> getAaa(...)
{
return ...
}
You need to use an ArraySchema annotation for this and assign it to the array attribute instead of the schema attribute of the #Content annotation.
You don't need to specify List.class only its type parameter MyClass.class.
#Operation(
summary = "Get a list of users",
description = "Get a list of users registered in the system",
responses = {#ApiResponse(
responseCode = "200",
description = "The response for the user request",
content = {
#Content(
mediaType = "application/json",
array = #ArraySchema(schema = #Schema(implementation = User.class))
)
})
}
)
#GET
#SecurityRequirement(name = "JWT")
#Path("/user")
public List<User> getUsers() {
return null;
}
Error
Resolved [org.springframework.web.HttpMediaTypeNotSupportedException: Invalid mime type "media": does not contain '/']
This API is all about file operations in google drive using Spring boot rest API and here I am using postman for testing. While I am performing file upload I am getting the error given above. How to resolve it.
Post request to upload file to the google drive
#PostMapping(value = "/upload",
consumes = {MediaType.MULTIPART_FORM_DATA_VALUE},
produces = {MediaType.APPLICATION_JSON_VALUE} )
public ResponseEntity<String> uploadSingleFileExample4(#RequestParam("file") MultipartFile file) {
//Log.info("Request contains, File: " + file.getOriginalFilename());
String fileId = fileManager.uploadFile(file);
if(fileId == null){
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
}
return ResponseEntity.ok("Success, FileId: "+ fileId);
}
Method to perform upload operation
public String uploadFile(MultipartFile file) {
try {
//String folderId = getFolderId(filePath);
if (file != null) {
File fileMetadata = new File();
//fileMetadata.setParents(Collections.singletonList(folderId));
fileMetadata.setName(file.getOriginalFilename());
File uploadFile = googleDriveManager.getInstance()
.files()
.create(fileMetadata, new InputStreamContent(
file.getContentType(),
new ByteArrayInputStream(file.getBytes()))
)
.setFields("id").execute();
return uploadFile.getId();
}
} catch (Exception e) {
System.out.print("Error: "+e);
}
return null;
}
Screenshot file upload
Add this dependency,
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
You need to import,
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.ObjectMapper;
Use this to convert to JSON,
#Autowired
private ObjectMapper objectMapper;
ObjectNode jsonObject = objectMapper.createObjectNode();
jsonObject.put("fileId", fileId);
jsonObject.put("message", "success");
return new ResponseEntity(jsonObject, HttpStatus.OK);
Change ResponseEntityofString to ResponseEntity
i want to mock the request entity and response to test the method on the controller method, this code has been written by another developer and i am supposed to test it using mockito.i'm mocking the controller class
i am trying to mock the request entity value and the respionse entity value , but it's not working and i'm getting a reflection error when i'm trying to debug
public class InquiryController {
private static final Logger log =
LoggerFactory.getLogger(InquiryController.class);
#Autowired
private InquiryProperties inquiryProperties;
#Autowired
private InquiryService inquiryService;
#Autowired
RestTemplate restTemplate;
public static int count = 0;
#Bean
private RestTemplate getRestTemplate() {
return new RestTemplate();
}
#PostMapping(value = "/endCustomer", produces = { MediaType.APPLICATION_JSON_VALUE }, consumes = {
MediaType.APPLICATION_JSON_VALUE })
public ResponseEntity<List<EndCustomerDTO>> endCustomer(#RequestBody CustomerInfo customerInfo)
throws IOException, JSONException {
log.info("### InquiryController.endCustomer() ===>");
List<EndCustomerDTO> endCustomerDTOs = null;
try {
//RestTemplate restTemplate = new RestTemplate();
RequestEntity<CustomerInfo> body = RequestEntity.post(new URI(inquiryProperties.getEndCustomer()))
.accept(MediaType.APPLICATION_JSON).body(customerInfo);
ResponseEntity<List<EndCustomerDTO>> response = restTemplate.exchange(body,
new ParameterizedTypeReference<List<EndCustomerDTO>>() {
});
endCustomerDTOs = (response != null ? response.getBody() : new ArrayList<EndCustomerDTO>());
} catch (RestClientException | URISyntaxException e) {
log.error("InquiryController.endCustomer()" + e.getMessage());
}
log.info("### END InquiryController.endCustomer() ===>");
if (null == endCustomerDTOs) {
return new ResponseEntity<List<EndCustomerDTO>>(new ArrayList<EndCustomerDTO>(), HttpStatus.OK);
}
return new ResponseEntity<List<EndCustomerDTO>>(endCustomerDTOs, HttpStatus.OK);
}
It's because instance of RestTemplate is not injected through Spring IOC when you do the REST call. You need to declare the getRestTemplate method of in the component class which is scanned during application startup or in other words during component scan. Thus making restTemplate available for autowire.
Once you separate the config from the controller as #chrylis suggested, you proceed further like this.
You must be trying to mock the RequestEntity.post method. Note that it is a static method and is mocked a bit differently than the usual public instance methods. For this, you need to use PowerMockito as Mockito won't do.
add the dependency in pom like this:
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito</artifactId>
<version>1.6.5</version>
<scope>test</scope>
</dependency>
then anotate the test class with #RunWith, and #PrepareForTest like so:
#RunWith(PowerMockRunner.class)
#PrepareForTest({RequestEntity.class})
public class TestClass {
}
and the mock the post method as so:
PowerMockito.mockStatic(RequestEntity.class); when(RequestEntity.post(any(URI.class))).thenReturn(getRequestEntityResponseBody());
private RequestEntity< CustomerInfo > getRequestEntityResponseBody(){
//code
}
UPDATE
CustomerInfo customerInfo = new CustomerInfo();
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.set("MyResponseHeader", "MyValue");
RequestEntity<CustomerInfo> customerInfoRequestEntity = new ResponseEntity<CustomerInfo>(customerInfo, responseHeaders, HttpStatus.OK);
PowerMockito.mockStatic(RequestEntity.class);
when(RequestEntity.post(any(URI.class))).thenReturn(customerInfoRequestEntity);
The problem is to return list of files with some file related information.
Here is my web service class:
#Path("/pdfsigning")
public class PdfSigningResource{
#EJB
private PdfSigningFacadeInt pdfSigningFacadeInt;
#POST
#Path("/unsignedfilelist")
#Produces({ MediaType.APPLICATION_OCTET_STREAM, MediaType.APPLICATION_JSON })
public Response getUnsignedFileList() {
List<FileInfoDto> unsignedFileInfoDtoList = pdfSigningFacadeInt
.getUnsignedFileList();
GenericEntity<List<FileInfoDto>> genericUnsignedFileInfoDtoList = new GenericEntity<List<FileInfoDto>>(
unsignedFileInfoDtoList) {
};
return Response.ok(Status.OK).entity(genericUnsignedFileInfoDtoList)
.build();
}
}
FileInfoDto class with getter/setter omitted:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class FileInfoDto implements Serializable {
private static final long serialVersionUID = 303766558349361898L;
#XmlElement(name = "fileName")
private String fileName;
#XmlElement(name = "fileType")
private String fileType;
// other fields ...
#XmlElement(name = "fileByteArray")
private byte[] fileByteArray;
And here is the jersey-1 client:
public class FileService implements FileServiceInt {
public List<FileInfoDto> getUnsignedFileList() {
//code to build url
WebResource unsignedFileListResource = unsignedFileListClient.resource(url);
ClientResponse response = unsignedFileListResource.type(MediaType.APPLICATION_JSON) .post(ClientResponse.class);
if (Constants.CLIENT_RESPONSE_STATUS != response.getStatus()) {
LOGGER.debug(">>>>>>>>>> Response Not OK <<<<<<<<<<<<<");
//TODO throw proper exception
} else {
LOGGER.debug(">>>>>>>>>> Response OK <<<<<<<<<<<<<");
}
// This line throw exception
List<FileInfoDto> entityList = response.getEntity(new GenericType<List<FileInfoDto>>() {
});
return entityList;
}
}
The response obtained from server is "OK". But the line
List<FileInfoDto> entityList = response.getEntity(new GenericType<List<FileInfoDto>>() {
});
throws Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: javax/mail/internet/MimeMultipart.
In POM, I've the dependency as
<dependency>
<groupId>com.sun.jersey.contribs</groupId>
<artifactId>jersey-multipart</artifactId>
<version>1.18.1</version>
</dependency>
What am I missing? Cann't we get list of files this way?
For this version of jersey the use of javax.mail.internet.MimeMultipart requires Java Mail (see Jersey 1.x documentation / Mail and MIME multipart).
Add this dependency to your pom.xml:
<dependency>
<groupId>javax.mail</groupId>
<artifactId>mail</artifactId>
<version>1.4.7</version>
</dependency>
I use ngFileUpload directive to send data to the client:
$scope.upload = function (dataUrl, formValid) {
if (formValid && formValid === true) {
console.log($scope.user);
Upload.upload({
url: 'http://localhost:8080/user/',
data: $scope.setPhotoAndReturnUser(Upload.dataUrltoBlob(dataUrl))
.......
$scope.setPhotoAndReturnUser = function (photo) {
$scope.user.photo.image = photo;
return $scope.user;
};
User object contains additional user information username, email etc.
This is how request looks like:
#RestController
#RequestMapping("/user")
public class UserRestController {
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity newUser(HttpServletRequest request) {
MultipartHttpServletRequest mRequest = (MultipartHttpServletRequest) request;
UserDTO user = new UserDTO(mRequest);
if (service.validate(user)) {
registrationService.register(user, request);
return new ResponseEntity(HttpStatus.NO_CONTENT);
}
return new ResponseEntity(HttpStatus.BAD_REQUEST);
}
My domain transfer object constructor:
public UserDTO(MultipartHttpServletRequest request) {
this.userEmail = request.getParameter("userEmail");
this.userName = request.getParameter("userName");
this.userPass = request.getParameter("userPass");
And this is the exception i get:
Caused by: java.lang.NoClassDefFoundError:
com/fasterxml/jackson/annotation/JsonProperty$Access
My multipart resolver configuration:
#Bean
MultipartResolver multipartResolver() {
CommonsMultipartResolver resolver = new CommonsMultipartResolver();
resolver.setMaxUploadSize(1000000);
return resolver;
}
I use:
jackson-core 2.4.6
jackson-databind: 2.6.4
But i cannot fully understand what is going on, i dont have any domain objects in my method parameters, and in accordance to developer tools the request is encoded like multipart/form data. Should i instead try to use spring anonymous authentication, and split the registration process in to two different requests?
Problem was in the wrong naming of the request parameters, i have to do this instead of sending all data as one object:
Upload.upload({
url: 'http://localhost:8080/user/',
file:Upload.dataUrltoBlob(dataUrl)
fields: {...}
Also i have problem with my maven dependancies , for spring 4 recomended jackson libery is
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.3.3</version>
</dependency>