Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
5 changes: 5 additions & 0 deletions .changeset/selfish-pandas-speak.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@redocly/openapi-core": patch
---

Updated typings for OAS 3.0 and OAS 3.1 Schemas.
18 changes: 12 additions & 6 deletions packages/cli/src/commands/join.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,17 @@
import { COMPONENTS, OPENAPI3_METHOD } from './split/types';
import { crawl, startsWithComponents } from './split';

import type { Oas3Definition, Document, Oas3Tag, Referenced } from '@redocly/openapi-core';
import type { Document, Referenced } from '@redocly/openapi-core';
import type { BundleResult } from '@redocly/openapi-core/lib/bundle';
import type {
Oas3Definition,
Oas3_1Definition,
Oas3Parameter,
Oas3PathItem,
Oas3Server,
Oas3_1Definition,
Oas3Tag,
} from '@redocly/openapi-core/lib/typings/openapi';
import type { StrictObject } from '@redocly/openapi-core/lib/utils';
import type { CommandArgs } from '../wrapper';
import type { VerifyConfigOptions } from '../types';

Expand All @@ -43,7 +46,7 @@
apiFilename: string;
apiTitle?: string;
tags: Oas3Tag[];
potentialConflicts: any;

Check warning on line 49 in packages/cli/src/commands/join.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Unexpected any. Specify a different type
tagsPrefix: string;
componentsPrefix: string | undefined;
};
Expand Down Expand Up @@ -161,7 +164,7 @@
}
}

const joinedDef: any = {};

Check warning on line 167 in packages/cli/src/commands/join.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Unexpected any. Specify a different type
const potentialConflicts = {
tags: {},
paths: {},
Expand Down Expand Up @@ -284,7 +287,7 @@
}

function getIndexGroup(name: string): number {
return joinedDef[xTagGroups].findIndex((item: any) => item.name === name);

Check warning on line 290 in packages/cli/src/commands/join.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Unexpected any. Specify a different type
}

function createXTagGroups(name: string) {
Expand All @@ -292,7 +295,7 @@
joinedDef[xTagGroups] = [];
}

if (!joinedDef[xTagGroups].some((g: any) => g.name === name)) {

Check warning on line 298 in packages/cli/src/commands/join.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Unexpected any. Specify a different type
joinedDef[xTagGroups].push({ name, tags: [] });
}

Expand All @@ -311,21 +314,24 @@
}
}

