ballerina-platform / ballerina-library

The Ballerina Library
https://ballerina.io/learn/api-docs/ballerina/
Apache License 2.0
138 stars 56 forks source link

The OpenAPI introspection resource returns an incorrect specification #6511

Open TharmiganK opened 1 month ago

TharmiganK commented 1 month ago

Description:

We can add an introspection resource to an HTTP service by adding the following OpenAPI annotation:

@openapi:ServiceInfo {
    embed: true
}

When the OpenAPI contract path is not specified in the above annotation, OpenAPI compiler plugin checks that and we generate the OpenAPI specification by passing the service declaration node to the OpenAPI tool. The generate specification is added to the http:ServiceConfig annotation via a code modifier in the OpenAPI tool. Then the HTTP compiler plugin reads that field and generate a introspection resource and inject that to the existing service.

But the issue arises with the HTTP code modifier which actually adds the @http:Payload annotation to the request payload. Since this code modifier is engaged after the OpenAPI compiler plugin, the OpenAPI tool tries to generate the specification without this specific annotation which ends up in a incorrect specification.

Steps to reproduce:

Run the following service:

import ballerina/http;
import ballerina/openapi;

type Post record {
    string text;
};

@openapi:ServiceInfo {
    embed: true
}
service /text\-processing on new http:Listener(9098) {

    resource function post api/sentiment(Post post) {}
}

Use the introspection resource to retrieve the specification:

$ curl http://localhost:9098/text-processing/openapi-doc-dygixywsw
{
  "openapi" : "3.0.1",
  "info" : {
    "title" : "Text Processing",
    "version" : "0.1.0"
  },
  "servers" : [ {
    "url" : "{server}:{port}/text-processing",
    "variables" : {
      "server" : {
        "default" : "http://localhost"
      },
      "port" : {
        "default" : "9098"
      }
    }
  } ],
  "paths" : {
    "/api/sentiment" : {
      "post" : {
        "operationId" : "postApiSentiment",
        "parameters" : [ {
          "name" : "post",
          "in" : "query",
          "required" : true,
          "content" : {
            "application/json" : {
              "schema" : {
                "$ref" : "#/components/schemas/Post"
              }
            }
          }
        } ],
        "responses" : {
          "202" : {
            "description" : "Accepted"
          },
          "400" : {
            "description" : "BadRequest",
            "content" : {
              "application/json" : {
                "schema" : {
                  "$ref" : "#/components/schemas/ErrorPayload"
                }
              }
            }
          }
        }
      }
    }
  },
  "components" : {
    "schemas" : {
      "ErrorPayload" : {
        "required" : [ "message", "method", "path", "reason", "status", "timestamp" ],
        "type" : "object",
        "properties" : {
          "timestamp" : {
            "type" : "string"
          },
          "status" : {
            "type" : "integer",
            "format" : "int64"
          },
          "reason" : {
            "type" : "string"
          },
          "message" : {
            "type" : "string"
          },
          "path" : {
            "type" : "string"
          },
          "method" : {
            "type" : "string"
          }
        }
      },
      "Post" : {
        "required" : [ "text" ],
        "type" : "object",
        "properties" : {
          "text" : {
            "type" : "string"
          }
        }
      }
    }
  }
}

Note : the Post is considered as query parameter but it should be the request body.

Affected Versions:

Ballerina SwanLake Update 2201.9.0

lnash94 commented 1 month ago

AFAIR code modifiers execute before compiler plugin code analyzers, and those modifiers, as well as the analyzer, cannot preserve a specific order while executing. Since both HTTP and OpenAPI plugins are code modifiers, shall we check any mechanism to guarantee that the code modification has occurred? (For example, maintaining a cache or having a private flag to the service node)

TharmiganK commented 1 month ago

Adding @azinneera for suggestions

azinneera commented 3 weeks ago

ATM, we don't guarantee the order of execution of the modifier plugins. What is the behavior we expect here?

TharmiganK commented 3 weeks ago

There is two imports openapi and http. Both have code modifiers. The requirement here is running the code modifier of the http module before running the openapi one.