How to show multiple example in swagger documentation? - java

In my REST API PATCH operation, I am using v3 swagger-annotation:2.0.6
I was trying to add more examples as swagger schema for my patch operation PATCH /users/id.
Request Body:
{
"operationList": [
{
"op": "replace",
"path": "/username",
"value": "john1991"
}
]
}
Currently I have following class for request model.
public class UserPatchOp extends PatchOperation {
#Override
#Schema(description = "some description", example = "replace", required = true)
public PatchOperator getOp() {
return super.getOp();
}
#Override
#Schema(description = "some description", example = "/username", required = true)
public String getPath() {
return super.getPath();
}
#Override
#Schema(description = "some description", , example = "john1991", required = true)
public Object getValue() {
return super.getValue();
}
}
In PatchOperation.java
public class PatchOperation {
/**
* {#link PatchOperator} operation to be performed
*/
#Schema(description = "Operation to be performed", required = true)
#JsonProperty
#NotNull
private PatchOperator op;
#Schema(description = "Path to target where operation will be performed", required = true)
#JsonProperty
#Pattern(regexp = "/([/A-Za-z0-9~])*-*", message = "Invalid path. Please follow regex pattern")
private String path;
#Schema(description = "Value used by operation")
#JsonProperty
private Object value;
public PatchOperation() {
}
}
In the swagger document, in UserPatchOp.java I have shown to the end-user that how to replace username.
On swagger, the request bogy comes with this example.
other than username through this patch operation, end-user can update description then path would be /description.
End-user can also update the usergroup from which it belongs to '/usergroups'
So in general, same way I want to add more example to swagger schema.
But I am not able to do it. Because at one time I can show one example only.
I want to show following multiple operations to the client on swagger page.
{
"operationList": [
{
"op": "replace",
"path": "/username",
"value": "john1991"
},
{
"op": "remove",
"path": "/description"
},
{
"op": "add",
"path": "/memberships"
"value":["1224","8907"]
}
]
}
And entry point method is
#PATCH
#Path("users/{id}")
#Consumes({MediaType.APPLICATION_JSON, APPLICATION_JSON_PATCH_JSON})
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = MessageConstants.OK, content = #Content(schema = #Schema(implementation = UserInfo.class))),
#ApiResponse(responseCode = "500", description = MessageConstants.SERVER_ERROR, content = #Content(schema = #Schema(implementation = RestError.class))),
#ApiResponse(responseCode = "400", description = MessageConstants.BAD_REQUEST, content = #Content(schema = #Schema(implementation = RestError.class))),
#ApiResponse(responseCode = "401", description = MessageConstants.UNAUTHORIZED, content = #Content(schema = #Schema(implementation = RestError.class))),
#ApiResponse(responseCode = "404", description = MessageConstants.NOT_FOUND, content = #Content(schema = #Schema(implementation = RestError.class)))})
public Response updatePartialUser(
#Parameter(description = "User Id", required = true) #PathParam("id") String id,
#Parameter(description = "User Update Info", required = true) #Valid PatchOperations<UserPatchOperation> operationList) {
Is there any way, I can add multiple examples for getOP, getPath and getValue method? Thank you.

It is possible to create multiple examples of responses which one method can return but it is possible to do only one example for one response code.
#Operation(description = "Retrieves status of application",
responses = {
#ApiResponse(responseCode = "200",
description = "Everything works fine.",
content = #Content(mediaType = "application/json",
examples = #ExampleObject(value = "{\n" +
" \"success\" : true,\n" +
" \"message\" : \"OK\"\n" +
"}"))),
#ApiResponse(responseCode = "500",
description = "Application not working properly",
content = #Content(mediaType = "application/json",
examples = #ExampleObject(value = "{\n" +
" \"success\" : false,\n" +
" \"message\" : \"Application not working properly\"\n" +
"}")))
})
#GetMapping("haproxy")
ResponseEntity<BaseResponse> getHaProxy();
I'm not sure if it's what you want but I don't see other way around.
Keep in mind that swagger documentation should be done in a way that someone will be able connect to your api and do some operations. You don't need to provide too much responses there. That's for OPTIONS rest method is. OPTIONS method is basically a GET which don't need any parameters and in response will return complete informations about what certain method can do and what the request/response will be. Here you have better explanation of that: RESTful API methods; HEAD & OPTIONS
Btw. you should update your dependencies, swagger-annotations is on 2.1.4 now, 2.0.6 is from 2 years ago
EDIT 2020-09-30 About request's body:
It is possible to add multiple requests examples like that:
#Operation(description = "Creates a User",
requestBody = #io.swagger.v3.oas.annotations.parameters.RequestBody(description = "Request examples",
content = #Content(examples = {
#ExampleObject(name = "doing weird stuff", value = "http://localhost:7080"),
#ExampleObject(name = "doing weirdest stuff", value = "{\n\"test\":\"12\"\n}"),
})),
responses = {
#ApiResponse(responseCode = "200",
description = "Everything works fine.",
content = #Content(mediaType = "application/json",
schema = #Schema(implementation = UserResponse.class))),
#ApiResponse(responseCode = "404",
description = "Not found",
content = #Content(mediaType = "application/json",
examples = #ExampleObject(value = "{\"success\":false,\"message\":\"That shop or API version is not accessible yet\",\"httpStatus\":\"NOT_FOUND\"}"))),
#ApiResponse(responseCode = "500",
description = "Something went wrong",
content = #Content(mediaType = "application/json",
schema = #Schema(implementation = SomeBasicResponse.class)))
})
#Parameters({
#Parameter(in = ParameterIn.HEADER, name = "url",
content = #Content(schema = #Schema(type = "string", defaultValue = "localhost:7080")))
})
#PostMapping
ResponseEntity<UserResponse> createUser(#RequestHeader(name = "login", required = false) String login,
#RequestHeader(name = "password") String password,
#RequestBody UserRequest request);
I hope that's what you are looking for.

I have added example at entry point with help of schema
#Parameter(description = "Update User", required = true, schema = #Schema (example = "{\n "
+ "\"operationList\": [\n "
+ "{\n \"op\": \"replace\",\n \"path\": \"/username\",\n \"value\": \"john1991\"\n },\n "
+ "{\n \"op\": \"replace\",\n \"path\": \"/description\",\n \"value\": \"NewDescription\"\n },\n "
+ "{\n \"op\": \"add\",\n \"path\": \"/memberships\",\n "
+ "\"value\":[\"1234\",\"6789\"]\n "
+ "{\n \"op\": \"remove\",\n \"path\": \"/privileges\",\n \"value\":[\"148\",\"155\"]\n "
+ "}\n ]\n}")) #Valid PatchOperations<UserPatchOperation> operationList) throws RestException

