-
Notifications
You must be signed in to change notification settings - Fork 0
feat: path change due to moving a prefix from server.url to path classified as annotation #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
d4c6038
93cf66d
3000261
3de6a50
0cbf0e1
3aa5c81
e52f68b
4b01d21
9a4ce06
d1f085a
76a3f5b
f252064
69f4ca9
901425f
bc4f37e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -23,6 +23,7 @@ import { | |
| JSON_SCHEMA_NODE_TYPE_STRING, | ||
| JsonSchemaNodesNormalizedType, | ||
| } from '@netcracker/qubership-apihub-api-unifier' | ||
| import { OpenAPIV3 } from 'openapi-types' | ||
|
|
||
| export const isObject = (value: unknown): value is Record<string | symbol, unknown> => { | ||
| return typeof value === 'object' && value !== null | ||
|
|
@@ -221,3 +222,121 @@ export const checkPrimitiveType = (value: unknown): PrimitiveType | undefined => | |
| } | ||
| return undefined | ||
| } | ||
|
|
||
| export function intersection(array1: string[], array2: string[]): string[] { | ||
| const set2 = new Set(array2) | ||
| return [...new Set(array1.filter(x => set2.has(x)))] | ||
| } | ||
|
|
||
| export function difference(array1: string[], array2: string[]): string[] { | ||
| const set2 = new Set(array2) | ||
| return [...new Set(array1.filter(x => !set2.has(x)))] | ||
| } | ||
|
|
||
| export function removeExcessiveSlashes(input: string): string { | ||
| return input | ||
| .replace(/\/+/g, '/') // Replace multiple consecutive slashes with single slash | ||
| .replace(/^\//, '') // Remove leading slash | ||
| .replace(/\/$/, '') // Remove trailing slash | ||
| } | ||
|
|
||
| /** | ||
| * Traverses the merged document starting from given obj to the bottom and aggregates the diffs with rollup from the bottom up. | ||
| * Each object in the tree will have aggregatedDiffProperty only if there are diffs in the object or in the children, | ||
| * otherwise the aggregatedDiffProperty is not added. | ||
| * Note, that adding/removing the object itself is not included in the aggregation for this object, | ||
| * you need retrieve this diffs from parent object if you need them. | ||
| * Supports cycled JSO, nested objects and arrays. | ||
| * @param obj - The object to aggregate the diffs of. | ||
| * @param diffProperty - The property of the object to aggregate the diffs of. | ||
| * @param aggregatedDiffProperty - The property of the object to store the aggregated diffs in. | ||
| * @returns The aggregated diffs of the given object. | ||
| */ | ||
|
|
||
| // TODO: generalize to other use cases (like collecting deprecated) | ||
| export function aggregateDiffsWithRollup(obj: any, diffProperty: any, aggregatedDiffProperty: any): Set<Diff> | undefined { | ||
|
|
||
| const visited = new Set<any>() | ||
|
|
||
| function _aggregateDiffsWithRollup(obj: any): Set<Diff> | undefined { | ||
| if (!isObject(obj)) { | ||
| return undefined | ||
| } | ||
|
|
||
| if (visited.has(obj)) { | ||
| return obj[aggregatedDiffProperty] as Set<Diff> | undefined | ||
| } | ||
|
|
||
| visited.add(obj) | ||
|
|
||
| // Process all children and collect their diffs | ||
| const childrenDiffs = new Array<Set<Diff>>() | ||
| if (Array.isArray(obj)) { | ||
| for (const item of obj) { | ||
| const childDiffs = _aggregateDiffsWithRollup(item) | ||
| childDiffs && childDiffs.size > 0 && childrenDiffs.push(childDiffs) | ||
| } | ||
| } else { | ||
| for (const [_, value] of Object.entries(obj)) { | ||
| const childDiffs = _aggregateDiffsWithRollup(value) | ||
| childDiffs && childDiffs.size > 0 && childrenDiffs.push(childDiffs) | ||
| } | ||
| } | ||
|
|
||
| const hasOwnDiffs = diffProperty in obj | ||
|
|
||
| if (hasOwnDiffs || childrenDiffs.length > 1) { | ||
| // obj aggregated diffs are different from children diffs | ||
| const aggregatedDiffs = new Set<Diff>() | ||
| for (const childDiffs of childrenDiffs) { | ||
| childDiffs.forEach(diff => aggregatedDiffs.add(diff)) | ||
| } | ||
| const diffs = obj[diffProperty] as Record<string, Diff> | ||
| for (const key in diffs) { | ||
| aggregatedDiffs.add(diffs[key]) | ||
| } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. do we really need empty There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Else is not empty, it contains comment:) This is to emphasize/remind the reader that in this case to aggregated diffs will be added. Formatted code. |
||
| // Store the aggregated diffs in the object | ||
| obj[aggregatedDiffProperty] = aggregatedDiffs | ||
| } else if (childrenDiffs.length === 1) { | ||
| // could reuse a child diffs if there is only one | ||
| [obj[aggregatedDiffProperty]] = childrenDiffs | ||
| } else { | ||
| // no diffs- no aggregated diffs get assigned | ||
| } | ||
|
|
||
| return obj[aggregatedDiffProperty] as Set<Diff> | undefined | ||
| } | ||
|
|
||
| return _aggregateDiffsWithRollup(obj) | ||
| } | ||
|
|
||
| /** | ||
| * Extracts the base path (path after the domain) from the first server URL in an array of OpenAPI ServerObjects. | ||
| * It replaces any URL variable placeholders (e.g. {host}) with their default values from the 'variables' property. | ||
| * The function will return the normalized pathname (without trailing slash) or an empty string on error or if the input is empty. | ||
| * | ||
| * @param {OpenAPIV3.ServerObject[]} [servers] - An array of OpenAPI ServerObject definitions. | ||
| * @returns {string} The base path (pathname) part of the URL, without a trailing slash, or an empty string if unavailable. | ||
| */ | ||
| export const extractOperationBasePath = (servers?: OpenAPIV3.ServerObject[]): string => { | ||
| if (!Array.isArray(servers) || !servers.length) { return '' } | ||
|
|
||
| try { | ||
| const [firstServer] = servers | ||
| let serverUrl = firstServer.url | ||
| if(!serverUrl) { | ||
| return '' | ||
| } | ||
|
|
||
| const { variables = {} } = firstServer | ||
|
|
||
| for (const param of Object.keys(variables)) { | ||
| serverUrl = serverUrl.replace(new RegExp(`{${param}}`, 'g'), variables[param].default) | ||
| } | ||
|
|
||
| const { pathname } = new URL(serverUrl, 'https://localhost') | ||
| return pathname.slice(-1) === '/' ? pathname.slice(0, -1) : pathname | ||
| } catch (error) { | ||
| return '' | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| openapi: 3.0.3 | ||
| info: | ||
| title: test | ||
| version: 0.1.0 | ||
| servers: | ||
| - url: https://example1.com/api/v2 | ||
| paths: | ||
| /changed1: | ||
| get: | ||
| responses: | ||
| '200': | ||
| description: a2 | ||
| servers: | ||
| - url: https://example1.com/api/v1 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have no comments but because all these manipulations like magic. So I would like to request some documentation for these operations because there a lot of utility functions calling each other and it's difficult to understand whole the algorithm
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added jsdoc for path mapper.