feat: improve design doc samples [INS-5526] (#8719)

* feat: improve design doc samples [INS-5526]

* update to latest petstore, fixing lint errors
This commit is contained in:
Ryan Willis
2025-05-19 11:45:01 -07:00
committed by GitHub
parent a0524841cc
commit 77b8ca4dc2
5 changed files with 1120 additions and 751 deletions

View File

@@ -5,10 +5,11 @@ import { test } from '../../playwright/test';
test('can render Spectral OpenAPI lint errors', async ({ page }) => {
await page.getByRole('button', { name: 'Create document' }).click();
await page.getByRole('dialog').getByRole('button', { name: 'Create' }).click();
await page.click('text=start from boilerplate');
await page.click('text=Use example');
await page.click('text=Pet Store');
const codeEditor = page.locator('.pane-one');
await expect.soft(codeEditor).toContainText('openapi: 3.0.0');
await expect.soft(codeEditor).toContainText('openapi: 3.0.4');
// Cause a lint error
await page.locator('[data-testid="CodeEditor"] >> text=info').click();

View File

@@ -5,6 +5,7 @@ import { Button, Heading, Menu, MenuItem, MenuTrigger, Popover } from 'react-ari
import { documentationLinks } from '../../common/documentation';
import { selectFileOrFolder } from '../../common/select-file-or-folder';
import { blankSpec as emptySpec, petStoreSpec, todoSpec } from './example-openapi-specs';
import { Icon } from './icon';
import { showPrompt } from './modals';
@@ -13,6 +14,35 @@ interface Props {
}
export const DesignEmptyState: FC<Props> = ({ onImport }) => {
const exampleSpecs = [
{
id: 'petstore-spec',
name: 'Pet Store',
icon: 'cat',
action: async () => onImport(petStoreSpec),
},
{
id: 'todo-spec',
name: 'Todo List',
icon: 'list-check',
action: async () => onImport(todoSpec),
},
{
id: 'separator1',
},
{
id: 'empty-spec',
name: 'Empty',
icon: 'file',
action: async () => onImport(emptySpec),
},
] satisfies {
id: string;
name?: string;
icon?: IconName;
action?: () => void;
}[];
const importActionsList = [
{
id: 'import-file',
@@ -63,59 +93,77 @@ export const DesignEmptyState: FC<Props> = ({ onImport }) => {
return (
<div className="pointer-events-none absolute left-0 top-0 flex h-full w-full select-none items-center">
<div className="flex h-full w-full flex-1 flex-col items-center gap-2 divide-y divide-solid divide-[--hl-md] overflow-hidden overflow-y-auto p-[--padding-md] text-[--hl-lg]">
<Heading className="flex flex-1 flex-col items-center gap-2 p-[--padding-sm] text-lg font-bold">
<Icon icon="drafting-compass" className="w-28 flex-1" />
<span>Enter an OpenAPI specification here</span>
<Heading className="text-md flex flex-1 flex-col items-center gap-2 p-[--padding-sm] font-bold">
<Icon icon="drafting-compass" className="w-20 flex-1" />
<span>Enter your OpenAPI specification here</span>
</Heading>
<div className="flex w-full flex-1 flex-col items-center justify-evenly gap-2 p-[--padding-sm]">
<div className="flex w-full flex-1 flex-col items-center justify-between p-[--padding-sm] pt-10">
<p className="flex items-center gap-2">
<Icon icon="lightbulb" />
<span className="flex items-center gap-2 truncate">
Or import an existing OpenAPI spec or
<Button
className="pointer-events-auto font-bold text-[--hl-lg] underline transition-colors hover:text-[--hl] focus:text-[--hl]"
onPress={async () => {
const spec = await import('./example-openapi-spec');
onImport(spec.exampleOpenApiSpec);
}}
>
start from boilerplate
</Button>
</span>
</p>
<MenuTrigger>
<Button
aria-label="Project Actions"
className="pointer-events-auto flex aspect-square h-6 items-center justify-center gap-2 rounded-sm bg-[--hl-xs] p-4 text-sm text-[--color-font] ring-1 ring-transparent transition-all hover:bg-[--hl-xs] hover:opacity-100 focus:opacity-100 focus:ring-inset focus:ring-[--hl-md] group-hover:opacity-100 group-focus:opacity-100 aria-pressed:bg-[--hl-sm] data-[pressed]:opacity-100"
>
<span>Import OpenAPI</span>
<Icon icon="caret-down" />
</Button>
<Popover className="flex min-w-max flex-col overflow-y-hidden">
<Menu
aria-label="Import OpenAPI Dropdown"
selectionMode="single"
onAction={key => {
importActionsList.find(({ id }) => key === id)?.action();
}}
items={importActionsList}
className="min-w-max select-none overflow-y-auto rounded-md border border-solid border-[--hl-sm] bg-[--color-bg] py-2 text-sm shadow-lg focus:outline-none"
>
{item => (
<MenuItem
key={item.id}
id={item.id}
className="text-md flex h-[--line-height-xs] w-full items-center gap-2 whitespace-nowrap bg-transparent px-[--padding-md] text-[--color-font] transition-colors hover:bg-[--hl-sm] focus:bg-[--hl-xs] focus:outline-none disabled:cursor-not-allowed aria-selected:font-bold"
aria-label={item.name}
<div className="flex flex-col items-center gap-6 truncate">
<span>Or quick start:</span>
<div className="flex gap-4">
<MenuTrigger>
<Button
aria-label="Start from a sample"
className="pointer-events-auto flex aspect-square h-6 items-center justify-center gap-2 rounded-sm bg-[--hl-xs] p-4 text-sm text-[--color-font] ring-1 ring-transparent transition-all hover:bg-[--hl-xs] hover:opacity-100 focus:opacity-100 focus:ring-inset focus:ring-[--hl-md] group-hover:opacity-100 group-focus:opacity-100 aria-pressed:bg-[--hl-sm] data-[pressed]:opacity-100"
>
<Icon icon={item.icon} />
<span>{item.name}</span>
</MenuItem>
)}
</Menu>
</Popover>
</MenuTrigger>
<Icon icon="file" />
<span>Use example</span>
<Icon icon="caret-down" />
</Button>
<Popover className="flex min-w-max flex-col overflow-y-hidden">
<Menu
aria-label="Start from a sample dropdown"
selectionMode="single"
onAction={key => {
exampleSpecs.find(({ id }) => key === id)?.action?.();
}}
items={exampleSpecs}
className="min-w-max cursor-pointer select-none overflow-y-auto rounded-md border border-solid border-[--hl-sm] bg-[--color-bg] text-sm shadow-lg focus:outline-none"
>
{item =>
item.id.startsWith('separator') ? (
<MenuItem
key={item.id}
id={item.id}
className="pointer-events-none m-0 my-1 h-px w-full border-0 bg-[--hl-md] p-0"
/>
) : (
<MenuItem
key={item.id}
id={item.id}
className="text-md flex h-[--line-height-xs] w-full items-center gap-2 whitespace-nowrap bg-transparent px-[--padding-md] text-[--color-font] transition-colors hover:bg-[--hl-sm] focus:bg-[--hl-xs] focus:outline-none disabled:cursor-not-allowed aria-selected:font-bold"
aria-label={item.name}
>
{item.icon && <Icon icon={item.icon} />}
<span>{item.name}</span>
</MenuItem>
)
}
</Menu>
</Popover>
</MenuTrigger>
<Button
className="pointer-events-auto flex aspect-square h-6 items-center justify-center gap-2 rounded-sm bg-[--hl-xs] p-4 text-sm text-[--color-font] ring-1 ring-transparent transition-all hover:bg-[--hl-xs] hover:opacity-100 focus:opacity-100 focus:ring-inset focus:ring-[--hl-md] group-hover:opacity-100 group-focus:opacity-100 aria-pressed:bg-[--hl-sm] data-[pressed]:opacity-100"
onPress={() => {
importActionsList.find(({ id }) => id === 'import-file')?.action();
}}
>
<Icon icon="file-import" />
Import file
</Button>
<Button
className="pointer-events-auto flex aspect-square h-6 items-center justify-center gap-2 rounded-sm bg-[--hl-xs] p-4 text-sm text-[--color-font] ring-1 ring-transparent transition-all hover:bg-[--hl-xs] hover:opacity-100 focus:opacity-100 focus:ring-inset focus:ring-[--hl-md] group-hover:opacity-100 group-focus:opacity-100 aria-pressed:bg-[--hl-sm] data-[pressed]:opacity-100"
onPress={() => {
importActionsList.find(({ id }) => id === 'import-url')?.action();
}}
>
<Icon icon="link" />
Import URL
</Button>
</div>
</div>
</p>
<ul className="flex flex-col gap-2">
<li>
<a

View File

@@ -1,617 +0,0 @@
export const exampleOpenApiSpec = `openapi: 3.0.0
info:
description: "This is a sample server Petstore server. You can find out more about
Swagger at [http://swagger.io](http://swagger.io) or on [irc.freenode.net,
#swagger](http://swagger.io/irc/). For this sample, you can use the api key
'special-key' to test the authorization filters."
version: 1.0.2
title: Swagger Petstore
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
- name: user
description: Operations about user
externalDocs:
description: Find out more about our store
url: http://swagger.io
paths:
/pet:
post:
tags:
- pet
summary: Add a new pet to the store
description: ""
operationId: addPet
requestBody:
$ref: "#/components/requestBodies/Pet"
responses:
"405":
description: Invalid input
put:
tags:
- pet
summary: Update an existing pet
description: ""
operationId: updatePet
requestBody:
$ref: "#/components/requestBodies/Pet"
responses:
"400":
description: Invalid ID supplied
"404":
description: Pet not found
"405":
description: Validation exception
/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: true
explode: true
schema:
type: array
items:
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
/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: true
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
deprecated: true
"/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
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
requestBody:
content:
application/x-www-form-urlencoded:
schema:
type: object
properties:
name:
description: Updated name of the pet
type: string
status:
description: Updated status of the pet
type: string
responses:
"405":
description: Invalid input
delete:
tags:
- pet
summary: Deletes a pet
description: ""
operationId: deletePet
parameters:
- name: api_key
in: header
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 ID supplied
"404":
description: Pet not found
/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
/store/order:
post:
tags:
- store
summary: Place an order for a pet
description: ""
operationId: placeOrder
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/Order"
description: order placed for purchasing the pet
required: true
responses:
"200":
description: successful operation
content:
application/xml:
schema:
$ref: "#/components/schemas/Order"
application/json:
schema:
$ref: "#/components/schemas/Order"
"400":
description: Invalid Order
"/store/order/{orderId}":
get:
tags:
- store
summary: Find purchase order by ID
description: For valid response try integer IDs with value >= 1 and <= 10. Other
values will generated exceptions
operationId: getOrderById
parameters:
- name: orderId
in: path
description: ID of pet that needs to be fetched
required: true
schema:
type: integer
format: int64
minimum: 1
maximum: 10
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
description: For valid response try integer IDs with positive integer value. Negative
or non-integer values 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
minimum: 1
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
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/User"
description: Created user object
required: true
responses:
default:
description: successful operation
/user/createWithArray:
post:
tags:
- user
summary: Creates list of users with given input array
description: ""
operationId: createUsersWithArrayInput
requestBody:
$ref: "#/components/requestBodies/UserArray"
responses:
default:
description: successful operation
/user/createWithList:
post:
tags:
- user
summary: Creates list of users with given input array
description: ""
operationId: createUsersWithListInput
requestBody:
$ref: "#/components/requestBodies/UserArray"
responses:
default:
description: successful operation
/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: true
schema:
type: string
- name: password
in: query
description: The password for login in clear text
required: true
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
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: Updated user
description: This can only be done by the logged in user.
operationId: updateUser
parameters:
- name: username
in: path
description: name that need to be updated
required: true
schema:
type: string
requestBody:
content:
application/json:
schema:
$ref: "#/components/schemas/User"
description: Updated user object
required: true
responses:
"400":
description: Invalid user supplied
"404":
description: User not found
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
servers:
- url: https://petstore.swagger.io/v2
components:
requestBodies:
UserArray:
content:
application/json:
schema:
type: array
items:
$ref: "#/components/schemas/User"
description: List of user object
required: true
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
required: true
schemas:
Order:
type: object
properties:
id:
type: integer
format: int64
petId:
type: integer
format: int64
quantity:
type: integer
format: int32
shipDate:
type: string
format: date-time
status:
type: string
description: Order Status
enum:
- placed
- approved
- delivered
complete:
type: boolean
default: false
xml:
name: Order
User:
type: object
properties:
id:
type: integer
format: int64
username:
type: string
firstName:
type: string
lastName:
type: string
email:
type: string
password:
type: string
phone:
type: string
userStatus:
type: integer
format: int32
description: User Status
xml:
name: User
Category:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Category
Tag:
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: Tag
Pet:
type: object
required:
- name
- photoUrls
properties:
id:
type: integer
format: int64
category:
$ref: "#/components/schemas/Category"
name:
type: string
example: doggie
photoUrls:
type: array
xml:
name: photoUrl
wrapped: true
items:
type: string
tags:
type: array
xml:
name: tag
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
`;

View File

@@ -0,0 +1,934 @@
export const petStoreSpec = `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.27-SNAPSHOT
externalDocs:
description: Find out more about Swagger
url: https://swagger.io
servers:
- url: https://petstore3.swagger.io/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
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
/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
/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
/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
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
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
/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
/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
default:
description: Unexpected error
/store/order:
post:
tags:
- store
summary: Place an order for a pet.
description: Place a new order in the store.
x-swagger-router-controller: OrderController
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.
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/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.
x-swagger-router-controller: OrderController
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.
x-swagger-router-controller: UserController
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.
x-swagger-router-controller: UserController
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.
x-swagger-router-controller: UserController
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:
x-swagger-router-model: io.swagger.petstore.model.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:
x-swagger-router-model: io.swagger.petstore.model.Category
type: object
properties:
id:
type: integer
format: int64
example: 1
name:
type: string
example: Dogs
xml:
name: category
User:
x-swagger-router-model: io.swagger.petstore.model.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:
x-swagger-router-model: io.swagger.petstore.model.Tag
type: object
properties:
id:
type: integer
format: int64
name:
type: string
xml:
name: tag
Pet:
x-swagger-router-model: io.swagger.petstore.model.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'
`;
export const todoSpec = `openapi: 3.0.0
info:
title: Simple TODO API
version: 1.0.0
description: A minimal API to manage a list of TODO items.
contact:
name: API Support
email: support@example.com
servers:
- url: https://api.example.com
tags:
- name: Todos
description: Operations related to TODO items
paths:
/todos:
get:
summary: List all todos
description: Returns a list of all TODO items.
operationId: listTodos
tags: [Todos]
responses:
'200':
description: A JSON array of TODOs
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/Todo'
post:
summary: Create a new todo
description: Creates a new TODO item.
operationId: createTodo
tags: [Todos]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TodoInput'
responses:
'201':
description: The created TODO item
content:
application/json:
schema:
$ref: '#/components/schemas/Todo'
/todos/{id}:
parameters:
- in: path
name: id
required: true
description: The ID of the TODO item
schema:
type: string
get:
summary: Get a todo by ID
description: Retrieves a specific TODO item by its ID.
operationId: getTodoById
tags: [Todos]
responses:
'200':
description: The requested TODO item
content:
application/json:
schema:
$ref: '#/components/schemas/Todo'
'404':
description: TODO item not found
put:
summary: Update a todo by ID
description: Updates a specific TODO item by its ID.
operationId: updateTodo
tags: [Todos]
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/TodoInput'
responses:
'200':
description: The updated TODO item
content:
application/json:
schema:
$ref: '#/components/schemas/Todo'
'404':
description: TODO item not found
delete:
summary: Delete a todo by ID
description: Deletes a specific TODO item by its ID.
operationId: deleteTodo
tags: [Todos]
responses:
'204':
description: TODO item deleted
'404':
description: TODO item not found
components:
schemas:
Todo:
type: object
description: A TODO item
properties:
id:
type: string
description: Unique identifier for the TODO item
title:
type: string
description: The title of the TODO item
completed:
type: boolean
description: Indicates if the TODO item is completed
required:
- id
- title
- completed
TodoInput:
type: object
description: Input data for creating or updating a TODO item
properties:
title:
type: string
description: The title of the TODO item
completed:
type: boolean
description: Indicates if the TODO item is completed
required:
- title
- completed
`;
export const blankSpec = `openapi: 3.0.0
info:
title: API
version: 0.0.0
description: An API
contact:
name: Support
email: sup@rest.rodeo
servers:
- url: https://api.rest.rodeo
description: Live
paths:
/:
description: Base
`;

View File

@@ -918,7 +918,8 @@ const Design: FC = () => {
showPrettifyButton
ref={editor}
lintOptions={lintOptions}
mode="openapi"
// only set the openapi mode if there are contents
mode={apiSpec.contents ? 'openapi' : undefined}
defaultValue={apiSpec.contents || ''}
onChange={onCodeEditorChange}
uniquenessKey={uniquenessKey}
@@ -940,90 +941,92 @@ const Design: FC = () => {
/>
)}
</div>
<div
className={`flex ${isLintPaneOpen ? '' : 'h-[--line-height-sm]'} box-border flex-col divide-y divide-solid divide-[--hl-md] overflow-hidden`}
>
<div className="flex items-center gap-2 p-[--padding-sm]">
<TooltipTrigger>
<Button className="flex cursor-pointer select-none items-center gap-2">
<Icon icon={rulesetPath ? 'file-circle-check' : 'file-circle-xmark'} />
Ruleset
</Button>
<Tooltip
placement="top end"
offset={8}
className="max-h-[85vh] max-w-xs select-none overflow-y-auto rounded-md border border-solid border-[--hl-sm] bg-[--color-bg] px-4 py-2 text-sm text-[--color-font] shadow-lg focus:outline-none"
>
<div>
{rulesetPath ? (
<Fragment>
<p>Using ruleset from</p>
<code className="break-words p-0">{rulesetPath}</code>
</Fragment>
) : (
<Fragment>
<p>Using default OAS ruleset.</p>
<p>
To use a custom ruleset add a <code className="p-0">.spectral.yaml</code> file to the root
of your git repository
</p>
</Fragment>
)}
{apiSpec.contents ? (
<div
className={`flex ${isLintPaneOpen ? '' : 'h-[--line-height-sm]'} box-border flex-col divide-y divide-solid divide-[--hl-md] overflow-hidden`}
>
<div className="flex items-center gap-2 p-[--padding-sm]">
<TooltipTrigger>
<Button className="flex cursor-pointer select-none items-center gap-2">
<Icon icon={rulesetPath ? 'file-circle-check' : 'file-circle-xmark'} />
Ruleset
</Button>
<Tooltip
placement="top end"
offset={8}
className="max-h-[85vh] max-w-xs select-none overflow-y-auto rounded-md border border-solid border-[--hl-sm] bg-[--color-bg] px-4 py-2 text-sm text-[--color-font] shadow-lg focus:outline-none"
>
<div>
{rulesetPath ? (
<Fragment>
<p>Using ruleset from</p>
<code className="break-words p-0">{rulesetPath}</code>
</Fragment>
) : (
<Fragment>
<p>Using default OAS ruleset.</p>
<p>
To use a custom ruleset add a <code className="p-0">.spectral.yaml</code> file to the
root of your git repository
</p>
</Fragment>
)}
</div>
</Tooltip>
</TooltipTrigger>
{lintErrors.length > 0 && (
<div className="flex select-none items-center gap-2">
<Icon icon="circle-xmark" className="text-[--color-danger]" />
{lintErrors.length}
</div>
</Tooltip>
</TooltipTrigger>
{lintErrors.length > 0 && (
<div className="flex select-none items-center gap-2">
<Icon icon="circle-xmark" className="text-[--color-danger]" />
{lintErrors.length}
</div>
)}
{lintWarnings.length > 0 && (
<div className="flex select-none items-center gap-2">
<Icon icon="triangle-exclamation" className="text-[--color-warning]" />
{lintWarnings.length}
</div>
)}
{lintMessages.length === 0 && apiSpec.contents && (
<div className="flex select-none items-center gap-2">
<Icon icon="check-square" className="text-[--color-success]" />
No lint problems
</div>
)}
<span className="flex-1" />
{lintMessages.length > 0 && (
<Button aria-label="Toggle lint panel" onPress={() => setIsLintPaneOpen(!isLintPaneOpen)}>
<Icon icon={isLintPaneOpen ? 'chevron-down' : 'chevron-up'} />
</Button>
)}
{lintWarnings.length > 0 && (
<div className="flex select-none items-center gap-2">
<Icon icon="triangle-exclamation" className="text-[--color-warning]" />
{lintWarnings.length}
</div>
)}
{lintMessages.length === 0 && apiSpec.contents && (
<div className="flex select-none items-center gap-2">
<Icon icon="check-square" className="text-[--color-success]" />
No lint problems
</div>
)}
<span className="flex-1" />
{lintMessages.length > 0 && (
<Button aria-label="Toggle lint panel" onPress={() => setIsLintPaneOpen(!isLintPaneOpen)}>
<Icon icon={isLintPaneOpen ? 'chevron-down' : 'chevron-up'} />
</Button>
)}
</div>
{isLintPaneOpen && (
<ListBox
className="flex-1 select-none overflow-y-auto"
onAction={index => {
const listIndex = parseInt(index.toString(), 10);
const lintMessage = lintMessages[listIndex];
handleScrollToLintMessage(lintMessage);
}}
items={lintMessages.map((message, index) => ({
...message,
id: index,
value: message,
}))}
>
{item => (
<ListBoxItem className="flex items-center gap-2 p-[--padding-sm] text-xs outline-none transition-colors even:bg-[--hl-xs] focus-within:bg-[--hl-md] data-[focused]:bg-[--hl-md]">
<Icon
className={item.type === 'error' ? 'text-[--color-danger]' : 'text-[--color-warning]'}
icon={item.type === 'error' ? 'circle-xmark' : 'triangle-exclamation'}
/>
<span className="truncate">{item.message}</span>
<span className="flex-shrink-0 text-[--hl-lg]">[Ln {item.line}]</span>
</ListBoxItem>
)}
</ListBox>
)}
</div>
{isLintPaneOpen && (
<ListBox
className="flex-1 select-none overflow-y-auto"
onAction={index => {
const listIndex = parseInt(index.toString(), 10);
const lintMessage = lintMessages[listIndex];
handleScrollToLintMessage(lintMessage);
}}
items={lintMessages.map((message, index) => ({
...message,
id: index,
value: message,
}))}
>
{item => (
<ListBoxItem className="flex items-center gap-2 p-[--padding-sm] text-xs outline-none transition-colors even:bg-[--hl-xs] focus-within:bg-[--hl-md] data-[focused]:bg-[--hl-md]">
<Icon
className={item.type === 'error' ? 'text-[--color-danger]' : 'text-[--color-warning]'}
icon={item.type === 'error' ? 'circle-xmark' : 'triangle-exclamation'}
/>
<span className="truncate">{item.message}</span>
<span className="flex-shrink-0 text-[--hl-lg]">[Ln {item.line}]</span>
</ListBoxItem>
)}
</ListBox>
)}
</div>
) : null}
</div>
</Panel>
{isSpecPaneOpen && (