Does anyone here have a good strategy for dealing with a changing API spec?
API change -> regenerate code -> implement changes.
I'm thinking generating Java interfaces and having separate class files which implement these interfaces. Any interface mismatches will show up in IDE (e.g. IntelliJ)
Right now if I do the implementation, the files where I wrote the implementation gets overwritten...
My generated swagger Code will be put under src/gen/Java. Everytime i generate the whole folder will be deleted. That way the implementation of the interfaces will not be overwritten. Also i do not commit the gen folder.
EDIT
To illustrate it a little bit more have a look at the templates: https://github.com/Mehtrick/swagger-templates
The important ones are the api and apicontroller.mustache
The generated controller classes look like this
@RestController
@Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Throwable.class)
public class UsersApiController {
@Autowired
private UsersApi UsersApiService;
@PreAuthorize("isAuthenticated() and hasAuthority('USER')")
@RequestMapping(value = "/users/current", produces = { "application/json" }, method = RequestMethod.GET)
public ResponseEntity<User> getCurrentUser() throws Exception {
return UsersApiService.getCurrentUser();
}
@RequestMapping(value = "/users", produces = { "application/json" }, method = RequestMethod.POST)
public ResponseEntity<Void> registerUser(
@Valid @RequestBody User body
) throws Exception {
return UsersApiService.registerUser(body);
}
}
And then the generated UsersApi looks like this:
public interface UsersApi {
/**
* Loads the current user
*/
ResponseEntity<User> getCurrentUser() throws Exception;
/**
* Registers a new user
*/
ResponseEntity<Void> registerUser(@Valid @RequestBody User body) throws Exception;
}
All I have to do is to implement the generated api interfaces as spring components.
These classes are generated from Swagger with this gradle plugin:
https://github.com/int128/gradle-swagger-generator-plugin
The build config looks like this:
ext {
swaggeryaml = file("${projectDir}/swagger/swagger.yaml")
}
apply plugin: "org.hidetake.swagger.generator"
validateSwagger {
inputFile = swaggeryaml
}
generateSwaggerCode {
language = 'spring'
inputFile = swaggeryaml
components = ['models','apis']
configFile = file("${swaggerconfigpath}")
templateDir = file("${projectDir}/swagger/templates")
}
In this example the normal spring-swagger-codegen is used but with my customized templates under ${projectDir}/swagger/templates
The buildscript is configured to move the generated files to src/gen/java
and before that it deletes the whole src/gen/java
folder
task cleanSwagger(type: Delete){
delete "${projectDir}/src/gen/java/"
}
task moveSwagger(type: Copy){
dependsOn "generateSwaggerCode"
from "${buildDir}/swagger-code/src/main/java"
into "${projectDir}/src/gen/java"
}
The build tasks are called when:
- the java code compiles (like on a ci-server)
- The IDE classpath (in my case Eclipse) is initialized
compileJava.dependsOn swaggerGen
Hope this helps