Skip to content

Commit 88cf3f8

Browse files
committed
Merge branch 'release'
2 parents 44e4e73 + 603dfa5 commit 88cf3f8

File tree

149 files changed

+2258
-704
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

149 files changed

+2258
-704
lines changed

.github/super-linter.env

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,10 @@ VALIDATE_TSX=false
3636
VALIDATE_TYPESCRIPT_ES=false
3737
VALIDATE_TYPESCRIPT_PRETTIER=false
3838
VALIDATE_TYPESCRIPT_STANDARD=false
39+
40+
# Skip OPENAPI check by CHECKOV for this repo as it contains invalid OAS 3.0 specs for testing purposes
41+
VALIDATE_CHECKOV=false
42+
VALIDATE_OPENAPI=false
43+
44+
# Skip checking of specially broken specifications
45+
FILTER_REGEX_EXCLUDE=test/projects/broken/.*

.github/workflows/delete-dist-tag.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ on:
55

66
jobs:
77
delete-dist-tag:
8-
uses: netcracker/qubership-apihub-ci/.github/workflows/delete-dist-tag.yaml@main
8+
uses: netcracker/qubership-apihub-ci/.github/workflows/delete-dist-tag.yaml@main

.qubership/grand-report.json

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
{
2+
"exclusions" : [ {
3+
"t-hash" : "795f701a5a477424919b8f2f6dbd915815e3aeaba2f01c5585b62088f0ea72f1",
4+
"f-hash" : "04da8a5d28577b892f965d7204a504ce7be0094cd46ab1fd31aaec5fdeac0052"
5+
}, {
6+
"t-hash" : "5efb34e1c2b422d1baa6e973c23a553c704b3f27ae23078f63fdd5aca517f2d5",
7+
"f-hash" : "0a84bd29d04961f0377f9ab44575048ad562ef15b31d6bf3b60150b8d9f21237"
8+
}, {
9+
"t-hash" : "5efb34e1c2b422d1baa6e973c23a553c704b3f27ae23078f63fdd5aca517f2d5",
10+
"f-hash" : "2d6dcc685bb4214db7bffbb0347351fe9a0ae105880cfc68323898dec7da8700"
11+
}, {
12+
"t-hash" : "795f701a5a477424919b8f2f6dbd915815e3aeaba2f01c5585b62088f0ea72f1",
13+
"f-hash" : "320fde79a56686a2b4ef150ebcb9ac86dbfe10defbd26b3bca53893a1158c214"
14+
}, {
15+
"t-hash" : "795f701a5a477424919b8f2f6dbd915815e3aeaba2f01c5585b62088f0ea72f1",
16+
"f-hash" : "35e1276fdf521ea0b800f523e10aa66fdd7ba45afff03a54320c2c78331662a1"
17+
}, {
18+
"t-hash" : "5efb34e1c2b422d1baa6e973c23a553c704b3f27ae23078f63fdd5aca517f2d5",
19+
"f-hash" : "57e985b5c4aa0d868764daa9a15b842020d3763ae5198b53ed0d9adf0653cdd1"
20+
}, {
21+
"t-hash" : "5efb34e1c2b422d1baa6e973c23a553c704b3f27ae23078f63fdd5aca517f2d5",
22+
"f-hash" : "5bc453e937cfe34a51d86403f19f1cc0c8326d1597ab1fc60bc5ce9cc6fc1c5e"
23+
}, {
24+
"t-hash" : "795f701a5a477424919b8f2f6dbd915815e3aeaba2f01c5585b62088f0ea72f1",
25+
"f-hash" : "5e0da273e11b29963bfa8dcc457505581dee1afea7c8c780f35e9a4114966b4b"
26+
}, {
27+
"t-hash" : "fab1e2e699b3b927cbf875046a64f2225df02d5cb306f3857424c2bbb87be61f",
28+
"f-hash" : "80218ffcd5d234a073e144cf6b0d1034d6fc354be74f13c80321eb68e62f2f4d"
29+
}, {
30+
"t-hash" : "fab1e2e699b3b927cbf875046a64f2225df02d5cb306f3857424c2bbb87be61f",
31+
"f-hash" : "ae2ae8e5980953fe6629af044dcb907d6725f9c9981e502809f87589fabd05db"
32+
}, {
33+
"t-hash" : "fab1e2e699b3b927cbf875046a64f2225df02d5cb306f3857424c2bbb87be61f",
34+
"f-hash" : "b145f0361ca381ae9878f62070575fa69461cd3883e57a8406fc87cec5cf5863"
35+
}, {
36+
"t-hash" : "fab1e2e699b3b927cbf875046a64f2225df02d5cb306f3857424c2bbb87be61f",
37+
"f-hash" : "bdf1ed998a5de05b64c9f6430cfd0b9445ee8450a29a8e2497533d1733592446"
38+
}, {
39+
"t-hash" : "fab1e2e699b3b927cbf875046a64f2225df02d5cb306f3857424c2bbb87be61f",
40+
"f-hash" : "caefe5cd42d4a2ee6ad4528381dd278ac78afd6bf91d2c642d3333b1992eb2c4"
41+
}, {
42+
"t-hash" : "fab1e2e699b3b927cbf875046a64f2225df02d5cb306f3857424c2bbb87be61f",
43+
"f-hash" : "d27f64389a9e6ea237157a200d5a50e3afdda16a382fd3d1b2b6d7714b72464e"
44+
}, {
45+
"t-hash" : "795f701a5a477424919b8f2f6dbd915815e3aeaba2f01c5585b62088f0ea72f1",
46+
"f-hash" : "d86f71972fd6048a9c92e445b2720a0e0c2c13c90046c79333ca8d3407cdc9ab"
47+
}, {
48+
"t-hash" : "00000000",
49+
"f-hash" : "deb9159a542d4bdb7426160a9722ed78d9a8582e0522fa8c581e06d5a79def2f"
50+
}, {
51+
"t-hash" : "fab1e2e699b3b927cbf875046a64f2225df02d5cb306f3857424c2bbb87be61f",
52+
"f-hash" : "e1566fd9f129a29f3c48553081d9ca3f5cc853f3a4dc746b0024c0d78671e574"
53+
} ]
54+
}

