Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(openapi): tooling upgrades #1126

Open
wants to merge 1 commit into
base: next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions __tests__/commands/openapi/__snapshots__/inspect.test.ts.snap

Large diffs are not rendered by default.

5 changes: 5 additions & 0 deletions __tests__/commands/openapi/inspect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('rdme openapi inspect', () => {
'@readme/oas-examples/3.0/json/petstore.json',
'@readme/oas-examples/3.0/json/readme.json',
'@readme/oas-examples/3.0/json/readme-extensions.json',
'@readme/oas-examples/3.1/json/train-travel.json',
])('should generate a report for %s', spec => {
return expect(run([require.resolve(spec)])).resolves.toMatchSnapshot();
});
Expand All @@ -42,6 +43,10 @@ describe('rdme openapi inspect', () => {
spec: '@readme/oas-examples/3.0/json/schema-circular.json',
feature: ['additionalProperties', 'circularRefs'],
},
{
spec: '@readme/oas-examples/3.1/json/train-travel.json',
feature: ['commonParameters'],
},

// Soft error cases where we may or may not contain the features we're querying for.
{
Expand Down
15 changes: 15 additions & 0 deletions __tests__/lib/__snapshots__/analyzeOas.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ exports[`#analyzeOas > should analyze an OpenAPI definition 1`] = `
"locations": [],
"present": false,
},
"commonParameters": {
"description": "Common parameters allow you to define parameters that are shared across multiple operations within your API.",
"locations": [],
"present": false,
"url": {
"3.0": "https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.4.md#path-item-object",
"3.1": "https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.1.md#path-item-object",
},
},
"discriminators": {
"description": "With schemas that can be, or contain, different shapes, discriminators help you assist your users in identifying and determining the kind of shape they can supply or receive.",
"locations": [],
Expand Down Expand Up @@ -135,6 +144,12 @@ exports[`#analyzeOas > should analyze an OpenAPI definition 1`] = `
"present": false,
"url": "https://docs.readme.com/main/docs/openapi-extensions#authentication-defaults",
},
"x-readme-ref-name": {
"description": "x-readme-ref-name is added by our tooling after dereferencing in order to preserve original reference schema names.",
"hidden": true,
"locations": [],
"present": false,
},
"x-readme.code-samples": {
"description": "The x-readme.code-samples extension allows you to custom, create static code samples on your API documentation.",
"locations": [],
Expand Down
1 change: 1 addition & 0 deletions __tests__/lib/analyzeOas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ describe('#getSupportedFeatures', () => {
'additionalProperties',
'callbacks',
'circularRefs',
'commonParameters',
'discriminators',
'links',
'style',
Expand Down
27 changes: 9 additions & 18 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 2 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@
"gray-matter": "^4.0.1",
"ignore": "^6.0.2",
"mime-types": "^2.1.35",
"oas": "^25.0.0",
"oas-normalize": "^11.1.2",
"oas": "^25.2.1",
"oas-normalize": "^12.0.0",
"ora": "^8.1.1",
"pluralize": "^8.0.0",
"prompts": "^2.4.2",
"semver": "^7.5.3",
"simple-git": "^3.19.1",
Expand Down Expand Up @@ -85,7 +84,6 @@
"@types/debug": "^4.1.7",
"@types/js-yaml": "^4.0.5",
"@types/mime-types": "^2.1.1",
"@types/pluralize": "^0.0.33",
"@types/prompts": "^2.4.2",
"@types/semver": "^7.3.12",
"@types/toposort": "^2.0.7",
Expand Down
1 change: 0 additions & 1 deletion src/commands/openapi/convert.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ export default class OpenAPIConvertCommand extends BaseCommand<typeof OpenAPICon
}

const { preparedSpec, specPath, specType } = await prepareOas(spec, 'openapi convert', {
convertToLatest: true,
title,
});
const parsedPreparedSpec: OASDocument = JSON.parse(preparedSpec);
Expand Down
15 changes: 13 additions & 2 deletions src/commands/openapi/inspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import type { OASDocument } from 'oas/types';
import { Flags } from '@oclif/core';
import chalk from 'chalk';
import ora from 'ora';
import pluralize from 'pluralize';
import { getBorderCharacters, table } from 'table';

import analyzeOas, { getSupportedFeatures } from '../../lib/analyzeOas.js';
Expand All @@ -14,6 +13,10 @@ import { oraOptions } from '../../lib/logger.js';
import prepareOas from '../../lib/prepareOas.js';
import SoftError from '../../lib/softError.js';

function pluralize(str: string, count: number) {
return count > 1 ? `${str}s` : str;
}

function getFeatureDocsURL(feature: AnalyzedFeature, definitionVersion: string): string | undefined {
if (!feature.url) {
return undefined;
Expand Down Expand Up @@ -68,6 +71,10 @@ function buildFeaturesReport(analysis: Analysis, features: string[]) {
}

Object.entries(analysis.readme).forEach(([feature, info]) => {
if (info.hidden) {
return;
}

if (!info.present) {
report.push(`${feature}: You do not use this.`);
hasUnusedFeature = true;
Expand Down Expand Up @@ -140,6 +147,10 @@ function buildFullReport(analysis: Analysis, definitionVersion: string, tableBor
];

Object.entries(analysis[component as 'openapi' | 'readme']).forEach(([feature, info]) => {
if (info.hidden) {
return;
}

const descriptions: string[] = [];
if (info.description) {
descriptions.push(info.description);
Expand Down Expand Up @@ -224,7 +235,7 @@ export default class OpenAPIInspectCommand extends BaseCommand<typeof OpenAPIIns
this.debug(`switching working directory from ${previousWorkingDirectory} to ${process.cwd()}`);
}

const { preparedSpec, definitionVersion } = await prepareOas(spec, 'openapi inspect', { convertToLatest: true });
const { preparedSpec, definitionVersion } = await prepareOas(spec, 'openapi inspect');
const parsedPreparedSpec: OASDocument = JSON.parse(preparedSpec);

const spinner = ora({ ...oraOptions() });
Expand Down
24 changes: 23 additions & 1 deletion src/lib/analyzeOas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import analyzer from 'oas/analyzer';

export interface AnalyzedFeature extends OASAnalysisFeature {
description: string;

/**
* The analyzed feature is not worth reporting within the inspector.
*/
hidden?: boolean;

url?:
| string
| {
Expand All @@ -21,6 +27,7 @@ export interface Analysis extends OASAnalysis {
additionalProperties: AnalyzedFeature;
callbacks: AnalyzedFeature;
circularRefs: AnalyzedFeature;
commonParameters: AnalyzedFeature;
discriminators: AnalyzedFeature;
links: AnalyzedFeature;
polymorphism: AnalyzedFeature;
Expand All @@ -37,6 +44,8 @@ export interface Analysis extends OASAnalysis {
raw_body?: AnalyzedFeature;

'x-default': AnalyzedFeature;
'x-readme-ref-name': AnalyzedFeature;

'x-readme.code-samples': AnalyzedFeature;
'x-readme.explorer-enabled': AnalyzedFeature;
'x-readme.headers': AnalyzedFeature;
Expand Down Expand Up @@ -69,6 +78,14 @@ const OPENAPI_FEATURE_DOCS: Record<keyof Analysis['openapi'], Pick<AnalyzedFeatu
circularRefs: {
description: 'Circular references are $ref pointers that at some point in their lineage reference themselves.',
},
commonParameters: {
description:
'Common parameters allow you to define parameters that are shared across multiple operations within your API.',
url: {
'3.0': 'https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.0.4.md#path-item-object',
'3.1': 'https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.1.md#path-item-object',
},
},
discriminators: {
description:
'With schemas that can be, or contain, different shapes, discriminators help you assist your users in identifying and determining the kind of shape they can supply or receive.',
Expand Down Expand Up @@ -121,12 +138,17 @@ const OPENAPI_FEATURE_DOCS: Record<keyof Analysis['openapi'], Pick<AnalyzedFeatu
},
};

const README_FEATURE_DOCS: Record<keyof Analysis['readme'], Pick<AnalyzedFeature, 'description' | 'url'>> = {
const README_FEATURE_DOCS: Record<keyof Analysis['readme'], Pick<AnalyzedFeature, 'description' | 'hidden' | 'url'>> = {
'x-default': {
description:
'The x-default extension allows you to define static authentication credential defaults for OAuth 2 and API Key security types.',
url: 'https://docs.readme.com/main/docs/openapi-extensions#authentication-defaults',
},
'x-readme-ref-name': {
description:
'x-readme-ref-name is added by our tooling after dereferencing in order to preserve original reference schema names.',
hidden: true,
},
'x-readme.code-samples': {
description:
'The x-readme.code-samples extension allows you to custom, create static code samples on your API documentation.',
Expand Down
20 changes: 11 additions & 9 deletions src/lib/prepareOas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,12 @@ export default async function prepareOas(
path: string | undefined,
command: 'openapi convert' | 'openapi inspect' | 'openapi reduce' | 'openapi validate' | 'openapi',
opts: {
/**
* Optionally convert the supplied or discovered API definition to the latest OpenAPI release.
*/
convertToLatest?: boolean;
/**
* An optional title to replace the value in the `info.title` field.
* @see {@link https://github.com/OAI/OpenAPI-Specification/blob/main/versions/3.1.0.md#info-object}
*/
title?: string;
} = {
convertToLatest: false,
},
} = {},
) {
let specPath = path;

Expand Down Expand Up @@ -189,12 +183,20 @@ export default async function prepareOas(
throw err;
});

// If we were supplied a Postman collection this will **always** convert it to OpenAPI 3.0.
let api: OpenAPI.Document = await oas.validate({ convertToLatest: opts.convertToLatest }).catch((err: Error) => {
let api: OpenAPI.Document;
await oas.validate().catch((err: Error) => {
spinner.fail();
debug(`raw validation error object: ${JSON.stringify(err)}`);
throw err;
});

// If we were supplied a Postman collection this will **always** convert it to OpenAPI 3.0.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mind adding a debug statement here, sort of like how we're doing on line 202?

api = await oas.convert().catch((err: Error) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we shouldn't need to do this if we're validating, correct?

spinner.fail();
debug(`raw openapi conversion error object: ${JSON.stringify(err)}`);
throw err;
});

spinner.stop();

debug('πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡ spec validated! logging spec below πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡πŸ‘‡');
Expand Down
Loading