For auto-generated server code, we want to generate 2 files for each API controller file:
1) an interface, which will be overwritten by code generation
2) an implementation class, which will not be overwritten by code generation
Java Spring has already implemented this:
https://github.com/openapitools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/JavaSpring/api.mustache
https://github.com/openapitools/openapi-generator/blob/master/modules/openapi-generator/src/main/resources/JavaSpring/apiController.mustache
The goal is to avoid application/business logic being overwritten by code generation so that there's less overhead when adding/deleting/updating endpoint definition.
Other server generators should leverage similar design.
Latest master
https://github.com/swagger-api/swagger-codegen/issues/5431
If anyone wants to contribute the enhancement, please reply to let us know.
In my opinion this is called the generation gap pattern
I did not check all the java servers, but for jersey2 this is already the case:
Under src/main/java the stuff you edit (and that you no longer regenerate).
Example: PetApiServiceImpl
The Framework classes (jaxrs) are under src/gen/java.
Example: PetApiService (this is a the parent class of PetApiServiceImpl)
The content of src/gen/java should be regenerated after each changes of the input specification.
For me this works as expected and as described here.
rust-server already does this - it generates a standalone library. A server-side user imports the library and _implements_ the API (a Rust trait), whilst a client-side user imports the library and _uses_ the API. The autogenerated code also includes example client/server integrations that users can copy from to make their integration a little easier.
For jaxrs-resteay source code generation, jaxrs annotations are set at class level (see api.mustache).
As proposed, jaxrs annotations could be set at interface level. Of course we still need the class implementation which would inherit from the interface.
This interface could also be used for client source code as described in Resteasy documentation (RESTEasy Proxy Framework).
I seems that having annotation on interface is a jaxrs-resteasy thing. JaxRS-Jersey2 does not seems to support it. See How to annotate JAX-RS on an interface while using Jersey.
From what I experienced neither standalone JaxRS-Jersey2 nor Spring Web could not get you having annotations only on interfaces.
Spring project has this ticket for years https://jira.spring.io/browse/SPR-11055
Spring Framework/Web 5.0.7 supports @RequestParam on interfaces, but not @RequestBody nor @PathVariable.
For Jersey2, as mentioned before such approach also does not work.
I was able get such separation of generated code and implementations by combining Jersey2 and Spring.
There is an ext in Jersey project for this - org.glassfish.jersey.ext:jersey-spring4
With org.springframework.boot:spring-boot-starter-jersey setting it up is even easier and less boilerplate is needed. Check example bellow.
I'd like to have Spring Web based only generated code, and not introducing Jersey2 dependencies at all. When Spring team fixes SPR-11055, transition to Spring Web based annotations on interfaces should be easy - almost as just change generator language.
Lets say PetApi.java is kept in separate module/jar. It could be manually created or regenerated from OpenApi every build.
//---------------- PetApi.java
@Path("/pet")
public interface PetApi {
@POST
@Consumes({ "application/json", "application/xml" })
void addPet(Pet pet);
@GET
@Path("/{petId}")
@Produces({ "application/xml", "application/json" })
Pet getPetById(@PathParam("petId") Long petId);
}
PetApiImpl.java could be kept in separate jar/module and is under source version control.
//---------------- PetApiImpl.java
import org.springframework.stereotype.Component;
@Component
public class PetApiImpl implements PetApi {
@Override
public void addPet(Pet pet) {
// do stuff
}
@Override
Pet getPetById(Long petId){
// do stuff
// return found Pet
}
}
//---------------- JerseyConfiguration.java
import org.glassfish.jersey.server.ResourceConfig;
import org.springframework.stereotype.Component;
@Component
public class JerseyConfig extends ResourceConfig {
public JerseyConfig() {
register(PetApiImpl.class);
}
}
I would love to implement this approach in PHP server generator. It was the most frustrating thing when I worked with SwaggerCodegen. You always need to be careful to not override your current progress. I've ended up with condition in router:
if (class_exists('\MyNamespace\Api\UserApi')) {
// use implementation
} else {
// use generated stub
}
I would extend your list with:
mockingEnabled: falseWith this approach we can generate complete mock API right away.
And I think we need to do the same thing with models. ModelInterface -> ModelMock -> ModelImplementation
Dumb question: Does generator create implementation class for the first time? Or maybe better not to do anyting in src folder at all, so user need to create implementation class manually?
cpp-qt5-qhttpengine-server is implementing this for c++. The method createApiHandlers
https://github.com/OpenAPITools/openapi-generator/blob/master/samples/server/petstore/cpp-qt5-qhttpengine-server/server/src/handlers/OAIApiRouter.h#L68
has to be overridden and that the handlers inherited that is all.
After discussion with @wing328 I decided that abstract class is better than interface for PHP server stubs.
Example with interface:
interface UserApi
{
/**
* Operation description
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
*/
public function getUserById($request, $response, $arguments);
}
Example with abstract class:
abstract class AbstractUserApi
{
/**
* Operation description
*
* @param ServerRequestInterface $request Request
* @param ResponseInterface $response Response
* @param array|null $args Path arguments
*
* @return ResponseInterface
*/
public function getUserById($request, $response, $arguments)
{
/// stubs for parsing all request parameters which user can copypaste to implementation
$userId = $arguments['userId'];
/// need to force user to overwrite thit method
throw new Exception('How about extend AbstractUserApi by \OpenAPIServer\Api\UserApi class implementing getUserById as a GET method?');
/// or return 501 Not implemented response
return $request->withStatus(501)->write('How about extend AbstractUserApi by \OpenAPIServer\Api\UserApi class implementing getUserById as a GET method?');
}
}
Question to community.
What do you think is better by default in just generated stubs, throw an Exception or return 501 Not implemented response :question: