mirror of
https://github.com/hanzo-docs/docs.git
synced 2026-10-25 04:20:02 +00:00
* feat(openapi): anchor links on schema properties Each property rendered inline in a request body, response body, or parameter now gets a stable id and a hover-revealed link icon, enabling deep links to specific fields from guides. Ids follow: - body-<prop> for request bodies - response-<status>-<prop> for responses - parameters-<location>-<name> for parameters A new idPrefix option on SchemaUI opts other Schema renderings into the same behavior. See fuma-nama/fumadocs#3322. * feat(openapi): deep-link nested schema properties Extend the property anchor scheme to support hashes that point inside a popover or below an array's "Show array" collapsible. Schema-internal segments are joined with `.` so `#body.user.address.street` resolves unambiguously regardless of dashes in property names. On hash match, the inline anchor scrolls into view, the matching popover auto-opens with its breadcrumb pre-navigated, the array collapsible expands, and oneOf/anyOf tabs switch to the variant containing the targeted field. The deep target is consumed after the first auto-open, so subsequent user clicks on the same trigger get the base view; a different deep hash re-arms it. The hover anchor is now a copy-link button (matches fumadocs heading anchors) that writes the full URL to the clipboard and updates the address bar via history.replaceState (no hashchange, no popover reset). * address PR review feedback - Clarified the changeset: split anchor ID rules by context (request/response property paths vs flat parameter anchors) so dots aren't implied to apply to parameters. - Namespaced anchors by media type when an operation has multiple request-body or response content types: prefix becomes `<media-slug>-body` / `response-<status>-<media-slug>`, and the content-type SelectTabs auto-switches to the targeted tab when a deep link arrives. Single-content-type operations keep the simple `body` / `response-<status>` form. - Made findUnionPropMatch validate the *full* remaining path (via new pathResolves helper) so two variants that share the first prop name no longer race; the wrong tab can't be selected for a deep hash anymore. - Reset SchemaUIPopover breadcrumb state when initialPath changes so a fresh deep hash takes effect on an already-open popover. - Replaced the lossy slugifyPropertyName with encodeURIComponent (plus % encoding for `.`) so property anchors stay collision-safe across case-only differences, punctuation-only differences, and non-ASCII keys, while `.` remains exclusively a path separator. * address PR review feedback - Fix potential hydration mismatch in useTabValueFromHash by initializing state with the fallback value and resolving from the URL hash inside useEffect; the deep-linked tab now corrects after hydration on the client. * fix(openapi): move useTabValueFromHash into a client component The previous hydration-mismatch fix put `useEffect`/`useState` directly in `operation/index.tsx`, which is a Server Component — Next refused to compile because RSC modules can't import client-only React APIs. Extract the hash-aware tab wrapper into `operation/hash-tabs.tsx` (with `"use client"`) and switch both body and response sites to `HashAwareSelectTabs`. The parent tree stays server-side; only the tab wrapper hydrates on the client. * address PR review feedback - Wrap decodeURIComponent in try/catch in both deep-link hash parsers so a malformed percent-encoded fragment (e.g. `#body%`) is treated as "no target" instead of throwing URIError and breaking tab / popover resolution. * address PR review feedback - Match the URL hash against DOM ids using the raw fragment first, with the fully-decoded form as a fallback, in both DeepLinkProvider and hash-tabs. Property anchors are generated via slugifyPropertyName (which percent-encodes reserved chars like `.`), so DOM ids contain the encoded form; decoding the hash before lookup caused links like `#body.foo%2Ebar` to miss the matching element. * redesign * fix types --------- Co-authored-by: Fuma Nama <[email protected]>
1445 lines
43 KiB
YAML
1445 lines
43 KiB
YAML
openapi: 3.1.1
|
|
info:
|
|
title: Scalar Galaxy
|
|
description: |
|
|
The Scalar Galaxy is an example OpenAPI document to test OpenAPI tools and libraries. It's a fictional universe with fictional planets and fictional data.
|
|
|
|
## Resources
|
|
|
|
* https://github.com/scalar/scalar
|
|
* https://github.com/OAI/OpenAPI-Specification
|
|
* https://scalar.com
|
|
|
|
## Markdown Support
|
|
|
|
All descriptions *can* contain ~~tons of text~~ **Markdown**. [If GitHub supports the syntax](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax), chances are we're supporting it, too.
|
|
|
|
<details>
|
|
<summary>Examples</summary>
|
|
|
|
**Blockquotes**
|
|
|
|
> I love OpenAPI. <3
|
|
|
|
**Tables**
|
|
|
|
| Feature | Availability |
|
|
| ---------------- | ------------ |
|
|
| Markdown Support | ✓ |
|
|
|
|
**Accordion**
|
|
|
|
```html
|
|
<details>
|
|
<summary>Using Details Tags</summary>
|
|
<p>HTML Example</p>
|
|
</details>
|
|
```
|
|
|
|
**Images**
|
|
|
|
Yes, there's support for images, too!
|
|
|
|

|
|
|
|
**Alerts**
|
|
|
|
> [!tip]
|
|
> You can use Markdown alerts in your descriptions.
|
|
|
|
</details>
|
|
version: 1.0.0
|
|
contact:
|
|
name: Scalar Support
|
|
url: https://scalar.com
|
|
email: [email protected]
|
|
license:
|
|
name: MIT
|
|
url: https://opensource.org/license/MIT
|
|
externalDocs:
|
|
description: Documentation
|
|
url: https://github.com/scalar/scalar
|
|
servers:
|
|
- url: https://galaxy.scalar.com
|
|
- url: '{protocol}://void.scalar.com/{path}'
|
|
description: Responds with your request data
|
|
variables:
|
|
protocol:
|
|
enum:
|
|
- https
|
|
- http
|
|
default: https
|
|
path:
|
|
default: ''
|
|
security:
|
|
- bearerAuth: []
|
|
- basicAuth: []
|
|
- apiKeyQuery: []
|
|
- apiKeyHeader: []
|
|
- apiKeyHeader: []
|
|
apiKeyQuery: []
|
|
- apiKeyCookie: []
|
|
- oAuth2: []
|
|
- openIdConnect: []
|
|
x-speakeasy-webhooks:
|
|
security:
|
|
type: signature
|
|
headerName: x-signature
|
|
signatureTextEncoding: base64
|
|
algorithm: hmac-sha256
|
|
tags:
|
|
- name: Authentication
|
|
description: Some endpoints are public, but some require authentication. We
|
|
provide all the required endpoints to create an account and authorize
|
|
yourself.
|
|
- name: Planets
|
|
description: Everything about planets
|
|
- name: Celestial Bodies
|
|
description: Celestial bodies are the planets and satellites in the Scalar Galaxy.
|
|
paths:
|
|
/planets:
|
|
get:
|
|
tags:
|
|
- Planets
|
|
summary: Get all planets
|
|
description: It's easy to say you know them all, but do you really? Retrieve all
|
|
the planets and check whether you missed one.
|
|
operationId: getAllData
|
|
security: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/limit'
|
|
- $ref: '#/components/parameters/offset'
|
|
responses:
|
|
'200':
|
|
description: OK
|
|
headers:
|
|
X-Request-ID:
|
|
$ref: '#/components/headers/X-Request-ID'
|
|
X-Pagination-Total:
|
|
$ref: '#/components/headers/X-Pagination-Total'
|
|
X-Pagination-Page:
|
|
$ref: '#/components/headers/X-Pagination-Page'
|
|
X-Pagination-Per-Page:
|
|
$ref: '#/components/headers/X-Pagination-Per-Page'
|
|
X-Cache-Control:
|
|
$ref: '#/components/headers/X-Cache-Control'
|
|
content:
|
|
application/json:
|
|
schema:
|
|
allOf:
|
|
- type: object
|
|
properties:
|
|
data:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Planet'
|
|
- $ref: '#/components/schemas/PaginatedResource'
|
|
application/xml:
|
|
schema:
|
|
allOf:
|
|
- type: object
|
|
xml:
|
|
name: planets
|
|
properties:
|
|
data:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Planet'
|
|
- $ref: '#/components/schemas/PaginatedResource'
|
|
x-post-response: |
|
|
pm.test("Status code is 200", () => {
|
|
pm.response.to.have.status(200);
|
|
});
|
|
|
|
pm.test("Content-Type header is present", () => {
|
|
pm.response.to.have.header("Content-Type");
|
|
});
|
|
|
|
pm.test("Response has data array and meta", () => {
|
|
const jsonData = pm.response.json();
|
|
pm.expect(jsonData).to.have.property("data");
|
|
pm.expect(jsonData.data).to.be.an("array");
|
|
pm.expect(jsonData).to.have.property("meta");
|
|
});
|
|
post:
|
|
tags:
|
|
- Planets
|
|
summary: Create a planet
|
|
description: Time to play god and create a new planet. What do you think? Ah,
|
|
don't think too much. What could go wrong anyway?
|
|
operationId: createPlanet
|
|
callbacks:
|
|
planetCreated:
|
|
'{$request.body#/successCallbackUrl}':
|
|
post:
|
|
security: []
|
|
requestBody:
|
|
description: Information about the newly created planet
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
responses:
|
|
'200':
|
|
description: Your server returns this code if it accepts the callback
|
|
'204':
|
|
description: Your server should return this HTTP status code if no longer
|
|
interested in further updates
|
|
planetCreationFailed:
|
|
'{$request.body#/failureCallbackUrl}':
|
|
post:
|
|
security: []
|
|
requestBody:
|
|
description: Information about which fields failed to validate
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
responses:
|
|
'200':
|
|
description: Your server returns this code if it accepts the failure callback notification
|
|
planetExploded:
|
|
'{$request.body#/successCallbackUrl}':
|
|
post:
|
|
security: []
|
|
requestBody:
|
|
description: Information about the newly exploded planet
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
responses:
|
|
'200':
|
|
description: Your server returns this code if it accepts the planet explosion callback notification
|
|
requestBody:
|
|
description: Planet
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
responses:
|
|
'201':
|
|
description: Created
|
|
headers:
|
|
X-Request-ID:
|
|
$ref: '#/components/headers/X-Request-ID'
|
|
Location:
|
|
$ref: '#/components/headers/Location'
|
|
X-Processing-Time:
|
|
$ref: '#/components/headers/X-Processing-Time'
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
x-post-response: |
|
|
pm.test("Status code is 201", () => {
|
|
pm.response.to.have.status(201);
|
|
});
|
|
|
|
pm.test("Content-Type header is present", () => {
|
|
pm.response.to.have.header("Content-Type");
|
|
});
|
|
|
|
pm.test("Response is a planet with id and name", () => {
|
|
const jsonData = pm.response.json();
|
|
pm.expect(jsonData).to.have.property("id");
|
|
pm.expect(jsonData).to.have.property("name");
|
|
});
|
|
/planets/{planetId}:
|
|
get:
|
|
tags:
|
|
- Planets
|
|
summary: Get a planet
|
|
description: You'll better learn a little bit more about the planets. It might
|
|
come in handy once space travel is available for everyone.
|
|
operationId: getPlanet
|
|
security: []
|
|
parameters:
|
|
- $ref: '#/components/parameters/planetId'
|
|
responses:
|
|
'200':
|
|
description: Planet Found
|
|
headers:
|
|
X-Request-ID:
|
|
$ref: '#/components/headers/X-Request-ID'
|
|
ETag:
|
|
$ref: '#/components/headers/ETag'
|
|
Last-Modified:
|
|
$ref: '#/components/headers/Last-Modified'
|
|
X-Cache-Control:
|
|
$ref: '#/components/headers/X-Cache-Control'
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
x-post-response: |
|
|
pm.test("Status code is 200", () => {
|
|
pm.response.to.have.status(200);
|
|
});
|
|
|
|
pm.test("Content-Type header is present", () => {
|
|
pm.response.to.have.header("Content-Type");
|
|
});
|
|
|
|
pm.test("Response is a planet with id and name", () => {
|
|
const jsonData = pm.response.json();
|
|
pm.expect(jsonData).to.have.property("id");
|
|
pm.expect(jsonData).to.have.property("name");
|
|
});
|
|
put:
|
|
tags:
|
|
- Planets
|
|
summary: Update a planet
|
|
description: Sometimes you make mistakes, that's fine. No worries, you can
|
|
update all planets.
|
|
operationId: updatePlanet
|
|
requestBody:
|
|
description: New information about the planet
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
parameters:
|
|
- $ref: '#/components/parameters/planetId'
|
|
responses:
|
|
'200':
|
|
description: Planet updated successfully
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
x-post-response: |
|
|
pm.test("Status code is 200", () => {
|
|
pm.response.to.have.status(200);
|
|
});
|
|
|
|
pm.test("Content-Type header is present", () => {
|
|
pm.response.to.have.header("Content-Type");
|
|
});
|
|
|
|
pm.test("Response is a planet with id and name", () => {
|
|
const jsonData = pm.response.json();
|
|
pm.expect(jsonData).to.have.property("id");
|
|
pm.expect(jsonData).to.have.property("name");
|
|
});
|
|
delete:
|
|
tags:
|
|
- Planets
|
|
summary: Delete a planet
|
|
operationId: deletePlanet
|
|
description: This endpoint was used to delete planets. Unfortunately, that
|
|
caused a lot of trouble for planets with life. So, this endpoint is now
|
|
deprecated and should not be used anymore.
|
|
x-scalar-stability: experimental
|
|
parameters:
|
|
- $ref: '#/components/parameters/planetId'
|
|
responses:
|
|
'204':
|
|
description: No Content
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
x-post-response: |
|
|
pm.test("Status code is 204", () => {
|
|
pm.response.to.have.status(204);
|
|
});
|
|
/planets/{planetId}/image:
|
|
post:
|
|
tags:
|
|
- Planets
|
|
summary: Upload an image to a planet
|
|
description: Got a crazy good photo of a planet? Share it with the world!
|
|
operationId: uploadImage
|
|
parameters:
|
|
- $ref: '#/components/parameters/planetId'
|
|
requestBody:
|
|
description: Image to upload
|
|
content:
|
|
multipart/form-data:
|
|
schema:
|
|
type: object
|
|
properties:
|
|
image:
|
|
type: string
|
|
format: binary
|
|
description: The image file to upload
|
|
examples:
|
|
- '@mars.jpg'
|
|
- '@jupiter.png'
|
|
responses:
|
|
'200':
|
|
$ref: '#/components/responses/ImageUploaded'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'404':
|
|
$ref: '#/components/responses/NotFound'
|
|
x-post-response: |
|
|
pm.test("Status code is 200", () => {
|
|
pm.response.to.have.status(200);
|
|
});
|
|
|
|
pm.test("Content-Type header is present", () => {
|
|
pm.response.to.have.header("Content-Type");
|
|
});
|
|
|
|
pm.test("Response has message and imageUrl", () => {
|
|
const jsonData = pm.response.json();
|
|
pm.expect(jsonData).to.have.property("message");
|
|
pm.expect(jsonData).to.have.property("imageUrl");
|
|
});
|
|
/celestial-bodies:
|
|
post:
|
|
tags:
|
|
- Celestial Bodies
|
|
summary: Create a celestial body
|
|
operationId: createCelestialBody
|
|
requestBody:
|
|
description: Celestial body to create
|
|
required: true
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CelestialBody'
|
|
responses:
|
|
'201':
|
|
description: Celestial body created
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/CelestialBody'
|
|
x-post-response: |
|
|
pm.test("Status code is 201", () => {
|
|
pm.response.to.have.status(201);
|
|
});
|
|
|
|
pm.test("Content-Type header is present", () => {
|
|
pm.response.to.have.header("Content-Type");
|
|
});
|
|
|
|
pm.test("Response is a celestial body with id and name", () => {
|
|
const jsonData = pm.response.json();
|
|
pm.expect(jsonData).to.have.property("id");
|
|
pm.expect(jsonData).to.have.property("name");
|
|
});
|
|
/user/signup:
|
|
post:
|
|
tags:
|
|
- Authentication
|
|
summary: Create a user
|
|
description: Time to create a user account, eh?
|
|
operationId: createUser
|
|
security: []
|
|
requestBody:
|
|
description: User to create
|
|
content:
|
|
application/json:
|
|
schema:
|
|
allOf:
|
|
- $ref: '#/components/schemas/User'
|
|
- $ref: '#/components/schemas/Credentials'
|
|
examples:
|
|
Marc:
|
|
value:
|
|
name: Marc
|
|
email: [email protected]
|
|
password: i-love-scalar
|
|
Cam:
|
|
value:
|
|
name: Cam
|
|
email: [email protected]
|
|
password: scalar-is-cool
|
|
Hans:
|
|
value:
|
|
name: Hans
|
|
email: [email protected]
|
|
password: 5c4l4r
|
|
application/xml:
|
|
schema:
|
|
allOf:
|
|
- $ref: '#/components/schemas/User'
|
|
- $ref: '#/components/schemas/Credentials'
|
|
examples:
|
|
Marc:
|
|
value:
|
|
name: Marc
|
|
email: [email protected]
|
|
password: i-love-scalar
|
|
Cam:
|
|
value:
|
|
name: Cam
|
|
email: [email protected]
|
|
password: scalar-is-cool
|
|
Hans:
|
|
value:
|
|
name: Hans
|
|
email: [email protected]
|
|
password: 5c4l4r
|
|
responses:
|
|
'201':
|
|
description: User account created successfully
|
|
headers:
|
|
X-Request-ID:
|
|
$ref: '#/components/headers/X-Request-ID'
|
|
Location:
|
|
$ref: '#/components/headers/Location'
|
|
X-Processing-Time:
|
|
$ref: '#/components/headers/X-Processing-Time'
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/User'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/User'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'409':
|
|
$ref: '#/components/responses/Conflict'
|
|
'422':
|
|
$ref: '#/components/responses/UnprocessableEntity'
|
|
x-post-response: |
|
|
pm.test("Status code is 201", () => {
|
|
pm.response.to.have.status(201);
|
|
});
|
|
|
|
pm.test("Content-Type header is present", () => {
|
|
pm.response.to.have.header("Content-Type");
|
|
});
|
|
|
|
pm.test("Response is a user with id and name", () => {
|
|
const jsonData = pm.response.json();
|
|
pm.expect(jsonData).to.have.property("id");
|
|
pm.expect(jsonData).to.have.property("name");
|
|
});
|
|
/auth/token:
|
|
post:
|
|
tags:
|
|
- Authentication
|
|
summary: Get a token
|
|
description: Yeah, this is the boring security stuff. Just get your super secret
|
|
token and move on.
|
|
operationId: getToken
|
|
security: []
|
|
requestBody:
|
|
description: Body for credentials to authenticate a user
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Credentials'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/Credentials'
|
|
responses:
|
|
'201':
|
|
description: Token Created
|
|
headers:
|
|
X-Request-ID:
|
|
$ref: '#/components/headers/X-Request-ID'
|
|
X-RateLimit-Limit:
|
|
$ref: '#/components/headers/X-RateLimit-Limit'
|
|
X-RateLimit-Remaining:
|
|
$ref: '#/components/headers/X-RateLimit-Remaining'
|
|
X-RateLimit-Reset:
|
|
$ref: '#/components/headers/X-RateLimit-Reset'
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Token'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/Token'
|
|
'400':
|
|
$ref: '#/components/responses/BadRequest'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
'429':
|
|
$ref: '#/components/responses/TooManyRequests'
|
|
x-post-response: |
|
|
pm.test("Status code is 201", () => {
|
|
pm.response.to.have.status(201);
|
|
});
|
|
|
|
pm.test("Content-Type header is present", () => {
|
|
pm.response.to.have.header("Content-Type");
|
|
});
|
|
|
|
pm.test("Response contains token", () => {
|
|
const jsonData = pm.response.json();
|
|
pm.expect(jsonData).to.have.property("token");
|
|
pm.expect(jsonData.token).to.be.an("string");
|
|
});
|
|
/me:
|
|
get:
|
|
tags:
|
|
- Authentication
|
|
summary: Get authenticated user
|
|
description: Find yourself they say. That's what you can do here.
|
|
operationId: getMe
|
|
security:
|
|
- basicAuth: []
|
|
- oAuth2:
|
|
- read:account
|
|
- bearerAuth: []
|
|
- apiKeyHeader: []
|
|
- apiKeyQuery: []
|
|
- apiKeyHeader: []
|
|
apiKeyQuery: []
|
|
responses:
|
|
'200':
|
|
description: Authenticated user information retrieved successfully
|
|
headers:
|
|
X-Request-ID:
|
|
$ref: '#/components/headers/X-Request-ID'
|
|
ETag:
|
|
$ref: '#/components/headers/ETag'
|
|
Last-Modified:
|
|
$ref: '#/components/headers/Last-Modified'
|
|
X-Cache-Control:
|
|
$ref: '#/components/headers/X-Cache-Control'
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/User'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/User'
|
|
'401':
|
|
$ref: '#/components/responses/Unauthorized'
|
|
'403':
|
|
$ref: '#/components/responses/Forbidden'
|
|
x-post-response: |
|
|
pm.test("Status code is 200", () => {
|
|
pm.response.to.have.status(200);
|
|
});
|
|
|
|
pm.test("Content-Type header is present", () => {
|
|
pm.response.to.have.header("Content-Type");
|
|
});
|
|
|
|
pm.test("Response is a user with id and name", () => {
|
|
const jsonData = pm.response.json();
|
|
pm.expect(jsonData).to.have.property("id");
|
|
pm.expect(jsonData).to.have.property("name");
|
|
});
|
|
webhooks:
|
|
newPlanet:
|
|
post:
|
|
tags:
|
|
- Planets
|
|
security: []
|
|
requestBody:
|
|
description: Information about a new planet
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Planet'
|
|
responses:
|
|
'200':
|
|
description: Return a 200 status to indicate that the data was received
|
|
successfully
|
|
x-post-response: |
|
|
pm.test("Status code is 200", () => {
|
|
pm.response.to.have.status(200);
|
|
});
|
|
components:
|
|
securitySchemes:
|
|
bearerAuth:
|
|
type: http
|
|
scheme: bearer
|
|
description: JWT Bearer token authentication
|
|
basicAuth:
|
|
type: http
|
|
scheme: basic
|
|
description: Basic HTTP authentication
|
|
apiKeyHeader:
|
|
type: apiKey
|
|
in: header
|
|
name: X-API-Key
|
|
description: API key request header
|
|
apiKeyQuery:
|
|
type: apiKey
|
|
in: query
|
|
name: api_key
|
|
description: API key query parameter
|
|
apiKeyCookie:
|
|
type: apiKey
|
|
in: cookie
|
|
name: api_key
|
|
description: API key browser cookie
|
|
oAuth2:
|
|
type: oauth2
|
|
description: OAuth 2.0 authentication
|
|
flows:
|
|
authorizationCode:
|
|
authorizationUrl: https://galaxy.scalar.com/oauth/authorize
|
|
tokenUrl: https://galaxy.scalar.com/oauth/token
|
|
scopes:
|
|
read:account: read your account information
|
|
write:planets: modify planets in your account
|
|
read:planets: read your planets
|
|
clientCredentials:
|
|
tokenUrl: https://galaxy.scalar.com/oauth/token
|
|
scopes:
|
|
read:account: read your account information
|
|
write:planets: modify planets in your account
|
|
read:planets: read your planets
|
|
# Legacy
|
|
implicit:
|
|
authorizationUrl: https://galaxy.scalar.com/oauth/authorize
|
|
scopes:
|
|
read:account: read your account information
|
|
write:planets: modify planets in your account
|
|
read:planets: read your planets
|
|
# Legacy
|
|
password:
|
|
tokenUrl: https://galaxy.scalar.com/oauth/token
|
|
scopes:
|
|
read:account: read your account information
|
|
write:planets: modify planets in your account
|
|
read:planets: read your planets
|
|
openIdConnect:
|
|
type: openIdConnect
|
|
openIdConnectUrl: https://galaxy.scalar.com/.well-known/openid-configuration
|
|
description: OpenID Connect Authentication
|
|
parameters:
|
|
planetId:
|
|
name: planetId
|
|
description: The ID of the planet to get
|
|
in: path
|
|
required: true
|
|
schema:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 1
|
|
limit:
|
|
name: limit
|
|
description: The number of items to return
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: integer
|
|
format: int64
|
|
default: 10
|
|
offset:
|
|
name: offset
|
|
description: The number of items to skip before starting to collect the result set
|
|
in: query
|
|
required: false
|
|
schema:
|
|
type: integer
|
|
format: int64
|
|
default: 0
|
|
headers:
|
|
X-RateLimit-Limit:
|
|
description: The number of allowed requests in the current period
|
|
schema:
|
|
type: integer
|
|
example: 100
|
|
X-RateLimit-Remaining:
|
|
description: The number of remaining requests in the current period
|
|
schema:
|
|
type: integer
|
|
example: 95
|
|
X-RateLimit-Reset:
|
|
description: The number of seconds left in the current period
|
|
schema:
|
|
type: integer
|
|
example: 3600
|
|
X-Request-ID:
|
|
description: Unique identifier for the request
|
|
schema:
|
|
type: string
|
|
format: uuid
|
|
example: 123e4567-e89b-12d3-a456-426614174000
|
|
X-Pagination-Total:
|
|
description: Total number of items available
|
|
schema:
|
|
type: integer
|
|
example: 1000
|
|
X-Pagination-Page:
|
|
description: Current page number
|
|
schema:
|
|
type: integer
|
|
example: 1
|
|
X-Pagination-Per-Page:
|
|
description: Number of items per page
|
|
schema:
|
|
type: integer
|
|
example: 10
|
|
X-Cache-Control:
|
|
description: Cache control directives
|
|
schema:
|
|
type: string
|
|
example: max-age=3600, public
|
|
ETag:
|
|
description: Entity tag for caching
|
|
schema:
|
|
type: string
|
|
example: '"33a64df551"'
|
|
Last-Modified:
|
|
description: The last modification date of the resource
|
|
schema:
|
|
type: string
|
|
format: date-time
|
|
example: 2023-12-01T12:00:00Z
|
|
Location:
|
|
description: The URL of the newly created resource
|
|
schema:
|
|
type: string
|
|
format: uri
|
|
example: https://galaxy.scalar.com/planets/123
|
|
X-Processing-Time:
|
|
description: The time taken to process the request in milliseconds
|
|
schema:
|
|
type: integer
|
|
example: 150
|
|
responses:
|
|
ImageUploaded:
|
|
description: Image uploaded
|
|
headers:
|
|
X-Request-ID:
|
|
$ref: '#/components/headers/X-Request-ID'
|
|
X-Cache-Control:
|
|
$ref: '#/components/headers/X-Cache-Control'
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ImageUploadedMessage'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/ImageUploadedMessage'
|
|
BadRequest:
|
|
description: Bad Request
|
|
headers:
|
|
X-Request-ID:
|
|
$ref: '#/components/headers/X-Request-ID'
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/BadRequestError'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/BadRequestError'
|
|
Forbidden:
|
|
description: Forbidden
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/ForbiddenError'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/ForbiddenError'
|
|
NotFound:
|
|
description: Not Found
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/NotFoundError'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/NotFoundError'
|
|
Unauthorized:
|
|
description: Unauthorized
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UnauthorizedError'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/UnauthorizedError'
|
|
Conflict:
|
|
description: Conflict
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/Conflict'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/Conflict'
|
|
UnprocessableEntity:
|
|
description: Unprocessable Entity
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/UnprocessableEntity'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/UnprocessableEntity'
|
|
TooManyRequests:
|
|
description: Too Many Requests
|
|
headers:
|
|
X-Request-ID:
|
|
$ref: '#/components/headers/X-Request-ID'
|
|
X-RateLimit-Limit:
|
|
$ref: '#/components/headers/X-RateLimit-Limit'
|
|
X-RateLimit-Remaining:
|
|
$ref: '#/components/headers/X-RateLimit-Remaining'
|
|
X-RateLimit-Reset:
|
|
$ref: '#/components/headers/X-RateLimit-Reset'
|
|
Retry-After:
|
|
description: The number of seconds to wait before retrying
|
|
schema:
|
|
type: integer
|
|
example: 60
|
|
content:
|
|
application/json:
|
|
schema:
|
|
$ref: '#/components/schemas/TooManyRequestsError'
|
|
application/xml:
|
|
schema:
|
|
$ref: '#/components/schemas/TooManyRequestsError'
|
|
schemas:
|
|
User:
|
|
description: A user
|
|
type: object
|
|
xml:
|
|
name: user
|
|
properties:
|
|
id:
|
|
type: integer
|
|
format: int64
|
|
readOnly: true
|
|
examples:
|
|
- 1
|
|
name:
|
|
type: string
|
|
examples:
|
|
- Marc
|
|
- Cam
|
|
- Hans
|
|
Credentials:
|
|
description: Credentials to authenticate a user
|
|
type: object
|
|
required:
|
|
- email
|
|
- password
|
|
properties:
|
|
email:
|
|
type: string
|
|
format: email
|
|
examples:
|
|
- [email protected]
|
|
- [email protected]
|
|
- [email protected]
|
|
password:
|
|
type: string
|
|
writeOnly: true
|
|
examples:
|
|
- i-love-scalar
|
|
- i-love-oss
|
|
- qwerty123
|
|
Token:
|
|
description: A token to authenticate a user
|
|
type: object
|
|
properties:
|
|
token:
|
|
type: string
|
|
examples:
|
|
- eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
|
|
CelestialBody:
|
|
oneOf:
|
|
- $ref: '#/components/schemas/Planet'
|
|
- $ref: '#/components/schemas/Satellite'
|
|
discriminator:
|
|
propertyName: type
|
|
mapping:
|
|
terrestrial: '#/components/schemas/Planet'
|
|
gas_giant: '#/components/schemas/Planet'
|
|
ice_giant: '#/components/schemas/Planet'
|
|
dwarf: '#/components/schemas/Planet'
|
|
super_earth: '#/components/schemas/Planet'
|
|
moon: '#/components/schemas/Satellite'
|
|
asteroid: '#/components/schemas/Satellite'
|
|
comet: '#/components/schemas/Satellite'
|
|
description: A celestial body which can be either a planet or a satellite
|
|
Planet:
|
|
description: A planet in the Scalar Galaxy
|
|
type: object
|
|
required:
|
|
- id
|
|
- name
|
|
additionalProperties: false
|
|
xml:
|
|
name: planet
|
|
properties:
|
|
id:
|
|
type: integer
|
|
format: int64
|
|
readOnly: true
|
|
examples:
|
|
- 1
|
|
x-variable: planetId
|
|
name:
|
|
type: string
|
|
examples:
|
|
- Mars
|
|
- Jupiter
|
|
- HD 40307g
|
|
description:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
examples:
|
|
- The red planet
|
|
- A gas giant with a great red spot
|
|
type:
|
|
type: string
|
|
enum:
|
|
- terrestrial
|
|
- gas_giant
|
|
- ice_giant
|
|
- dwarf
|
|
- super_earth
|
|
x-enum-varnames:
|
|
- Terrestrial
|
|
- GasGiant
|
|
- IceGiant
|
|
- Dwarf
|
|
- SuperEarth
|
|
x-enum-descriptions:
|
|
terrestrial: Rocky planets with solid surfaces, like Earth and Mars
|
|
gas_giant: Large planets composed mainly of hydrogen and helium, like Jupiter
|
|
and Saturn
|
|
ice_giant: Planets composed of water, ammonia, and methane ices, like Uranus and
|
|
Neptune
|
|
dwarf: Small planetary bodies that don't meet full planet criteria, like Pluto
|
|
super_earth: Rocky planets larger than Earth but smaller than gas giants
|
|
examples:
|
|
- terrestrial
|
|
habitabilityIndex:
|
|
type: number
|
|
format: float
|
|
minimum: 0
|
|
maximum: 1
|
|
description: A score from 0 to 1 indicating potential habitability
|
|
examples:
|
|
- 0.68
|
|
physicalProperties:
|
|
type: object
|
|
additionalProperties:
|
|
x-additionalPropertiesName: measurement
|
|
type: number
|
|
format: float
|
|
description: Additional physical measurements for the planet
|
|
properties:
|
|
mass:
|
|
type: number
|
|
format: float
|
|
exclusiveMinimum: 0
|
|
description: Mass in Earth masses (must be greater than 0)
|
|
examples:
|
|
- 0.107
|
|
radius:
|
|
type: number
|
|
format: float
|
|
exclusiveMinimum: 0
|
|
description: Radius in Earth radii (must be greater than 0)
|
|
examples:
|
|
- 0.532
|
|
gravity:
|
|
type: number
|
|
format: float
|
|
description: Surface gravity in Earth g
|
|
examples:
|
|
- 0.378
|
|
temperature:
|
|
type: object
|
|
additionalProperties:
|
|
x-additionalPropertiesName: temperatureMetric
|
|
type: number
|
|
format: float
|
|
description: Additional temperature-related measurements in Kelvin
|
|
properties:
|
|
min:
|
|
type: number
|
|
format: float
|
|
description: Minimum temperature in Kelvin
|
|
examples:
|
|
- 130
|
|
max:
|
|
type: number
|
|
format: float
|
|
description: Maximum temperature in Kelvin
|
|
examples:
|
|
- 308
|
|
average:
|
|
type: number
|
|
format: float
|
|
description: Average temperature in Kelvin
|
|
examples:
|
|
- 210
|
|
atmosphere:
|
|
type: array
|
|
description: Atmospheric composition
|
|
items:
|
|
type: object
|
|
additionalProperties:
|
|
x-additionalPropertiesName: atmosphericData
|
|
type: string
|
|
description: Additional atmospheric composition data
|
|
properties:
|
|
compound:
|
|
type: string
|
|
examples:
|
|
- CO2
|
|
- N2
|
|
percentage:
|
|
type: number
|
|
format: float
|
|
exclusiveMaximum: 100
|
|
examples:
|
|
- 95.3
|
|
discoveredAt:
|
|
type: string
|
|
format: date-time
|
|
examples:
|
|
- 1610-01-07T00:00:00Z
|
|
image:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
examples:
|
|
- https://cdn.scalar.com/photos/mars.jpg
|
|
satellites:
|
|
type: array
|
|
items:
|
|
$ref: '#/components/schemas/Satellite'
|
|
creator:
|
|
$ref: '#/components/schemas/User'
|
|
tags:
|
|
type: array
|
|
items:
|
|
type: string
|
|
examples:
|
|
- - solar-system
|
|
- rocky
|
|
- explored
|
|
lastUpdated:
|
|
type: string
|
|
format: date-time
|
|
readOnly: true
|
|
examples:
|
|
- 2024-01-15T14:30:00Z
|
|
successCallbackUrl:
|
|
type: string
|
|
format: uri
|
|
description: URL which gets invoked upon a successful operation
|
|
examples:
|
|
- https://example.com/webhook
|
|
failureCallbackUrl:
|
|
type: string
|
|
format: uri
|
|
description: URL which gets invoked upon a failed operation
|
|
examples:
|
|
- https://example.com/webhook
|
|
Satellite:
|
|
description: Every satellite in the Scalar Galaxy
|
|
type: object
|
|
required:
|
|
- name
|
|
properties:
|
|
id:
|
|
type: integer
|
|
format: int64
|
|
readOnly: true
|
|
examples:
|
|
- 1
|
|
name:
|
|
type: string
|
|
examples:
|
|
- Phobos
|
|
description:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
examples:
|
|
- Phobos is the larger and innermost of the two moons of Mars.
|
|
diameter:
|
|
type: number
|
|
format: float
|
|
description: Diameter in kilometers
|
|
examples:
|
|
- 22.2
|
|
type:
|
|
type: string
|
|
enum:
|
|
- moon
|
|
- asteroid
|
|
- comet
|
|
x-enum-varnames:
|
|
- Moon
|
|
- Asteroid
|
|
- Comet
|
|
x-enum-descriptions:
|
|
moon: Natural satellites that orbit planets
|
|
asteroid: Rocky objects that orbit the sun, typically found in the asteroid belt
|
|
comet: Icy bodies that release gas when approaching the sun, creating visible
|
|
tails
|
|
examples:
|
|
- moon
|
|
orbit:
|
|
type: object
|
|
properties:
|
|
planet:
|
|
$ref: '#/components/schemas/Planet'
|
|
orbitalPeriod:
|
|
type: number
|
|
format: float
|
|
description: Orbital period in Earth days
|
|
examples:
|
|
- 0.319
|
|
distance:
|
|
type: number
|
|
format: float
|
|
description: Average distance from the planet in kilometers
|
|
examples:
|
|
- 9376
|
|
PaginatedResource:
|
|
description: A paginated resource
|
|
type: object
|
|
properties:
|
|
meta:
|
|
type: object
|
|
properties:
|
|
limit:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 10
|
|
offset:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 0
|
|
total:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 100
|
|
next:
|
|
type:
|
|
- string
|
|
- 'null'
|
|
examples:
|
|
- /planets?limit=10&offset=10
|
|
ImageUploadedMessage:
|
|
x-scalar-ignore: true
|
|
description: Message about an image upload
|
|
type: object
|
|
properties:
|
|
message:
|
|
type: string
|
|
examples:
|
|
- Image uploaded successfully
|
|
imageUrl:
|
|
type: string
|
|
description: The URL where the uploaded image can be accessed
|
|
examples:
|
|
- https://cdn.scalar.com/images/8f47c132-9d1f-4f83-b5a4-91db5ee757ab.jpg
|
|
uploadedAt:
|
|
type: string
|
|
format: date-time
|
|
description: Timestamp when the image was uploaded
|
|
examples:
|
|
- 2024-01-15T14:30:00Z
|
|
fileSize:
|
|
type: integer
|
|
description: Size of the uploaded image in bytes
|
|
examples:
|
|
- 1048576
|
|
mimeType:
|
|
type: string
|
|
description: The content type of the uploaded image
|
|
examples:
|
|
- image/jpeg
|
|
- image/png
|
|
BadRequestError:
|
|
x-scalar-ignore: true
|
|
description: RFC 7807 (https://datatracker.ietf.org/doc/html/rfc7807)
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
examples:
|
|
- https://example.com/errors/bad-request
|
|
title:
|
|
type: string
|
|
examples:
|
|
- Bad Request
|
|
status:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 400
|
|
detail:
|
|
type: string
|
|
examples:
|
|
- The request was invalid.
|
|
ForbiddenError:
|
|
x-scalar-ignore: true
|
|
description: Error response for forbidden access (RFC 7807). Returned when the user does not have permission to access the requested resource.
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
examples:
|
|
- https://example.com/errors/forbidden
|
|
title:
|
|
type: string
|
|
examples:
|
|
- Forbidden
|
|
status:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 403
|
|
detail:
|
|
type: string
|
|
examples:
|
|
- You are not authorized to access this resource.
|
|
NotFoundError:
|
|
x-scalar-ignore: true
|
|
description: Error response for resource not found (RFC 7807). Returned when the requested resource does not exist.
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
examples:
|
|
- https://example.com/errors/not-found
|
|
title:
|
|
type: string
|
|
examples:
|
|
- Not Found
|
|
status:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 404
|
|
detail:
|
|
type: string
|
|
examples:
|
|
- The resource you are trying to access does not exist.
|
|
UnauthorizedError:
|
|
x-scalar-ignore: true
|
|
description: Error response for unauthorized access (RFC 7807). Returned when authentication is required or has failed.
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
examples:
|
|
- https://example.com/errors/not-found
|
|
title:
|
|
type: string
|
|
examples:
|
|
- Unauthorized
|
|
status:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 401
|
|
detail:
|
|
type: string
|
|
examples:
|
|
- You are not authorized to access this resource.
|
|
Conflict:
|
|
x-scalar-ignore: true
|
|
description: Error response for resource conflicts (RFC 7807). Returned when the request conflicts with the current state of the resource.
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
examples:
|
|
- https://example.com/errors/conflict
|
|
title:
|
|
type: string
|
|
examples:
|
|
- Conflict
|
|
status:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 409
|
|
detail:
|
|
type: string
|
|
examples:
|
|
- The resource you are trying to access is in conflict.
|
|
UnprocessableEntity:
|
|
x-scalar-ignore: true
|
|
description: Error response for unprocessable entity (RFC 7807). Returned when the request is well-formed but contains semantic errors.
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
examples:
|
|
- https://example.com/errors/unprocessable-entity
|
|
title:
|
|
type: string
|
|
examples:
|
|
- Unprocessable Entity
|
|
status:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 422
|
|
detail:
|
|
type: string
|
|
examples:
|
|
- The request was invalid.
|
|
TooManyRequestsError:
|
|
x-scalar-ignore: true
|
|
description: Error response for rate limiting (RFC 7807). Returned when the client has exceeded the rate limit for requests.
|
|
type: object
|
|
properties:
|
|
type:
|
|
type: string
|
|
examples:
|
|
- https://example.com/errors/too-many-requests
|
|
title:
|
|
type: string
|
|
examples:
|
|
- Too Many Requests
|
|
status:
|
|
type: integer
|
|
format: int64
|
|
examples:
|
|
- 429
|
|
detail:
|
|
type: string
|
|
examples:
|
|
- Rate limit exceeded. Please try again later.
|