snapshot of https://github.com/mountain-loop/yaak.git (main, 2026-09-23; blobs > 131072B omitted)
This commit is contained in:
16
plugins/importer-openapi/package.json
Normal file
16
plugins/importer-openapi/package.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"name": "@yaak/importer-openapi",
|
||||
"displayName": "OpenAPI Importer",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "Import API specifications from OpenAPI/Swagger format",
|
||||
"scripts": {
|
||||
"build": "yaakcli build",
|
||||
"dev": "yaakcli dev",
|
||||
"test": "vp test --run tests",
|
||||
"test:roundtrip": "node tests/roundtrip.mjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"yaml": "^2.8.3"
|
||||
}
|
||||
}
|
||||
2268
plugins/importer-openapi/src/index.ts
Normal file
2268
plugins/importer-openapi/src/index.ts
Normal file
File diff suppressed because it is too large
Load Diff
2947
plugins/importer-openapi/tests/__snapshots__/index.test.ts.snap
Normal file
2947
plugins/importer-openapi/tests/__snapshots__/index.test.ts.snap
Normal file
File diff suppressed because it is too large
Load Diff
819
plugins/importer-openapi/tests/fixtures/petstore.yaml
vendored
Normal file
819
plugins/importer-openapi/tests/fixtures/petstore.yaml
vendored
Normal file
@@ -0,0 +1,819 @@
|
||||
openapi: 3.0.2
|
||||
servers:
|
||||
- url: /v3
|
||||
info:
|
||||
description: |-
|
||||
This is a sample Pet Store Server based on the OpenAPI 3.0 specification. You can find out more about
|
||||
Swagger at [http://swagger.io](http://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)
|
||||
version: 1.0.20-SNAPSHOT
|
||||
title: Swagger Petstore - OpenAPI 3.0
|
||||
termsOfService: "http://swagger.io/terms/"
|
||||
contact:
|
||||
email: apiteam@swagger.io
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: "http://www.apache.org/licenses/LICENSE-2.0.html"
|
||||
tags:
|
||||
- name: pet
|
||||
description: Everything about your Pets
|
||||
externalDocs:
|
||||
description: Find out more
|
||||
url: "http://swagger.io"
|
||||
- name: store
|
||||
description: Access to Petstore orders
|
||||
externalDocs:
|
||||
description: Find out more about our store
|
||||
url: "http://swagger.io"
|
||||
- name: user
|
||||
description: Operations about user
|
||||
paths:
|
||||
/pet:
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Add a new pet to the store
|
||||
description: Add a new pet to the store
|
||||
operationId: addPet
|
||||
responses:
|
||||
"200":
|
||||
description: Successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
"405":
|
||||
description: Invalid input
|
||||
security:
|
||||
- petstore_auth:
|
||||
- "write:pets"
|
||||
- "read:pets"
|
||||
requestBody:
|
||||
description: Create a new pet in the store
|
||||
required: true
|
||||
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"
|
||||
put:
|
||||
tags:
|
||||
- pet
|
||||
summary: Update an existing pet
|
||||
description: Update an existing pet by Id
|
||||
operationId: updatePet
|
||||
responses:
|
||||
"200":
|
||||
description: Successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
"400":
|
||||
description: Invalid ID supplied
|
||||
"404":
|
||||
description: Pet not found
|
||||
"405":
|
||||
description: Validation exception
|
||||
security:
|
||||
- petstore_auth:
|
||||
- "write:pets"
|
||||
- "read:pets"
|
||||
requestBody:
|
||||
description: Update an existent pet in the store
|
||||
required: true
|
||||
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"
|
||||
/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
|
||||
enum:
|
||||
- available
|
||||
- pending
|
||||
- sold
|
||||
default: available
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
"400":
|
||||
description: Invalid status value
|
||||
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/xml:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
"400":
|
||||
description: Invalid tag value
|
||||
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/xml:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
"400":
|
||||
description: Invalid ID supplied
|
||||
"404":
|
||||
description: Pet not found
|
||||
security:
|
||||
- api_key: []
|
||||
- petstore_auth:
|
||||
- "write:pets"
|
||||
- "read:pets"
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: Updates a pet in the store with form data
|
||||
description: ""
|
||||
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:
|
||||
"405":
|
||||
description: Invalid input
|
||||
security:
|
||||
- petstore_auth:
|
||||
- "write:pets"
|
||||
- "read:pets"
|
||||
delete:
|
||||
tags:
|
||||
- pet
|
||||
summary: Deletes a pet
|
||||
description: ""
|
||||
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:
|
||||
"400":
|
||||
description: Invalid pet value
|
||||
security:
|
||||
- petstore_auth:
|
||||
- "write:pets"
|
||||
- "read:pets"
|
||||
"/pet/{petId}/uploadImage":
|
||||
post:
|
||||
tags:
|
||||
- pet
|
||||
summary: uploads an image
|
||||
description: ""
|
||||
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
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/ApiResponse"
|
||||
security:
|
||||
- petstore_auth:
|
||||
- "write:pets"
|
||||
- "read:pets"
|
||||
requestBody:
|
||||
content:
|
||||
application/octet-stream:
|
||||
schema:
|
||||
type: string
|
||||
format: binary
|
||||
/store/inventory:
|
||||
get:
|
||||
tags:
|
||||
- store
|
||||
summary: Returns pet inventories by status
|
||||
description: Returns a map of status codes to quantities
|
||||
operationId: getInventory
|
||||
x-swagger-router-controller: OrderController
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: object
|
||||
additionalProperties:
|
||||
type: integer
|
||||
format: int32
|
||||
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
|
||||
x-swagger-router-controller: OrderController
|
||||
responses:
|
||||
"200":
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Order"
|
||||
"405":
|
||||
description: Invalid input
|
||||
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"
|
||||
"/store/order/{orderId}":
|
||||
get:
|
||||
tags:
|
||||
- store
|
||||
summary: Find purchase order by ID
|
||||
x-swagger-router-controller: OrderController
|
||||
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/xml:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Order"
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Order"
|
||||
"400":
|
||||
description: Invalid ID supplied
|
||||
"404":
|
||||
description: Order not found
|
||||
delete:
|
||||
tags:
|
||||
- store
|
||||
summary: Delete purchase order by ID
|
||||
x-swagger-router-controller: OrderController
|
||||
description: >-
|
||||
For valid response try integer IDs with value < 1000. Anything above
|
||||
1000 or nonintegers 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:
|
||||
"400":
|
||||
description: Invalid ID supplied
|
||||
"404":
|
||||
description: Order not found
|
||||
/user:
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Create user
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: createUser
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/User"
|
||||
application/xml:
|
||||
schema:
|
||||
$ref: "#/components/schemas/User"
|
||||
requestBody:
|
||||
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"
|
||||
description: Created user object
|
||||
/user/createWithList:
|
||||
post:
|
||||
tags:
|
||||
- user
|
||||
summary: Creates list of users with given input array
|
||||
description: "Creates list of users with given input array"
|
||||
x-swagger-router-controller: UserController
|
||||
operationId: createUsersWithListInput
|
||||
responses:
|
||||
"200":
|
||||
description: Successful operation
|
||||
content:
|
||||
application/xml:
|
||||
schema:
|
||||
$ref: "#/components/schemas/User"
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/User"
|
||||
default:
|
||||
description: successful operation
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/User"
|
||||
/user/login:
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Logs user into the system
|
||||
description: ""
|
||||
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
|
||||
/user/logout:
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Logs out current logged in user session
|
||||
description: ""
|
||||
operationId: logoutUser
|
||||
parameters: []
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
"/user/{username}":
|
||||
get:
|
||||
tags:
|
||||
- user
|
||||
summary: Get user by user name
|
||||
description: ""
|
||||
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/xml:
|
||||
schema:
|
||||
$ref: "#/components/schemas/User"
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/User"
|
||||
"400":
|
||||
description: Invalid username supplied
|
||||
"404":
|
||||
description: User not found
|
||||
put:
|
||||
tags:
|
||||
- user
|
||||
summary: Update user
|
||||
x-swagger-router-controller: UserController
|
||||
description: This can only be done by the logged in user.
|
||||
operationId: updateUser
|
||||
parameters:
|
||||
- name: username
|
||||
in: path
|
||||
description: name that needs to be updated
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
default:
|
||||
description: successful operation
|
||||
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"
|
||||
delete:
|
||||
tags:
|
||||
- user
|
||||
summary: Delete user
|
||||
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:
|
||||
"400":
|
||||
description: Invalid username supplied
|
||||
"404":
|
||||
description: User not found
|
||||
externalDocs:
|
||||
description: Find out more about Swagger
|
||||
url: "http://swagger.io"
|
||||
components:
|
||||
schemas:
|
||||
Order:
|
||||
x-swagger-router-model: io.swagger.petstore.model.Order
|
||||
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
|
||||
enum:
|
||||
- placed
|
||||
- approved
|
||||
- delivered
|
||||
example: approved
|
||||
complete:
|
||||
type: boolean
|
||||
xml:
|
||||
name: order
|
||||
type: object
|
||||
Customer:
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
example: 100000
|
||||
username:
|
||||
type: string
|
||||
example: fehguy
|
||||
address:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/Address"
|
||||
xml:
|
||||
wrapped: true
|
||||
name: addresses
|
||||
xml:
|
||||
name: customer
|
||||
type: object
|
||||
Address:
|
||||
properties:
|
||||
street:
|
||||
type: string
|
||||
example: 437 Lytton
|
||||
city:
|
||||
type: string
|
||||
example: Palo Alto
|
||||
state:
|
||||
type: string
|
||||
example: CA
|
||||
zip:
|
||||
type: string
|
||||
example: 94301
|
||||
xml:
|
||||
name: address
|
||||
type: object
|
||||
Category:
|
||||
x-swagger-router-model: io.swagger.petstore.model.Category
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
example: 1
|
||||
name:
|
||||
type: string
|
||||
example: Dogs
|
||||
xml:
|
||||
name: category
|
||||
type: object
|
||||
User:
|
||||
x-swagger-router-model: io.swagger.petstore.model.User
|
||||
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
|
||||
format: int32
|
||||
example: 1
|
||||
description: User Status
|
||||
xml:
|
||||
name: user
|
||||
type: object
|
||||
Tag:
|
||||
x-swagger-router-model: io.swagger.petstore.model.Tag
|
||||
properties:
|
||||
id:
|
||||
type: integer
|
||||
format: int64
|
||||
name:
|
||||
type: string
|
||||
xml:
|
||||
name: tag
|
||||
type: object
|
||||
Pet:
|
||||
x-swagger-router-model: io.swagger.petstore.model.Pet
|
||||
required:
|
||||
- name
|
||||
- photoUrls
|
||||
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"
|
||||
xml:
|
||||
name: tag
|
||||
status:
|
||||
type: string
|
||||
description: pet status in the store
|
||||
enum:
|
||||
- available
|
||||
- pending
|
||||
- sold
|
||||
xml:
|
||||
name: pet
|
||||
type: object
|
||||
ApiResponse:
|
||||
properties:
|
||||
code:
|
||||
type: integer
|
||||
format: int32
|
||||
type:
|
||||
type: string
|
||||
message:
|
||||
type: string
|
||||
xml:
|
||||
name: "##default"
|
||||
type: object
|
||||
requestBodies:
|
||||
Pet:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
application/xml:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Pet"
|
||||
description: Pet object that needs to be added to the store
|
||||
UserArray:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: "#/components/schemas/User"
|
||||
description: List of user object
|
||||
securitySchemes:
|
||||
petstore_auth:
|
||||
type: oauth2
|
||||
flows:
|
||||
implicit:
|
||||
authorizationUrl: "https://petstore.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
|
||||
8
plugins/importer-openapi/tests/fixtures/real-world/SOURCES.md
vendored
Normal file
8
plugins/importer-openapi/tests/fixtures/real-world/SOURCES.md
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
# Real-World OpenAPI Fixtures
|
||||
|
||||
These fixtures were copied from the public APIs.guru OpenAPI directory:
|
||||
|
||||
- `apis-guru.yaml`: https://api.apis.guru/v2/specs/apis.guru/2.2.0/openapi.yaml
|
||||
- `httpbin.yaml`: https://api.apis.guru/v2/specs/httpbin.org/0.9.2/openapi.yaml
|
||||
- `nasa-apod.yaml`: https://api.apis.guru/v2/specs/nasa.gov/apod/1.0.0/openapi.yaml
|
||||
- `xkcd.yaml`: https://api.apis.guru/v2/specs/xkcd.com/1.0.0/openapi.yaml
|
||||
399
plugins/importer-openapi/tests/fixtures/real-world/apis-guru.yaml
vendored
Normal file
399
plugins/importer-openapi/tests/fixtures/real-world/apis-guru.yaml
vendored
Normal file
@@ -0,0 +1,399 @@
|
||||
openapi: 3.0.0
|
||||
servers:
|
||||
- url: https://api.apis.guru/v2
|
||||
info:
|
||||
contact:
|
||||
email: mike.ralphson@gmail.com
|
||||
name: APIs.guru
|
||||
url: https://APIs.guru
|
||||
description: |
|
||||
Wikipedia for Web APIs. Repository of API definitions in OpenAPI format.
|
||||
**Warning**: If you want to be notified about changes in advance please join our [Slack channel](https://join.slack.com/t/mermade/shared_invite/zt-g78g7xir-MLE_CTCcXCdfJfG3CJe9qA).
|
||||
Client sample: [[Demo]](https://apis.guru/simple-ui) [[Repo]](https://github.com/APIs-guru/simple-ui)
|
||||
license:
|
||||
name: CC0 1.0
|
||||
url: https://github.com/APIs-guru/openapi-directory#licenses
|
||||
title: APIs.guru
|
||||
version: 2.2.0
|
||||
x-apisguru-categories:
|
||||
- open_data
|
||||
- developer_tools
|
||||
x-logo:
|
||||
url: https://api.apis.guru/v2/cache/logo/https_apis.guru_branding_logo_vertical.svg
|
||||
x-origin:
|
||||
- format: openapi
|
||||
url: https://api.apis.guru/v2/openapi.yaml
|
||||
version: "3.0"
|
||||
x-providerName: apis.guru
|
||||
x-tags:
|
||||
- API
|
||||
- Catalog
|
||||
- Directory
|
||||
- REST
|
||||
- Swagger
|
||||
- OpenAPI
|
||||
externalDocs:
|
||||
url: https://github.com/APIs-guru/openapi-directory/blob/master/API.md
|
||||
security: []
|
||||
tags:
|
||||
- description: Actions relating to APIs in the collection
|
||||
name: APIs
|
||||
paths:
|
||||
/list.json:
|
||||
get:
|
||||
description: |
|
||||
List all APIs in the directory.
|
||||
Returns links to the OpenAPI definitions for each API in the directory.
|
||||
If API exist in multiple versions `preferred` one is explicitly marked.
|
||||
Some basic info from the OpenAPI definition is cached inside each object.
|
||||
This allows you to generate some simple views without needing to fetch the OpenAPI definition for each API.
|
||||
operationId: listAPIs
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/APIs"
|
||||
description: OK
|
||||
summary: List all APIs
|
||||
tags:
|
||||
- APIs
|
||||
/metrics.json:
|
||||
get:
|
||||
description: |
|
||||
Some basic metrics for the entire directory.
|
||||
Just stunning numbers to put on a front page and are intended purely for WoW effect :)
|
||||
operationId: getMetrics
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/Metrics"
|
||||
description: OK
|
||||
summary: Get basic metrics
|
||||
tags:
|
||||
- APIs
|
||||
/providers.json:
|
||||
get:
|
||||
description: |
|
||||
List all the providers in the directory
|
||||
operationId: getProviders
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
items:
|
||||
minLength: 1
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
type: object
|
||||
description: OK
|
||||
summary: List all providers
|
||||
tags:
|
||||
- APIs
|
||||
"/specs/{provider}/{api}.json":
|
||||
get:
|
||||
description: Returns the API entry for one specific version of an API where there is no serviceName.
|
||||
operationId: getAPI
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/provider"
|
||||
- $ref: "#/components/parameters/api"
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/API"
|
||||
description: OK
|
||||
summary: Retrieve one version of a particular API
|
||||
tags:
|
||||
- APIs
|
||||
"/specs/{provider}/{service}/{api}.json":
|
||||
get:
|
||||
description: Returns the API entry for one specific version of an API where there is a serviceName.
|
||||
operationId: getServiceAPI
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/provider"
|
||||
- in: path
|
||||
name: service
|
||||
required: true
|
||||
schema:
|
||||
example: graph
|
||||
maxLength: 255
|
||||
minLength: 1
|
||||
type: string
|
||||
- $ref: "#/components/parameters/api"
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/API"
|
||||
description: OK
|
||||
summary: Retrieve one version of a particular API with a serviceName.
|
||||
tags:
|
||||
- APIs
|
||||
"/{provider}.json":
|
||||
get:
|
||||
description: |
|
||||
List all APIs in the directory for a particular providerName
|
||||
Returns links to the individual API entry for each API.
|
||||
operationId: getProvider
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/provider"
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: "#/components/schemas/APIs"
|
||||
description: OK
|
||||
summary: List all APIs for a particular provider
|
||||
tags:
|
||||
- APIs
|
||||
"/{provider}/services.json":
|
||||
get:
|
||||
description: |
|
||||
List all serviceNames in the directory for a particular providerName
|
||||
operationId: getServices
|
||||
parameters:
|
||||
- $ref: "#/components/parameters/provider"
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
items:
|
||||
minLength: 0
|
||||
type: string
|
||||
minItems: 1
|
||||
type: array
|
||||
type: object
|
||||
description: OK
|
||||
summary: List all serviceNames for a particular provider
|
||||
tags:
|
||||
- APIs
|
||||
components:
|
||||
parameters:
|
||||
api:
|
||||
in: path
|
||||
name: api
|
||||
required: true
|
||||
schema:
|
||||
example: 2.1.0
|
||||
maxLength: 255
|
||||
minLength: 1
|
||||
type: string
|
||||
provider:
|
||||
in: path
|
||||
name: provider
|
||||
required: true
|
||||
schema:
|
||||
example: apis.guru
|
||||
maxLength: 255
|
||||
minLength: 1
|
||||
type: string
|
||||
schemas:
|
||||
API:
|
||||
additionalProperties: false
|
||||
description: Meta information about API
|
||||
properties:
|
||||
added:
|
||||
description: Timestamp when the API was first added to the directory
|
||||
format: date-time
|
||||
type: string
|
||||
preferred:
|
||||
description: Recommended version
|
||||
type: string
|
||||
versions:
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/ApiVersion"
|
||||
description: List of supported versions of the API
|
||||
minProperties: 1
|
||||
type: object
|
||||
required:
|
||||
- added
|
||||
- preferred
|
||||
- versions
|
||||
type: object
|
||||
APIs:
|
||||
additionalProperties:
|
||||
$ref: "#/components/schemas/API"
|
||||
description: |
|
||||
List of API details.
|
||||
It is a JSON object with API IDs(`<provider>[:<service>]`) as keys.
|
||||
example:
|
||||
googleapis.com:drive:
|
||||
added: 2015-02-22T20:00:45.000Z
|
||||
preferred: v3
|
||||
versions:
|
||||
v2:
|
||||
added: 2015-02-22T20:00:45.000Z
|
||||
info:
|
||||
title: Drive
|
||||
version: v2
|
||||
x-apiClientRegistration:
|
||||
url: https://console.developers.google.com
|
||||
x-logo:
|
||||
url: https://api.apis.guru/v2/cache/logo/https_www.gstatic.com_images_icons_material_product_2x_drive_32dp.png
|
||||
x-origin:
|
||||
format: google
|
||||
url: https://www.googleapis.com/discovery/v1/apis/drive/v2/rest
|
||||
version: v1
|
||||
x-preferred: false
|
||||
x-providerName: googleapis.com
|
||||
x-serviceName: drive
|
||||
swaggerUrl: https://api.apis.guru/v2/specs/googleapis.com/drive/v2/swagger.json
|
||||
swaggerYamlUrl: https://api.apis.guru/v2/specs/googleapis.com/drive/v2/swagger.yaml
|
||||
updated: 2016-06-17T00:21:44.000Z
|
||||
v3:
|
||||
added: 2015-12-12T00:25:13.000Z
|
||||
info:
|
||||
title: Drive
|
||||
version: v3
|
||||
x-apiClientRegistration:
|
||||
url: https://console.developers.google.com
|
||||
x-logo:
|
||||
url: https://api.apis.guru/v2/cache/logo/https_www.gstatic.com_images_icons_material_product_2x_drive_32dp.png
|
||||
x-origin:
|
||||
format: google
|
||||
url: https://www.googleapis.com/discovery/v1/apis/drive/v3/rest
|
||||
version: v1
|
||||
x-preferred: true
|
||||
x-providerName: googleapis.com
|
||||
x-serviceName: drive
|
||||
swaggerUrl: https://api.apis.guru/v2/specs/googleapis.com/drive/v3/swagger.json
|
||||
swaggerYamlUrl: https://api.apis.guru/v2/specs/googleapis.com/drive/v3/swagger.yaml
|
||||
updated: 2016-06-17T00:21:44.000Z
|
||||
minProperties: 1
|
||||
type: object
|
||||
ApiVersion:
|
||||
additionalProperties: false
|
||||
properties:
|
||||
added:
|
||||
description: Timestamp when the version was added
|
||||
format: date-time
|
||||
type: string
|
||||
externalDocs:
|
||||
description: Copy of `externalDocs` section from OpenAPI definition
|
||||
minProperties: 1
|
||||
type: object
|
||||
info:
|
||||
description: Copy of `info` section from OpenAPI definition
|
||||
minProperties: 1
|
||||
type: object
|
||||
link:
|
||||
description: Link to the individual API entry for this API
|
||||
format: url
|
||||
type: string
|
||||
openapiVer:
|
||||
description: The value of the `openapi` or `swagger` property of the source definition
|
||||
type: string
|
||||
swaggerUrl:
|
||||
description: URL to OpenAPI definition in JSON format
|
||||
format: url
|
||||
type: string
|
||||
swaggerYamlUrl:
|
||||
description: URL to OpenAPI definition in YAML format
|
||||
format: url
|
||||
type: string
|
||||
updated:
|
||||
description: Timestamp when the version was updated
|
||||
format: date-time
|
||||
type: string
|
||||
required:
|
||||
- added
|
||||
- updated
|
||||
- swaggerUrl
|
||||
- swaggerYamlUrl
|
||||
- info
|
||||
- openapiVer
|
||||
type: object
|
||||
Metrics:
|
||||
additionalProperties: false
|
||||
description: List of basic metrics
|
||||
example:
|
||||
datasets: []
|
||||
fixedPct: 22
|
||||
fixes: 81119
|
||||
invalid: 598
|
||||
issues: 28
|
||||
numAPIs: 2501
|
||||
numDrivers: 10
|
||||
numEndpoints: 106448
|
||||
numProviders: 659
|
||||
numSpecs: 3329
|
||||
stars: 2429
|
||||
thisWeek:
|
||||
added: 45
|
||||
updated: 171
|
||||
unofficial: 25
|
||||
unreachable: 123
|
||||
properties:
|
||||
datasets:
|
||||
description: Data used for charting etc
|
||||
items: {}
|
||||
type: array
|
||||
fixedPct:
|
||||
description: Percentage of all APIs where auto fixes have been applied
|
||||
type: integer
|
||||
fixes:
|
||||
description: Total number of fixes applied across all APIs
|
||||
type: integer
|
||||
invalid:
|
||||
description: Number of newly invalid APIs
|
||||
type: integer
|
||||
issues:
|
||||
description: Open GitHub issues on our main repo
|
||||
type: integer
|
||||
numAPIs:
|
||||
description: Number of unique APIs
|
||||
minimum: 1
|
||||
type: integer
|
||||
numDrivers:
|
||||
description: Number of methods of API retrieval
|
||||
type: integer
|
||||
numEndpoints:
|
||||
description: Total number of endpoints inside all definitions
|
||||
minimum: 1
|
||||
type: integer
|
||||
numProviders:
|
||||
description: Number of API providers in directory
|
||||
type: integer
|
||||
numSpecs:
|
||||
description: Number of API definitions including different versions of the same API
|
||||
minimum: 1
|
||||
type: integer
|
||||
stars:
|
||||
description: GitHub stars for our main repo
|
||||
type: integer
|
||||
thisWeek:
|
||||
description: Summary totals for the last 7 days
|
||||
properties:
|
||||
added:
|
||||
description: APIs added in the last week
|
||||
type: integer
|
||||
updated:
|
||||
description: APIs updated in the last week
|
||||
type: integer
|
||||
type: object
|
||||
unofficial:
|
||||
description: Number of unofficial APIs
|
||||
type: integer
|
||||
unreachable:
|
||||
description: Number of unreachable (4XX,5XX status) APIs
|
||||
type: integer
|
||||
required:
|
||||
- numSpecs
|
||||
- numAPIs
|
||||
- numEndpoints
|
||||
type: object
|
||||
x-optic-standard: "@febf8ac6-ee67-4565-b45a-5c85a469dca7/Fz6KU3_wMIO5iJ6_VUZ30"
|
||||
x-optic-url: https://app.useoptic.com/organizations/febf8ac6-ee67-4565-b45a-5c85a469dca7/apis/_0fKWqUvhs9ssYNkq1k-c
|
||||
1121
plugins/importer-openapi/tests/fixtures/real-world/httpbin.yaml
vendored
Normal file
1121
plugins/importer-openapi/tests/fixtures/real-world/httpbin.yaml
vendored
Normal file
File diff suppressed because it is too large
Load Diff
69
plugins/importer-openapi/tests/fixtures/real-world/nasa-apod.yaml
vendored
Normal file
69
plugins/importer-openapi/tests/fixtures/real-world/nasa-apod.yaml
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
openapi: 3.0.0
|
||||
servers:
|
||||
- url: https://api.nasa.gov/planetary
|
||||
- url: http://api.nasa.gov/planetary
|
||||
info:
|
||||
contact:
|
||||
email: evan.t.yates@nasa.gov
|
||||
description: This endpoint structures the APOD imagery and associated metadata so that it can be repurposed for other applications. In addition, if the concept_tags parameter is set to True, then keywords derived from the image explanation are returned. These keywords could be used as auto-generated hashtags for twitter or instagram feeds; but generally help with discoverability of relevant imagery
|
||||
license:
|
||||
name: Apache 2.0
|
||||
url: http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
title: APOD
|
||||
version: 1.0.0
|
||||
x-apisguru-categories:
|
||||
- media
|
||||
- open_data
|
||||
x-origin:
|
||||
- format: swagger
|
||||
url: https://raw.githubusercontent.com/nasa/api-docs/gh-pages/assets/json/APOD
|
||||
version: "2.0"
|
||||
x-providerName: nasa.gov
|
||||
x-serviceName: apod
|
||||
x-logo:
|
||||
url: https://api.apis.guru/v2/cache/logo/https_apis.guru_assets_images_no-logo.svg
|
||||
tags:
|
||||
- description: An example tag
|
||||
externalDocs:
|
||||
description: Here's a link
|
||||
url: https://example.com
|
||||
name: request tag
|
||||
paths:
|
||||
/apod:
|
||||
get:
|
||||
description: Returns the picture of the day
|
||||
parameters:
|
||||
- description: The date of the APOD image to retrieve
|
||||
in: query
|
||||
name: date
|
||||
required: false
|
||||
schema:
|
||||
type: string
|
||||
- description: Retrieve the URL for the high resolution image
|
||||
in: query
|
||||
name: hd
|
||||
required: false
|
||||
schema:
|
||||
type: boolean
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
items:
|
||||
x-thing: ok
|
||||
type: array
|
||||
description: successful operation
|
||||
"400":
|
||||
description: Date must be between Jun 16, 1995 and Mar 28, 2019.
|
||||
security:
|
||||
- api_key: []
|
||||
summary: Returns images
|
||||
tags:
|
||||
- request tag
|
||||
components:
|
||||
securitySchemes:
|
||||
api_key:
|
||||
in: query
|
||||
name: api_key
|
||||
type: apiKey
|
||||
78
plugins/importer-openapi/tests/fixtures/real-world/xkcd.yaml
vendored
Normal file
78
plugins/importer-openapi/tests/fixtures/real-world/xkcd.yaml
vendored
Normal file
@@ -0,0 +1,78 @@
|
||||
openapi: 3.0.0
|
||||
servers:
|
||||
- url: http://xkcd.com/
|
||||
info:
|
||||
description: Webcomic of romance, sarcasm, math, and language.
|
||||
title: XKCD
|
||||
version: 1.0.0
|
||||
x-apisguru-categories:
|
||||
- media
|
||||
x-logo:
|
||||
url: https://api.apis.guru/v2/cache/logo/http_imgs.xkcd.com_static_terrible_small_logo.png
|
||||
x-origin:
|
||||
- format: openapi
|
||||
url: https://raw.githubusercontent.com/APIs-guru/unofficial_openapi_specs/master/xkcd.com/1.0.0/openapi.yaml
|
||||
version: "3.0"
|
||||
x-providerName: xkcd.com
|
||||
x-tags:
|
||||
- humor
|
||||
- comics
|
||||
x-unofficialSpec: true
|
||||
externalDocs:
|
||||
url: https://xkcd.com/json.html
|
||||
paths:
|
||||
/info.0.json:
|
||||
get:
|
||||
description: |
|
||||
Fetch current comic and metadata.
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
"*/*":
|
||||
schema:
|
||||
$ref: "#/components/schemas/comic"
|
||||
description: OK
|
||||
"/{comicId}/info.0.json":
|
||||
get:
|
||||
description: |
|
||||
Fetch comics and metadata by comic id.
|
||||
parameters:
|
||||
- in: path
|
||||
name: comicId
|
||||
required: true
|
||||
schema:
|
||||
type: number
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
"*/*":
|
||||
schema:
|
||||
$ref: "#/components/schemas/comic"
|
||||
description: OK
|
||||
components:
|
||||
schemas:
|
||||
comic:
|
||||
properties:
|
||||
alt:
|
||||
type: string
|
||||
day:
|
||||
type: string
|
||||
img:
|
||||
type: string
|
||||
link:
|
||||
type: string
|
||||
month:
|
||||
type: string
|
||||
news:
|
||||
type: string
|
||||
num:
|
||||
type: number
|
||||
safe_title:
|
||||
type: string
|
||||
title:
|
||||
type: string
|
||||
transcript:
|
||||
type: string
|
||||
year:
|
||||
type: string
|
||||
type: object
|
||||
2419
plugins/importer-openapi/tests/index.test.ts
Normal file
2419
plugins/importer-openapi/tests/index.test.ts
Normal file
File diff suppressed because it is too large
Load Diff
306
plugins/importer-openapi/tests/roundtrip.mjs
Normal file
306
plugins/importer-openapi/tests/roundtrip.mjs
Normal file
@@ -0,0 +1,306 @@
|
||||
// Round-trip harness: import a spec with the local importer plugin, send every
|
||||
// request through the real Yaak CLI pipeline against a Prism mock of the same
|
||||
// spec, and report Prism's validation verdict for each request.
|
||||
//
|
||||
// Prism independently validates each incoming request against the spec (paths,
|
||||
// required parameters, body schemas, security), so a violation here is an
|
||||
// importer bug found by a second OpenAPI implementation rather than a snapshot
|
||||
// of our own output.
|
||||
//
|
||||
// Usage: node tests/roundtrip.mjs [spec.yaml ...]
|
||||
// YAAK_BIN=/path/to/yaak overrides the CLI (defaults to the repo debug build,
|
||||
// which embeds the plugins vendored from this checkout).
|
||||
|
||||
import { execFileSync, spawn } from "node:child_process";
|
||||
import * as fs from "node:fs";
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const here = path.dirname(fileURLToPath(import.meta.url));
|
||||
const repoRoot = path.resolve(here, "../../..");
|
||||
const yaakBin = process.env.YAAK_BIN ?? path.join(repoRoot, "target/debug/yaak");
|
||||
const specs =
|
||||
process.argv.length > 2
|
||||
? process.argv.slice(2)
|
||||
: [
|
||||
path.join(here, "fixtures/petstore.yaml"),
|
||||
...fs
|
||||
.readdirSync(path.join(here, "fixtures/real-world"))
|
||||
.filter((f) => f.endsWith(".yaml"))
|
||||
.map((f) => path.join(here, "fixtures/real-world", f)),
|
||||
];
|
||||
|
||||
if (!fs.existsSync(yaakBin)) {
|
||||
console.error(`Yaak CLI not found at ${yaakBin}. Build it with: cargo build -p yaak-cli`);
|
||||
process.exit(2);
|
||||
}
|
||||
|
||||
// Pinned so local runs and CI judge against the same validator
|
||||
const PRISM_PACKAGE = "@stoplight/prism-cli@5.15.11";
|
||||
|
||||
// Accepted spec-quality gray zones, not importer bugs. httpbin's required
|
||||
// `url` query parameter has no example, and an empty value is preferable to
|
||||
// inventing fake query data even though Prism counts it as missing.
|
||||
const KNOWN_FLAGS = new Set(["httpbin.yaml GET ${[baseUrl]}/redirect-to"]);
|
||||
|
||||
function yaak(dataDir, args) {
|
||||
return execFileSync(yaakBin, ["--data-dir", dataDir, ...args], {
|
||||
encoding: "utf-8",
|
||||
stdio: ["ignore", "pipe", "pipe"],
|
||||
});
|
||||
}
|
||||
|
||||
function yaakJson(dataDir, args) {
|
||||
return JSON.parse(yaak(dataDir, args));
|
||||
}
|
||||
|
||||
function listIds(output) {
|
||||
return output
|
||||
.split("\n")
|
||||
.map((line) => line.match(/^(\w+_\w+) - /)?.[1])
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
async function startPrism(spec, port) {
|
||||
for (let attempt = 0; attempt < 20; attempt++, port++) {
|
||||
const prism = spawn(
|
||||
"npx",
|
||||
["-y", PRISM_PACKAGE, "mock", "--errors", "-p", String(port), "-h", "127.0.0.1", spec],
|
||||
{ stdio: ["ignore", "pipe", "pipe"] },
|
||||
);
|
||||
let log = "";
|
||||
prism.stdout.on("data", (d) => (log += d));
|
||||
prism.stderr.on("data", (d) => (log += d));
|
||||
// Generous: the first run downloads Prism through npx
|
||||
const deadline = Date.now() + 120_000;
|
||||
let failed = false;
|
||||
while (Date.now() < deadline) {
|
||||
if (log.includes("Prism is listening")) return { prism, port, getLog: () => log };
|
||||
if (log.includes("EADDRINUSE") || prism.exitCode != null) {
|
||||
failed = true;
|
||||
break;
|
||||
}
|
||||
await new Promise((r) => setTimeout(r, 200));
|
||||
}
|
||||
prism.kill();
|
||||
if (failed) {
|
||||
// The exit can be observed before its buffered stderr arrives; wait for
|
||||
// the streams to close so the bind error is distinguishable
|
||||
await Promise.race([
|
||||
new Promise((r) => prism.once("close", r)),
|
||||
new Promise((r) => setTimeout(r, 2000)),
|
||||
]);
|
||||
if (log.includes("EADDRINUSE")) continue;
|
||||
throw new Error(`Prism exited:\n${log}`);
|
||||
}
|
||||
throw new Error(`Prism did not start in time:\n${log}`);
|
||||
}
|
||||
throw new Error("No free port found for Prism");
|
||||
}
|
||||
|
||||
function pointVariablesAtPrism(variables, prismUrl) {
|
||||
return variables.map((v) => {
|
||||
if (v.name === "baseUrl" || v.name.startsWith("serverUrl")) return { ...v, value: prismUrl };
|
||||
if (v.name === "baseUrlOrigin") return { ...v, value: prismUrl };
|
||||
if (v.value === "") return { ...v, value: "test-value" };
|
||||
return v;
|
||||
});
|
||||
}
|
||||
|
||||
// Prism reports its verdict in the sl-violations response header. Violations
|
||||
// located in the request are importer bugs; violations located in the response
|
||||
// mean Prism could not fabricate a spec-valid mock response (the spec's own
|
||||
// examples are broken), which says nothing about the import.
|
||||
function classify(response, bodyText) {
|
||||
if (response.error) return { verdict: "SEND ERROR", detail: response.error };
|
||||
|
||||
const violationsHeader = (response.headers ?? []).find(
|
||||
(h) => h.name?.toLowerCase() === "sl-violations",
|
||||
);
|
||||
let violations = [];
|
||||
try {
|
||||
violations = JSON.parse(violationsHeader?.value ?? "[]");
|
||||
} catch {}
|
||||
const requestViolations = violations.filter((v) => v.location?.[0] === "request");
|
||||
const detail = requestViolations.map((v) => `${v.location.join(".")}: ${v.message}`).join("; ");
|
||||
|
||||
if (requestViolations.length > 0) return { verdict: "VIOLATION", detail };
|
||||
|
||||
// A spec-defined error response (e.g. an operation whose only response is a
|
||||
// 405) mocks as that status with no Prism error type; only Prism's own error
|
||||
// bodies mark a request Prism could not accept.
|
||||
let body = null;
|
||||
try {
|
||||
body = JSON.parse(bodyText);
|
||||
} catch {}
|
||||
const prismError =
|
||||
typeof body?.type === "string" && body.type.includes("stoplight.io/prism/errors")
|
||||
? body.type.split("#")[1]
|
||||
: null;
|
||||
if (prismError == null) return { verdict: "ok", detail: "" };
|
||||
|
||||
// Failures to fabricate a mock response say nothing about the request we sent
|
||||
if (prismError === "NO_COMPLEX_OBJECT_TEXT" || prismError === "NO_RESPONSE_DEFINED") {
|
||||
return { verdict: "ok", detail: "" };
|
||||
}
|
||||
|
||||
const bodyViolations = Array.isArray(body.validation)
|
||||
? body.validation.filter((v) => v.location?.[0] === "request")
|
||||
: [];
|
||||
if (prismError === "VIOLATIONS" && bodyViolations.length === 0) {
|
||||
return { verdict: "ok", detail: "" }; // response-side only
|
||||
}
|
||||
return {
|
||||
verdict:
|
||||
prismError === "VIOLATIONS" || prismError === "UNPROCESSABLE_ENTITY"
|
||||
? "VIOLATION"
|
||||
: prismError.includes("MATCHED")
|
||||
? "NO ROUTE"
|
||||
: prismError === "UNAUTHORIZED"
|
||||
? "SECURITY"
|
||||
: prismError,
|
||||
detail:
|
||||
bodyViolations.map((v) => `${v.location.join(".")}: ${v.message}`).join("; ") ||
|
||||
body.detail ||
|
||||
"",
|
||||
};
|
||||
}
|
||||
|
||||
let totalProblems = 0;
|
||||
let port = 4010;
|
||||
|
||||
for (const spec of specs) {
|
||||
const name = path.basename(spec);
|
||||
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "yaak-roundtrip-"));
|
||||
console.log(`\n=== ${name} ===`);
|
||||
|
||||
try {
|
||||
yaak(dataDir, ["import", spec]);
|
||||
const workspaceId = listIds(yaak(dataDir, ["workspace", "list"]))[0];
|
||||
if (workspaceId == null) {
|
||||
console.log(" IMPORT PRODUCED NO WORKSPACE");
|
||||
totalProblems++;
|
||||
continue;
|
||||
}
|
||||
// Prism mocks redirect responses without a Location header; following them
|
||||
// would fail the send for a reason unrelated to the import. Workspace-level
|
||||
// OAuth2 gets the same dummy-bearer treatment as request-level below.
|
||||
const workspace = yaakJson(dataDir, ["workspace", "show", workspaceId]);
|
||||
yaak(dataDir, [
|
||||
"workspace",
|
||||
"update",
|
||||
"--json",
|
||||
JSON.stringify({
|
||||
id: workspaceId,
|
||||
settingFollowRedirects: false,
|
||||
...(workspace.authenticationType === "oauth2"
|
||||
? {
|
||||
authenticationType: "bearer",
|
||||
authentication: { token: "test-token", prefix: "Bearer" },
|
||||
}
|
||||
: {}),
|
||||
}),
|
||||
]);
|
||||
|
||||
const { prism, port: boundPort, getLog } = await startPrism(spec, port);
|
||||
port = boundPort + 1;
|
||||
try {
|
||||
const prismUrl = `http://127.0.0.1:${boundPort}`;
|
||||
const environmentIds = listIds(yaak(dataDir, ["environment", "list", workspaceId]));
|
||||
let activeEnvironment = null;
|
||||
for (const id of environmentIds) {
|
||||
const environment = yaakJson(dataDir, ["environment", "show", id]);
|
||||
yaak(dataDir, [
|
||||
"environment",
|
||||
"update",
|
||||
"--json",
|
||||
JSON.stringify({
|
||||
id,
|
||||
variables: pointVariablesAtPrism(environment.variables ?? [], prismUrl),
|
||||
}),
|
||||
]);
|
||||
if (environment.parentModel === "environment" && activeEnvironment == null) {
|
||||
activeEnvironment = id;
|
||||
}
|
||||
}
|
||||
|
||||
const requestIds = listIds(yaak(dataDir, ["request", "list", workspaceId]));
|
||||
const requests = new Map();
|
||||
for (const id of requestIds) {
|
||||
const request = yaakJson(dataDir, ["request", "show", id]);
|
||||
requests.set(id, request);
|
||||
// OAuth2 would try to fetch a real token; Prism only checks that the
|
||||
// Authorization header is present, so a dummy bearer keeps it satisfied.
|
||||
if (request.authenticationType === "oauth2") {
|
||||
yaak(dataDir, [
|
||||
"request",
|
||||
"update",
|
||||
"--json",
|
||||
JSON.stringify({
|
||||
id,
|
||||
authenticationType: "bearer",
|
||||
authentication: { token: "test-token", prefix: "Bearer" },
|
||||
}),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
const sendArgs = ["send", workspaceId];
|
||||
if (activeEnvironment != null) sendArgs.push("-e", activeEnvironment);
|
||||
try {
|
||||
yaak(dataDir, sendArgs);
|
||||
} catch {
|
||||
// Individual send failures surface per-request below.
|
||||
}
|
||||
|
||||
let problems = 0;
|
||||
for (const id of requestIds) {
|
||||
const request = requests.get(id);
|
||||
const label = `${request.method} ${request.url}`;
|
||||
let response = null;
|
||||
let bodyText = "";
|
||||
try {
|
||||
response = yaakJson(dataDir, ["response", "show", id]);
|
||||
try {
|
||||
bodyText = yaak(dataDir, ["response", "body", id]);
|
||||
} catch {}
|
||||
} catch {
|
||||
console.log(` NEVER SENT ${label}`);
|
||||
problems++;
|
||||
continue;
|
||||
}
|
||||
const { verdict, detail } = classify(response, bodyText);
|
||||
if (verdict === "ok") continue;
|
||||
if (KNOWN_FLAGS.has(`${name} ${label}`)) {
|
||||
console.log(` known ${label}`);
|
||||
continue;
|
||||
}
|
||||
problems++;
|
||||
console.log(` ${verdict.padEnd(12)} ${label}`);
|
||||
console.log(` sent: ${response.url ?? "?"}`);
|
||||
if (detail) console.log(` ${detail}`);
|
||||
}
|
||||
|
||||
const requestCount = requestIds.length;
|
||||
if (problems === 0) {
|
||||
console.log(` all ${requestCount} requests validated clean against the mock`);
|
||||
} else {
|
||||
console.log(` ${problems}/${requestCount} requests flagged`);
|
||||
totalProblems += problems;
|
||||
}
|
||||
const inputWarnings = getLog()
|
||||
.split("\n")
|
||||
.filter((l) => l.includes("[VALIDATOR]") && !l.includes("output"));
|
||||
if (inputWarnings.length > 0) {
|
||||
console.log(` prism validator log lines: ${inputWarnings.length}`);
|
||||
}
|
||||
} finally {
|
||||
prism.kill();
|
||||
}
|
||||
} finally {
|
||||
fs.rmSync(dataDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
|
||||
process.exit(totalProblems > 0 ? 1 : 0);
|
||||
3
plugins/importer-openapi/tsconfig.json
Normal file
3
plugins/importer-openapi/tsconfig.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json"
|
||||
}
|
||||
Reference in New Issue
Block a user