Skip to content

Commit

Permalink
feat(oas): adding support for common parameter detection to the analy…
Browse files Browse the repository at this point in the history
…zer (#919)

## 🧰 Changes

This adds support to `oas/analyzer` for surfacing information on if a
given API definition utilizes [common
parameters](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object).

I've also gone through and updated a bunch of bad deep links to parts of
the OAS. Would be nice if the OAI would stop breaking these deep links
every couple of years.
  • Loading branch information
erunion authored Dec 16, 2024
1 parent 1a67b47 commit 05ad5c1
Show file tree
Hide file tree
Showing 15 changed files with 136 additions and 98 deletions.
17 changes: 9 additions & 8 deletions packages/oas/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,8 @@ Because this library has full TypeScript types and docblocks this README is not
<!-- prettier-ignore-start -->
| Method | Description |
| :--- | :--- |
| `.getExtension()` | Retrieve a given [specification extension](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#specificationExtensions) if it exists at the root of the API definition. |
| `.hasExtension()` | Determine if a given [specification extension](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#specificationExtensions) exists on the root of the API definition. |
| `.getExtension()` | Retrieve a given [specification extension](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#specification-extensions) if it exists at the root of the API definition. |
| `.hasExtension()` | Determine if a given [specification extension](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#specification-extensions) exists on the root of the API definition. |
| `.validateExtension()` | Determine if a given [ReadMe custom OpenAPI extension](https://docs.readme.com/docs/openapi-extensions) is valid or not. |
| `.validateExtensions()` | Validate all of our [ReadMe custom OpenAPI extension](https://docs.readme.com/docs/openapi-extensions), throwing exceptions when necessary. |
<!-- prettier-ignore-end -->
Expand Down Expand Up @@ -153,7 +153,7 @@ const operation = petstore.operation('/pet', 'post');
| Method | Description |
| :--- | :--- |
| `.getContentType()` | Retrieve the primary request body content type. If multiple are present, prefer whichever is JSON-compliant. |
| `.getDescription()` | Retrieve the `description` that's set on this operation. This supports common descriptions that may be set at the [path item level](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#pathItemObject). |
| `.getDescription()` | Retrieve the `description` that's set on this operation. This supports common descriptions that may be set at the [path item level](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object). |
| `.getOperationId()` | Retrieve the `operationId` that's present on the operation, and if one is not present one will be created based off the method + path and returned instead. |
| `.hasOperationId()` | Determine if the operation has an `operationId` present. |
| `.isDeprecated()` | Determine if this operation is marked as deprecated. |
Expand All @@ -164,8 +164,8 @@ const operation = petstore.operation('/pet', 'post');
| `.isWebhook()` | Determine if this operation is an instance of the `Webhook` class. |
| `.getExampleGroups()` | Returns an object with groups of all example definitions (body/header/query/path/response/etc.). The examples are grouped by their key when defined via the `examples` map. |
| `.getHeaders()` | Retrieve all headers that can either be sent for or returned from this operation. This includes header-based authentication schemes, common header parameters, and request body and response content types. |
| `.getSummary()` | Retrieve the `summary` that's set on this operation. This supports common summaries that may be set at the [path item level](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#pathItemObject). |
| `.getTags()` | Retrieve all tags, and [their metadata](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#tagObject), that exist on this operation. |
| `.getSummary()` | Retrieve the `summary` that's set on this operation. This supports common summaries that may be set at the [path item level](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object). |
| `.getTags()` | Retrieve all tags, and [their metadata](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#tag-object), that exist on this operation. |
<!-- prettier-ignore-end -->

#### Callbacks
Expand All @@ -182,7 +182,7 @@ const operation = petstore.operation('/pet', 'post');
#### Parameters

> [!NOTE]
> All parameter accessors here support, and will automatically retrieve and handle, common parameters that may be set at the [path item level](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#pathItemObject).
> All parameter accessors here support, and will automatically retrieve and handle, common parameters that may be set at the [path item level](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object).
<!-- prettier-ignore-start -->
| Method | Description |
Expand Down Expand Up @@ -231,14 +231,14 @@ const operation = petstore.operation('/pet', 'post');
<!-- prettier-ignore-start -->
| Method | Description |
| :--- | :--- |
| `.hasExtension()` | Determine if a given [specification extension](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#specificationExtensions) exists on this operation. |
| `.hasExtension()` | Determine if a given [specification extension](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#specification-extensions) exists on this operation. |
<!-- prettier-ignore-end -->

Information about ReadMe's supported OpenAPI extensions at https://docs.readme.com/docs/openapi-extensions.

### Callbacks

The `Callback` class inherits `Operation` so every API available on instances of `Operation` is available here too. Much like `Operation`, we also support common parameters, summaries, and descriptions that may be set at the [path item level](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#pathItemObject) within a `callbacks` definition.
The `Callback` class inherits `Operation` so every API available on instances of `Operation` is available here too. Much like `Operation`, we also support common parameters, summaries, and descriptions that may be set at the [path item level](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object) within a `callbacks` definition.

#### General

Expand Down Expand Up @@ -285,6 +285,7 @@ console.log(await analyzer(petstore));
| `additionalProperties` | Does your API use `additionalProperties`? |
| `callbacks` | Does your API use [callbacks](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#callback-object)? |
| `circularRefs` | Does your API have any circular `$ref` pointers, and if so where are they located? |
| `commonParameters` | Does your API utilize [common parameters](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object)? |
| `discriminators` | Does your API use polymorphic [discriminators](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#discriminator-object)? |
| `links` | Does your API use [links](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#link-object)? |
| `style` | Do any parameters in your API require [style](https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#user-content-parameterstyle) serialization?
Expand Down
5 changes: 5 additions & 0 deletions packages/oas/src/analyzer/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export default async function analyzer(definition: OASDocument): Promise<OASAnal
const additionalProperties = OPENAPI_QUERIES.additionalProperties(definition);
const callbacks = OPENAPI_QUERIES.callbacks(definition);
const circularRefs = await OPENAPI_QUERIES.circularRefs(definition);
const commonParameters = OPENAPI_QUERIES.commonParameters(definition);
const discriminators = OPENAPI_QUERIES.discriminators(definition);
const links = OPENAPI_QUERIES.links(definition);
const parameterSerialization = OPENAPI_QUERIES.parameterSerialization(definition);
Expand Down Expand Up @@ -60,6 +61,10 @@ export default async function analyzer(definition: OASDocument): Promise<OASAnal
present: !!circularRefs.length,
locations: circularRefs,
},
commonParameters: {
present: !!commonParameters.length,
locations: commonParameters,
},
discriminators: {
present: !!discriminators.length,
locations: discriminators,
Expand Down
40 changes: 25 additions & 15 deletions packages/oas/src/analyzer/queries/openapi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export function additionalProperties(definition: OASDocument) {
/**
* Determine if a given API definition utilizes `callbacks`.
*
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#callbackObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#callbackObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#callback-object}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#callback-object}
*/
export function callbacks(definition: OASDocument) {
return query(['$.components.callbacks', '$.paths..callbacks'], definition).map(res => refizePointer(res.pointer));
Expand All @@ -42,11 +42,21 @@ export async function circularRefs(definition: OASDocument) {
return results;
}

/**
* Determine if a given API definition utilizes common parameters.
*
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#path-item-object}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#path-item-object}
*/
export function commonParameters(definition: OASDocument) {
return query(['$..paths[*].parameters'], definition).map(res => refizePointer(res.pointer));
}

/**
* Determine if a given API definition utilizes discriminators.
*
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#discriminatorObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#discriminatorObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#discriminator-object}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#discriminator-object}
*/
export function discriminators(definition: OASDocument) {
return query(['$..discriminator'], definition).map(res => refizePointer(res.pointer));
Expand All @@ -55,8 +65,8 @@ export function discriminators(definition: OASDocument) {
/**
* Determine if a given API definition utilizes `links`.
*
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#linkObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#linkObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#link-object}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#link-object}
*/
export function links(definition: OASDocument) {
return query(['$..links'], definition).map(res => refizePointer(res.pointer));
Expand Down Expand Up @@ -114,8 +124,8 @@ export function polymorphism(definition: OASDocument) {
/**
* Determine every kind of security type that a given API definition has documented.
*
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#securitySchemeObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#securitySchemeObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#security-scheme-object}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#security-scheme-object}
*/
export function securityTypes(definition: OASDocument) {
return Array.from(new Set(query(['$.components.securitySchemes..type'], definition).map(res => res.value as string)));
Expand All @@ -124,8 +134,8 @@ export function securityTypes(definition: OASDocument) {
/**
* Determine if a given API definition utilizes server variables.
*
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#serverVariableObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#serverVariableObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#server-variable-object}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#server-variable-object}
*/
export function serverVariables(definition: OASDocument) {
return query(['$.servers..variables^'], definition).map(res => refizePointer(res.pointer));
Expand All @@ -134,8 +144,8 @@ export function serverVariables(definition: OASDocument) {
/**
* Determine how many operations are defined in a given API definition.
*
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operationObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#operationObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#operation-object}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#operation-object}
*/
export function totalOperations(definition: OASDocument) {
return query(['$..paths[*]'], definition)
Expand All @@ -146,7 +156,7 @@ export function totalOperations(definition: OASDocument) {
/**
* Determine if a given API definition utilizes `webhooks` support in OpenAPI 3.1.
*
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#oasObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#oasWebhooks}
*/
export function webhooks(definition: OASDocument) {
return query(['$.webhooks[*]'], definition).map(res => refizePointer(res.pointer));
Expand All @@ -156,8 +166,8 @@ export function webhooks(definition: OASDocument) {
* Determine if a given API definition has XML schemas, payloads, or responses.
*
* @todo detect `+xml` media types
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#xmlObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#xmlObject}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.3.md#xml-object}
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#xml-object}
*/
export function xml(definition: OASDocument) {
return query(
Expand Down
1 change: 1 addition & 0 deletions packages/oas/src/analyzer/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface OASAnalysis {
additionalProperties: OASAnalysisFeature;
callbacks: OASAnalysisFeature;
circularRefs: OASAnalysisFeature;
commonParameters: OASAnalysisFeature;
discriminators: OASAnalysisFeature;
links: OASAnalysisFeature;
polymorphism: OASAnalysisFeature;
Expand Down
Loading

0 comments on commit 05ad5c1

Please sign in to comment.