Differences
This shows you the differences between two versions of the page.
Next revision | Previous revision Last revision Both sides next revision | ||
devel:documentation:architecture:dev:swagger [2018/03/22 15:25] stloukalp created |
devel:documentation:architecture:dev:swagger [2023/10/19 10:43] chalupat [#3330] Replace Springfox with springdoc |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== API documentation (Swagger) ====== | ||
+ | {{tag> documentation}} | ||
+ | {{indexmenu_n> | ||
+ | |||
+ | |||
+ | We use [[http:// | ||
+ | * [[http:// | ||
+ | * [[http:// | ||
+ | * [[https:// | ||
+ | |||
+ | <note tip> | ||
+ | Difference between dynamic and static documentation: | ||
+ | * **Dynamic documentation** is exposed by Swagger UI from rest controllers with '' | ||
+ | * **Static documentation** is generated from raw swagger specification (e.g. ''< | ||
+ | </ | ||
+ | |||
+ | ===== Configuration ===== | ||
+ | |||
+ | Parent project contains basic settings for module documentation. When a new module is added, some steps have to be done. | ||
+ | Complete configuration can be found in '' | ||
+ | |||
+ | ==== Java ==== | ||
+ | |||
+ | === Module properties === | ||
+ | |||
+ | Use '' | ||
+ | |||
+ | <code java> | ||
+ | /** | ||
+ | * Example module descriptor | ||
+ | */ | ||
+ | @Component | ||
+ | @PropertySource(" | ||
+ | @ConfigurationProperties(prefix = " | ||
+ | public class ExampleModuleDescriptor extends PropertyModuleDescriptor { | ||
+ | |||
+ | public static final String MODULE_ID = " | ||
+ | |||
+ | @Override | ||
+ | public String getId() { | ||
+ | return MODULE_ID; | ||
+ | } | ||
+ | | ||
+ | /** | ||
+ | * Enables links to swagger documentation | ||
+ | */ | ||
+ | @Override | ||
+ | public boolean isDocumentationAvailable() { | ||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === Swagger endpoint === | ||
+ | |||
+ | <code java> | ||
+ | /** | ||
+ | * Example module swagger configuration | ||
+ | */ | ||
+ | @Configuration | ||
+ | @ConditionalOnProperty(prefix = " | ||
+ | public class ExampleSwaggerConfig extends AbstractSwaggerConfig { | ||
+ | |||
+ | @Autowired private ExampleModuleDescriptor moduleDescriptor; | ||
+ | |||
+ | @Override | ||
+ | protected ModuleDescriptor getModuleDescriptor() { | ||
+ | return moduleDescriptor; | ||
+ | } | ||
+ | |||
+ | @Bean | ||
+ | public Docket exampleApi() { | ||
+ | return api(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | springdoc version (13.1.0+) | ||
+ | <code java> | ||
+ | /** | ||
+ | * Example module swagger configuration | ||
+ | */ | ||
+ | @Configuration | ||
+ | @ConditionalOnProperty(prefix = " | ||
+ | public class ExampleSwaggerConfig extends AbstractSwaggerConfig { | ||
+ | |||
+ | @Autowired private ExampleModuleDescriptor moduleDescriptor; | ||
+ | |||
+ | @Override | ||
+ | protected ModuleDescriptor getModuleDescriptor() { | ||
+ | return moduleDescriptor; | ||
+ | } | ||
+ | |||
+ | @Bean | ||
+ | public GroupedOpenApi exampleApi() { | ||
+ | return api(" | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === Rest controller === | ||
+ | |||
+ | Add [[https:// | ||
+ | |||
+ | <code java> | ||
+ | /** | ||
+ | * Ping pong example controller | ||
+ | */ | ||
+ | @RestController | ||
+ | @RequestMapping(value = BaseController.BASE_PATH + "/ | ||
+ | @Api(value = " | ||
+ | public class ExampleController { | ||
+ | |||
+ | @ResponseBody | ||
+ | @RequestMapping(method = RequestMethod.GET) | ||
+ | @ApiOperation( | ||
+ | value = "Ping - Pong operation", | ||
+ | notes= " | ||
+ | nickname = " | ||
+ | tags={ " | ||
+ | response = Pong.class, | ||
+ | authorizations = { | ||
+ | @Authorization(SwaggerConfig.AUTHENTICATION_BASIC), | ||
+ | @Authorization(SwaggerConfig.AUTHENTICATION_CIDMST) | ||
+ | }) | ||
+ | public ResponseEntity<?> | ||
+ | @ApiParam(value = "In / out message", | ||
+ | @RequestParam(required = false, defaultValue = " | ||
+ | ) { | ||
+ | return new ResponseEntity<> | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | For springdoc version (13.1.0+) add [[https:// | ||
+ | |||
+ | <code java> | ||
+ | |||
+ | /** | ||
+ | * Ping pong example controller | ||
+ | */ | ||
+ | @RestController | ||
+ | @Enabled(ExampleModuleDescriptor.MODULE_ID) | ||
+ | @RequestMapping(value = BaseController.BASE_PATH + "/ | ||
+ | @Tag(name = ExampleController.TAG, | ||
+ | |||
+ | public class ExampleController { | ||
+ | |||
+ | protected static final String TAG = " | ||
+ | @Autowired private ExampleService service; | ||
+ | |||
+ | @ResponseBody | ||
+ | @RequestMapping(method = RequestMethod.GET, | ||
+ | @Operation( | ||
+ | summary = "Ping - Pong operation", | ||
+ | description= " | ||
+ | operationId = " | ||
+ | tags={ ExampleController.TAG } | ||
+ | responses = @ApiResponse( | ||
+ | responseCode = " | ||
+ | content = { | ||
+ | @Content( | ||
+ | mediaType = BaseController.APPLICATION_HAL_JSON_VALUE, | ||
+ | schema = @Schema( | ||
+ | implementation = Pong.class | ||
+ | ) | ||
+ | ) | ||
+ | } | ||
+ | )) | ||
+ | @SecurityRequirements({ | ||
+ | @SecurityRequirement(name = SwaggerConfig.AUTHENTICATION_BASIC), | ||
+ | @SecurityRequirement(name = SwaggerConfig.AUTHENTICATION_CIDMST) | ||
+ | }) | ||
+ | public ResponseEntity< | ||
+ | @Parameter(description = "In / out message", | ||
+ | @RequestParam(required = false, defaultValue = " | ||
+ | ) { | ||
+ | return new ResponseEntity<> | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === Model === | ||
+ | |||
+ | Add [[https:// | ||
+ | |||
+ | <code java> | ||
+ | /** | ||
+ | * Example ping - pong response dto | ||
+ | */ | ||
+ | @ApiModel(description = "Ping - Pong response" | ||
+ | public class Pong implements BaseDto { | ||
+ | |||
+ | private static final long serialVersionUID = 1L; | ||
+ | // | ||
+ | @ApiModelProperty(required = true, notes = " | ||
+ | private UUID id; | ||
+ | @ApiModelProperty(notes = "Ping - Pong response message" | ||
+ | private String message; | ||
+ | @ApiModelProperty(required = true, notes = " | ||
+ | private DateTime created; | ||
+ | |||
+ | // ... getters, setters | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | For springdoc version (13.1.0+) add [[https:// | ||
+ | |||
+ | <code java> | ||
+ | /** | ||
+ | * Example ping - pong response dto | ||
+ | */ | ||
+ | @Schema(description = "Ping - Pong response" | ||
+ | public class Pong implements BaseDto { | ||
+ | |||
+ | private static final long serialVersionUID = 1L; | ||
+ | // | ||
+ | @Schema(requiredMode = Schema.RequiredMode.REQUIRED, | ||
+ | private UUID id; | ||
+ | @Schema(description = "Ping - Pong response message" | ||
+ | private String message; | ||
+ | @Schema(requiredMode = Schema.RequiredMode.REQUIRED, | ||
+ | private ZonedDateTime created; | ||
+ | |||
+ | // ... getters, setters | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | === Test === | ||
+ | |||
+ | Create integration test: | ||
+ | |||
+ | <code java> | ||
+ | /** | ||
+ | * Static swagger generation to sources - will be used as input for swagger2Markup build | ||
+ | */ | ||
+ | public class Swagger2MarkupTest extends AbstractSwaggerTest { | ||
+ | |||
+ | @Test | ||
+ | public void testConvertSwagger() throws Exception { | ||
+ | super.convertSwagger(ExampleModuleDescriptor.MODULE_ID); | ||
+ | } | ||
+ | | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Static documentation folder structure ==== | ||
+ | |||
+ | We are using asciidoctor maven plugin for static documentation (see maven chapter). Maven plugins prepare all generated artifacts, but requires some static artifacts with static / written documentation in structure in module sources folder: | ||
+ | * ''< | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | * '' | ||
+ | |||
+ | All files have to be written in asciidoc format. Read more about extensions in swagger2markup [[http:// | ||
+ | |||
+ | === Example extension === | ||
+ | |||
+ | Add security information about demo identity credentials. Create | ||
+ | |||
+ | <code asciidoc> | ||
+ | == Demo credentials | ||
+ | |||
+ | admin / admin | ||
+ | </ | ||
+ | |||
+ | This content will be automatically included to static documentation to the security section. Read more about available [[http:// | ||
+ | |||
+ | ==== Maven ==== | ||
+ | |||
+ | Documentation is generated under '' | ||
+ | |||
+ | <code xml> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | <!-- First, use the swagger2markup plugin to generate asciidoc --> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | |||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | |||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | |||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | <!-- | ||
+ | Run the generated asciidoc through Asciidoctor to generate other | ||
+ | documentation types, such as PDFs or HTML5 | ||
+ | --> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | <!-- Configure generic document generation settings --> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | <!-- | ||
+ | Since each execution can only handle one backend, run separate | ||
+ | executions for each desired output type | ||
+ | --> | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | All maven properties are preconfigured in parent '' | ||
+ | |||
+ | <code xml> | ||
+ | < | ||
+ | < | ||
+ | |||
+ | < | ||
+ | properties are used ind test and doc profile, | ||
+ | test: swagger.json input is generated | ||
+ | doc: swagger2markup and asciidoctor plugins require them | ||
+ | | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | < | ||
+ | <!-- static documentation will be available as webjars --> | ||
+ | <!-- e.g. http:// | ||
+ | < | ||
+ | < | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Conventions ===== | ||
+ | |||
+ | * Add Swagger annotation. What can be written into annotation, will be written to annotation - will be shown in dynamic and static documentation. Static documentation extension is used, when annotation doesn' | ||
+ | * Use module-< | ||
+ | * Use '' | ||
+ | * Use '' | ||
+ | |||
+ | ===== Aggregator ===== | ||
+ | |||
+ | When module **aggregator** is built under '' | ||
+ | <code shell> | ||
+ | cd aggregator/ | ||
+ | </ | ||
+ | <code shell> | ||
+ | mvn package -Prelease -DdocumentationOnly=true | ||
+ | </ | ||
+ | |||
+ | <note info> | ||
+ | Which documentation are packed into documentation archive is configured in ''/ | ||
+ | </ | ||
+ | |||
+ | ===== Tips ===== | ||
+ | |||
+ | Use IdmIdentityController as inspiration. | ||
+ | |||
+ | Export swagger.json by running single test: | ||
+ | |||
+ | <code shell> | ||
+ | mvn clean package -DskipTests | ||
+ | mvn surefire: | ||
+ | </ | ||
+ | |||
+ | Generate static documentation only (swagger.json has to be exported - see previous tip). Generated html will be available in project' | ||
+ | |||
+ | <code shell> | ||
+ | mvn package -DskipTests -Prelease | ||
+ | </ | ||
+ | |||
+ | Static html documentation will be available from url (application' | ||
+ | |||
+ | <code url> | ||
+ | < | ||
+ | // e.g. | ||
+ | http:// | ||
+ | </ | ||
+ | |||
+ | |||
+ | ===== Implementation details ===== | ||
+ | |||
+ | * Static documentation contains all files for now (copy / paste redundancy - see security section - same in all modules) - will be improved soon. | ||
+ | * JSR303 for model documentation (comming soon) | ||
+ | * Model SPI (comming soon) | ||
+ | * UUID, GuardedString types (comming soon) | ||
+ | |||