ballerina-platform / ballerina-library

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

Adding example details to the generated OpenAPI Specification #5983

Closed lnash94 closed 2 months ago

lnash94 commented 8 months ago

Description: This feature aims to add an example for the generated OAS. An API specification can include examples for:

Examples can be used by tools and libraries, for instance, Swagger UI auto-populates request bodies based on input schema examples, and some API mocking tools use examples to generate mock responses.

Tasks:

lnash94 commented 3 months ago

Proposed annotation for the openapi:ResourceInfo

#  This annotation represents a record for storing resource meta information.
#
# + summary - A brief summary of the resource.  
# + tags - Tags associated with the resource.  
# + operationId - Unique identifier for the operation.  
# + example - This section contains detailed examples for responses and request bodies.
public type ResourceInformation record {|
    string summary?;
    string[] tags?;
    string operationId?;
    Examples examples?;
|};

# Represents an example of a response for a specific status code.
#
# + headers - The headers for the response.  
# + mediaType - The media type of the response.  
# + examples - Detailed examples of the response content.
public type ResponseExample record {
    map<string> headers?;
    string mediaType;
    map<record {}> examples;
};

# Represents an example of a request body for a specific media type.
#
# + mediaType - The media type of the request body.  
# + examples - Detailed examples of the request body content.
public type RequestExample record {
    string mediaType;
    map<record {}> examples;
};

# Represents examples for resource function.
#
# + response - Response examples  
# + requestBody - Requst examples
public type Examples record {|
    map<ResponseExample[]> response?;
    RequestExample[] requestBody?;
|};

User experience

import ballerina/openapi;

service / on ep0 {
    @openapi:ResourceInfo {
        operationId: "operationId",
        tags: ["pets", "food"],
        examples: {
            response: {
                "200": [
                    {
                        headers: {"x-expires-after": "2024-06-01T23:59:59Z"},
                        mediaType: "application/json",
                        examples: {
                            "pet": {name: "Rova", age: 5}
                        }
                    }
                ]
            },
            requestBody: [
                {
                    mediaType: "application/json",
                    examples: {
                        "pet": {name: "Tommy", age: 6}
                    }
                }
            ]
        }
    }
    resource function post pet(@http:Payload Pet body) returns Pet {
    }
}
lnash94 commented 3 months ago

Redesigned annotation v2

# This annotation represents a record for storing resource meta information.
#
# + summary - A brief summary of the resource.  
# + tags - Tags associated with the resource.  
# + operationId - Unique identifier for the operation.  
# + examples - This section contains detailed examples for responses and request bodies.
public type ResourceInformation record {|
    string summary?;
    string[] tags?;
    string operationId?;
    Examples examples?;
|};

# Represents an example of a response for a specific status code.
#
# + headers - The headers for the response.  
# + examples - Detailed examples of the response content.
public type ResponseExample record {
    map<string> headers?;
    map<map<record {|record {} value;|}>> examples?;

};

# Represents an example of a request body for a specific media type.
public type RequestExamples map<anydata>;

# Represents examples for resource function.
#
# + response - Response examples  
# + requestBody - Requst examples
public type Examples record {|
    map<ResponseExample> response?;
    RequestExamples requestBody?;
|};

public const annotation ResourceInformation ResourceInfo on object function;
lnash94 commented 3 months ago

Workaround done for the design: https://github.com/ballerina-platform/openapi-tools/pull/1722#issuecomment-2172419121 Related ballerina lang issue: https://github.com/ballerina-platform/ballerina-lang/issues/42933

lnash94 commented 3 months ago
lnash94 commented 3 months ago

Add support for the request payload example mapping

OAS swagger allows to have only an example for payload, the annotation design below is for getting the example details

# Represents an example of a request body for a specific media type.
#<mediaType, <name, value for the examples>>
public type RequestExamples map<map<record {|record {}|string value;|}>>;

Annotation usage in ballerina service

import ballerina/http;
import ballerina/openapi;

listener http:Listener httpListener = check new (9000);
service /convert on httpListener {

    @openapi:ResourceInfo {
        examples: {
            "requestBody": {
                "application/json": {
                    "requestExample01": {
                        "value": "/Users/hansaninissanka/Documents/Ballerina_Projects/example_feature/examples.json" //with reference file
                    },
                    "requestExample02": {
                        "value": { // with inline value
                            "fromCurrancy": "LKR",
                            "toCurrancy": "USD"
                        }
                    }
                }
            }
        }
    }
    resource function post xe_data(record{} payload) returns record {}? {
        return {};
    }
}

Generated OAS:

/xe_data:
    post:
     ...
     requestBody:
        content:
          application/json:
            schema:
              type: object
              properties: {}
            examples:
              requestExample01:
                value:
                  toAmount: 60000
                  fromCurrency: USD
                  toCurrency: LKR
                  fromAmount: 200
                  timestamp: 2024-06-14
              requestExample02:
                value:
                  fromCurrancy: LKR
                  toCurrancy: USD
        required: true
      responses:
        "201":
          description: Created
          content:
            application/json:
              schema:
lnash94 commented 2 months ago

Meeting date: 2024/07/09 Attendees: @shafreenAnfar , @TharmiganK , @dilanSachi, @lnash94

lnash94 commented 2 months ago

Having two optional fields value and filePath may enable the user to have use both at a once therefore we change it into two different record

public type ResponseExample record {|
    map<string> headers?;
    map<map<record {|record {} value;|}|record {|string filePath;|}>> examples?;
|};
public type RequestExamples readonly & map<map<record {|record {} value?;|}| record {| string filePath?;|}>>;
lnash94 commented 2 months ago

Closed with https://github.com/ballerina-platform/openapi-tools/pull/1736