Related

java springdoc #ApiResponses how to define a List as return object using

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;
}

Overriding any parameter in io.swagger.v3.oas.annotations.media.Schema throws exception

I have this open API annotation
#GET
#Operation(method = "Get orders", description = "GetOrdersRoute",
parameters = {
#Parameter(in = ParameterIn.QUERY, name = "batchSize", required = true,
schema = #Schema(type = "")),
#Parameter(in = ParameterIn.QUERY, name = "fromDate", required = true),
#Parameter(in = ParameterIn.QUERY, name = "filter", required = true)},
responses = {
#ApiResponse(description = "The order",
content = #Content(mediaType = "application/json",
schema = #Schema(implementation = OrderDto.class)))})
#Override
public String handle(#Parameter(hidden = true) Request request, #Parameter(hidden = true) Response response) {
return api.exec();
}
It works. But when I add to #Parameter to #Schema any value like schema = #Schema(type = "integer")) I get an exception:
java.lang.NoSuchMethodError: org.apache.commons.lang3.math.NumberUtils.isCreatable(Ljava/lang/String;)Z
In other words: #Schema(type = "")) works fine but #Schema(type = "integer")) doesn't work.
And not only type parameter in #Schema annotation. Any override parameter in #Schema annotation throws this exception.
But #Schema in #ApiResponse works fine with any override parameter.
I was getting the same issue today when using
#ApiResponse(
responseCode = "200",
content = #Content(mediaType = "application/json", schema = #Schema(implementation = MyDto.class)),
description = "Returns MyDto.")
with io.swagger.v3.oas.annotations.media.Schema from swagger 2.1.9.
After trying different swagger versions (which were producing other errors in my setup), it worked when downgrading to swagger 2.0.10.

#ApiResponse with empty response body (Spring Boot)

I'm looking for a way to tell swagger that a certain API response code doesn't have a response body. A get response, for example, that can either return a 200 code with the actual object as a response or a 404 if the object associated with the passed ID doesn't exist:
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "Object found"),
#ApiResponse(responseCode = "404", description = "Invalid object ID", content = #Content)
})
This is the closest thing I could figure out but it's not perfect, I still get an annoying "Media type" under the description of the 404 response.
Thanks!
If you are not specifying the content attribute of #ApiResponse annotation the return type of the controller method will be your response content. To prevent this define content explicitly:
#ApiResponse(responseCode = "200", description = "OK",
content = #Content(schema = #Schema(implementation = Void.class)))
Or you can simply return ResponseEntity<Void>.
This is probably the better (and shorter) way:
#ApiResponse(
responseCode = "404",
description = "Not found",
content = #Content(schema = #Schema(hidden = true)))
You can use the following on top of your method in v2
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Success", response = YourObject.class),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 403, message="Forbidden"),
#ApiResponse(code = 404, message = "Not Found"),
#ApiResponse(code = 500, message = "Failure")
})
For V3, you could try something like this in case your method is returning some object
#Operation(summary = "Add a new object", description = "", tags = { "yourObject" })
#ApiResponses(value = {
#ApiResponse(responseCode = "201", description = "Object created",content = #Content(schema = #Schema(implementation = YourObject.class))),
#ApiResponse(responseCode = "400", description = "Invalid input"),
#ApiResponse(responseCode = "409", description = "Object already exists") })
#PostMapping(value = "/your-url", consumes = {"application/json","application/xml" })
public ResponseEntity<YourObject> addObject(
...
return ...
}
In case your method is returning void try this one
#Operation(summary = "Update an existing object", description = "", tags = { "yourObject" })
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "successful operation"),
#ApiResponse(responseCode = "400", description = "Invalid ID supplied"),
#ApiResponse(responseCode = "404", description = "Object not found"),
#ApiResponse(responseCode = "405", description = "Validation exception") })
#PutMapping(value = "/your-url/{id}", consumes = { "application/json", "application/xml" })
public ResponseEntity<Void> addObject(
...
return ...
}
Not sure if it's a feature, but an empty #Content worked for me:
interface MyControllerOpenApiSpec {
#ApiResponse(responseCode = "200") // shows MyDTO schema
#ApiResponse(responseCode = "404", content = #Content) // no schema shown
MyDTO getMyDTO();
}
There is not any content method; maybe, it is changed.
public #interface ApiResponse {
int code();
String message();
Class<?> response() default Void.class;
String reference() default "";
ResponseHeader[] responseHeaders() default {#ResponseHeader(
name = "",
response = Void.class
)};
String responseContainer() default "";
Example examples() default #Example({#ExampleProperty(
value = "",
mediaType = ""
)});
}

