Swagger Springfox annotations not working - spring

These are the method headers for the sample application I'm trying to make. Unfortunately I don't see the #ApiOperation or #ApiResponse or #ApiResponses annotations taking effect.
I also have a sample class and an Application class(which contains swagger configuration info and the like)
#Api(value = "/v1/", description = "Test API", produces = "application/json")
#RestController
#RequestMapping("/v1/")
class SampleRestController {
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully added"),
#ApiResponse(code = 416, message = "List is empty") })
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, value = "samples/{sb}",
method = RequestMethod.GET)
public Map<String, String> sbs(#PathVariable("sb") String sb) {
}
#ApiOperation(
value = "Seeing where this shows up",
notes = "we should see this in the implememtation notes",
response = Sample.class,
httpMethod = "GET"
)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully added"),
#ApiResponse(code = 416, message = "List is empty") })
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, value = "samples/{pb}",
method = RequestMethod.GET)
public Sample ps(#PathVariable String pb) {
}
#RequestMapping(produces = MediaType.APPLICATION_JSON_VALUE, value = "samples",
method = RequestMethod.GET)
public Collection<Map<String, String>> cs(#RequestParam(value = "cid", required = true, defaultValue = "")
String cid) {
}
}
Here is the relevant portion of my swagger json
{"swagger":"2.0","info":{"description":"All about the Samples","title":"Samples API","contact":{},"license":{}},"host":"localhost:8080","basePath":"/","tags":[{"name":"sample-rest-controller","description":"Sample Rest Controller"}],
"paths":{
"/v1/samples":{"get":{"tags":["sample-rest-controller"],"summary":"cs","operationId":"csUsingGET","consumes":["application/json"],"produces":["application/json"],"parameters":[{"name":"cid","in":"query","description":"cid","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Collection«Map«string,string»»"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},
"/v1/samples/{pb}":{"get":{"tags":["sample-rest-controller"],"summary":"ps","operationId":"psUsingGET","consumes":["application/json"],"produces":["application/json"],"parameters":[{"name":"pb","in":"path","description":"pb","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"$ref":"#/definitions/Sample"}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}},
"/v1/samples/{sb}":{"get":{"tags":["sample-rest-controller"],"summary":"sbs","operationId":"sbsUsingGET","consumes":["application/json"],"produces":["application/json"],"parameters":[{"name":"sb","in":"path","description":"sb","required":true,"type":"string"}],"responses":{"200":{"description":"OK","schema":{"type":"object","additionalProperties":{"type":"string"}}},"401":{"description":"Unauthorized"},"403":{"description":"Forbidden"},"404":{"description":"Not Found"}}}}},
I'm using springfox-swagger2 version 2.1.1 and I'm viewing my UI through the default UI provided by swagger at their live demo app.
Can someone tell me what I'm doing wrong?
Thanks!

I was playing around and changed my import from
import com.wordnik.swagger.annotations.;
to
import io.swagger.annotations.;
and all the annotations appear to work.
So, looks like the com.wordnik.swagger.annotations import no longer works, or at least doesn't work properly?
Confirmed by Dilip Krish here

Related

How to add description to #requestbody?

As shown in the picture, I want to add my description in my Swagger UI. The value "Example" (in name and description) comes from the parameter "Example" in the controller where I have used the #Requestbody. I am using Spring framework. How do I add my description to it?
This is my controller file
#ApiOperation(value = "It will be used to print the document of quotation.",response = GenerateDocPrintResponse.class)
#ApiResponses(value = {
#ApiResponse(code = 200, message = "Successfully retrieved list"),
#ApiResponse(code = 401, message = "You are not authorized to view the resource"),
#ApiResponse(code = 403, message = "Accessing the resource you were trying to reach is forbidden"),
#ApiResponse(code = 404, message = "The resource you were trying to reach is not found")
}
)
#RequestMapping(value = "/service/generateDocPrint", method = RequestMethod.POST)
public #ResponseBody String generateDocPrint(#RequestBody GetdocprintRequest Example,HttpServletRequest request, HttpServletResponse response,
#RequestBody String newJson) throws Exception {
................
}
This is my Pojo file
package com.iii.fw.models.generatedocprint;
import com.iii.fw.models.common.RequestHeader;
import io.swagger.annotations.ApiModelProperty;
public class GetdocprintRequest {
RequestHeader reqHeader;
private ReqPayloadGDP reqPayload;
public ReqPayloadGDP getReqPayload() {
return reqPayload;
}
public void setReqPayload(ReqPayloadGDP reqPayload) {
this.reqPayload = reqPayload;
}
public GetdocprintRequest withReqPayload(ReqPayloadGDP reqPayload) {
this.reqPayload = reqPayload;
return this;
}
}
#ApiOperation(value = "It will be used to print the document of quotation. ", notes = "put your description here ", response = )
Does this answer your question ?
Use the notes proptery.
Using notes, we can provide more details about the operation. For instance, we can place a text describing the endpoint's restrictions:
https://www.baeldung.com/swagger-apioperation-vs-apiresponse#2-the-notes-property
#ApiOperation(value = "Gets customer by ID", notes = "Customer must exist")

springdoc-openapi-ui swagger 3 change API description

I am new to String and Swagger 3.
How can I change the default API description i.e. OpenAPI definition in Swagger springdoc-openapi-ui swagger 3
Also version, developer information..
I am using
implementation "org.springframework.boot:spring-boot-starter-web:2.6.6"
implementation "org.springdoc:springdoc-openapi-ui:1.6.8"
In search, I see only they are showing default Swagger UI
Simply use following annotations :
In application launcher class(Configuration class):
#OpenAPIDefinition(info=#Info(title="Name of project"))
In Controller
import io.swagger.v3.oas.annotations.*
Calss level:
#OpenAPIDefinition()
Or
#Tag(name = "", description = "")
Method level :
#PutMapping(value = "/test")
#Operation(summary = "info...")
#ApiResponses(value = {
#ApiResponse(responseCode = "200", description = "successfully updated"),
#ApiResponse(responseCode = "400", description = "Bad request"),
#ApiResponse(responseCode = "500", description = "Internal Server Error")})
For standard Version I used:
#Bean
public OpenAPI customOpenAPI() {
final String locUrl = "http://localhost:8080";
final String devUrl = "https://.de";
final String testUrl = "https://.de";
final String preUrl = "https://.de";
final String proUrl = "https://.grp";
return new OpenAPI().addServersItem(new Server().url(locUrl)).addServersItem(new Server().url(
devUrl)).addServersItem(new Server().url(testUrl)).addServersItem(new Server().url(preUrl))
.addServersItem(new Server().url(proUrl)).info(
new Info().version("v1").title("XApp application API")
.description("(NOTE: If having Swagger UI issues w/ Chrome then use Firefox instead.)")
.contact(new Contact().name("Edi")));
}
and to change based on grouping I did:
#Profile("!dev")
#Bean
public GroupedOpenApi groupedPublicOpenApi10() {
return GroupedOpenApi
.builder()
.addOpenApiCustomiser(openApi -> openApi.getInfo().setVersion("v1"))
.group("API-v1")
.pathsToMatch("/api/**")
.displayName("API v1")
.build();
}

Swagger declaration schema = #Schema(implementation = Map.class) represents Schema as String in swagger-ui

I am trying to create springdoc swagger documentation, and I would like to represent a request body having data type Map<String, Object> in a better readable way for clients. But when I declare #io.swagger.v3.oas.annotations.parameters.RequestBody(content = #Content(schema = #Schema(implementation = Map.class) the Schema is coming as String(attached the screenshot below)
Method declaration
#Operation(security = {#SecurityRequirement(name = "bearer-key")}, summary = "Create Data", operationId = "createData", description = "Create createData for the **`type`**. ")
#ApiResponses(value = {
#ApiResponse(responseCode = "201", description = "Data created", content = #Content(schema = #Schema(implementation = Map.class),
examples = {#ExampleObject(value = "{\n" +
" \"id\": \"927d810c-3ac5-4584-ba58-7c11befabf54\",\n" +
"}")})),
#ApiResponse(responseCode = "400", description = "BAD Request")})
#PostMapping(value = "/data/type", produces = APPLICATION_JSON_VALUE, consumes = APPLICATION_JSON_VALUE)
#io.swagger.v3.oas.annotations.parameters.RequestBody(content = #Content(schema = #Schema(implementation = Map.class),
examples = {#ExampleObject(value = "{\n" +
" \"label\":\"tourism\",\n" +
" \"location\":\"France\"\n" +
" }")}))
ResponseEntity<Map<String, Object>> createData(#Parameter(name = "type", required = true) #PathVariable("type") String type, #Parameter(name = "request payload") #Valid #RequestBody Map<String, Object> body);
Though the Spring boot automatically infers the type based on the method signature, it is not clear for the data type Map. For instance, by default, the type Map<String, Object> will be inferred as below
But I would like to show the Schema in a more understandable way for the client who refers to my API. I could see there is a closed ticket without a proper solution in Github. As per my requirement, the request body should be a type agnostic and dynamic key-value pairs, so there is no other way apart from receiving the request as Map<String, Object>. has anyone implemented a better way with type Map rather than creating a custom request/response model?
I have one API endpoint, the request body expects a HashMap. There is not much information on how to fix the "Example value" issue. Prasanth's answer lead me to the right place. I'm posting my solution for completeness but all credit goes to him. (PS: I tried to upvote his answer but I don't have enough "points")
The configurations side:
#Configuration
#OpenAPIDefinition
public class DocsConfiguration {
#Bean
public OpenAPI customOpenAPI() {
Schema newUserSchema = new Schema<Map<String, Object>>()
.addProperties("name",new StringSchema().example("John123"))
.addProperties("password",new StringSchema().example("P4SSW0RD"))
.addProperties("image",new StringSchema().example("https://robohash.org/John123.png"));
return new OpenAPI()
//.servers(servers)
.info(new Info()
.title("Your app title")
.description("App description")
.version("1.0")
.license(new License().name("GNU/GPL").url("https://www.gnu.org/licenses/gpl-3.0.html"))
)
.components(new Components()
.addSchemas("NewUserBody" , newUserSchema)
);
}
}
The controller side:
#io.swagger.v3.oas.annotations.parameters.RequestBody(
content = #Content(mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = #Schema(ref = "#/components/schemas/NewUserBody")))
#PostMapping("/v1/users")
public Response<User> upsertUser(#RequestBody HashMap<String,Object> user) {
//Your code here
}
Sharing my working approach for the issue, I have done a workaround for the #io.swagger.v3.oas.annotations.parameters.RequestBody(content = #Content(schema = #Schema(implementation = Map.class) the Schema is coming as String issue.
I have declared a custom schema called Map in the OpenAPI bean declaration as below
new OpenAPI()
.components(new Components()
.addSchemas("Map", new Schema<Map<String, Object>>().addProperties("< * >", new ObjectSchema())
))
.....
.....
and used the above schema in the Schema declaration as below
#io.swagger.v3.oas.annotations.parameters.RequestBody(
content = #Content(mediaType = APPLICATION_JSON_VALUE,
schema = #Schema(ref = "#/components/schemas/Map"))
The above approach can be used in the place of ApiResponse also as below
#io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200",
content = #Content(mediaType = APPLICATION_JSON_VALUE,
schema = #Schema(ref = "#/components/schemas/Map"))
Note: If we use the above custom schema approach, we don't need to alter or ignore any of the types which SpringDoc is using internally.
This is the default behaviour of the springdoc-openapi library in order to ignore other injectable parameters supported by Spring MVC.
https://docs.spring.io/spring/docs/5.1.x/spring-framework-reference/web.html#mvc-ann-arguments
If you want to change this behaviour, you can just exlcude it as follow:
SpringDocUtils.getConfig().removeRequestWrapperToIgnore(Map.class);
Id like to update rodiri's answer for my situation. I had to combine the answer by rodiri and this answer by Ondřej Černobila to the SO question SpringDoc - How to Add schemas programmatically. I am using java 11, spring-boot-starter-parent 2.5.6, and springdoc-openapi-ui 1.5.12 which I believe is using swagger 3.52.5
<!-- https://mvnrepository.com/artifact/org.springdoc/springdoc-openapi-ui -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
<version>1.5.12</version>
</dependency>
My config
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
import io.swagger.v3.oas.models.media.ObjectSchema;
import io.swagger.v3.oas.models.media.StringSchema;
import org.springdoc.core.customizers.OpenApiCustomiser;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
#OpenAPIDefinition
public class DocsConfiguration {
#Bean
public OpenApiCustomiser openApiCustomiser() {
return openApi -> {
var NewUserBodySchema = new ObjectSchema()
.name("NewUserBody")
.title("NewUserBody")
.description("Object description")
.addProperties("name", new StringSchema().example("John123"))
.addProperties("password", new StringSchema().example("P4SSW0RD"))
.addProperties("image", new StringSchema().example("https://robohash.org/John123.png"));
var schemas = openApi.getComponents().getSchemas();
schemas.put(NewUserBodySchema.getName(), NewUserBodySchema);
};
}
}
For my endpoint I am using a get that returns a Map so its different from the accepted answer.
#GetMapping(value = "/{userId}")
#Operation(
summary = "Get Something",
description = "Some desciption",
responses = {
#ApiResponse(
responseCode = "200",
description = "The Map Response",
content = {
#Content(
mediaType = MediaType.APPLICATION_JSON_VALUE,
schema = #Schema(ref = "#/components/schemas/NewUserBody")
)
})
}
)
public ResponseEntity<Map<String, Object>> getMap(#PathVariable String userId)
I ran into this problem myself today. As it turns out, this is actually a design problem with Swagger (#see related question).
Nonetheless, I tried my hand at it too, using the approaches from here and the other thread.
Here is my OpenAPI with one custom schema for a Map<Integer,String>:
#Configuration
#OpenAPIDefinition(
info = #io.swagger.v3.oas.annotations.info.Info(
title = "ACME Inc. REST API",
version = "1.0",
description = "This is an overview of all REST endpoints of this application",
contact = #io.swagger.v3.oas.annotations.info.Contact(name = "John Doe", url = "https://acme-inc.com/", email = "john.doe#acme-inc.com")
)
)
public class OpenAPIConfig {
public static final String ERROR_CODE_MAPPER = "ErrorCode-Mapper";
#Bean
public OpenApiCustomiser openApiCustomiser() {
return openApi -> {
Components components = openApi.getComponents();
for(Schema<?> schema: buildCustomSchemas()) {
components.addSchemas(schema.getName(), schema);
}
};
}
private static List<Schema<?>> buildCustomSchemas() {
ArrayList<Schema<?>> result = new ArrayList<>();
Schema<?> integerStringMap = new Schema<Map<Integer, String>>()
.name(ERROR_CODE_MAPPER)
.type("object")
.addProperty("error code", new StringSchema().example("Error message belonging to the error code")).example(getErrorCodeExample());
result.add(integerStringMap);
// Build more custom schemas...
return result;
}
private static Map<Integer, String> getErrorCodeExample() {
Map<Integer, String> example = new HashMap<>();
example.put(666, "Oh no..., the devil himself showed up and stopped your request");
return example;
}
}
(NOTE: Look up your swagger source code io.swagger.v3.oas.models.media for useful utility classes like StringSchema. You don't have write everything from scratch.)
And this is my REST endpoint:
#Operation(summary = "This endpoint returns a list of system error codes, that can occur during processing requests.")
#ApiResponses(value = {
#ApiResponse(
responseCode = "200",
description = "Map of all system error codes mapping to their messages",
content = {#Content(mediaType = MediaType.APPLICATION_JSON_VALUE, schema = #Schema(ref = "#/components/schemas/"+ ERROR_CODE_MAPPER))}
)
})
#GetMapping("/error-codes")
public Map<Integer, String> listErrorCodes() {
// return your map here...
}
This produces something like this:
It is important to know that in a JSON object the key is always of type string. So the type does not have to be written explicitly. With that in mind, this is the schema:
I created a HashMap extension class:
#Schema(description = "Response-Object Map<String, EcoBalance).")
public class EcoMap extends HashMap<String, EcoBalance> {
#JsonIgnore
#Override
public boolean isEmpty() {
return super.isEmpty();
}
}
use it as response object
#ApiResponse(responseCode = "200", content = #Content(mediaType = .., schema = #Schema(implementation = EcoMap.class)), headers = ..
be aware the OpenAPI 3 generator does not generate such a client-model, but is properly referenced in openapi.yml (and even validates).

