diff --git a/docs/ballerina-to-oas/proposals/constraint-support.md b/docs/ballerina-to-oas/proposals/constraint-support.md new file mode 100644 index 000000000..6b3f2da72 --- /dev/null +++ b/docs/ballerina-to-oas/proposals/constraint-support.md @@ -0,0 +1,352 @@ +# Proposal: Map Ballerina constraints to OpenAPI Specification + +_Owners_: @SachinAkash01 +_Reviewers_: @lnash94 @TharmiganK +_Created_: 2023/09/08 +_Updated_: 2023/09/21 +_Issue_: [#4788](https://github.com/ballerina-platform/ballerina-library/issues/4788) + +## Summary +Implement the capabilities to support constraint validations in the generated OpenAPI specification of the Ballerina +OpenAPI tool. + +## Goals +- Enhance the Ballerina OpenAPI tool by adding support for constraint validations in the generated OpenAPI +specification. + +## Motivation +OpenAPI is a widely used standard for documenting APIs. It allows developers to specify the structure and data types +expected in API requests and responses. One of its valuable features is built-in schema validation, which ensures that +the data exchanged between different software components conforms to the defined structure and types. + +Constraints are the OpenAPI schema validations equivalent in the Ballerina programming language. The existing tool for +generating OpenAPI specification from Ballerina code lacks support for including these constraints in the OpenAPI +specification. This means that when developers use constraints in their Ballerina code, this information is not properly +reflected in the OpenAPI specification. + +Overall, improving the OpenAPI tool to support Ballerina constraints in the generated OpenAPI specification is a +worthwhile investment that will improve the overall developer experience. + +## Description +The feature implementation focuses on integrating Ballerina constraints into generated OpenAPI specifications to +accurately document the API. Following is the proposed list of Ballerina constraint types and the corresponding +OpenAPI schema validations. + +**1. OpenAPI data type: integer (formats: int32, int64) : (ballerina mapping): int** + + + +
+ +| Ballerina Mapping | Keywords | +|-------------------------------------------------| -- | +| `@constraint:Int { minValue: }` | minimum | +| `@constraint:Int { maxValue: }` | maximum | +| `@constraint:Int { minValueExclusive: }` | exclusiveMinimum | +| `@constraint:Int { maxValueExclusive: }` | exclusiveMaximum | +
+ + + +1.1. Sample Ballerina Code: +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:Int{ + minValue: 5, + maxValue: 20 +} +public type Age int; + +type Person record{ + Age age?; +}; + +service / on new http:Listener(9090){ + resource function post newPerson(Person body) returns error?{ + return; + } +} +``` + +1.2. Generated OpenAPI Specification: +```yaml +components: + schemas: + Age: + minimum: 5 + maximum: 20 + type: integer + format: int32 + Person: + type: object + properties: + age: + $ref: '#/components/schemas/Age' +``` + +**2. OpenAPI data type: number (format: float) : (ballerina mapping): float** + + + +
+ +| Ballerina Mapping | Keywords | +|---------------------------------------------------| -- | +| `@constraint:Float { minValue: }` | minimum | +| `@constraint:Float { maxValue: }` | maximum | +| `@constraint:Float { minValueExclusive: }` | exclusiveMinimum | +| `@constraint:Float { maxValueExclusive: }` | exclusiveMaximum | +
+ + + +2.1. Ballerina Sample Code: +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:Float{ + maxValue: 1000.5 +} +public type Salary float; + +type Person record{ + Salary salary?; +}; + +service / on new http:Listener(9090){ + resource function post newPerson(Person body) returns error?{ + return; + } +} +``` + +2.2. Generated OpenAPI Specification: +```yaml +components: + schemas: + Salary: + maximum: 1000.5 + type: number + format: float + Person: + type: object + properties: + salary: + $ref: '#/components/schemas/Salary' +``` + +**3. OpenAPI data type: number (format: decimal) : (ballerina mapping): decimal** + + + +
+ +| Ballerina Mapping | Keywords | +|----------------------------------------------------| -- | +| `@constraint:Number { minValue: }` | minimum | +| `@constraint:Number { maxValue: }` | maximum | +| `@constraint:Number { minValueExclusive: }` | exclusiveMinimum | +| `@constraint:Number { maxValueExclusive: }` | exclusiveMaximum | +
+ + + +3.1. Sample Ballerina Code: +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:Number{ + maxValueExclusive: 5.1 +} +public type Rating decimal; + +type Hotel record{ + Rating rate?; +}; + +service / on new http:Listener(9090){ + resource function post newHotel(Hotel body) returns error?{ + return; + } +} +``` + +3.2. Generated OpenAPI Specification: +```yaml +components: + schemas: + Rating: + maximum: 5.1 + exclusiveMaximum: true + type: number + format: decimal + Hotel: + type: object + properties: + rate: + $ref: '#/components/schemas/Rating' +``` + +**4. OpenAPI data type: string : (ballerina mapping): string** + + + +
+ +| Ballerina Mapping | Keywords | +|--------------------------------------------| -- | +| `@constraint:String { minLength: }` | minLength | +| `@constraint:String { maxLength: }` | maxLength | +| `@constraint:String { length: }` | minLength: ``, maxLength: `` | +| `@constraint:String { pattern: }` | pattern | +
+ + + +4.1.1. Sample Ballerina Code (minLength, maxLength, pattern): +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:String{ + minLength: 5, + maxLength: 20, + pattern: re `^[a-zA-Z0-9_]+$` +} +public type Username string; + +type Person record{ + Username username?; +}; + +service / on new http:Listener(9090){ + resource function post newUser(Person body) returns error?{ + return; + } +} +``` + +4.1.2. Generated OpenAPI Specification: +```yaml +components: + schemas: + Username: + minLength: 3 + maxLength: 20 + pattern: "^[a-zA-Z0-9_]+$" + type: string + Person: + type: object + properties: + username: + $ref: '#/components/schemas/Username' +``` + +4.2.1. Sample Ballerina Code (length): +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:String{ + length: 5 +} +public type ID string; + +type Employee record{ + ID id; +}; + +service / on new http:Listener(9090){ + resource function post newEmployee(Employee body) returns error?{ + return; + } +} +``` + +4.2.2. Generated OpenAPI Specification: +```yaml +components: + schemas: + ID: + minLength: 5 + maxLength: 5 + type: string + Employee: + type: object + properties: + id: + $ref: '#/components/schemas/ID' +``` + +**5. OpenAPI data type: array : (ballerina mapping): array** + + + +
+ +| Ballerina Mapping | Keywords | +|---------------------------------------------| -- | +| `@constraint:Array { minLength: }` | minItems | +| `@constraint:Array { maxLength: }` | maxItems | +| `@constraint:Array { length: }` | minItems: ``, maxItems: `` | + +
+ + + +5.1. Sample Ballerina Code: +```ballerina +import ballerina/http; +import ballerina/constraint; + +@constraint:String{ + maxLength: 23 +} +public type HobbyItemsString string; + +@constraint:Array{ + minLength: 2, + maxLength: 5 +} +public type Hobby HobbyItemsString[]; + +public type person record{ + Hobby hobby?; +}; + +service / on new http:Listener(9090){ + resource function post hobby(Person body) returns error?{ + return; + } +} +``` + +5.2. Generated OpenAPI Specification: +```yaml +components: + schemas: + HobbyItemsString: + maxLength: 23 + type: string + Hobby: + minLength: 2 + maxLength: 5 + type: array + items: + $ref: '#/components/schemas/HobbyItemsString' + Person: + type: object + properties: + hobby: + $ref: '#/components/schemas/Hobby' +``` + +## Testing +- **Unit Testing:** evaluate the individual components and core logic of the Ballerina OpenAPI tool, focusing on +functions, methods, and modules to ensure correctness and constraint handling. +- **Integration Testing:** assess the interactions and collaborations between various modules and components of the tool, +verifying the seamless integration of data validation constraints and rule generation into the OpenAPI specification.