Skip to content

Commit 74729f1

Browse files
committed
Merge branch 'release'
2 parents 08c87cd + cde3643 commit 74729f1

18 files changed

+350
-45
lines changed

.github/super-linter.env

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,6 @@ VALIDATE_TSX=false
3636
VALIDATE_TYPESCRIPT_ES=false
3737
VALIDATE_TYPESCRIPT_PRETTIER=false
3838
VALIDATE_TYPESCRIPT_STANDARD=false
39+
40+
# Skip OPENAPI check for this repo as soon as it contains OAS 3.1 which is not supported by super-linter and contains invalid OAS 3.0 specs for testing purposes
41+
VALIDATE_OPENAPI=false

.qubership/grand-report.json

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"exclusions" : [ {
3+
"t-hash" : "d0c92c764c5647d43d7166efc3e7d350751de063c1388bd2e113fab4865529d3",
4+
"f-hash" : "3780d8d44a0ffb112a67bef412882b39ed01c95e09ad51e0567d45cbf43dc639"
5+
}, {
6+
"t-hash" : "d0c92c764c5647d43d7166efc3e7d350751de063c1388bd2e113fab4865529d3",
7+
"f-hash" : "c084bbbd31296a950bb007771c0a53b3d8830ae129c7a5fe2333cdf043532c75"
8+
}, {
9+
"t-hash" : "d0c92c764c5647d43d7166efc3e7d350751de063c1388bd2e113fab4865529d3",
10+
"f-hash" : "e04d08ed8d22eff358a8e65fded2a4ef6cc954fc3070acf96f5795ec99e742c1"
11+
}, {
12+
"t-hash" : "d0c92c764c5647d43d7166efc3e7d350751de063c1388bd2e113fab4865529d3",
13+
"f-hash" : "e0d1bbde9173b4d91a89fb10ddf9ec131de758861a826c8ca0d1fc2d12681035"
14+
} ]
15+
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@netcracker/qubership-apihub-api-unifier",
3-
"version": "2.3.0",
3+
"version": "2.4.0",
44
"description": "Tools for JsonSchema/Openapi/GraphQL spec for unified processing",
55
"module": "./dist/index.es.js",
66
"main": "./dist/index.cjs.js",