Netflix Feign Code Generation using Swagger

I found the project https://github.com/swagger-api/swagger-codegen .
However this is generating a client that is based on OpenFeign.
Is there a way to generate a client interface automatically that uses Netflix's feign annotation with request mappings?
Example:
#FeignClient(name = "ldap-proxy")
public interface LdapProxyClient {
#RequestMapping(path = "/ldap-proxy/v1/users/{userNameOrEMail}", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
LdapUser search(#PathVariable("userNameOrEMail") String userNameOrEMail);
}
As opposed to the class at:
https://github.com/swagger-api/swagger-codegen/blob/master/samples/client/petstore/java/feign/src/main/java/io/swagger/client/ApiClient.java
Thannks
You can try spring-cloud swagger-codegen library.
The example of the command to generate the client:
java -jar modules/swagger-codegen-cli/target/swagger-codegen-cli.jar generate \
-i http://petstore.swagger.io/v2/swagger.json \
-l spring \
--library spring-cloud \
-o samples/client/petstore/java
Here is the example of the generated files:
PetApi.java
#Api(value = "Pet", description = "the Pet API")
public interface PetApi {
#ApiOperation(value = "Add a new pet to the store", nickname = "addPet", notes = "", authorizations = {
#Authorization(value = "petstore_auth", scopes = {
#AuthorizationScope(scope = "write:pets", description = "modify pets in your account"),
#AuthorizationScope(scope = "read:pets", description = "read your pets")
})
}, tags={ "pet", })
#ApiResponses(value = {
#ApiResponse(code = 405, message = "Invalid input") })
#RequestMapping(value = "/pet",
produces = "application/json",
consumes = "application/json",
method = RequestMethod.POST)
ResponseEntity<Void> addPet(#ApiParam(value = "Pet object that needs to be added to the store" ,required=true ) #Valid #RequestBody Pet body);
}
PetApiClient.java
#FeignClient(name="${swaggerPetstore.name:swaggerPetstore}", url="${swaggerPetstore.url:http://petstore.swagger.io/v2}", configuration = ClientConfiguration.class)
public interface PetApiClient extends PetApi {
}

