oatpp icon indicating copy to clipboard operation
oatpp copied to clipboard

[TODO]OpenAPI client/server generator

Open rhard opened this issue 5 years ago • 25 comments

First of all thank you for this great project!

Is there any plans to create REST API client/server genearator for Oatpp from OpenAPI/swagger files?

rhard avatar Oct 29 '20 11:10 rhard

Hello @rhard ,

Thanks for writing!

Is there any plans to create REST API client/server genearator for Oatpp from OpenAPI/swagger files?

This is definitely what we want to have.
I'll prepare a list of tasks for the next milestone (1.3.0) in a few days, potentially it will be there - depends on the load and priorities. I'll update this issue.

Regards, Leonid

lganzzzo avatar Oct 29 '20 11:10 lganzzzo

This is definitely what we want to have

This is great! Thank you!

rhard avatar Oct 29 '20 12:10 rhard

Once the OpenAPI/Swagger spec is available, you may want to use OpenAPI Generator to generate API clients, server stubs, documentation, and more.

We also welcome contributions to add an Oat++ server stub generator. Let me know if anyone is interested and I can show some good starting points.

Disclosure: I'm the top contributor to OpenAPI Generator.

wing328 avatar Nov 07 '20 02:11 wing328

Hello @wing328 ,

We also welcome contributions to add an Oat++ server stub generator.

This feature is in the roadmap and already scheduled for development to be out together with the next release of Oat++. You can expect our contributions to OpenAPI Generator in the comparably near future.

Let me know if anyone is interested and I can show some good starting points.

I'm interested, it will be really helpful!

lganzzzo avatar Nov 07 '20 02:11 lganzzzo

I'll see what I can do in the coming week. Will keep you posted.

wing328 avatar Nov 08 '20 12:11 wing328

I've created https://github.com/OpenAPITools/openapi-generator/pull/7903 to start with.

wing328 avatar Nov 09 '20 10:11 wing328

Got it!

lganzzzo avatar Nov 09 '20 13:11 lganzzzo

Hi, any update on this feature? I think this could be very useful

I started using oatpp and it's very powerful!

marsiliano avatar Mar 01 '22 08:03 marsiliano

this feature is wanted in my job. Because many REST-like service providers describe interfaces with OpenAPI

makru86 avatar Nov 30 '22 05:11 makru86

Hello guys,

Thanks for the comments and upvotes!

At the moment I have no free time to work on this issue. If someone is willing to contribute I will provide the necessary assistance oatpp-wise.

lganzzzo avatar Dec 09 '22 01:12 lganzzzo

I am working on that issue on my free time. Hope to finish in couple of months (it is not much work, can be done in couple of weeks)

Question - would you like cpp-oatpp-server generator to generate normal or async controllers? i started with async.

creating cpp-oatpp-server generator more easy with reference project.i almost finished it. https://github.com/makru86/oatpp-petstore

check it and give a comment - i will appreciate.

makru86 avatar Apr 24 '23 16:04 makru86

@lganzzzo may i ask you to assign me to this issue on github? Or may i assign myself?

makru86 avatar Apr 24 '23 17:04 makru86

Hello @makru86 ,

I am working on that issue on my free time. Hope to finish in couple of months (it is not much work, can be done in couple of weeks)

These are great news! Please let me know in case you need any help.

Question - would you like cpp-oatpp-server generator to generate normal or async controllers? i started with async.

The Simple API is definitely a priority as it used much more often. Async API is just for some specific cases.

Regards, Leonid

lganzzzo avatar Apr 24 '23 20:04 lganzzzo

@makru86 ,

https://github.com/makru86/oatpp-petstore/blob/888e08f003c7f27c8f18f94c7bbf75d700ace417/src/dto/UserDTO.hpp#L36

FYI:

For DTOs you can use DTO_HC_EQ it will define hashCode function and equals operator for objects to enable it to be stored in hash set/map. It also takes into account object hierarchy.

class UserDTO : public oatpp::DTO {

    DTO_INIT(UserDTO, DTO)