function collectServers(openapi: Oas3Definition) {
function collectServers(openapi: Oas3Definition | Oas3_1Definition) {
const { servers } = openapi;
if (servers) {
if (!joinedDef.hasOwnProperty('servers')) {
joinedDef['servers'] = [];
}
for (const server of servers) {
if (!joinedDef.servers.some((s: any) => s.url === server.url)) {

Check warning on line 324 in packages/cli/src/commands/join.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Unexpected any. Specify a different type
joinedDef.servers.push(server);
}
}
}
}

function collectExternalDocs(openapi: Oas3Definition, { api }: JoinDocumentContext) {
function collectExternalDocs(
openapi: Oas3Definition | Oas3_1Definition,
{ api }: JoinDocumentContext
) {
const { externalDocs } = openapi;
if (externalDocs) {
if (joinedDef.hasOwnProperty('externalDocs')) {
Expand All @@ -339,7 +345,7 @@
}

function collectPaths(
openapi: Oas3Definition,
openapi: Oas3Definition | Oas3_1Definition,
{
apiFilename,
apiTitle,
Expand Down Expand Up @@ -567,7 +573,7 @@

function collectWebhooks(
oasVersion: SpecVersion,
openapi: Oas3_1Definition,
openapi: StrictObject<Oas3Definition | Oas3_1Definition>,
{
apiFilename,
apiTitle,
Expand Down Expand Up @@ -617,7 +623,7 @@
}

function addInfoSectionAndSpecVersion(
documents: any,

Check warning on line 626 in packages/cli/src/commands/join.ts

View workflow job for this annotation

GitHub Actions / code-style-check

Unexpected any. Specify a different type
prefixComponentsWithInfoProp: string | undefined
) {
const firstApi = documents[0];
Expand Down
30 changes: 16 additions & 14 deletions packages/cli/src/commands/split/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,18 @@ import {
OPENAPI3_COMPONENT_NAMES,
} from './types';

import type { OasRef } from '@redocly/openapi-core';
import type { Oas3Definition, Oas3_1Definition, Oas2Definition } from '@redocly/openapi-core';
import type {
Definition,
Oas2Definition,
Oas3Schema,
Oas3Definition,
Oas3_1Definition,
Oas3_1Schema,
Oas3Components,
Oas3_1Components,
Oas3ComponentName,
ComponentsFiles,
RefObject,
Oas3PathItem,
OasRef,
Referenced,
} from './types';
} from '@redocly/openapi-core/lib/typings/openapi';
import type { ComponentsFiles, Definition, RefObject } from './types';
import type { CommandArgs } from '../../wrapper';
import type { VerifyConfigOptions } from '../../types';

Expand Down Expand Up @@ -239,7 +237,7 @@ function doesFileDiffer(filename: string, componentData: any) {

function removeEmptyComponents(
openapi: Oas3Definition | Oas3_1Definition,
componentType: Oas3ComponentName
componentType: Oas3ComponentName<Oas3Schema | Oas3_1Schema>
) {
if (openapi.components && isEmptyObject(openapi.components[componentType])) {
delete openapi.components[componentType];
Expand All @@ -264,15 +262,17 @@ function getFileNamePath(componentDirPath: string, componentName: string, ext: s
}

function gatherComponentsFiles(
components: Oas3Components,
components: Oas3Components | Oas3_1Components,
componentsFiles: ComponentsFiles,
componentType: Oas3ComponentName,
componentType: Oas3ComponentName<Oas3Schema | Oas3_1Schema>,
componentName: string,
filename: string
) {
let inherits: string[] = [];
if (componentType === OPENAPI3_COMPONENT.Schemas) {
inherits = ((components?.[componentType]?.[componentName] as Oas3Schema)?.allOf || [])
inherits = (
(components?.[componentType]?.[componentName] as Oas3Schema | Oas3_1Schema)?.allOf || []
)
.map(({ $ref }) => $ref)
.filter(isTruthy);
}
Expand Down Expand Up @@ -347,7 +347,9 @@ function iterateComponents(
componentTypes.forEach(iterateComponentTypes);

// eslint-disable-next-line no-inner-declarations
function iterateAndGatherComponentsFiles(componentType: Oas3ComponentName) {
function iterateAndGatherComponentsFiles(
componentType: Oas3ComponentName<Oas3Schema | Oas3_1Schema>
) {
const componentDirPath = path.join(componentsDir, componentType);
for (const componentName of Object.keys(components?.[componentType] || {})) {
const filename = getFileNamePath(componentDirPath, componentName, ext);
Expand All @@ -356,7 +358,7 @@ function iterateComponents(
}

// eslint-disable-next-line no-inner-declarations
function iterateComponentTypes(componentType: Oas3ComponentName) {
function iterateComponentTypes(componentType: Oas3ComponentName<Oas3Schema | Oas3_1Schema>) {
const componentDirPath = path.join(componentsDir, componentType);
createComponentDir(componentDirPath, componentType);
for (const componentName of Object.keys(components?.[componentType] || {})) {
Expand Down
29 changes: 3 additions & 26 deletions packages/cli/src/commands/split/types.ts
Original file line number Diff line number Diff line change
@@ -1,29 +1,6 @@
import {
Oas3Schema,
Oas3_1Schema,
Oas3Definition,
Oas3_1Definition,
Oas3Components,
Oas3PathItem,
Oas3Paths,
Oas3ComponentName,
Oas3_1Webhooks,
Oas2Definition,
Referenced,
} from '@redocly/openapi-core';
export {
Oas3_1Definition,
Oas3Definition,
Oas2Definition,
Oas3Components,
Oas3Paths,
Oas3PathItem,
Oas3ComponentName,
Oas3_1Schema,
Oas3Schema,
Oas3_1Webhooks,
Referenced,
};
import type { Oas2Definition } from '@redocly/openapi-core';
import type { Oas3_1Definition, Oas3Definition } from '@redocly/openapi-core/lib/typings/openapi';

export type Definition = Oas3_1Definition | Oas3Definition | Oas2Definition;
export interface ComponentsFiles {
[schemas: string]: any;
Expand Down
95 changes: 95 additions & 0 deletions packages/core/src/__tests__/lint.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1674,4 +1674,99 @@ describe('lint', () => {

expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
});

it('should throw an error for $schema not expected here - OAS 3.0.x', async () => {
const document = parseYamlToDocument(
outdent`
openapi: 3.0.4
info:
title: test json schema validation keyword $schema - should use an OAS Schema, not JSON Schema
version: 1.0.0
paths:
'/thing':
get:
summary: a sample api
responses:
'200':
description: OK
content:
'application/json':
schema:
$schema: http://json-schema.org/draft-04/schema#
type: object
properties: {}
`,
''
);

const configFilePath = path.join(__dirname, '..', '..', '..', 'redocly.yaml');

const results = await lintDocument({
externalRefResolver: new BaseResolver(),
document,
config: await makeConfig({
rules: { spec: 'error' },
decorators: undefined,
configPath: configFilePath,
}),
});

expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
[
{
"from": undefined,
"location": [
{
"pointer": "#/paths/~1thing/get/responses/200/content/application~1json/schema/$schema",
"reportOnKey": true,
"source": "",
},
],
"message": "Property \`$schema\` is not expected here.",
"ruleId": "struct",
"severity": "error",
"suggest": [],
},
]
`);
});

it('should allow for $schema to be defined - OAS 3.1.x', async () => {
const document = parseYamlToDocument(
outdent`
openapi: 3.1.1
info:
title: test json schema validation keyword $schema - should allow a JSON Schema
version: 1.0.0
paths:
'/thing':
get:
summary: a sample api
responses:
'200':
description: OK
content:
'application/json':
schema:
$schema: http://json-schema.org/draft-04/schema#
type: object
properties: {}
`,
''
);

const configFilePath = path.join(__dirname, '..', '..', '..', 'redocly.yaml');

const results = await lintDocument({
externalRefResolver: new BaseResolver(),
document,
config: await makeConfig({
rules: { spec: 'error' },
decorators: undefined,
configPath: configFilePath,
}),
});

expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
});
});
20 changes: 16 additions & 4 deletions packages/core/src/decorators/oas3/remove-unused-components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,26 @@ import { isEmptyObject } from '../../utils';

import type { Location } from '../../ref-utils';
import type { Oas3Decorator } from '../../visitors';
import type { Oas3Components, Oas3Definition } from '../../typings/openapi';
import type {
Oas3Definition,
Oas3_1Definition,
Oas3Components,
Oas3_1Components,
} from '../../typings/openapi';

export const RemoveUnusedComponents: Oas3Decorator = () => {
const components = new Map<
string,
{ usedIn: Location[]; componentType?: keyof Oas3Components; name: string }
{
usedIn: Location[];
componentType?: keyof (Oas3Components | Oas3_1Components);
name: string;
}
>();

function registerComponent(
location: Location,
componentType: keyof Oas3Components,
componentType: keyof (Oas3Components | Oas3_1Components),
name: string
): void {
components.set(location.absolutePointer, {
Expand All @@ -22,7 +31,10 @@ export const RemoveUnusedComponents: Oas3Decorator = () => {
});
}

function removeUnusedComponents(root: Oas3Definition, removedPaths: string[]): number {
function removeUnusedComponents(
root: Oas3Definition | Oas3_1Definition,
removedPaths: string[]
): number {
const removedLengthStart = removedPaths.length;

for (const [path, { usedIn, name, componentType }] of components) {
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ export { ConfigTypes } from './types/redocly-yaml';
export type {
Oas3Definition,
Oas3_1Definition,
Oas3Components,
Oas3ComponentsBase,
Oas3_1Components,
Oas3PathItem,
Oas3Paths,
Oas3ComponentName,
Oas3Schema,
Oas3_1Schema,
Oas3Tag,
Oas3_1Webhooks,
Referenced,
OasRef,
} from './typings/openapi';
Expand Down
4 changes: 2 additions & 2 deletions packages/core/src/rules/common/operation-tag-defined.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import type { Oas3Rule, Oas2Rule } from '../../visitors';
import type { Oas2Definition, Oas2Operation } from '../../typings/swagger';
import type { Oas3Definition, Oas3Operation } from '../../typings/openapi';
import type { Oas3Definition, Oas3_1Definition, Oas3Operation } from '../../typings/openapi';
import type { UserContext } from '../../walk';

export const OperationTagDefined: Oas3Rule | Oas2Rule = () => {
let definedTags: Set<string>;

return {
Root(root: Oas2Definition | Oas3Definition) {
Root(root: Oas2Definition | Oas3Definition | Oas3_1Definition) {
definedTags = new Set((root.tags ?? []).map((t) => t.name));
},
Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) {
Expand Down
3 changes: 2 additions & 1 deletion packages/core/src/rules/common/security-defined.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type {
} from '../../typings/swagger';
import type {
Oas3Definition,
Oas3_1Definition,
Oas3Operation,
Oas3PathItem,
Oas3SecurityScheme,
Expand All @@ -31,7 +32,7 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = (opts: {

return {
Root: {
leave(root: Oas2Definition | Oas3Definition, { report }: UserContext) {
leave(root: Oas2Definition | Oas3Definition | Oas3_1Definition, { report }: UserContext) {
for (const [name, scheme] of referencedSchemes.entries()) {
if (scheme.defined) continue;
for (const reportedFromLocation of scheme.from) {
Expand Down
7 changes: 5 additions & 2 deletions packages/core/src/rules/common/tags-alphabetical.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import type { Oas3Rule, Oas2Rule } from '../../visitors';
import type { Oas2Definition, Oas2Tag } from '../../typings/swagger';
import type { Oas3Definition, Oas3Tag } from '../../typings/openapi';
import type { Oas3Definition, Oas3Tag, Oas3_1Definition } from '../../typings/openapi';
import type { UserContext } from '../../walk';

export const TagsAlphabetical: Oas3Rule | Oas2Rule = ({ ignoreCase = false }) => {
return {
Root(root: Oas2Definition | Oas3Definition, { report, location }: UserContext) {
Root(
root: Oas2Definition | Oas3Definition | Oas3_1Definition,
{ report, location }: UserContext
) {
if (!root.tags) return;
for (let i = 0; i < root.tags.length - 1; i++) {
if (getTagName(root.tags[i], ignoreCase) > getTagName(root.tags[i + 1], ignoreCase)) {
Expand Down
Loading
Loading