package-lock.json

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

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@netcracker/qubership-apihub-api-processor",
3-
"version": "4.1.2",
3+
"version": "4.2.0",
44
"description": "",
55
"license": "Apache-2.0",
66
"module": "dist/esm/src/index.js",
@@ -31,8 +31,8 @@
3131
"update-lock-file": "update-lock-file @netcracker"
3232
},
3333
"dependencies": {
34-
"@netcracker/qubership-apihub-api-diff": "2.2.0",
35-
"@netcracker/qubership-apihub-api-unifier": "2.3.0",
34+
"@netcracker/qubership-apihub-api-diff": "2.3.0",
35+
"@netcracker/qubership-apihub-api-unifier": "2.4.0",
3636
"@netcracker/qubership-apihub-graphapi": "1.0.8",
3737
"@netcracker/qubership-apihub-json-crawl": "1.0.4",
3838
"adm-zip": "0.5.10",

src/apitypes/rest/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ import { OpenAPIV3 } from 'openapi-types'
1919
import { buildRestDocument, createRestExportDocument, dumpRestDocument } from './rest.document'
2020
import { REST_API_TYPE, REST_DOCUMENT_TYPE } from './rest.consts'
2121
import { compareRestOperationsData } from './rest.changes'
22-
import { buildRestOperations, createNormalizedOperationId } from './rest.operations'
22+
import { buildRestOperations } from './rest.operations'
2323
import { parseRestFile } from './rest.parser'
24-
2524
import { ApiBuilder } from '../../types'
25+
import { calculateNormalizedOperationId } from '../../utils'
2626

2727
export * from './rest.consts'
2828

@@ -34,6 +34,6 @@ export const restApiBuilder: ApiBuilder<OpenAPIV3.Document> = {
3434
buildOperations: buildRestOperations,
3535
dumpDocument: dumpRestDocument,
3636
compareOperationsData: compareRestOperationsData,
37-
createNormalizedOperationId: createNormalizedOperationId,
37+
createNormalizedOperationId: calculateNormalizedOperationId,
3838
createExportDocument: createRestExportDocument,
3939
}

src/apitypes/rest/rest.operation.ts

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,29 +19,35 @@ import { OpenAPIV3 } from 'openapi-types'
1919
import { REST_API_TYPE, REST_KIND_KEY } from './rest.consts'
2020
import { operationRules } from './rest.rules'
2121
import type * as TYPE from './rest.types'
22-
import type {
22+
import { RestOperationData } from './rest.types'
23+
import {
2324
BuildConfig,
2425
CrawlRule,
2526
DeprecateItem,
2627
NotificationMessage,
2728
OperationCrawlState,
29+
OperationId,
2830
SearchScopes,
2931
} from '../../types'
3032
import {
3133
buildSearchScope,
34+
calculateOperationId,
3235
capitalize,
36+
extractSymbolProperty,
3337
getKeyValue,
3438
getSplittedVersionKey,
39+
getSymbolValueIfDefined,
3540
isDeprecatedOperationItem,
3641
isOperationDeprecated,
42+
isValidHttpMethod,
3743
normalizePath,
3844
rawToApiKind,
3945
setValueByPath,
4046
takeIf,
4147
takeIfDefined,
4248
} from '../../utils'
4349
import { API_KIND, INLINE_REFS_FLAG, ORIGINS_SYMBOL, VERSION_STATUS } from '../../consts'
44-
import { getCustomTags, resolveApiAudience } from './rest.utils'
50+
import { getCustomTags, getOperationBasePath, resolveApiAudience } from './rest.utils'
4551
import { DebugPerformanceContext, syncDebugPerformance } from '../../utils/logs'
4652
import {
4753
calculateDeprecatedItems,
@@ -59,6 +65,7 @@ import {
5965
} from '@netcracker/qubership-apihub-api-unifier'
6066
import { calculateObjectHash } from '../../utils/hashes'
6167
import { calculateTolerantHash } from '../../components/deprecated'
68+
import { getValueByPath } from '../../utils/path'
6269

6370
export const buildRestOperation = (
6471
operationId: string,
@@ -143,7 +150,7 @@ export const buildRestOperation = (
143150
security,
144151
components?.securitySchemes,
145152
)
146-
calculateSpecRefs(document.data, refsOnlySingleOperationSpec, specWithSingleOperation, models, componentsHashMap)
153+
calculateSpecRefs(document.data, refsOnlySingleOperationSpec, specWithSingleOperation, [operationId], models, componentsHashMap)
147154
const dataHash = calculateObjectHash(specWithSingleOperation)
148155
return [specWithSingleOperation, dataHash]
149156
}, debugCtx)
@@ -180,7 +187,14 @@ export const buildRestOperation = (
180187
}
181188
}
182189

183-
export const calculateSpecRefs = (sourceDocument: unknown, normalizedSpec: unknown, resultSpec: unknown, models?: Record<string, string>, componentsHashMap?: Map<string, string>): void => {
190+
export const calculateSpecRefs = (
191+
sourceDocument: TYPE.RestOperationData,
192+
normalizedSpec: TYPE.RestOperationData,
193+
resultSpec: TYPE.RestOperationData,
194+
operations: OperationId[],
195+
models?: Record<string, string>,
196+
componentsHashMap?: Map<string, string>,
197+
): void => {
184198
const handledObjects = new Set<unknown>()
185199
const inlineRefs = new Set<string>()
186200
syncCrawl(
@@ -212,10 +226,11 @@ export const calculateSpecRefs = (sourceDocument: unknown, normalizedSpec: unkno
212226
return
213227
}
214228
const componentName = matchResult.grepValues[grepKey].toString()
215-
const component = getKeyValue(sourceDocument, ...matchResult.path)
229+
const component = getKeyValue(sourceDocument, ...matchResult.path) as Record<string, unknown>
216230
if (!component) {
217231
return
218232
}
233+
219234
if (models && !models[componentName] && isComponentsSchemaRef(matchResult.path)) {
220235
let componentHash = componentsHashMap?.get(componentName)
221236
if (componentHash) {
@@ -226,8 +241,68 @@ export const calculateSpecRefs = (sourceDocument: unknown, normalizedSpec: unkno
226241
models[componentName] = componentHash
227242
}
228243
}
244+
229245
setValueByPath(resultSpec, matchResult.path, component)
230246
})
247+
248+
if (operations?.length) {
249+
reduceComponentPathItemsToOperations(resultSpec, normalizedSpec, operations)
250+
}
251+
}
252+
253+
function reduceComponentPathItemsToOperations(
254+
resultSpec: RestOperationData,
255+
normalizedDocument: RestOperationData,
256+
operations: OperationId[],
257+
): void {
258+
const { paths } = normalizedDocument
259+
260+
for (const path of Object.keys(paths)) {
261+
const sourcePathItem = paths[path] as OpenAPIV3.PathItemObject
262+
const pathItemComponentJsonPath = getPathItemComponentJsonPath(sourcePathItem)
263+
if (!pathItemComponentJsonPath) {
264+
continue
265+
}
266+
267+
const pathItemComponent = getValueByPath(resultSpec, pathItemComponentJsonPath) as OpenAPIV3.PathItemObject
268+
269+
const operationIds: OpenAPIV3.HttpMethods[] = (Object.keys(pathItemComponent) as OpenAPIV3.HttpMethods[])
270+
.filter((httpMethod) => isValidHttpMethod(httpMethod))
271+
.filter(httpMethod => {
272+
const methodData = sourcePathItem[httpMethod as OpenAPIV3.HttpMethods]
273+
if (!methodData) return false
274+
const basePath = getOperationBasePath(
275+
methodData?.servers ||
276+
sourcePathItem?.servers ||
277+
[],
278+
)
279+
const operationId = calculateOperationId(basePath, httpMethod, path)
280+
return operations.includes(operationId)
281+
})
282+
283+
if (operationIds?.length) {
284+
const pathItemObject = {
285+
...extractCommonPathItemProperties(pathItemComponent),
286+
...operationIds.reduce<OpenAPIV3.PathItemObject>((pathItemObject: OpenAPIV3.PathItemObject, operationId: OpenAPIV3.HttpMethods) => {
287+
const operationData = pathItemComponent[operationId]
288+
if (operationData) {
289+
pathItemObject[operationId] = { ...operationData }
290+
}
291+
return pathItemObject
292+
}, {}),
293+
}
294+
setValueByPath(resultSpec, pathItemComponentJsonPath, pathItemObject)
295+
}
296+
}
297+
}
298+
299+
const getPathItemComponentJsonPath = (sourcePathItem: OpenAPIV3.PathItemObject): JsonPath | undefined => {
300+
const refs = getSymbolValueIfDefined(sourcePathItem, INLINE_REFS_FLAG) as string[] | undefined
301+
if (!refs || refs.length === 0) {
302+
return undefined
303+
}
304+
305+
return parseRef(refs[0])?.jsonPath
231306
}
232307

233308
export const isComponentsSchemaRef = (path: JsonPath): boolean => {
@@ -236,6 +311,7 @@ export const isComponentsSchemaRef = (path: JsonPath): boolean => {
236311
[[OPEN_API_PROPERTY_COMPONENTS, OPEN_API_PROPERTY_SCHEMAS, PREDICATE_UNCLOSED_END]],
237312
)
238313
}
314+
239315
const isOperationPaths = (paths: JsonPath[]): boolean => {
240316
return !!matchPaths(
241317
paths,
@@ -257,15 +333,19 @@ const createSingleOperationSpec = (
257333
): TYPE.RestOperationData => {
258334
const pathData = document.paths[path] as OpenAPIV3.PathItemObject
259335

336+
const isRefPathData = !!pathData.$ref
260337
return {
261338
openapi: openapi ?? '3.0.0',
262339
...takeIfDefined({ servers }),
263340
...takeIfDefined({ security }), // TODO: remove duplicates in security
264341
paths: {
265-
[path]: {
266-
...extractCommonPathItemProperties(pathData),
267-
[method]: { ...pathData[method] },
268-
},
342+
[path]: isRefPathData
343+
? pathData
344+
: {
345+
...extractCommonPathItemProperties(pathData),
346+
[method]: { ...pathData[method] },
347+
...extractSymbolProperty(pathData, INLINE_REFS_FLAG),
348+
},
269349
},
270350
components: {
271351
...takeIfDefined({ securitySchemes }),

src/apitypes/rest/rest.operations.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,8 @@
1717
import { OpenAPIV3 } from 'openapi-types'
1818

1919
import { buildRestOperation } from './rest.operation'
20-
import { OperationIdNormalizer, OperationsBuilder } from '../../types'
21-
import {
22-
createBundlingErrorHandler,
23-
IGNORE_PATH_PARAM_UNIFIED_PLACEHOLDER,
24-
removeComponents,
25-
removeFirstSlash,
26-
slugify,
27-
} from '../../utils'
20+
import { OperationsBuilder } from '../../types'
21+
import { calculateOperationId, createBundlingErrorHandler, removeComponents } from '../../utils'
2822
import { getOperationBasePath } from './rest.utils'
2923
import type * as TYPE from './rest.types'
3024
import { HASH_FLAG, INLINE_REFS_FLAG, MESSAGE_SEVERITY, NORMALIZE_OPTIONS, ORIGINS_SYMBOL } from '../../consts'
@@ -61,7 +55,7 @@ export const buildRestOperations: OperationsBuilder<OpenAPIV3.Document> = async
6155
debugCtx,
6256
)
6357

64-
const { paths, servers } = document.data
58+
const { paths, servers } = effectiveDocument
6559

6660
const operations: TYPE.VersionRestOperation[] = []
6761
if (!paths) { return [] }
@@ -77,9 +71,8 @@ export const buildRestOperations: OperationsBuilder<OpenAPIV3.Document> = async
7771
await asyncFunction(() => {
7872
const methodData = pathData[key as OpenAPIV3.HttpMethods]
7973
const basePath = getOperationBasePath(methodData?.servers || pathData?.servers || servers || [])
80-
const operationPath = basePath + path
8174

82-
const operationId = slugify(`${removeFirstSlash(operationPath)}-${key}`)
75+
const operationId = calculateOperationId(basePath, key, path)
8376

8477
if (ctx.operationResolver(operationId)) {
8578
ctx.notifications.push({
@@ -112,8 +105,3 @@ export const buildRestOperations: OperationsBuilder<OpenAPIV3.Document> = async
112105
}
113106
return operations
114107
}
115-
116-
export const createNormalizedOperationId: OperationIdNormalizer = (operation) => {
117-
const { metadata: { path, method } } = operation
118-
return slugify(`${path}-${method}`, [], IGNORE_PATH_PARAM_UNIFIED_PLACEHOLDER)
119-
}

0 commit comments

Comments
 (0)