    DTO_FIELD(Int64, id);
    DTO_FIELD(String, username);
    DTO_FIELD(String, firstName);
    DTO_FIELD(String, lastName);
    DTO_FIELD(String, email);
    DTO_FIELD(String, password);
    DTO_FIELD(String, phone);
    DTO_FIELD(Int32, userStatus);

    DTO_HC_EQ(id, username, firstName, lastName, email, password, phone, userStatus);

};

lganzzzo avatar Apr 24 '23 20:04 lganzzzo

Thanks, @lganzzzo.

I started new project with simple controllers instead of async. https://github.com/makru86/oatpp-petstore-2

Thanks for the hint to use DTO_HC_EQ.

Currently I don't have any questions. Maybe later. I will post updates in this thread, especially when oatpp-petstore-2 is ready, before implementing generator

makru86 avatar May 10 '23 16:05 makru86

@lganzzzo , If you have, please share an example how to implement AuthorizationHandler for API-Key authorization scheme, with custom header name (api_key in case of petstore https://github.com/makru86/oatpp-petstore-2/blob/master/api/petstore.yaml#L623)

As I saw from examples, Basic and Bearer both use Authorization header. How to make oatpp to pass api_key header value into handleAuthorization override?

makru86 avatar May 13 '23 12:05 makru86

Hello @makru86 ,

It might be possible to do it like follows:

In swagger-component

    auto ss = oatpp::swagger::SecurityScheme::createShared();
    ss->type = "apiKey";
    ss->name = "api_key";
    ss->in = "header";
    //ss->scheme = ???;
    builder.addSecurityScheme("api_key", ss);

    return builder.build();

In Endpoint

  ENDPOINT_INFO(getInventory) {
    auto authHandler = ApiController::getDefaultAuthorizationHandler();
    if(authHandler) {
      info->headers.add<oatpp::String>("api_key").description = authHandler->getScheme();
      info->authorization = authHandler->getScheme();
    }
  }
  ENDPOINT("GET", "store/inventory", getInventory,
           BUNDLE(String, apiKey)) // pass auth payload as bundle from interceptor
  {
    ...
  }

In AuthInterceptor

For complete auth example with interceptor see https://github.com/oatpp/example-jwt/blob/master/src/interceptor/AuthInterceptor.cpp

  auto authHeader = request->getHeader("api_key");

  auto authObject = std::static_pointer_cast<MyAuthObject>(m_authHandler.handleAuthorization(authHeader));
  if(authObject) {
    request->putBundleData("apiKey", authObject->apiKey);
    // TODO check API KEY
    return nullptr; // Continue - API-KEY is valid.
  }

Please let me know if this works

lganzzzo avatar May 13 '23 21:05 lganzzzo

It works! Thanks!

D |2023-05-15 17:29:35 1684151975973748| ApiKeyInHeaderInterceptor:endpoint: GET /store/inventory
 D |2023-05-15 17:29:35 1684151975973786| ApiKeyAuthHandler:authHeader: ***********
 D |2023-05-15 17:29:35 1684151975973792| ApiKeyAuthHandler:allow access: uid-admin
 D |2023-05-15 17:29:35 1684151975973797| ApiKeyInHeaderInterceptor:authorization granted: uid-admin
 D |2023-05-15 17:29:35 1684151975973814| getInventory:apiKeyUserId=uid-admin
 if(authObject) {
    request->putBundleData("apiKey", authObject->apiKey);
    // TODO check API KEY
    return nullptr; // Continue - API-KEY is valid.
  }

I did it little different - checking API key in handleAuthorization(), and bundling apiKeyUserId instead of apiKey. https://github.com/makru86/oatpp-petstore-2/blob/master/src/auth/ApiKeyAuth.hpp

makru86 avatar May 15 '23 12:05 makru86

@lganzzzo

I think the server is mostly finished and I plan to start working on generator soon. Remaining work for the server:

  • add endpoints to the test/MyApiTestClient.hpp
  • add tests for all controllers, like in https://github.com/makru86/oatpp-petstore-starter/blob/master/utility/test.sh but in C++

What do you think - maybe something is missing? Examles:

  • CORS not enabled
  • Swagger UI not enabled (ENDPOINT_INFOs are done partially, but project does not linked with oatpp-swagger)

https://github.com/makru86/oatpp-petstore-starter/

makru86 avatar May 19 '23 16:05 makru86

Hello @makru86 ,

https://github.com/makru86/oatpp-petstore-starter/

A quick comment (https://github.com/makru86/oatpp-petstore-starter/blob/master/src/auth/ApiKeyAuth.hpp#L90): I think it's better not to explicitly list all auth-required endpoints. Instead just list non-auth endpoints and all others treat as auth-required.

What do you think - maybe something is missing?

As for the petstore example it looks great. However, as for "API first" generated project structure should be a bit different.

The general approach is that you want to have the API part to be generated separately from your business logic and then just implement interfaces provided.

So basically what you want to have is something like this:

Generated part

/**
 Pet service interface
 **/
class PetSerivce {
public:
  virtual std::shared_ptr<OutgoingResponse> addPet(const Object<PetDTO>& body) = 0;
}

/**
 Controller
 **/
class PetController : public oatpp::web::server::api::ApiController {
private:
  std::shared_ptr<PetService> m_service;
public:

  explicit PetController(OATPP_COMPONENT(std::shared_ptr<PetService>, petService),
                         OATPP_COMPONENT(std::shared_ptr<ObjectMapper>, objectMapper))
    : oatpp::web::server::api::ApiController(objectMapper)
    , m_service(petService)
  {}


  ENDPOINT("POST", "/pet", addPet, 
           BODY_DTO(Object<PetDTO>, body))
  {
    return m_serivce->addPet(body);  
  }

};

User part

Then I can easily implement my logic by extending PetService:

class MyPetSerivce : public PetService {
public:
  std::shared_ptr<OutgoingResponse> addPet(const Object<PetDTO>& body) override;
}

...

router->addController(std::make_shared<PetController>(std::make_shared<MyPetService>()));

Also, it's a good idea to have the generated part as a library with versions

lganzzzo avatar May 20 '23 14:05 lganzzzo

Thanks for comments, @lganzzzo , It is interesting to learn Oat++ from you.

Changes:

  • added tests for controllers using MyApiTestClient
  • added service interfaces and implementations
  • extracted a library with generated sources
  • version property added to it.

Project needs to be cleaned:

  • maybe merge ApiKeyInterceptor and OAuth2Interceptor
  • parsing of content type 'x-www-form-urlencoded` not implemented (I saw https://github.com/oatpp/oatpp/issues/333).

Please comment if something can be improved.

About endpoint list in auth interceptor: given endpoints (https://github.com/makru86/oatpp-petstore-starter/blob/master/generated/auth/ApiKeyAuth.hpp#L90 )

    m_authEndpoints.route("GET", "/user/logout", apiKeyAuth);
    m_authEndpoints.route("GET", "/user/{username}", false);

I think it is unavoidable to list the first one as apiKeyAuth==true so that route GET:"/user/logout" does not match the second.

edit: library verison: https://github.com/makru86/oatpp-petstore-starter/blob/master/CMakeLists.txt#L21

edit: project layout: https://github.com/makru86/oatpp-petstore-starter/blob/master/README.md?plain=1#L67

makru86 avatar Jun 02 '23 15:06 makru86

another examples of C++ generators can be viewed here https://github.com/OpenAPITools/openapi-generator/tree/master/samples/server/petstore

makru86 avatar Jun 02 '23 15:06 makru86

I just started work on the generator cpp-oatpp-server https://github.com/makru86/openapi-generator

for information, @lganzzzo .

makru86 avatar Jun 13 '23 06:06 makru86

I must pause work on this for couple months - other work. Hope to finish it after.

Commented in https://github.com/OpenAPITools/openapi-generator/pull/7903

makru86 avatar Jun 24 '23 17:06 makru86