Swager CodGen generating code ApiController.java with multiple try catch

I am looking for generating code with multiple try catch in APIController.java which is generated by SwagerCodeGen.
swagger.yaml file
paths:
/system:
post:
tags:
- New
summary: System info
description: Initial System Info
operationId: createSystem
consumes:
- application/json
produces:
- application/json
parameters:
- in: body
name: systemDetails
description: Contains the JSON Object
required: true
schema:
$ref: '#/definitions/SystemDetails'
- name: AuthKey
in: query
description: Key for Authentication and Authorization.
required: true
type: string
format: uuid
responses:
'201':
description: System Created Successfully
schema:
$ref: '#/definitions/Response'
'400':
description: Request Failed
'401':
description: Unauthorized
'500':
description: Internal Server Error
Swager generated code as below,
SystemApi.java
#ApiOperation(value = "System", notes = "Initial System Info", response = Response.class, tags={ "System", })
#ApiResponses(value = {
#ApiResponse(code = 201, message = "System Created Successfully", response = Response.class),
#ApiResponse(code = 400, message = "Request Failed", response = Void.class),
#ApiResponse(code = 401, message = "Unauthorized", response = Void.class),
#ApiResponse(code = 404, message = "Not Found", response = Void.class),
#ApiResponse(code = 500, message = "Internal Server Error", response = Void.class) })
#RequestMapping(value = "/system",
produces = { "application/json" },
consumes = { "application/json" },
method = RequestMethod.POST)
ResponseEntity<Response> createSystem(#ApiParam(value = "Contains the JSON Object" ,required=true ) #Valid #RequestBody SystemDetails systemDetails, #NotNull#ApiParam(value = "Key for Authentication and Authorization.", required = true) #RequestParam(value = "AuthKey", required = true) UUID authKey);
SystemApiController.Java
public ResponseEntity<Response> createSystem(
#ApiParam(value = "Contains the JSON Object",
required = true) #Valid #RequestBody SystemDetails systemDetails,
#NotNull #ApiParam(
value = "Key for Authentication and Authorization.",
required = true) #RequestParam(value = "AuthKey", required = true) UUID authKey) {
// do some magic!
return delegate.createSystem(systemDetails, authKey);
}
Is there any way to add try catch auto generated from Swagger CodeGen like below?
public ResponseEntity<Response> createSystem(
#ApiParam(value = "Contains the JSON Object",
required = true) #Valid #RequestBody SystemDetails systemDetails,
#NotNull #ApiParam(
value = "Key for Authentication and Authorization.",
required = true) #RequestParam(value = "AuthKey", required = true) UUID authKey) {
// do some magic!
try{
return delegate.createSystem(systemDetails, authKey);
}catch(InvalidInputException e){
return new ResponseEntity(new Response(HTTPSTATUS.BAD_REQUEST));
}
}
Or please suggest alternate way. Now I am handling all exceptions in SystemService.java, not throwing any exception to Controller. Because of this I need to rollback all Transactions manually in service side.
Swagger codegen uses moustache templates to generate your code.
You can refer to https://github.com/swagger-api/swagger-codegen#modifying-the-client-library-format to customize the syntax of generated classes

