Skip to content
Merged
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
8 changes: 8 additions & 0 deletions .changeset/quiet-snakes-buy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@redocly/openapi-core": patch
"@redocly/cli": patch
---

Fixed an issue where `--component-renaming-conflicts-severity` ignored conflicts when different files had components with the same name but different content.

**Warning:** Autogenrated component names and `$ref` paths in bundled documents may differ from older releases.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ components:
$ref: '#/components/schemas/schema-a'
examples:
first:
$ref: '#/components/examples/param-a-first'
$ref: '#/components/examples/first-2'
second:
$ref: '#/components/examples/second'
path-param:
Expand All @@ -41,7 +41,7 @@ components:
examples:
first:
value: b1
param-a-first:
first-2:
value: a1
second:
value: a2
Expand Down
72 changes: 70 additions & 2 deletions packages/core/src/__tests__/__snapshots__/bundle.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,28 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`bundle > should bundle external pointer refs and warn for conflicting names 1`] = `
openapi: 3.1.0
paths:
/foo:
put:
parameters:
- $ref: '#/components/parameters/User-2'
get:
parameters:
- $ref: '#/components/parameters/User'
components:
parameters:
User:
name: test
in: query
description: test-B
User-2:
name: test
in: path
description: test-A

`;

exports[`bundle > should bundle external refs 1`] = `
openapi: 3.0.0
paths:
Expand All @@ -26,7 +49,7 @@ components:
$ref: '#/components/schemas/schema-a'
examples:
first:
$ref: '#/components/examples/param-a-first'
$ref: '#/components/examples/first-2'
second:
$ref: '#/components/examples/second'
path-param:
Expand All @@ -41,7 +64,7 @@ components:
examples:
first:
value: b1
param-a-first:
first-2:
value: a1
second:
value: a2
Expand Down Expand Up @@ -124,6 +147,51 @@ components:

`;

exports[`bundle > should keep dotted JSON pointer schema keys and rename conflicting User schemas 1`] = `
openapi: 3.0.0
info:
title: Test ref with dots in component names
version: '1.0'
description: Demo
license:
name: DEMO
url: https://demo.com
servers:
- url: http://demo.com/api
paths:
/org-user:
get:
responses:
'200':
description: ok
content:
application/json:
schema:
$ref: '#/components/schemas/my.org.User'
post:
responses:
'200':
description: ok
content:
application/json:
schema:
additionalProperties:
$ref: '#/components/schemas/my.User'
components:
schemas:
my.org.User:
type: object
properties:
orgId:
type: string
my.User:
type: object
properties:
displayName:
type: string

`;

