From 9cab13c0f31ffa364dead6e7adc7b8981b1cc3d8 Mon Sep 17 00:00:00 2001 From: Yusuke Tsutsumi Date: Wed, 4 Dec 2024 22:16:06 -0800 Subject: [PATCH] fix: updates for oas linter fixes to the generated OAS specification to be compliant with AEP guidance. --- .github/workflows/test.yml | 6 +- example/bookstore/v1/bookstore_openapi.json | 252 +++++++++++++------- example/bookstore/v1/bookstore_openapi.yaml | 212 +++++++++------- go.mod | 2 +- go.sum | 4 + scripts/run-oas-linter.sh | 8 + spectral.yaml | 6 + 7 files changed, 316 insertions(+), 174 deletions(-) create mode 100755 scripts/run-oas-linter.sh create mode 100644 spectral.yaml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b6c2805..43ae956 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -28,6 +28,10 @@ jobs: run: | ./scripts/test_http_api.sh - # - name: run api-linter + - name: run OAS api-linter + run: | + ./scripts/run-oas-linter.sh + + # - name: run protobuf api-linter # run: | # ./scripts/run-api-linter.sh diff --git a/example/bookstore/v1/bookstore_openapi.json b/example/bookstore/v1/bookstore_openapi.json index 153b780..7ac3351 100644 --- a/example/bookstore/v1/bookstore_openapi.json +++ b/example/bookstore/v1/bookstore_openapi.json @@ -7,15 +7,15 @@ ], "info": { "title": "bookstore.example.com", - "description": "", + "description": "An API for bookstore.example.com", "version": "version not set" }, "paths": { "/isbns": { "post": { "summary": "", - "description": "", - "operationId": "", + "description": "Create method for isbn", + "operationId": "isbn.create", "parameters": [], "responses": { "200": { @@ -45,22 +45,26 @@ "/publishers": { "get": { "summary": "", - "description": "", - "operationId": "", + "description": "List method for publisher", + "operationId": "publisher.list", "parameters": [ { "name": "max_page_size", "in": "query", "description": "", - "required": true, - "type": "integer" + "required": false, + "schema": { + "type": "integer" + } }, { "name": "page_token", "in": "query", "description": "", - "required": true, - "type": "string" + "required": false, + "schema": { + "type": "string" + } } ], "responses": { @@ -86,8 +90,8 @@ }, "post": { "summary": "", - "description": "", - "operationId": "", + "description": "Create method for publisher", + "operationId": "publisher.create", "parameters": [], "responses": { "200": { @@ -117,15 +121,17 @@ "/publishers/{publisher}": { "get": { "summary": "", - "description": "", - "operationId": "", + "description": "Get method for publisher", + "operationId": "publisher.get", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { @@ -143,15 +149,17 @@ }, "patch": { "summary": "", - "description": "", - "operationId": "", + "description": "Update method for publisher", + "operationId": "publisher.update", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { @@ -180,15 +188,17 @@ }, "put": { "summary": "", - "description": "", - "operationId": "", + "description": "Apply method for publisher", + "operationId": "publisher.apply", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { @@ -217,28 +227,36 @@ }, "delete": { "summary": "", - "description": "", - "operationId": "", + "description": "Delete method for publisher", + "operationId": "publisher.delete", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "force", "in": "query", "description": "", "required": false, - "type": "boolean" + "schema": { + "type": "boolean" + } } ], "responses": { - "200": { - "description": "", - "content": null + "204": { + "description": "Successful response", + "content": { + "application/json": { + "schema": {} + } + } } } } @@ -246,29 +264,35 @@ "/publishers/{publisher}/books": { "get": { "summary": "", - "description": "", - "operationId": "", + "description": "List method for book", + "operationId": "book.list", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "max_page_size", "in": "query", "description": "", - "required": true, - "type": "integer" + "required": false, + "schema": { + "type": "integer" + } }, { "name": "page_token", "in": "query", "description": "", - "required": true, - "type": "string" + "required": false, + "schema": { + "type": "string" + } } ], "responses": { @@ -300,15 +324,17 @@ }, "post": { "summary": "", - "description": "", - "operationId": "", + "description": "Create method for book", + "operationId": "book.create", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { @@ -339,22 +365,26 @@ "/publishers/{publisher}/books/{book}": { "get": { "summary": "", - "description": "", - "operationId": "", + "description": "Get method for book", + "operationId": "book.get", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { @@ -372,22 +402,26 @@ }, "patch": { "summary": "", - "description": "", - "operationId": "", + "description": "Update method for book", + "operationId": "book.update", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { @@ -416,22 +450,26 @@ }, "put": { "summary": "", - "description": "", - "operationId": "", + "description": "Apply method for book", + "operationId": "book.apply", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { @@ -460,35 +498,45 @@ }, "delete": { "summary": "", - "description": "", - "operationId": "", + "description": "Delete method for book", + "operationId": "book.delete", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "force", "in": "query", "description": "", "required": false, - "type": "boolean" + "schema": { + "type": "boolean" + } } ], "responses": { - "200": { - "description": "", - "content": null + "204": { + "description": "Successful response", + "content": { + "application/json": { + "schema": {} + } + } } } } @@ -496,36 +544,44 @@ "/publishers/{publisher}/books/{book}/editions": { "get": { "summary": "", - "description": "", - "operationId": "", + "description": "List method for book-edition", + "operationId": "book-edition.list", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "max_page_size", "in": "query", "description": "", - "required": true, - "type": "integer" + "required": false, + "schema": { + "type": "integer" + } }, { "name": "page_token", "in": "query", "description": "", - "required": true, - "type": "string" + "required": false, + "schema": { + "type": "string" + } } ], "responses": { @@ -551,22 +607,26 @@ }, "post": { "summary": "", - "description": "", - "operationId": "", + "description": "Create method for book-edition", + "operationId": "book-edition.create", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { @@ -597,29 +657,35 @@ "/publishers/{publisher}/books/{book}/editions/{book-edition}": { "get": { "summary": "", - "description": "", - "operationId": "", + "description": "Get method for book-edition", + "operationId": "book-edition.get", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book-edition", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { @@ -637,35 +703,45 @@ }, "delete": { "summary": "", - "description": "", - "operationId": "", + "description": "Delete method for book-edition", + "operationId": "book-edition.delete", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book-edition", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { - "200": { - "description": "", - "content": null + "204": { + "description": "Successful response", + "content": { + "application/json": { + "schema": {} + } + } } } } @@ -673,22 +749,26 @@ "/publishers/{publisher}/books/{book}:archive": { "post": { "summary": "", - "description": "", - "operationId": "", + "description": "Custom method archive for book", + "operationId": "book:archive", "parameters": [ { "name": "publisher", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } }, { "name": "book", "in": "path", "description": "", "required": true, - "type": "string" + "schema": { + "type": "string" + } } ], "responses": { diff --git a/example/bookstore/v1/bookstore_openapi.yaml b/example/bookstore/v1/bookstore_openapi.yaml index 24908c9..8e489df 100644 --- a/example/bookstore/v1/bookstore_openapi.yaml +++ b/example/bookstore/v1/bookstore_openapi.yaml @@ -98,15 +98,15 @@ components: plural: publishers singular: publisher info: - description: "" + description: An API for bookstore.example.com title: bookstore.example.com version: version not set openapi: 3.1.0 paths: /isbns: post: - description: "" - operationId: "" + description: Create method for isbn + operationId: isbn.create parameters: [] requestBody: content: @@ -125,19 +125,21 @@ paths: summary: "" /publishers: get: - description: "" - operationId: "" + description: List method for publisher + operationId: publisher.list parameters: - description: "" in: query name: max_page_size - required: true - type: integer + required: false + schema: + type: integer - description: "" in: query name: page_token - required: true - type: string + required: false + schema: + type: string responses: "200": content: @@ -152,8 +154,8 @@ paths: description: Successful response summary: "" post: - description: "" - operationId: "" + description: Create method for publisher + operationId: publisher.create parameters: [] requestBody: content: @@ -172,33 +174,38 @@ paths: summary: "" /publishers/{publisher}: delete: - description: "" - operationId: "" + description: Delete method for publisher + operationId: publisher.delete parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: query name: force required: false - type: boolean + schema: + type: boolean responses: - "200": - content: null - description: "" + "204": + content: + application/json: + schema: {} + description: Successful response summary: "" get: - description: "" - operationId: "" + description: Get method for publisher + operationId: publisher.get parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string responses: "200": content: @@ -208,14 +215,15 @@ paths: description: Successful response summary: "" patch: - description: "" - operationId: "" + description: Update method for publisher + operationId: publisher.update parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string requestBody: content: application/json: @@ -232,14 +240,15 @@ paths: description: Successful response summary: "" put: - description: "" - operationId: "" + description: Apply method for publisher + operationId: publisher.apply parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string requestBody: content: application/json: @@ -257,24 +266,27 @@ paths: summary: "" /publishers/{publisher}/books: get: - description: "" - operationId: "" + description: List method for book + operationId: book.list parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: query name: max_page_size - required: true - type: integer + required: false + schema: + type: integer - description: "" in: query name: page_token - required: true - type: string + required: false + schema: + type: string responses: "200": content: @@ -293,14 +305,15 @@ paths: description: Successful response summary: "" post: - description: "" - operationId: "" + description: Create method for book + operationId: book.create parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string requestBody: content: application/json: @@ -318,43 +331,50 @@ paths: summary: "" /publishers/{publisher}/books/{book}: delete: - description: "" - operationId: "" + description: Delete method for book + operationId: book.delete parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: path name: book required: true - type: string + schema: + type: string - description: "" in: query name: force required: false - type: boolean + schema: + type: boolean responses: - "200": - content: null - description: "" + "204": + content: + application/json: + schema: {} + description: Successful response summary: "" get: - description: "" - operationId: "" + description: Get method for book + operationId: book.get parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: path name: book required: true - type: string + schema: + type: string responses: "200": content: @@ -364,19 +384,21 @@ paths: description: Successful response summary: "" patch: - description: "" - operationId: "" + description: Update method for book + operationId: book.update parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: path name: book required: true - type: string + schema: + type: string requestBody: content: application/json: @@ -393,19 +415,21 @@ paths: description: Successful response summary: "" put: - description: "" - operationId: "" + description: Apply method for book + operationId: book.apply parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: path name: book required: true - type: string + schema: + type: string requestBody: content: application/json: @@ -423,29 +447,33 @@ paths: summary: "" /publishers/{publisher}/books/{book}/editions: get: - description: "" - operationId: "" + description: List method for book-edition + operationId: book-edition.list parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: path name: book required: true - type: string + schema: + type: string - description: "" in: query name: max_page_size - required: true - type: integer + required: false + schema: + type: integer - description: "" in: query name: page_token - required: true - type: string + required: false + schema: + type: string responses: "200": content: @@ -460,19 +488,21 @@ paths: description: Successful response summary: "" post: - description: "" - operationId: "" + description: Create method for book-edition + operationId: book-edition.create parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: path name: book required: true - type: string + schema: + type: string requestBody: content: application/json: @@ -490,48 +520,56 @@ paths: summary: "" /publishers/{publisher}/books/{book}/editions/{book-edition}: delete: - description: "" - operationId: "" + description: Delete method for book-edition + operationId: book-edition.delete parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: path name: book required: true - type: string + schema: + type: string - description: "" in: path name: book-edition required: true - type: string + schema: + type: string responses: - "200": - content: null - description: "" + "204": + content: + application/json: + schema: {} + description: Successful response summary: "" get: - description: "" - operationId: "" + description: Get method for book-edition + operationId: book-edition.get parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: path name: book required: true - type: string + schema: + type: string - description: "" in: path name: book-edition required: true - type: string + schema: + type: string responses: "200": content: @@ -542,19 +580,21 @@ paths: summary: "" /publishers/{publisher}/books/{book}:archive: post: - description: "" - operationId: "" + description: Custom method archive for book + operationId: book:archive parameters: - description: "" in: path name: publisher required: true - type: string + schema: + type: string - description: "" in: path name: book required: true - type: string + schema: + type: string requestBody: content: application/json: diff --git a/go.mod b/go.mod index 95e9baf..38a9821 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/ProtonMail/go-crypto v1.1.0-alpha.2 // indirect github.com/PuerkitoBio/purell v1.1.0 // indirect github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect - github.com/aep-dev/aep-lib-go v0.0.0-20241204054628-0a7e1306ab81 // indirect + github.com/aep-dev/aep-lib-go v0.0.0-20241205062045-39517ccc141d // indirect github.com/aep-dev/terraform-provider-aep v0.0.0-20241112052633-f48d45460768 // indirect github.com/agext/levenshtein v1.2.2 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect diff --git a/go.sum b/go.sum index 5c37212..1de19a5 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,10 @@ github.com/aep-dev/aep-lib-go v0.0.0-20241204054210-2ad17c016174 h1:kUenvKVfyPNI github.com/aep-dev/aep-lib-go v0.0.0-20241204054210-2ad17c016174/go.mod h1:M+h1D6T2uIUPelmaEsJbjR6JhqKsTlPX3lxp25zQQsk= github.com/aep-dev/aep-lib-go v0.0.0-20241204054628-0a7e1306ab81 h1:jgj7s9Y17wbLZO9gub6+0Fjfwr+Fj4cGNTq8c+zg++I= github.com/aep-dev/aep-lib-go v0.0.0-20241204054628-0a7e1306ab81/go.mod h1:M+h1D6T2uIUPelmaEsJbjR6JhqKsTlPX3lxp25zQQsk= +github.com/aep-dev/aep-lib-go v0.0.0-20241205061430-404ce902bcf0 h1:wYRECZA+YtigZMEafB6UJRxcJKVy0/j1pKEya5SGldM= +github.com/aep-dev/aep-lib-go v0.0.0-20241205061430-404ce902bcf0/go.mod h1:M+h1D6T2uIUPelmaEsJbjR6JhqKsTlPX3lxp25zQQsk= +github.com/aep-dev/aep-lib-go v0.0.0-20241205062045-39517ccc141d h1:oM4zhowSgjTjdPG+qlJSv1XHE9ahwwesNsRt/UOy6kg= +github.com/aep-dev/aep-lib-go v0.0.0-20241205062045-39517ccc141d/go.mod h1:M+h1D6T2uIUPelmaEsJbjR6JhqKsTlPX3lxp25zQQsk= github.com/aep-dev/terraform-provider-aep v0.0.0-20241112052633-f48d45460768 h1:b5fRfpIIsOsdsT2N1MsBxr0K/fZacCUlWp0uY9/BJzM= github.com/aep-dev/terraform-provider-aep v0.0.0-20241112052633-f48d45460768/go.mod h1:sUuUJSkWTc4GBxp8GEZXCeEI38VMyuM5msPQ9BG0kMA= github.com/agext/levenshtein v1.2.2 h1:0S/Yg6LYmFJ5stwQeRp6EeOcCbj7xiqQSdNelsXvaqE= diff --git a/scripts/run-oas-linter.sh b/scripts/run-oas-linter.sh new file mode 100755 index 0000000..cd0864b --- /dev/null +++ b/scripts/run-oas-linter.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -e +if [ ! -f /tmp/spectral ]; then + curl "https://github.com/stoplightio/spectral/releases/download/v6.14.2/spectral-linux-x64" -o /tmp/spectral + chmod +x /tmp/spectral +fi + +/tmp/spectral lint --ruleset "./spectral.yaml" "./example/bookstore/v1/bookstore_openapi.yaml" \ No newline at end of file diff --git a/spectral.yaml b/spectral.yaml new file mode 100644 index 0000000..228ef12 --- /dev/null +++ b/spectral.yaml @@ -0,0 +1,6 @@ +extends: + - https://raw.githubusercontent.com/aep-dev/aep-openapi-linter/refs/heads/js-based-linter/spectral.yaml +rules: + # disabling for now, as required path parameters (which are acceptable) + # will violate this rule. + aep-132-request-required-params: off