src/define-origins-and-resolve-ref.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
ResolveOptions,
2323
RichReference,
2424
} from './types'
25-
import { resolveSpec, SPEC_TYPE_GRAPH_API, SPEC_TYPE_OPEN_API_31 } from './spec-type'
25+
import { resolveSpec } from './spec-type'
2626
import { ErrorMessage } from './errors'
2727
import { createCycledJsoHandlerHook } from './cycle-jso'
2828
import { JSON_SCHEMA_PROPERTY_ALL_OF, JSON_SCHEMA_PROPERTY_REF } from './rules/jsonschema.const'
@@ -60,12 +60,14 @@ const IMPOSSIBLE_ORIGIN_PARENT: ChainItem = { parent: undefined, value: 'ERROR!!
6060

6161
export const defineOriginsAndResolveRef = (value: unknown, options?: ResolveOptions) => {
6262
const spec = resolveSpec(value)
63+
const source = options?.source ?? value
64+
6365
const internalOptions = {
6466
resolveRef: DEFAULT_OPTION_RESOLVE_REF,
6567
originsAlreadyDefined: !!options?.originsFlag,
6668
...options,
6769
originsFlag: options?.originsAlreadyDefined ? undefined : options?.originsFlag,
68-
source: options?.source ?? value,
70+
source,
6971
ignoreSymbols: new Set([
7072
...(options?.originsFlag ? [options.originsFlag] : []),
7173
...(options?.inlineRefsFlag ? [options.inlineRefsFlag] : []),
@@ -140,7 +142,13 @@ export const deDefineOriginsAndResolvedRefSymbols = (value: unknown, options?: R
140142
const createDefineOriginsAndResolveRefHook: (rootJso: unknown, options: InternalResolveOptions, cycleJsoHook: SyncCloneHook<DefineOriginsAndResolveRefState>) => DefineOriginsAndResolveRefSyncCloneHook = (rootJso, options, cycleJsoHook) => {
141143
const cyclingGuard: Set<unknown> = new Set()
142144
const syntheticTitleCache: Map<string, Record<PropertyKey, unknown>> = new Map()
143-
const defineOriginsAndResolveRefHook: DefineOriginsAndResolveRefSyncCloneHook = ({ key, value, state, path, rules, }) => {
145+
const defineOriginsAndResolveRefHook: DefineOriginsAndResolveRefSyncCloneHook = ({
146+
key,
147+
value,
148+
state,
149+
path,
150+
rules,
151+
}) => {
144152
if (state.ignoreTreeUnderSymbols) {
145153
return { value }
146154
}
@@ -189,7 +197,7 @@ const createDefineOriginsAndResolveRefHook: (rootJso: unknown, options: Internal
189197
}
190198
const reference = parseRef($ref)
191199

192-
const processWrapRefWithAllOfReference = (resolvedRefWithSibling: ResolvedRefWithSiblings) => {
200+
const processWrapRefWithAllOfReference = (resolvedRefWithSibling: ResolvedRefWithSiblings) => {
193201
const {
194202
refValue,
195203
origin,
@@ -282,9 +290,9 @@ const createDefineOriginsAndResolveRefHook: (rootJso: unknown, options: Internal
282290
}
283291
}
284292

285-
const processResolvedReference = (resolvedRefWithSibling: ResolvedRefWithSiblings) => {
293+
const processResolvedReference = (resolvedRefWithSibling: ResolvedRefWithSiblings) => {
286294
if (hasChildrenOrigins(resolvedRefWithSibling)) {
287-
return processReferenceWithChildren(resolvedRefWithSibling)
295+
return processReferenceWithChildren(resolvedRefWithSibling)
288296
}
289297
return processWrapRefWithAllOfReference(resolvedRefWithSibling)
290298
}

src/normalize.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
NormalizeOptions,
1111
} from './types'
1212
import { deDefineOriginsAndResolvedRefSymbols, defineOriginsAndResolveRef } from './define-origins-and-resolve-ref'
13-
import { validate } from './validate'
13+
import { preValidate, validate } from './validate'
1414
import { merge } from './merge'
1515
import { cleanUpSynthetic, deCleanUpSynthetic, deUnify, unify } from './unify'
1616
import { deHash, hash } from './hash'
@@ -19,7 +19,8 @@ import { removeOasExtensions } from './remove-oas-extensions'
1919
export const normalize = (value: unknown, options: NormalizeOptions = {}) => {
2020
const optionsWithDefaults = createOptionsWithDefaults(options)
2121
let spec = value
22-
if (optionsWithDefaults.resolveRef || (!optionsWithDefaults.originsAlreadyDefined && optionsWithDefaults.originsFlag)) { spec = defineOriginsAndResolveRef(spec, optionsWithDefaults) }
22+
if (optionsWithDefaults.validate) { preValidate(spec, options) }
23+
if (optionsWithDefaults.resolveRef || (!optionsWithDefaults.originsAlreadyDefined && optionsWithDefaults.originsFlag)) {spec = defineOriginsAndResolveRef(spec, optionsWithDefaults)}
2324
if (optionsWithDefaults.validate) { spec = validate(spec, optionsWithDefaults) }
2425
if (optionsWithDefaults.mergeAllOf) { spec = merge(spec, optionsWithDefaults) }
2526
if (optionsWithDefaults.unify) { spec = unify(spec, optionsWithDefaults) }

src/rules/jsonschema.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -438,7 +438,6 @@ export const jsonSchemaRules: (
438438
hashStrategy: CURRENT_DATA_LEVEL,
439439
},
440440
'/properties': {
441-
442441
'/*': () => ({
443442
...self(),
444443
newDataLayer: true,

src/rules/openapi.const.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ export const OPEN_API_PROPERTY_SCHEMAS = 'schemas'
4747
export const OPEN_API_PROPERTY_SCHEMA = 'schema'
4848
export const OPEN_API_PROPERTY_LINKS = 'links'
4949
export const OPEN_API_PROPERTY_SECURITY_SCHEMAS = 'securitySchemes'
50+
export const OPEN_API_PROPERTY_PATH_ITEMS = 'pathItems'
5051

5152
export const OPEN_API_PROPERTY_DESCRIPTION = 'description'
5253
export const OPEN_API_PROPERTY_SUMMARY = 'summary'

src/rules/openapi.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ const customFor30JsonSchemaRulesFactory = (): NormalizationRules => {
436436
...customFor30JsonSchemaRules,
437437
merge: resolvers.itemsMergeResolver,
438438
hashStrategy: CURRENT_DATA_LEVEL,
439-
newDataLayer: true
439+
newDataLayer: true,
440440
}),
441441
'/additionalItems': {
442442
validate: () => false,
@@ -704,10 +704,7 @@ const openApiPathItemRules = (version: OpenApiSpecVersion): NormalizationRules =
704704
validate: checkType(TYPE_OBJECT),
705705
}),
706706
'/parameters': openApiParametersRules(version),
707-
referenceHandler: referenceObjectRuleFunction({
708-
version,
709-
allowedOverrides: [OPEN_API_PROPERTY_SUMMARY, OPEN_API_PROPERTY_DESCRIPTION],
710-
}),
707+
referenceHandler: referenceObjectResolver(),
711708
validate: checkType(TYPE_OBJECT),
712709
unify: pathItemsUnification,
713710
})
@@ -822,6 +819,18 @@ export const openApiRules = (version: OpenApiSpecVersion): NormalizationRules =>
822819
...openApiExtensionRulesFunction(() => openApiPathItemRules(version)),
823820
},
824821
},
822+
/**
823+
* Note: For OAS 3.0, `components.pathItems` is not a valid property.
824+
* We intentionally keep these rules and do not delete this path here
825+
* because invalid `components.pathItems` entries are pre-processed and
826+
* handled during pre-validation step.
827+
* Additionally, the reference resolver contains checks that guard against
828+
* misuse in OAS 3.0. See: validate.ts
829+
*/
830+
'/pathItems': ({
831+
...openApiExtensionRulesFunction(openApiPathItemRules(version)),
832+
validate: checkType(TYPE_OBJECT),
833+
}),
825834
...openApiExamplesRules(version),
826835
...openApiExtensionRules,
827836
validate: checkType(TYPE_OBJECT),

src/validate.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,33 @@ import {
77
ValidateSyncCloneHook,
88
} from './types'
99
import { isArray, isObject, JSON_ROOT_KEY, syncClone } from '@netcracker/qubership-apihub-json-crawl'
10-
import { resolveSpec } from './spec-type'
10+
import { resolveSpec, SPEC_TYPE_OPEN_API_30 } from './spec-type'
1111
import { createCycledJsoHandlerHook } from './cycle-jso'
1212
import { RULES } from './rules'
1313
import { cleanSeveralOrigins } from './origins'
14+
import { OPEN_API_PROPERTY_COMPONENTS, OPEN_API_PROPERTY_PATH_ITEMS } from './rules/openapi.const'
15+
16+
/**
17+
* Preprocesses an OpenAPI specification prior to reference resolution/origin definition.
18+
*
19+
* For OpenAPI 3.0 documents, `components.pathItems` is not a valid field (it was
20+
* added in OAS 3.1). Some tools may still emit it. To keep the input compliant,
21+
* deterministic, and to avoid misinterpreting invalid nodes as real API paths,
22+
* this function removes `components.pathItems` for OAS 3.0 and emits an optional
23+
* validation message via `options.onValidateError`.
24+
*/
25+
export function preValidate(source: unknown, options?: ValidateOptions & ResolveOptions): void {
26+
const spec = resolveSpec(source)
27+
if (spec.type !== SPEC_TYPE_OPEN_API_30 || !isObject(source)) {
28+
return
29+
}
30+
if (OPEN_API_PROPERTY_COMPONENTS in source && isObject(source.components)) {
31+
const components = source.components as Record<string, unknown>
32+
if (Reflect.deleteProperty(components, OPEN_API_PROPERTY_PATH_ITEMS)) {
33+
(options as ValidateOptions)?.onValidateError?.(`Invalid property 'components.pathItems' for OpenAPI 3.0. The property has been removed to maintain 3.0 compliance.`, ['components', 'pathItems'], 'pathItems')
34+
}
35+
}
36+
}
1437

1538
const createValidationHook: (options: InternalValidationOptions) => ValidateSyncCloneHook = (options) => {
1639
const validateHook: ValidateSyncCloneHook = ({ key, path, value, rules, state }) => {

0 commit comments

Comments
 (0)