exports[`bundle > should normalize self-file explicit $ref in nested referenced file 1`] = `
openapi: 3.0.0
info:
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/__tests__/bundle-oas.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ describe('bundle-oas', () => {
config: await createEmptyRedoclyConfig({}),
ref: path.join(__dirname, 'fixtures/refs/openapi-with-external-refs.yaml'),
});
expect(problems).toHaveLength(0);
expect(problems).toHaveLength(1);
expect(problems[0].severity).toBe('warn');
expect(problems[0].message).toEqual(
`Two schemas are referenced with the same name but different content. Renamed first to first-2.`
);
expect(res.parsed).toMatchSnapshot();
});

Expand Down
67 changes: 66 additions & 1 deletion packages/core/src/__tests__/bundle.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,11 @@ describe('bundle', () => {
config: await createConfig({}),
ref: path.join(__dirname, 'fixtures/refs/openapi-with-external-refs.yaml'),
});
expect(problems).toHaveLength(0);
expect(problems).toHaveLength(1);
expect(problems[0].severity).toBe('warn');
expect(problems[0].message).toEqual(
`Two schemas are referenced with the same name but different content. Renamed first to first-2.`
);
expect(res.parsed).toMatchSnapshot();
});

Expand Down Expand Up @@ -119,6 +123,67 @@ describe('bundle', () => {
);
});

it('should bundle external pointer refs and warn for conflicting names', async () => {
const { bundle: res, problems } = await bundle({
config: await createConfig({}),
ref: path.join(
__dirname,
'fixtures/refs/openapi-with-external-refs-pointer-conflicting-names.yaml'
),
});
expect(problems).toHaveLength(1);
expect(problems[0].severity).toBe('warn');
expect(problems[0].message).toEqual(
`Two schemas are referenced with the same name but different content. Renamed User to User-2.`
);
expect(res.parsed).toMatchSnapshot();
});

it('should bundle external pointer refs and do not show warnings for conflicting names', async () => {
const { problems } = await bundle({
config: await createConfig({}),
ref: path.join(
__dirname,
'fixtures/refs/openapi-with-external-refs-pointer-conflicting-names.yaml'
),
componentRenamingConflicts: 'off',
});
expect(problems).toHaveLength(0);
});

it('should report error-severity problems for conflicting pointer ref names', async () => {
const { problems } = await bundle({
config: await createConfig({}),
ref: path.join(
__dirname,
'fixtures/refs/openapi-with-external-refs-pointer-conflicting-names.yaml'
),
componentRenamingConflicts: 'error',
});
expect(problems).toHaveLength(1);
expect(problems[0].severity).toBe('error');
expect(problems[0].message).toEqual(
`Two schemas are referenced with the same name but different content. Renamed User to User-2.`
);
});

it('should keep dotted JSON pointer schema keys and rename conflicting User schemas', async () => {
const { bundle: res, problems } = await bundle({
config: await createConfig({}),
ref: path.join(
__dirname,
'fixtures/refs/openapi-bundle-external-schema-names-and-user-conflict.yaml'
),
});

expect(problems).toHaveLength(0);
const schemas = (res.parsed as { components: { schemas: Record<string, unknown> } }).components
.schemas;
expect(schemas['my.org.User']).toBeDefined();
expect(schemas['my.User']).toBeDefined();
expect(res.parsed).toMatchSnapshot();
});

it('should dereferenced correctly when used with dereference', async () => {
const { bundle: res, problems } = await bundleDocument({
externalRefResolver: new BaseResolver(),
Expand Down
6 changes: 6 additions & 0 deletions packages/core/src/__tests__/fixtures/refs/component-a.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
components:
parameters:
User:
name: test
in: path
description: test-A
6 changes: 6 additions & 0 deletions packages/core/src/__tests__/fixtures/refs/component-b.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
components:
parameters:
User:
name: test
in: query
description: test-B
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
components:
schemas:
my.org.User:
type: object
properties:
orgId:
type: string
my.User:
type: object
properties:
displayName:
type: string
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
openapi: 3.0.0
info:
title: Test ref with dots in component names
version: '1.0'
description: Demo
license:
name: DEMO
url: https://demo.com
servers:
- url: http://demo.com/api
paths:
/org-user:
get:
responses:
'200':
description: ok
content:
application/json:
schema:
$ref: external-ref-with-dots.yaml#/components/schemas/my.org.User
post:
responses:
'200':
description: ok
content:
application/json:
schema:
additionalProperties:
$ref: external-ref-with-dots.yaml#/components/schemas/my.User
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
openapi: 3.1.0
paths:
/foo:
put:
parameters:
- $ref: 'component-a.yaml#/components/parameters/User'
get:
parameters:
- $ref: 'component-b.yaml#/components/parameters/User'
26 changes: 4 additions & 22 deletions packages/core/src/bundle/bundle-visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ import {
replaceRef,
isExternalValue,
isRef,
pointerBaseName,
refBaseName,
type Location,
} from '../ref-utils.js';
import { type ResolvedRefMap, type Document } from '../resolve.js';
import { reportUnresolvedRef } from '../rules/common/no-unresolved-refs.js';
import { type OasRef } from '../typings/openapi.js';
import { dequal } from '../utils/dequal.js';
import { isTruthy } from '../utils/is-truthy.js';
import { makeRefId } from '../utils/make-ref-id.js';
import { type Oas3Visitor, type Oas2Visitor } from '../visitors.js';
import { type UserContext, type ResolveResult } from '../walk.js';
Expand Down Expand Up @@ -274,27 +274,9 @@ export function makeBundleVisitor({
componentType: string,
ctx: UserContext
) {
const [fileRef, pointer] = [target.location.source.absoluteRef, target.location.pointer];
const componentsGroup = components[componentType];

let name = '';

const refParts = pointer.slice(2).split('/').filter(isTruthy); // slice(2) removes "#/"
while (refParts.length > 0) {
name = refParts.pop() + (name ? `-${name}` : '');
if (
!componentsGroup ||
!componentsGroup[name] ||
isEqualOrEqualRef(componentsGroup[name], target, ctx)
) {
return name;
Comment thread
kanoru3101 marked this conversation as resolved.
}
}

name = refBaseName(fileRef) + (name ? `_${name}` : '');
if (!componentsGroup[name] || isEqualOrEqualRef(componentsGroup[name], target, ctx)) {
return name;
}
const [fileRef, pointer] = [target.location.source.absoluteRef, target.location.pointer];
let name = pointerBaseName(pointer) || refBaseName(fileRef);

const prevName = name;
let serialId = 2;
Expand All @@ -303,7 +285,7 @@ export function makeBundleVisitor({
serialId++;
}

if (!componentsGroup[name]) {
if (!componentsGroup[name] && prevName !== name) {
Comment thread
tatomyr marked this conversation as resolved.
ctx.report({
message: `Two schemas are referenced with the same name but different content. Renamed ${prevName} to ${name}.`,
location: ctx.location,
Expand Down
Loading