I've been playing with swagger codegen for a springboot project. I am now able to automatically generate the client code in java, ignore the generation of the data models (importing my own in the swagger-codegen-maven-plugin via importMapping). Furthermore, I am also able to:
- specify tags directly in the springboot code to help organize the openapi contract,
- use the @Operation annotation to specify method names,
- use the @ApiResponses annotation to detail the expected response codes and descriptions,etc.
Example below:
@RestController
@RequestMapping(value="/api/external/")
@Tag(name = "addResources", description = "The Add Resources API")
public class ControllerAddResources {
private final ServiceInterAddResources externalSources;
public ControllerAddResources(
ServiceInterAddResources externalSources){
this.externalSources = externalSources;
}
@Operation(operationId="importDataV3", summary = "Import Data V3", description = "Import a Data V3 file from source", tags = { "addResources" })
@PostMapping(path="/{source}/datav3", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public @ResponseBody String importDataV3(
@PathVariable String source,
MultipartFile file) throws ExceptionInvalidDataV3, IOException{
return externalSources.importDataV3(source, file);
}
All of this is built into the openapi contract which is then used by codegen to generate the client library (in this case, I'm using resttemplate), such as the one below:
@javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaClientCodegen", date = "2020-03-05T12:15:11.537Z[Europe/Lisbon]")@Component("com.xxx.connector.api.AddResourcesApi")
public class AddResourcesApi {
private ApiClient apiClient;
public AddResourcesApi() {
this(new ApiClient());
}
@Autowired
public AddResourcesApi(ApiClient apiClient) {
this.apiClient = apiClient;
}
public ApiClient getApiClient() {
return apiClient;
}
public void setApiClient(ApiClient apiClient) {
this.apiClient = apiClient;
}
/**
* Import Data V3
* Import a Data V3 file from source
* <p><b>412</b> - default response
* <p><b>409</b> - default response
* <p><b>200</b> - default response
* @param source The source parameter
* @param file The file parameter
* @return String
* @throws RestClientException if an error occurs while attempting to invoke the API
*/
public String importDataV3(String source, File file) throws RestClientException {
Object postBody = null;
// verify the required parameter 'source' is set
if (source == null) {
throw new HttpClientErrorException(HttpStatus.BAD_REQUEST, "Missing the required parameter 'source' when calling importDataV3");
}
// create path and map variables
final Map<String, Object> uriVariables = new HashMap<String, Object>();
uriVariables.put("source", source);
String path = UriComponentsBuilder.fromPath("/api/external/{source}/datav3").buildAndExpand(uriVariables).toUriString();
final MultiValueMap<String, String> queryParams = new LinkedMultiValueMap<String, String>();
final HttpHeaders headerParams = new HttpHeaders();
final MultiValueMap<String, Object> formParams = new LinkedMultiValueMap<String, Object>();
if (file != null)
formParams.add("file", new FileSystemResource(file));
final String[] accepts = {
"*/*", "application/json"
};
final List<MediaType> accept = apiClient.selectHeaderAccept(accepts);
final String[] contentTypes = {
"multipart/form-data"
};
final MediaType contentType = apiClient.selectHeaderContentType(contentTypes);
String[] authNames = new String[] { };
ParameterizedTypeReference<String> returnType = new ParameterizedTypeReference<String>() {};
return apiClient.invokeAPI(path, HttpMethod.POST, queryParams, postBody, headerParams, formParams, accept, contentType, authNames, returnType);
}
In the generated sources, codegen also includes scaffolds for junit tests, like the one below:
/**
* API tests for AddResourcesApi
*/
@Ignore
public class AddResourcesApiTest {
private final AddResourcesApi api = new AddResourcesApi();
/**
* Import Data V2
*
* Import an Data V2 file from source
*
* @throws ApiException
* if the Api call fails
*/
@Test
public void imporDataV2Test() {
String source = null;
File file = null;
String response = api.importDataV2(source, file);
// TODO: test validations
}
/**
* Import Data V3
*
* Import an Data V3 file from source [%source%]
*
* @throws ApiException
* if the Api call fails
*/
@Test
public void importDataV3Test() {
String source = null;
File file = null;
String response = api.importDataV3(source, file);
// TODO: test validations
}
}
However, the validation code is empty, as expected. Since the client is being continuously generated in a CI/CD environment and being deployed to a dependency management system that I'm running internally (Artifactory), this defeats the purpose of manually writing the tests each time I execute codegen.
Is there a way of specifying the validation code (or validation requisites) directly using java annotations (or a templating mechanism) at the springboot project level? This would allow for fully automated client library generation with testing included.