How can I specify a multipart file upload with params

I'm trying to document an API method that will receive a file and two parameters as int. Using swagger editor I was able to describe what I want, but couldn't replicate that using annotations.
This is what I draw on swagger editor
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
flow:
type: integer
environment:
type: integer
file:
type: string
format: binary
required: true
If I use consumes = MediaType.MULTIPART_FORM_DATA I get the params. And if I use consumes = MediaType.APPLICATION_OCTET_STREAM I get the file to upload.
#Operation(summary = "Unpack Files",
description = "Receives a packed zip or gzip file with xml files inside or receives xml files",
security = #SecurityRequirement(name = "apiKey"),
responses = {
#ApiResponse(responseCode = "201", description = "Created"),
#ApiResponse(responseCode = "400", description = "Something Went Wrong"),
#ApiResponse(responseCode = "401", description = "Unauthorized"),
#ApiResponse(responseCode = "503", description = "Service Unavailable")
},
requestBody = #RequestBody(
content = #Content(
mediaType = MediaType.MULTIPART_FORM_DATA,
schema = #Schema(implementation = Document.class, format = "binary"),
encoding = #Encoding(
name = "file",
contentType = "application/xml, application/zip, application/gzip"
)
),
required = true
)
)
#Post(value = "/unpack", consumes = MediaType.APPLICATION_OCTET_STREAM)
public Single<HttpResponse<String>> upload(StreamingFileUpload file, int flow, int environment) throws IOException {
return Single.just(new Document(file.getFilename(), environment, flow))
.flatMap(DocumentValidation::validateDocumentExtension)
.doOnError(throwable -> {
log.error("Validation exception: {}", throwable.getMessage());
exception = throwable.getMessage();
})
.doOnSuccess(doc -> {
log.info("File saved successfuly");
File tempFile = File.createTempFile(file.getFilename(), "temp");
file.transferTo(tempFile);
})
.map(success -> {
if (exception != null || !exception.equals("")) {
return HttpResponse.<String>status(HttpStatus.CREATED).body("Uploaded");
} else {
return HttpResponse.<String>status(HttpStatus.SERVICE_UNAVAILABLE).body(exception);
}
}
);
}
Thanks in advance.
Looks like missing #QueryValue
From documentation 6.4 Simple Request Binding:
Bindings from a request URI variable or request parameter | #QueryValue String myParam
From documentation 6.19 File Uploads:
The method is set to consume MULTIPART_FORM_DATA
The method parameters match form attribute names. In this case the file will match for example an
The StreamingFileUpload.transferTo(java.lang.String) method is used to transfer the file to the server.
Kotlin simple:
#Controller
class SomeController {
#Post(value = "/", consumes = [MediaType.MULTIPART_FORM_DATA])
fun upload(file: StreamingFileUpload,
#QueryValue flow: Int,
#QueryValue environment: Int): Single<HttpResponse<String>> {
val tempFile = File.createTempFile(file.filename, "temp")
return Single.fromPublisher(file.transferTo(tempFile))
.map { success ->
if (success) {
HttpResponse.ok("Uploaded");
} else {
HttpResponse.status<String>(HttpStatus.CONFLICT)
.body("Upload Failed")
}
}
}
}

Categories