diff --git a/src/openapi/openapi3.classify.ts b/src/openapi/openapi3.classify.ts index 07f3931..6bcd1cd 100644 --- a/src/openapi/openapi3.classify.ts +++ b/src/openapi/openapi3.classify.ts @@ -14,6 +14,7 @@ import { getKeyValue, isExist, isNotEmptyArray } from '../utils' import { emptySecurity, getDefaultStyle, includeSecurity } from './openapi3.utils' import type { ClassifyRule, CompareContext } from '../types' import { DiffType } from '../types' +import { hidePathParamNames } from './openapi3.mapping' export const paramClassifyRule: ClassifyRule = [ ({ after }) => { @@ -72,7 +73,7 @@ export const parameterAllowReservedClassifyRule: ClassifyRule = [ export const parameterNameClassifyRule: ClassifyRule = [ nonBreaking, breaking, - ({ before }) => (getKeyValue(before.parent, 'in') === 'path' ? nonBreaking : breaking), + ({ before }) => (getKeyValue(before.parent, 'in') === 'path' ? annotation : breaking), ] export const parameterRequiredClassifyRule: ClassifyRule = [ @@ -141,3 +142,17 @@ export const operationSecurityItemClassifyRule: ClassifyRule = [ after, }) => (includeSecurity(after.parent, before.parent) || emptySecurity(after.value) ? nonBreaking : breaking), ] + +export const pathChangeClassifyRule: ClassifyRule = [ + nonBreaking, + breaking, + ({ before, after }) => { + const beforePath = before.key as string + const afterPath = after.key as string + const unifiedBeforePath = hidePathParamNames(beforePath) + const unifiedAfterPath = hidePathParamNames(afterPath) + + // If unified paths are the same, it means only parameter names changed + return unifiedBeforePath === unifiedAfterPath ? annotation : breaking + } +] diff --git a/src/openapi/openapi3.mapping.ts b/src/openapi/openapi3.mapping.ts index b455f88..f8b0be6 100644 --- a/src/openapi/openapi3.mapping.ts +++ b/src/openapi/openapi3.mapping.ts @@ -124,7 +124,7 @@ export const contentMediaTypeMappingResolver: MappingResolver = (before, return result } -function hidePathParamNames(path: string): string { +export function hidePathParamNames(path: string): string { return path.replace(PATH_PARAMETER_REGEXP, PATH_PARAM_UNIFIED_PLACEHOLDER) } diff --git a/src/openapi/openapi3.rules.ts b/src/openapi/openapi3.rules.ts index 46cd227..a6a38c8 100644 --- a/src/openapi/openapi3.rules.ts +++ b/src/openapi/openapi3.rules.ts @@ -51,6 +51,7 @@ import { parameterNameClassifyRule, parameterRequiredClassifyRule, parameterStyleClassifyRule, + pathChangeClassifyRule, } from './openapi3.classify' import { contentMediaTypeMappingResolver, @@ -341,7 +342,7 @@ export const openApi3Rules = (options: OpenApi3RulesOptions): CompareRules => { $: allUnclassified, mapping: options.mode === COMPARE_MODE_OPERATION ? singleOperationPathMappingResolver : pathMappingResolver, '/*': { - $: [nonBreaking, breaking, breaking], + $: pathChangeClassifyRule, mapping: options.mode === COMPARE_MODE_OPERATION ? singleOperationPathMappingResolver : pathMappingResolver, '/summary': { $: allAnnotation }, '/description': { $: allAnnotation }, diff --git a/test/openapi.diff.test.ts b/test/openapi.diff.test.ts index 1d211a5..0e74306 100644 --- a/test/openapi.diff.test.ts +++ b/test/openapi.diff.test.ts @@ -7,6 +7,7 @@ import { DIFF_META_KEY, DiffAction, nonBreaking, + annotation, } from '../src' import { OpenapiBuilder, TEST_DIFF_FLAG, TEST_ORIGINS_FLAG, TEST_SYNTHETIC_TITLE_FLAG } from './helper' @@ -186,20 +187,19 @@ describe('Openapi3 operation changes', () => { action: DiffAction.rename, beforeDeclarationPaths: [['paths', '/path1/{param1}/{anotherParam1}']], afterDeclarationPaths: [['paths', '/path1/{param2}/{anotherParam2}']], - // todo fix - type: breaking, + type: annotation, // Only parameter names changed, unified paths are the same }), expect.objectContaining({ action: DiffAction.replace, beforeDeclarationPaths: [['paths', '/path1/{param1}/{anotherParam1}', 'parameters', 0, 'name']], afterDeclarationPaths: [['paths', '/path1/{param2}/{anotherParam2}', 'parameters', 1, 'name']], - type: nonBreaking, + type: annotation, }), expect.objectContaining({ action: DiffAction.replace, beforeDeclarationPaths: [['paths', '/path1/{param1}/{anotherParam1}', 'parameters', 1, 'name']], afterDeclarationPaths: [['paths', '/path1/{param2}/{anotherParam2}', 'parameters', 0, 'name']], - type: nonBreaking, + type: annotation, }), ])) }) @@ -211,20 +211,19 @@ describe('Openapi3 operation changes', () => { action: DiffAction.rename, beforeDeclarationPaths: [['paths', '/path1/{param1}/{anotherParam1}']], afterDeclarationPaths: [['paths', '/path1/{param2}/{anotherParam2}']], - // todo fix - type: breaking, + type: annotation, // Only parameter names changed, unified paths are the same }), expect.objectContaining({ action: DiffAction.replace, beforeDeclarationPaths: [['paths', '/path1/{param1}/{anotherParam1}', 'get', 'parameters', 0, 'name']], afterDeclarationPaths: [['paths', '/path1/{param2}/{anotherParam2}', 'get', 'parameters', 1, 'name']], - type: nonBreaking, + type: annotation, }), expect.objectContaining({ action: DiffAction.replace, beforeDeclarationPaths: [['paths', '/path1/{param1}/{anotherParam1}', 'get', 'parameters', 1, 'name']], afterDeclarationPaths: [['paths', '/path1/{param2}/{anotherParam2}', 'get', 'parameters', 0, 'name']], - type: nonBreaking, + type: annotation, }), ])) }) @@ -253,14 +252,13 @@ describe('Openapi3 operation changes', () => { action: DiffAction.rename, beforeDeclarationPaths: [['paths', '/path1/{param1}']], afterDeclarationPaths: [['paths', '/path1/{param2}']], - // todo fix - type: breaking, + type: annotation, // Only parameter name changed, unified paths are the same }), expect.objectContaining({ action: DiffAction.replace, beforeDeclarationPaths: [['paths', '/path1/{param1}', 'get', 'parameters', 0, 'name']], afterDeclarationPaths: [['paths', '/path1/{param2}', 'get', 'parameters', 0, 'name']], - type: nonBreaking, + type: annotation, }), expect.objectContaining({ action: DiffAction.add,