@redocly/openapi-core 1.27.1 → 1.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -76,7 +76,7 @@ const testPortalConfig = parseYamlToDocument(
76
76
  # name: Must be reported as a missing required prop
77
77
  value: 123 # Must be a string
78
78
 
79
- ssoOnPrem:
79
+ ssoDirect:
80
80
  oidc:
81
81
  title: 456 # Must be a string
82
82
  type: OIDC
@@ -791,7 +791,7 @@ describe('lint', () => {
791
791
  "from": undefined,
792
792
  "location": [
793
793
  {
794
- "pointer": "#/ssoOnPrem/oidc/title",
794
+ "pointer": "#/ssoDirect/oidc/title",
795
795
  "reportOnKey": false,
796
796
  "source": "",
797
797
  },
@@ -805,7 +805,7 @@ describe('lint', () => {
805
805
  "from": undefined,
806
806
  "location": [
807
807
  {
808
- "pointer": "#/ssoOnPrem/oidc/defaultTeams/0",
808
+ "pointer": "#/ssoDirect/oidc/defaultTeams/0",
809
809
  "reportOnKey": false,
810
810
  "source": "",
811
811
  },
@@ -819,7 +819,7 @@ describe('lint', () => {
819
819
  "from": undefined,
820
820
  "location": [
821
821
  {
822
- "pointer": "#/ssoOnPrem/oidc/configuration",
822
+ "pointer": "#/ssoDirect/oidc/configuration",
823
823
  "reportOnKey": true,
824
824
  "source": "",
825
825
  },
@@ -833,7 +833,7 @@ describe('lint', () => {
833
833
  "from": undefined,
834
834
  "location": [
835
835
  {
836
- "pointer": "#/ssoOnPrem/oidc/configuration/token_endpoint",
836
+ "pointer": "#/ssoDirect/oidc/configuration/token_endpoint",
837
837
  "reportOnKey": false,
838
838
  "source": "",
839
839
  },
@@ -847,7 +847,7 @@ describe('lint', () => {
847
847
  "from": undefined,
848
848
  "location": [
849
849
  {
850
- "pointer": "#/ssoOnPrem/oidc/authorizationRequestCustomParams/login_hint",
850
+ "pointer": "#/ssoDirect/oidc/authorizationRequestCustomParams/login_hint",
851
851
  "reportOnKey": false,
852
852
  "source": "",
853
853
  },
@@ -861,7 +861,7 @@ describe('lint', () => {
861
861
  "from": undefined,
862
862
  "location": [
863
863
  {
864
- "pointer": "#/ssoOnPrem/sso-config-schema-without-configurationUrl",
864
+ "pointer": "#/ssoDirect/sso-config-schema-without-configurationUrl",
865
865
  "reportOnKey": true,
866
866
  "source": "",
867
867
  },
@@ -875,7 +875,7 @@ describe('lint', () => {
875
875
  "from": undefined,
876
876
  "location": [
877
877
  {
878
- "pointer": "#/ssoOnPrem/sso-config-schema-without-configurationUrl",
878
+ "pointer": "#/ssoDirect/sso-config-schema-without-configurationUrl",
879
879
  "reportOnKey": true,
880
880
  "source": "",
881
881
  },
@@ -1205,12 +1205,12 @@ describe('lint', () => {
1205
1205
  "from": undefined,
1206
1206
  "location": [
1207
1207
  {
1208
- "pointer": "#/ssoOnPrem",
1208
+ "pointer": "#/ssoDirect",
1209
1209
  "reportOnKey": true,
1210
1210
  "source": "",
1211
1211
  },
1212
1212
  ],
1213
- "message": "Property \`ssoOnPrem\` is not expected here.",
1213
+ "message": "Property \`ssoDirect\` is not expected here.",
1214
1214
  "ruleId": "configuration spec",
1215
1215
  "severity": "error",
1216
1216
  "suggest": [],
@@ -1674,4 +1674,99 @@ describe('lint', () => {
1674
1674
 
1675
1675
  expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
1676
1676
  });
1677
+
1678
+ it('should throw an error for $schema not expected here - OAS 3.0.x', async () => {
1679
+ const document = parseYamlToDocument(
1680
+ outdent`
1681
+ openapi: 3.0.4
1682
+ info:
1683
+ title: test json schema validation keyword $schema - should use an OAS Schema, not JSON Schema
1684
+ version: 1.0.0
1685
+ paths:
1686
+ '/thing':
1687
+ get:
1688
+ summary: a sample api
1689
+ responses:
1690
+ '200':
1691
+ description: OK
1692
+ content:
1693
+ 'application/json':
1694
+ schema:
1695
+ $schema: http://json-schema.org/draft-04/schema#
1696
+ type: object
1697
+ properties: {}
1698
+ `,
1699
+ ''
1700
+ );
1701
+
1702
+ const configFilePath = path.join(__dirname, '..', '..', '..', 'redocly.yaml');
1703
+
1704
+ const results = await lintDocument({
1705
+ externalRefResolver: new BaseResolver(),
1706
+ document,
1707
+ config: await makeConfig({
1708
+ rules: { spec: 'error' },
1709
+ decorators: undefined,
1710
+ configPath: configFilePath,
1711
+ }),
1712
+ });
1713
+
1714
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`
1715
+ [
1716
+ {
1717
+ "from": undefined,
1718
+ "location": [
1719
+ {
1720
+ "pointer": "#/paths/~1thing/get/responses/200/content/application~1json/schema/$schema",
1721
+ "reportOnKey": true,
1722
+ "source": "",
1723
+ },
1724
+ ],
1725
+ "message": "Property \`$schema\` is not expected here.",
1726
+ "ruleId": "struct",
1727
+ "severity": "error",
1728
+ "suggest": [],
1729
+ },
1730
+ ]
1731
+ `);
1732
+ });
1733
+
1734
+ it('should allow for $schema to be defined - OAS 3.1.x', async () => {
1735
+ const document = parseYamlToDocument(
1736
+ outdent`
1737
+ openapi: 3.1.1
1738
+ info:
1739
+ title: test json schema validation keyword $schema - should allow a JSON Schema
1740
+ version: 1.0.0
1741
+ paths:
1742
+ '/thing':
1743
+ get:
1744
+ summary: a sample api
1745
+ responses:
1746
+ '200':
1747
+ description: OK
1748
+ content:
1749
+ 'application/json':
1750
+ schema:
1751
+ $schema: http://json-schema.org/draft-04/schema#
1752
+ type: object
1753
+ properties: {}
1754
+ `,
1755
+ ''
1756
+ );
1757
+
1758
+ const configFilePath = path.join(__dirname, '..', '..', '..', 'redocly.yaml');
1759
+
1760
+ const results = await lintDocument({
1761
+ externalRefResolver: new BaseResolver(),
1762
+ document,
1763
+ config: await makeConfig({
1764
+ rules: { spec: 'error' },
1765
+ decorators: undefined,
1766
+ configPath: configFilePath,
1767
+ }),
1768
+ });
1769
+
1770
+ expect(replaceSourceWithRef(results)).toMatchInlineSnapshot(`[]`);
1771
+ });
1677
1772
  });
@@ -2,17 +2,26 @@ import { isEmptyObject } from '../../utils';
2
2
 
3
3
  import type { Location } from '../../ref-utils';
4
4
  import type { Oas3Decorator } from '../../visitors';
5
- import type { Oas3Components, Oas3Definition } from '../../typings/openapi';
5
+ import type {
6
+ Oas3Definition,
7
+ Oas3_1Definition,
8
+ Oas3Components,
9
+ Oas3_1Components,
10
+ } from '../../typings/openapi';
6
11
 
7
12
  export const RemoveUnusedComponents: Oas3Decorator = () => {
8
13
  const components = new Map<
9
14
  string,
10
- { usedIn: Location[]; componentType?: keyof Oas3Components; name: string }
15
+ {
16
+ usedIn: Location[];
17
+ componentType?: keyof (Oas3Components | Oas3_1Components);
18
+ name: string;
19
+ }
11
20
  >();
12
21
 
13
22
  function registerComponent(
14
23
  location: Location,
15
- componentType: keyof Oas3Components,
24
+ componentType: keyof (Oas3Components | Oas3_1Components),
16
25
  name: string
17
26
  ): void {
18
27
  components.set(location.absolutePointer, {
@@ -22,7 +31,10 @@ export const RemoveUnusedComponents: Oas3Decorator = () => {
22
31
  });
23
32
  }
24
33
 
25
- function removeUnusedComponents(root: Oas3Definition, removedPaths: string[]): number {
34
+ function removeUnusedComponents(
35
+ root: Oas3Definition | Oas3_1Definition,
36
+ removedPaths: string[]
37
+ ): number {
26
38
  const removedLengthStart = removedPaths.length;
27
39
 
28
40
  for (const [path, { usedIn, name, componentType }] of components) {
package/src/index.ts CHANGED
@@ -18,13 +18,13 @@ export type {
18
18
  Oas3Definition,
19
19
  Oas3_1Definition,
20
20
  Oas3Components,
21
+ Oas3_1Components,
21
22
  Oas3PathItem,
22
23
  Oas3Paths,
23
24
  Oas3ComponentName,
24
25
  Oas3Schema,
25
26
  Oas3_1Schema,
26
27
  Oas3Tag,
27
- Oas3_1Webhooks,
28
28
  Referenced,
29
29
  OasRef,
30
30
  } from './typings/openapi';
@@ -1,12 +1,7 @@
1
1
  import { setRedoclyDomain } from '../domains';
2
2
  import { RedoclyClient } from '../index';
3
3
 
4
- jest.mock('node-fetch', () => ({
5
- default: jest.fn(() => ({
6
- ok: true,
7
- json: jest.fn().mockResolvedValue({}),
8
- })),
9
- }));
4
+ const originalFetch = global.fetch;
10
5
 
11
6
  describe('RedoclyClient', () => {
12
7
  const REDOCLY_DOMAIN_US = 'redocly.com';
@@ -15,6 +10,19 @@ describe('RedoclyClient', () => {
15
10
  const testRedoclyDomain = 'redoclyDomain.com';
16
11
  const testToken = 'test-token';
17
12
 
13
+ beforeAll(() => {
14
+ global.fetch = jest.fn(() =>
15
+ Promise.resolve({
16
+ ok: true,
17
+ json: jest.fn().mockResolvedValue({}),
18
+ } as any)
19
+ );
20
+ });
21
+
22
+ afterAll(() => {
23
+ global.fetch = originalFetch;
24
+ });
25
+
18
26
  afterEach(() => {
19
27
  delete process.env.REDOCLY_DOMAIN;
20
28
  setRedoclyDomain('');
@@ -1,8 +1,6 @@
1
- import fetch from 'node-fetch';
2
1
  import { getProxyAgent, isNotEmptyObject } from '../utils';
3
2
  import { getRedoclyDomain } from './domains';
4
3
 
5
- import type { RequestInit, HeadersInit } from 'node-fetch';
6
4
  import type {
7
5
  NotFoundProblemResponse,
8
6
  PrepareFileuploadOKResponse,
@@ -43,10 +41,13 @@ export class RegistryApi {
43
41
  throw new Error('Unauthorized');
44
42
  }
45
43
 
46
- const response = await fetch(
47
- `${this.getBaseUrl()}${path}`,
48
- Object.assign({}, options, { headers, agent: getProxyAgent() })
49
- );
44
+ const requestOptions = {
45
+ ...options,
46
+ headers,
47
+ agent: getProxyAgent(),
48
+ };
49
+
50
+ const response = await fetch(`${this.getBaseUrl()}${path}`, requestOptions);
50
51
 
51
52
  if (response.status === 401) {
52
53
  throw new Error('Unauthorized');
@@ -1,13 +1,13 @@
1
1
  import type { Oas3Rule, Oas2Rule } from '../../visitors';
2
2
  import type { Oas2Definition, Oas2Operation } from '../../typings/swagger';
3
- import type { Oas3Definition, Oas3Operation } from '../../typings/openapi';
3
+ import type { Oas3Definition, Oas3_1Definition, Oas3Operation } from '../../typings/openapi';
4
4
  import type { UserContext } from '../../walk';
5
5
 
6
6
  export const OperationTagDefined: Oas3Rule | Oas2Rule = () => {
7
7
  let definedTags: Set<string>;
8
8
 
9
9
  return {
10
- Root(root: Oas2Definition | Oas3Definition) {
10
+ Root(root: Oas2Definition | Oas3Definition | Oas3_1Definition) {
11
11
  definedTags = new Set((root.tags ?? []).map((t) => t.name));
12
12
  },
13
13
  Operation(operation: Oas2Operation | Oas3Operation, { report, location }: UserContext) {
@@ -9,6 +9,7 @@ import type {
9
9
  } from '../../typings/swagger';
10
10
  import type {
11
11
  Oas3Definition,
12
+ Oas3_1Definition,
12
13
  Oas3Operation,
13
14
  Oas3PathItem,
14
15
  Oas3SecurityScheme,
@@ -31,7 +32,7 @@ export const SecurityDefined: Oas3Rule | Oas2Rule = (opts: {
31
32
 
32
33
  return {
33
34
  Root: {
34
- leave(root: Oas2Definition | Oas3Definition, { report }: UserContext) {
35
+ leave(root: Oas2Definition | Oas3Definition | Oas3_1Definition, { report }: UserContext) {
35
36
  for (const [name, scheme] of referencedSchemes.entries()) {
36
37
  if (scheme.defined) continue;
37
38
  for (const reportedFromLocation of scheme.from) {
@@ -1,11 +1,14 @@
1
1
  import type { Oas3Rule, Oas2Rule } from '../../visitors';
2
2
  import type { Oas2Definition, Oas2Tag } from '../../typings/swagger';
3
- import type { Oas3Definition, Oas3Tag } from '../../typings/openapi';
3
+ import type { Oas3Definition, Oas3Tag, Oas3_1Definition } from '../../typings/openapi';
4
4
  import type { UserContext } from '../../walk';
5
5
 
6
6
  export const TagsAlphabetical: Oas3Rule | Oas2Rule = ({ ignoreCase = false }) => {
7
7
  return {
8
- Root(root: Oas2Definition | Oas3Definition, { report, location }: UserContext) {
8
+ Root(
9
+ root: Oas2Definition | Oas3Definition | Oas3_1Definition,
10
+ { report, location }: UserContext
11
+ ) {
9
12
  if (!root.tags) return;
10
13
  for (let i = 0; i < root.tags.length - 1; i++) {
11
14
  if (getTagName(root.tags[i], ignoreCase) > getTagName(root.tags[i + 1], ignoreCase)) {
@@ -12,15 +12,18 @@ export const ArrayParameterSerialization: Oas3Rule = (
12
12
  ): Oas3Visitor => {
13
13
  return {
14
14
  Parameter: {
15
- leave(node: Oas3Parameter, ctx) {
15
+ leave(node, ctx) {
16
16
  if (!node.schema) {
17
17
  return;
18
18
  }
19
- const schema = isRef(node.schema)
20
- ? ctx.resolve<Oas3_1Schema>(node.schema).node
21
- : (node.schema as Oas3_1Schema);
19
+ const schema = (
20
+ isRef(node.schema) ? ctx.resolve(node.schema).node : node.schema
21
+ ) as Oas3_1Schema;
22
22
 
23
- if (schema && shouldReportMissingStyleAndExplode(node, schema, options)) {
23
+ if (
24
+ schema &&
25
+ shouldReportMissingStyleAndExplode(node as Oas3Parameter<Oas3_1Schema>, schema, options)
26
+ ) {
24
27
  ctx.report({
25
28
  message: `Parameter \`${node.name}\` should have \`style\` and \`explode \` fields`,
26
29
  location: ctx.location,
@@ -32,7 +35,7 @@ export const ArrayParameterSerialization: Oas3Rule = (
32
35
  };
33
36
 
34
37
  function shouldReportMissingStyleAndExplode(
35
- node: Oas3Parameter,
38
+ node: Oas3Parameter<Oas3_1Schema>,
36
39
  schema: Oas3_1Schema,
37
40
  options: ArrayParameterSerializationOptions
38
41
  ) {
@@ -3,10 +3,12 @@ import type { Problem, UserContext } from '../../walk';
3
3
  import type { Oas2Rule, Oas3Rule, Oas3Visitor } from '../../visitors';
4
4
  import type {
5
5
  Oas3Definition,
6
+ Oas3_1Definition,
6
7
  Oas3Parameter,
7
8
  Oas3RequestBody,
8
9
  Oas3Response,
9
10
  Oas3Schema,
11
+ Oas3_1Schema,
10
12
  OasRef,
11
13
  } from '../../typings/openapi';
12
14
 
@@ -54,7 +56,7 @@ export const ComponentNameUnique: Oas3Rule | Oas2Rule = (options) => {
54
56
  },
55
57
  },
56
58
  Root: {
57
- leave(root: Oas3Definition, ctx: UserContext) {
59
+ leave(root: Oas3Definition | Oas3_1Definition, ctx: UserContext) {
58
60
  components.forEach((value, key, _) => {
59
61
  if (value.absolutePointers.size > 1) {
60
62
  const component = getComponentFromKey(key);
@@ -82,7 +84,7 @@ export const ComponentNameUnique: Oas3Rule | Oas2Rule = (options) => {
82
84
 
83
85
  if (options.schemas != 'off') {
84
86
  rule.NamedSchemas = {
85
- Schema(_: Oas3Schema, { location }: UserContext) {
87
+ Schema(_: Oas3Schema | Oas3_1Schema, { location }: UserContext) {
86
88
  addComponentFromAbsoluteLocation(TYPE_NAME_SCHEMA, location);
87
89
  },
88
90
  };
@@ -84,7 +84,7 @@ const ReusableObject: NodeType = {
84
84
  };
85
85
  const Parameter: NodeType = {
86
86
  properties: {
87
- in: { type: 'string', enum: ['header', 'query', 'path', 'cookie', 'body'] },
87
+ in: { type: 'string', enum: ['header', 'query', 'path', 'cookie'] },
88
88
  name: { type: 'string' },
89
89
  value: {}, // any
90
90
  },
@@ -182,6 +182,8 @@ export const Schema: NodeType = {
182
182
  const: null,
183
183
  $comment: { type: 'string' },
184
184
  'x-tags': { type: 'array', items: { type: 'string' } },
185
+ $dynamicAnchor: { type: 'string' },
186
+ $dynamicRef: { type: 'string' },
185
187
  },
186
188
  extensionsPrefix: 'x-',
187
189
  };
@@ -1093,7 +1093,7 @@ const ConfigReferenceDocs: NodeType = {
1093
1093
  showObjectSchemaExamples: { type: 'boolean' },
1094
1094
  disableTryItRequestUrlEncoding: { type: 'boolean' },
1095
1095
  sidebarLinks: 'ConfigSidebarLinks',
1096
- sideNavStyle: { enum: ['summary-only', 'path-first', 'id-only'] },
1096
+ sideNavStyle: { enum: ['summary-only', 'path-first', 'id-only', 'path-only'] },
1097
1097
  simpleOneOfTypeLabel: { type: 'boolean' },
1098
1098
  sortEnumValuesAlphabetically: { type: 'boolean' },
1099
1099
  sortOperationsAlphabetically: { type: 'boolean' },
@@ -21,7 +21,7 @@ export interface ArazzoSourceDescription {
21
21
  export type SourceDescription = OpenAPISourceDescription | ArazzoSourceDescription;
22
22
 
23
23
  export interface Parameter {
24
- in?: 'header' | 'query' | 'path' | 'cookie' | 'body';
24
+ in?: 'header' | 'query' | 'path' | 'cookie';
25
25
  name: string;
26
26
  value: string | number | boolean;
27
27
  reference?: string;