mirror of
https://github.com/GitbookIO/gitbook.git
synced 2026-09-12 05:48:57 +00:00
Less strict OpenAPI parser (#3529)
Co-authored-by: Nolann Biron <biron.nolann@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"gitbook": patch
|
||||
---
|
||||
|
||||
Update OpenAPI parser
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@gitbook/openapi-parser": major
|
||||
---
|
||||
|
||||
Less strict OpenAPI parser, now returns { errors, filesystem }
|
||||
@@ -7,7 +7,7 @@ const spec = await Bun.file(new URL('./fixtures/multiline-spec.yaml', import.met
|
||||
|
||||
describe('#enrichFilesystem', () => {
|
||||
it('supports multiline descriptions', async () => {
|
||||
const filesystem = await parseOpenAPI({
|
||||
const { filesystem } = await parseOpenAPI({
|
||||
value: spec,
|
||||
rootURL: null,
|
||||
});
|
||||
|
||||
@@ -108,13 +108,7 @@ async function fetchFilesystemUncached(
|
||||
}
|
||||
|
||||
const text = await response.text();
|
||||
const filesystem = await parseOpenAPI({
|
||||
value: text,
|
||||
rootURL: url,
|
||||
// If we fetch the OpenAPI specification
|
||||
// it's the legacy system, it means the spec can be trusted here.
|
||||
trust: true,
|
||||
});
|
||||
const { filesystem } = await parseOpenAPI({ value: text, rootURL: url });
|
||||
const richFilesystem = await enrichFilesystem(filesystem);
|
||||
|
||||
return richFilesystem;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,830 @@
|
||||
openapi: 3.0.4
|
||||
info:
|
||||
title: Swagger Petstore - OpenAPI 3.0
|
||||
description: |-
|
||||
This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
|
||||
Swagger at [https://swagger.io](https://swagger.io). In the third iteration of the pet store, we've switched to the design first approach!
|
||||
You can now help us improve the API whether it's by making changes to the definition itself or to the code.
|
||||
That way, with time, we can improve the API in general, and expose some of the new features in OAS3.
|
||||
|
||||
Some useful links:
|
||||
- [The Pet Store repository](https://github.com/swagger-api/swagger-petstore)
|
||||
- [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml)
|
||||
termsOfService: https://swagger.io/terms/
|
||||
contact:
|
||||
email: apiteam@swagger.io
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: https://www.apache.org/licenses/LICENSE-2.0.html
|
||||
version: 1.0.26
|
||||
externalDocs:
|
||||
description: Find out more about Swagger
|
||||
url: https://swagger.io
|
||||
servers:
|
||||
- url: "/api/v3"
|
||||
tags:
|
||||
- name: pet
|
||||
description: Everything about your Pets
|
||||
externalDocs:
|
||||
description: Find out more
|
||||
url: https://swagger.io
|
||||
- name: store
|
||||
description: Access to Petstore orders
|
||||
externalDocs:
|
||||
description: Find out more about our store
|
||||
url: https://swagger.io
|
||||
- name: user
|
||||
description: Operations about user
|
||||
paths:
|
||||
"/pet":
|
||||
put:
|
||||
tags:
|
||||
- pet
|
||||
summary: Update an existing pet.
|
||||
description: Update an existing pet by Id.
|
||||
operationId: updatePet
|
||||
requestBody:
|
||||
description: Update an existent pet in the store
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Pet not found
|
||||
'422':
|
||||
description: Validation exception
|
||||
default:
|
||||
description: Unexpected error
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Add a new pet to the store.
|
||||
description: Add a new pet to the store.
|
||||
operationId: addPet
|
||||
requestBody:
|
||||
description: Create a new pet in the store
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
required: true
|
||||
responses:
|
||||
'200':
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
'400':
|
||||
description: Invalid input
|
||||
'422':
|
||||
description: Validation exception
|
||||
default:
|
||||
description: Unexpected error
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/pet/findByStatus":
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: Finds Pets by status.
|
||||
description: Multiple status values can be provided with comma separated strings.
|
||||
operationId: findPetsByStatus
|
||||
parameters:
|
||||
- name: status
|
||||
in: query
|
||||
description: Status values that need to be considered for filter
|
||||
required: false
|
||||
explode: true
|
||||
schema:
|
||||
type: string
|
||||
default: available
|
||||
enum:
|
||||
- available
|
||||
- pending
|
||||
- sold
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
'400':
|
||||
description: Invalid status value
|
||||
default:
|
||||
description: Unexpected error
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/pet/findByTags":
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: Finds Pets by tags.
|
||||
description: Multiple tags can be provided with comma separated strings. Use
|
||||
tag1, tag2, tag3 for testing.
|
||||
operationId: findPetsByTags
|
||||
parameters:
|
||||
- name: tags
|
||||
in: query
|
||||
description: Tags to filter by
|
||||
required: false
|
||||
explode: true
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
'400':
|
||||
description: Invalid tag value
|
||||
default:
|
||||
description: Unexpected error
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/pet/{petId}":
|
||||
get:
|
||||
tags:
|
||||
- pet
|
||||
summary: Find pet by ID.
|
||||
description: Returns a single pet.
|
||||
operationId: getPetById
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
description: ID of pet to return
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Pet not found
|
||||
default:
|
||||
description: Unexpected error
|
||||
security:
|
||||
- api_key: []
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Updates a pet in the store with form data.
|
||||
description: Updates a pet resource based on the form data.
|
||||
operationId: updatePetWithForm
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
description: ID of pet that needs to be updated
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
- name: name
|
||||
in: query
|
||||
description: Name of pet that needs to be updated
|
||||
schema:
|
||||
type: string
|
||||
- name: status
|
||||
in: query
|
||||
description: Status of pet that needs to be updated
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
'400':
|
||||
description: Invalid input
|
||||
default:
|
||||
description: Unexpected error
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
delete:
|
||||
tags:
|
||||
- pet
|
||||
summary: Deletes a pet.
|
||||
description: Delete a pet.
|
||||
operationId: deletePet
|
||||
parameters:
|
||||
- name: api_key
|
||||
in: header
|
||||
description: ''
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: petId
|
||||
in: path
|
||||
description: Pet id to delete
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'200':
|
||||
description: Pet deleted
|
||||
'400':
|
||||
description: Invalid pet value
|
||||
default:
|
||||
description: Unexpected error
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/pet/{petId}/uploadImage":
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Uploads an image.
|
||||
description: Upload image of the pet.
|
||||
operationId: uploadFile
|
||||
parameters:
|
||||
- name: petId
|
||||
in: path
|
||||
description: ID of pet to update
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
- name: additionalMetadata
|
||||
in: query
|
||||
description: Additional Metadata
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/ApiResponse"
|
||||
'400':
|
||||
description: No file uploaded
|
||||
'404':
|
||||
description: Pet not found
|
||||
default:
|
||||
description: Unexpected error
|
||||
security:
|
||||
- petstore_auth:
|
||||
- write:pets
|
||||
- read:pets
|
||||
"/store/inventory":
|
||||
get:
|
||||
tags:
|
||||
- store
|
||||
summary: Returns pet inventories by status.
|
||||
description: Returns a map of status codes to quantities.
|
||||
operationId: getInventory
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: integer
|
||||
format: int32
|
||||
default:
|
||||
description: Unexpected error
|
||||
security:
|
||||
- api_key: []
|
||||
"/store/order":
|
||||
post:
|
||||
tags:
|
||||
- store
|
||||
summary: Place an order for a pet.
|
||||
description: Place a new order in the store.
|
||||
operationId: placeOrder
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
'400':
|
||||
description: Invalid input
|
||||
'422':
|
||||
description: Validation exception
|
||||
default:
|
||||
description: Unexpected error
|
||||
"/store/order/{orderId}":
|
||||
get:
|
||||
tags:
|
||||
- store
|
||||
summary: Find purchase order by ID.
|
||||
description: For valid response try integer IDs with value <= 5 or > 10. Other
|
||||
values will generate exceptions.
|
||||
operationId: getOrderById
|
||||
parameters:
|
||||
- name: orderId
|
||||
in: path
|
||||
description: ID of order that needs to be fetched
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Order"
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Order not found
|
||||
default:
|
||||
description: Unexpected error
|
||||
delete:
|
||||
tags:
|
||||
- store
|
||||
summary: Delete purchase order by identifier.
|
||||
description: For valid response try integer IDs with value < 1000. Anything
|
||||
above 1000 or non-integers will generate API errors.
|
||||
operationId: deleteOrder
|
||||
parameters:
|
||||
- name: orderId
|
||||
in: path
|
||||
description: ID of the order that needs to be deleted
|
||||
required: true
|
||||
schema:
|
||||
type: integer
|
||||
format: int64
|
||||
responses:
|
||||
'200':
|
||||
description: order deleted
|
||||
'400':
|
||||
description: Invalid ID supplied
|
||||
'404':
|
||||
description: Order not found
|
||||
default:
|
||||
description: Unexpected error
|
||||
"/user":
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Create user.
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: createUser
|
||||
requestBody:
|
||||
description: Created user object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
default:
|
||||
description: Unexpected error
|
||||
"/user/createWithList":
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Creates list of users with given input array.
|
||||
description: Creates list of users with given input array.
|
||||
operationId: createUsersWithListInput
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/User"
|
||||
responses:
|
||||
'200':
|
||||
description: Successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
default:
|
||||
description: Unexpected error
|
||||
"/user/login":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Logs user into the system.
|
||||
description: Log into the system.
|
||||
operationId: loginUser
|
||||
parameters:
|
||||
- name: username
|
||||
in: query
|
||||
description: The user name for login
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- name: password
|
||||
in: query
|
||||
description: The password for login in clear text
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
headers:
|
||||
X-Rate-Limit:
|
||||
description: calls per hour allowed by the user
|
||||
schema:
|
||||
type: integer
|
||||
format: int32
|
||||
X-Expires-After:
|
||||
description: date in UTC when token expires
|
||||
schema:
|
||||
type: string
|
||||
format: date-time
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
type: string
|
||||
application/json:
|
||||
schema:
|
||||
type: string
|
||||
'400':
|
||||
description: Invalid username/password supplied
|
||||
default:
|
||||
description: Unexpected error
|
||||
"/user/logout":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Logs out current logged in user session.
|
||||
description: Log user out of the system.
|
||||
operationId: logoutUser
|
||||
parameters: []
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
default:
|
||||
description: Unexpected error
|
||||
"/user/{username}":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Get user by user name.
|
||||
description: Get user detail based on username.
|
||||
operationId: getUserByName
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: The name that needs to be fetched. Use user1 for testing
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
'400':
|
||||
description: Invalid username supplied
|
||||
'404':
|
||||
description: User not found
|
||||
default:
|
||||
description: Unexpected error
|
||||
put:
|
||||
tags:
|
||||
- user
|
||||
summary: Update user resource.
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: updateUser
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: name that need to be deleted
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
requestBody:
|
||||
description: Update an existent user in the store
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
application/x-www-form-urlencoded:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/User"
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
'400':
|
||||
description: bad request
|
||||
'404':
|
||||
description: user not found
|
||||
default:
|
||||
description: Unexpected error
|
||||
delete:
|
||||
tags:
|
||||
- user
|
||||
summary: Delete user resource.
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: deleteUser
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: The name that needs to be deleted
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: User deleted
|
||||
'400':
|
||||
description: Invalid username supplied
|
||||
'404':
|
||||
description: User not found
|
||||
default:
|
||||
description: Unexpected error
|
||||
components:
|
||||
schemas:
|
||||
Order:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
example: 10
|
||||
petId:
|
||||
type: integer
|
||||
format: int64
|
||||
example: 198772
|
||||
quantity:
|
||||
type: integer
|
||||
format: int32
|
||||
example: 7
|
||||
shipDate:
|
||||
type: string
|
||||
format: date-time
|
||||
status:
|
||||
type: string
|
||||
description: Order Status
|
||||
example: approved
|
||||
enum:
|
||||
- placed
|
||||
- approved
|
||||
- delivered
|
||||
complete:
|
||||
type: boolean
|
||||
xml:
|
||||
name: order
|
||||
Category:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
example: 1
|
||||
name:
|
||||
type: string
|
||||
example: Dogs
|
||||
xml:
|
||||
name: category
|
||||
User:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
example: 10
|
||||
username:
|
||||
type: string
|
||||
example: theUser
|
||||
firstName:
|
||||
type: string
|
||||
example: John
|
||||
lastName:
|
||||
type: string
|
||||
example: James
|
||||
email:
|
||||
type: string
|
||||
example: john@email.com
|
||||
password:
|
||||
type: string
|
||||
example: '12345'
|
||||
phone:
|
||||
type: string
|
||||
example: '12345'
|
||||
userStatus:
|
||||
type: integer
|
||||
description: User Status
|
||||
format: int32
|
||||
example: 1
|
||||
xml:
|
||||
name: user
|
||||
Tag:
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
xml:
|
||||
name: tag
|
||||
Pet:
|
||||
required:
|
||||
- name
|
||||
- photoUrls
|
||||
type: object
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
example: 10
|
||||
name:
|
||||
type: string
|
||||
example: doggie
|
||||
category:
|
||||
"$ref": "#/components/schemas/Category"
|
||||
photoUrls:
|
||||
type: array
|
||||
xml:
|
||||
wrapped: true
|
||||
items:
|
||||
type: string
|
||||
xml:
|
||||
name: photoUrl
|
||||
tags:
|
||||
type: array
|
||||
xml:
|
||||
wrapped: true
|
||||
items:
|
||||
"$ref": "#/components/schemas/Tag"
|
||||
status:
|
||||
type: string
|
||||
description: pet status in the store
|
||||
enum:
|
||||
- available
|
||||
- pending
|
||||
- sold
|
||||
xml:
|
||||
name: pet
|
||||
ApiResponse:
|
||||
type: object
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
format: int32
|
||||
type:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
xml:
|
||||
name: "##default"
|
||||
requestBodies:
|
||||
Pet:
|
||||
description: Pet object that needs to be added to the store
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
"$ref": "#/components/schemas/Pet"
|
||||
UserArray:
|
||||
description: List of user object
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
"$ref": "#/components/schemas/User"
|
||||
securitySchemes:
|
||||
petstore_auth:
|
||||
type: oauth2
|
||||
flows:
|
||||
implicit:
|
||||
authorizationUrl: https://petstore3.swagger.io/oauth/authorize
|
||||
scopes:
|
||||
write:pets: modify pets in your account
|
||||
read:pets: read your pets
|
||||
api_key:
|
||||
type: apiKey
|
||||
name: api_key
|
||||
in: header
|
||||
@@ -4,26 +4,30 @@ import { parseOpenAPI } from './parse';
|
||||
|
||||
const spec = await Bun.file(new URL('./fixtures/recursive-spec.json', import.meta.url)).text();
|
||||
const specV2 = await Bun.file(new URL('./fixtures/spec-v2.json', import.meta.url)).text();
|
||||
const petstoreyaml = await Bun.file(new URL('./fixtures/petstore.yaml', import.meta.url)).text();
|
||||
const petstoreInvalid = await Bun.file(
|
||||
new URL('./fixtures/petstore-invalid.json', import.meta.url)
|
||||
).text();
|
||||
|
||||
const html = `<!DOCTYPE html><html lang=\"en\"><head> <meta charset=\"utf-8\"> <title>API Documentation</title> <base href=\"/api-docs/\"> <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"> <link rel=\"icon\" type=\"image/x-icon\" href=\"favicon.ico\"> <link rel=\"stylesheet\" href=\"https://use.fontawesome.com/releases/v5.7.0/css/all.css\" integrity=\"sha384-lZN37f5QGtY3VHgisS14W3ExzMWZxybE1SJSEsQp9S+oqd12jhcu+A56Ebc1zFSJ\" crossorigin=\"anonymous\"> <style>@import\"https://fonts.googleapis.com/css2?family=Nunito:wght@200&display=swap\";@charset \"UTF-8\";:root{--bs-blue:#0d6efd;--bs-indigo:#6610f2;--bs-purple:#6f42c1;--bs-pink:#d63384;--bs-red:#dc3545;--bs-orange:#fd7e14;--bs-yellow:#ffc107;--bs-green:#198754;--bs-teal:#20c997;--bs-cyan:#0dcaf0;--bs-black:#000;--bs-white:#fff;--bs-gray:#6c757d;--bs-gray-dark:#343a40;--bs-gray-100:#f8f9fa;--bs-gray-200:#e9ecef;--bs-gray-300:#dee2e6;--bs-gray-400:#ced4da;--bs-gray-500:#adb5bd;--bs-gray-600:#6c757d;--bs-gray-700:#495057;--bs-gray-800:#343a40;--bs-gray-900:#212529;--bs-primary:#0d6efd;--bs-secondary:#6c757d;--bs-success:#198754;--bs-info:#0dcaf0;--bs-warning:#ffc107;--bs-danger:#dc3545;--bs-light:#f8f9fa;--bs-dark:#212529;--bs-primary-rgb:13,110,253;--bs-secondary-rgb:108,117,125;--bs-success-rgb:25,135,84;--bs-info-rgb:13,202,240;--bs-warning-rgb:255,193,7;--bs-danger-rgb:220,53,69;--bs-light-rgb:248,249,250;--bs-dark-rgb:33,37,41;--bs-white-rgb:255,255,255;--bs-black-rgb:0,0,0;--bs-body-color-rgb:33,37,41;--bs-body-bg-rgb:255,255,255;--bs-font-sans-serif:system-ui,-apple-system,\"Segoe UI\",Roboto,\"Helvetica Neue\",\"Noto Sans\",\"Liberation Sans\",Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\",\"Segoe UI Symbol\",\"Noto Color Emoji\";--bs-font-monospace:SFMono-Regular,Menlo,Monaco,Consolas,\"Liberation Mono\",\"Courier New\",monospace;--bs-gradient:linear-gradient(180deg, rgba(255, 255, 255, .15), rgba(255, 255, 255, 0));--bs-body-font-family:var(--bs-font-sans-serif);--bs-body-font-size:1rem;--bs-body-font-weight:400;--bs-body-line-height:1.5;--bs-body-color:#212529;--bs-body-bg:#fff;--bs-border-width:1px;--bs-border-style:solid;--bs-border-color:#dee2e6;--bs-border-color-translucent:rgba(0, 0, 0, .175);--bs-border-radius:.375rem;--bs-border-radius-sm:.25rem;--bs-border-radius-lg:.5rem;--bs-border-radius-xl:1rem;--bs-border-radius-2xl:2rem;--bs-border-radius-pill:50rem;--bs-link-color:#0d6efd;--bs-link-hover-color:#0a58ca;--bs-code-color:#d63384;--bs-highlight-bg:#fff3cd}*,:after,:before{box-sizing:border-box}@media (prefers-reduced-motion:no-preference){:root{scroll-behavior:smooth}}body{margin:0;font-family:var(--bs-body-font-family);font-size:var(--bs-body-font-size);font-weight:var(--bs-body-font-weight);line-height:var(--bs-body-line-height);color:var(--bs-body-color);text-align:var(--bs-body-text-align);background-color:var(--bs-body-bg);-webkit-text-size-adjust:100%;-webkit-tap-highlight-color:transparent}:root{--primaryColor1:#373e3e;--primaryColor2:#373e3e;--white:#ffffff;--lightGrey:#F8F9F9;--grey:#F8F9F9;--darkGrey:#6c757d;--black:black}*{box-sizing:border-box}body{font-family:Nunito,sans-serif;background-color:var(--grey);margin:5px auto}</style><link rel=\"stylesheet\" href=\"styles.css\" media=\"print\" onload=\"this.media='all'\"><noscript><link rel=\"stylesheet\" href=\"styles.css\"></noscript></head> <body> <app-root></app-root> <script> parent.postMessage(location.hash, location.origin); </script> <script src=\"runtime.js\" type=\"module\"></script><script src=\"polyfills.js\" type=\"module\"></script><script src=\"scripts.js\" defer></script><script src=\"main.js\" type=\"module\"></script> <script type=\"text/javascript\" src=\"/_Incapsula_Resource?SWJIYLWA=719d34d31c8e3a6e6fffd425f7e032f3&ns=2&cb=242625292\" async></script></body></html>`;
|
||||
|
||||
describe('#parseOpenAPI', () => {
|
||||
it('parses a recursive OpenAPI document', async () => {
|
||||
const schema = await parseOpenAPI({
|
||||
const result = await parseOpenAPI({
|
||||
value: spec,
|
||||
rootURL: null,
|
||||
});
|
||||
// Ensure the structure returned is not recursive (not dereferenced).
|
||||
JSON.stringify(schema);
|
||||
JSON.stringify(result.filesystem);
|
||||
});
|
||||
|
||||
it('parses a swagger v2', async () => {
|
||||
const schema = await parseOpenAPI({
|
||||
const result = await parseOpenAPI({
|
||||
value: specV2,
|
||||
rootURL: null,
|
||||
});
|
||||
// Ensure the structure returned is not recursive (not dereferenced).
|
||||
JSON.stringify(schema);
|
||||
JSON.stringify(result.filesystem);
|
||||
});
|
||||
|
||||
it('throws an error for invalid OpenAPI document', async () => {
|
||||
@@ -39,4 +43,23 @@ describe('#parseOpenAPI', () => {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('allows a document yaml', async () => {
|
||||
const result = await parseOpenAPI({
|
||||
value: petstoreyaml,
|
||||
rootURL: null,
|
||||
});
|
||||
// Ensure the structure returned is not recursive (not dereferenced).
|
||||
JSON.stringify(result.filesystem);
|
||||
});
|
||||
|
||||
it('allows a document with errors', async () => {
|
||||
const result = await parseOpenAPI({
|
||||
value: petstoreInvalid,
|
||||
rootURL: null,
|
||||
});
|
||||
// Ensure the structure returned is not recursive (not dereferenced).
|
||||
JSON.stringify(result.filesystem);
|
||||
expect(result.errors).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { AnyObject, bundle } from '@scalar/openapi-parser';
|
||||
import type { AnyObject, ErrorObject, bundle } from '@scalar/openapi-parser';
|
||||
import { OpenAPIParseError } from './error';
|
||||
import type { Filesystem, OpenAPIV3xDocument } from './types';
|
||||
import { convertOpenAPIV2ToOpenAPIV3 } from './v2';
|
||||
import { parseOpenAPIV3 } from './v3';
|
||||
|
||||
@@ -14,10 +15,6 @@ export interface ParseOpenAPIInput {
|
||||
* The root URL of the specified OpenAPI document.
|
||||
*/
|
||||
rootURL: string | null;
|
||||
/**
|
||||
* Trust the input. This will skip advanced validation.
|
||||
*/
|
||||
trust?: boolean;
|
||||
/**
|
||||
* Options for the parser.
|
||||
*/
|
||||
@@ -26,6 +23,18 @@ export interface ParseOpenAPIInput {
|
||||
};
|
||||
}
|
||||
|
||||
export interface ParseOpenAPIResult {
|
||||
/**
|
||||
* Informational errors that were found while parsing the OpenAPI document.
|
||||
*/
|
||||
errors: ErrorObject[];
|
||||
|
||||
/**
|
||||
* The parsed OpenAPI document.
|
||||
*/
|
||||
filesystem: Filesystem<OpenAPIV3xDocument>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse a raw string into an OpenAPI document.
|
||||
* It will also convert Swagger 2.0 to OpenAPI 3.0.
|
||||
|
||||
@@ -5,12 +5,12 @@ const specV2 = await Bun.file(new URL('./fixtures/spec-v2.json', import.meta.url
|
||||
|
||||
describe('#convertOpenAPIV2ToOpenAPIV3', () => {
|
||||
it('converts an OpenAPIV2 in V3', async () => {
|
||||
const schema = await convertOpenAPIV2ToOpenAPIV3({
|
||||
const result = await convertOpenAPIV2ToOpenAPIV3({
|
||||
value: specV2,
|
||||
rootURL: null,
|
||||
});
|
||||
// Ensure the structure returned is not recursive (not dereferenced).
|
||||
JSON.stringify(schema);
|
||||
expect(schema[0]?.specification.openapi).toBe('3.1.1');
|
||||
JSON.stringify(result.filesystem);
|
||||
expect(result.filesystem[0]?.specification.openapi).toBe('3.1.1');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { upgrade } from '@scalar/openapi-parser';
|
||||
import { OpenAPIParseError } from './error';
|
||||
import type { ParseOpenAPIInput } from './parse';
|
||||
import type { Filesystem, OpenAPIV3xDocument } from './types';
|
||||
import type { ParseOpenAPIInput, ParseOpenAPIResult } from './parse';
|
||||
import { parseOpenAPIV3 } from './v3';
|
||||
|
||||
/**
|
||||
@@ -9,24 +8,28 @@ import { parseOpenAPIV3 } from './v3';
|
||||
*/
|
||||
export async function convertOpenAPIV2ToOpenAPIV3(
|
||||
input: ParseOpenAPIInput
|
||||
): Promise<Filesystem<OpenAPIV3xDocument>> {
|
||||
const { value, rootURL } = input;
|
||||
|
||||
const upgradeResult = (() => {
|
||||
try {
|
||||
return upgrade(value);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new OpenAPIParseError('Failed to convert Swagger 2.0 to OpenAPI 3.1.1', {
|
||||
code: 'v2-conversion',
|
||||
rootURL,
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
})();
|
||||
|
||||
return parseOpenAPIV3({ ...input, rootURL, value: upgradeResult.specification, trust: true });
|
||||
): Promise<ParseOpenAPIResult> {
|
||||
const result = upgradeFromInput(input);
|
||||
return parseOpenAPIV3({ ...input, rootURL: input.rootURL, value: result.specification });
|
||||
}
|
||||
|
||||
/**
|
||||
* Upgrade a Swagger 2.0 schema to an OpenAPI 3.0 schema.
|
||||
* This function will throw an error if the conversion fails.
|
||||
*/
|
||||
function upgradeFromInput(input: ParseOpenAPIInput) {
|
||||
const { value, rootURL } = input;
|
||||
try {
|
||||
return upgrade(value);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
throw new OpenAPIParseError('Failed to convert Swagger 2.0 to OpenAPI 3.1.1', {
|
||||
code: 'v2-conversion',
|
||||
rootURL,
|
||||
cause: error,
|
||||
});
|
||||
}
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,38 +1,15 @@
|
||||
import { validate } from '@scalar/openapi-parser';
|
||||
import { OpenAPIParseError } from './error';
|
||||
import { createFileSystem } from './filesystem';
|
||||
import type { ParseOpenAPIInput } from './parse';
|
||||
import type { Filesystem, OpenAPIV3xDocument } from './types';
|
||||
import type { ParseOpenAPIInput, ParseOpenAPIResult } from './parse';
|
||||
|
||||
/**
|
||||
* Parse a raw string into an OpenAPI document.
|
||||
* It will also convert Swagger 2.0 to OpenAPI 3.0.
|
||||
* It can throw an `OpenAPIFetchError` if the document is invalid.
|
||||
*/
|
||||
export async function parseOpenAPIV3(
|
||||
input: ParseOpenAPIInput
|
||||
): Promise<Filesystem<OpenAPIV3xDocument>> {
|
||||
const { value, rootURL, trust, options = {} } = input;
|
||||
const specification = trust
|
||||
? await trustedValidate({ value, rootURL })
|
||||
: await untrustedValidate({ value, rootURL });
|
||||
|
||||
const filesystem = await createFileSystem({
|
||||
value: specification,
|
||||
rootURL,
|
||||
options,
|
||||
});
|
||||
|
||||
return filesystem;
|
||||
}
|
||||
|
||||
type ValidateOpenAPIV3Input = Pick<ParseOpenAPIInput, 'value' | 'rootURL'>;
|
||||
|
||||
/**
|
||||
* Validate an untrusted OpenAPI v3 document.
|
||||
*/
|
||||
async function untrustedValidate(input: ValidateOpenAPIV3Input) {
|
||||
const { value, rootURL } = input;
|
||||
export async function parseOpenAPIV3(input: ParseOpenAPIInput): Promise<ParseOpenAPIResult> {
|
||||
const { value, rootURL, options = {} } = input;
|
||||
const result = await validate(value);
|
||||
|
||||
if (result.version === '2.0') {
|
||||
@@ -42,8 +19,9 @@ async function untrustedValidate(input: ValidateOpenAPIV3Input) {
|
||||
});
|
||||
}
|
||||
|
||||
// Spec is invalid, we stop here.
|
||||
if (!result.valid) {
|
||||
// We don't rely on `result.invalid` because it's too strict.
|
||||
// If we succeed in parsing a schema, then we consider it valid.
|
||||
if (!result.specification || !result.version) {
|
||||
throw new OpenAPIParseError('Invalid OpenAPI document', {
|
||||
code: 'invalid',
|
||||
rootURL,
|
||||
@@ -51,34 +29,11 @@ async function untrustedValidate(input: ValidateOpenAPIV3Input) {
|
||||
});
|
||||
}
|
||||
|
||||
return result.specification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate a trusted OpenAPI v3 document.
|
||||
* It assumes the specification is already a valid specification.
|
||||
* It's faster than `untrustedValidate`.
|
||||
*/
|
||||
async function trustedValidate(input: ValidateOpenAPIV3Input) {
|
||||
const { value, rootURL } = input;
|
||||
const result = (() => {
|
||||
if (typeof value === 'string') {
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
} catch (_error) {
|
||||
/** In case of an invalid JSON, we fallback to untrusted validation. */
|
||||
return untrustedValidate(input);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
})();
|
||||
|
||||
if ('swagger' in result && result.swagger) {
|
||||
throw new OpenAPIParseError('Only OpenAPI v3 is supported', {
|
||||
code: 'parse-v2-in-v3',
|
||||
rootURL,
|
||||
});
|
||||
}
|
||||
|
||||
return result;
|
||||
const filesystem = await createFileSystem({
|
||||
value: result.specification,
|
||||
rootURL,
|
||||
options,
|
||||
});
|
||||
|
||||
return { filesystem, errors: result.errors ?? [] };
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { resolveOpenAPIOperation } from './resolveOpenAPIOperation';
|
||||
async function fetchFilesystem(url: string) {
|
||||
const response = await fetch(url);
|
||||
const text = await response.text();
|
||||
const filesystem = await parseOpenAPI({ value: text, rootURL: url });
|
||||
const { filesystem } = await parseOpenAPI({ value: text, rootURL: url });
|
||||
const transformedFs = await traverse(filesystem, async (node) => {
|
||||
if ('description' in node && typeof node.description === 'string' && node.description) {
|
||||
node['x-gitbook-description-html'] = node.description;
|
||||
|
||||
@@ -6,7 +6,7 @@ import { resolveOpenAPISchemas } from './resolveOpenAPISchemas';
|
||||
async function fetchFilesystem(url: string) {
|
||||
const response = await fetch(url);
|
||||
const text = await response.text();
|
||||
const filesystem = await parseOpenAPI({ value: text, rootURL: url });
|
||||
const { filesystem } = await parseOpenAPI({ value: text, rootURL: url });
|
||||
const transformedFs = await traverse(filesystem, async (node) => {
|
||||
if ('description' in node && typeof node.description === 'string' && node.description) {
|
||||
node['x-gitbook-description-html'] = node.description;
|
||||
|
||||
Reference in New Issue
Block a user