ID Should Not Show Up for Model Schema with Swagger + Spring

I'm using Swagger2 with Springfox and Spring Boot. I have an endpoint defined like so:
#ApiOperation(value = "save", nickname = "Save Store")
#ApiResponses(value = {
#ApiResponse(code = 201, message = "Created"),
#ApiResponse(code = 401, message = "Unauthorized"),
#ApiResponse(code = 403, message = "Forbidden"),
#ApiResponse(code = 500, message = "Failure", response = ErrorResource.class)})
#RequestMapping(method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(HttpStatus.CREATED)
public void save(#Valid #RequestBody Store store, BindingResult bindingResult, HttpServletRequest request, HttpServletResponse response) {
if (bindingResult.hasErrors()) {
throw new InvalidRequestException("Invalid Store", bindingResult);
}
this.storeService.save(store);
response.setHeader("Location", request.getRequestURL().append("/").append(store.getId()).toString());
}
The generated API docs are showing the id of Store in the Model Schema. Technically, when creating a Store the JSON should not contain the id. I'm trying to figure out how to tell Swagger/Springfox to ignore the id but only for this endpoint.
You can hide a field from a model by annotating the property of the class with #ApiModelProperty and setting its hidden property to true.
import io.swagger.annotations.ApiModelProperty;
public class Store {
#ApiModelProperty(hidden = true)
private Long id;
}
Unfortunately, by doing so, you will hide the id field on every endpoint which uses the Store class as an input. Showing the field for another endpoint would require a separate class.

Resources