openapi-to-postmanv2
Advanced tools
Comparing version 4.16.0-beta.1 to 4.16.0
@@ -5,2 +5,8 @@ # OpenAPI-Postman Changelog | ||
## [v4.16.0] - 2023-08-18 | ||
### Added | ||
- Added support for remote $ref resolution in bundle() API. | ||
## [v4.15.0] - 2023-06-27 | ||
@@ -45,3 +51,3 @@ | ||
- Fixed issue where for certain path segments, collection generation failed. | ||
- Fixed TypeError occurring while checking typeof bodyContent in getXmlVersionContent. | ||
- Fixed TypeError occurring while checking typeof bodyContent in getXmlVersionContent. | ||
@@ -300,3 +306,3 @@ ## [v4.12.0] - 2023-05-04 | ||
- Add supported formats for schema resolution (deref). | ||
- Fix for [#7643](https://github.com/postmanlabs/postman-app-support/issues/7643), [#7914](https://github.com/postmanlabs/postman-app-support/issues/7914), [#9004](https://github.com/postmanlabs/postman-app-support/issues/9004) - Added support for Auth params in response/example. | ||
- Fix for [#7643](https://github.com/postmanlabs/postman-app-support/issues/7643), [#7914](https://github.com/postmanlabs/postman-app-support/issues/7914), [#9004](https://github.com/postmanlabs/postman-app-support/issues/9004) - Added support for Auth params in response/example. | ||
- Bumped up multiple dependecies and dev-dependencies versions to keep them up-to-date. | ||
@@ -388,3 +394,3 @@ - Updated code coverage tool from deprecated istanbul to nyc. | ||
- Added support for detailed validation body mismatches with option detailedBlobValidation. | ||
- Fix for [#8098](https://github.com/postmanlabs/postman-app-support/issues/8098) - Unable to validate schema with type array. | ||
- Fix for [#8098](https://github.com/postmanlabs/postman-app-support/issues/8098) - Unable to validate schema with type array. | ||
- Fixed URIError for invalid URI in transaction. | ||
@@ -595,4 +601,6 @@ - Fix for [#152](https://github.com/postmanlabs/openapi-to-postman/issues/152) - Path references not resolved due to improver handling of special characters. | ||
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.15.0...HEAD | ||
[Unreleased]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.16.0...HEAD | ||
[v4.16.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.15.0...v4.16.0 | ||
[v4.15.0]: https://github.com/postmanlabs/openapi-to-postman/compare/v4.14.0...v4.15.0 | ||
@@ -599,0 +607,0 @@ |
const _ = require('lodash'), | ||
{ | ||
isExtRef, | ||
isExtURLRef, | ||
stringIsAValidUrl, | ||
isExtRemoteRef, | ||
getKeyInComponents, | ||
@@ -8,2 +11,3 @@ getJsonPointerRelationToRoot, | ||
localPointer, | ||
httpSeparator, | ||
jsonPointerLevelSeparator, | ||
@@ -87,11 +91,18 @@ isLocalRef, | ||
function findNodeFromPath(referencePath, allData) { | ||
const partialComponents = referencePath.split(localPointer); | ||
let isPartial = partialComponents.length > 1, | ||
node = allData.find((node) => { | ||
if (isPartial) { | ||
referencePath = partialComponents[0]; | ||
} | ||
return comparePaths(node.fileName, referencePath); | ||
}); | ||
const isReferenceRemoteURL = stringIsAValidUrl(referencePath), | ||
partialComponents = referencePath.split(localPointer), | ||
isPartial = partialComponents.length > 1; | ||
let node = allData.find((node) => { | ||
if (isPartial && !isReferenceRemoteURL) { | ||
referencePath = partialComponents[0]; | ||
} | ||
if (isReferenceRemoteURL) { | ||
return _.startsWith(node.path, referencePath); | ||
} | ||
return comparePaths(node.fileName, referencePath); | ||
}); | ||
return node; | ||
@@ -295,10 +306,83 @@ } | ||
* @param {object} globalReferences - The accumulated global references from all nodes | ||
* @param {function} remoteRefResolver - The function that would be called to fetch remote ref contents | ||
* @returns {object} - The references in current node and the new content from the node | ||
*/ | ||
function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, version, rootMainKeys, | ||
commonPathFromData, allData, globalReferences) { | ||
async function getReferences (currentNode, isOutOfRoot, pathSolver, parentFilename, version, rootMainKeys, | ||
commonPathFromData, allData, globalReferences, remoteRefResolver) { | ||
let referencesInNode = [], | ||
nodeReferenceDirectory = {}, | ||
mainKeys = {}; | ||
mainKeys = {}, | ||
remoteRefContentMap = new Map(), | ||
remoteRefSet = new Set(), | ||
remoteRefResolutionPromises = []; | ||
remoteRefResolver && traverseUtility(currentNode).forEach(function (property) { | ||
if (property) { | ||
let hasReferenceTypeKey; | ||
hasReferenceTypeKey = Object.keys(property) | ||
.find( | ||
(key) => { | ||
const isExternal = isExtURLRef(property, key), | ||
isReferenciable = isExternal; | ||
return isReferenciable; | ||
} | ||
); | ||
if (hasReferenceTypeKey) { | ||
const tempRef = calculatePath(parentFilename, property.$ref), | ||
isRefEncountered = remoteRefSet.has(tempRef); | ||
if (isRefEncountered) { | ||
return; | ||
} | ||
remoteRefResolutionPromises.push( | ||
new Promise(async (resolveInner) => { | ||
/** | ||
* Converts contents received from remoteRefResolver into stringified JSON | ||
* @param {string | object} content - contents from remoteRefResolver | ||
* @returns {string} Stringified JSON contents | ||
*/ | ||
function convertToJSONString (content) { | ||
if (typeof content === 'object') { | ||
return JSON.stringify(content); | ||
} | ||
const parsedFile = parseFile(content); | ||
return JSON.stringify(parsedFile.oasObject); | ||
} | ||
try { | ||
let contentFromRemote = await remoteRefResolver(property.$ref), | ||
nodeTemp = { | ||
fileName: tempRef, | ||
path: tempRef, | ||
content: convertToJSONString(contentFromRemote), | ||
href: property.$ref | ||
}; | ||
remoteRefContentMap.set(tempRef, contentFromRemote); | ||
allData.push(nodeTemp); | ||
} | ||
catch (err) { | ||
// swallow the err | ||
} | ||
finally { | ||
resolveInner(); | ||
} | ||
}) | ||
); | ||
remoteRefSet.add(tempRef); | ||
} | ||
} | ||
}); | ||
await Promise.all(remoteRefResolutionPromises); | ||
traverseUtility(currentNode).forEach(function (property) { | ||
@@ -377,2 +461,90 @@ if (property) { | ||
} | ||
const hasRemoteReferenceTypeKey = Object.keys(property) | ||
.find( | ||
(key) => { | ||
const isExternal = isExtURLRef(property, key), | ||
// Only process URL refs if remoteRefResolver is provided and a valid function | ||
isReferenciable = isExternal && _.isFunction(remoteRefResolver); | ||
return isReferenciable; | ||
} | ||
), | ||
handleRemoteURLReference = () => { | ||
const tempRef = calculatePath(parentFilename, property.$ref); | ||
if (remoteRefContentMap.get(tempRef) === undefined) { | ||
return; | ||
} | ||
let nodeTrace = handleLocalCollisions( | ||
getTraceFromParentKeyInComponents(this, tempRef, mainKeys, version, commonPathFromData), | ||
rootMainKeys | ||
), | ||
componentKey = nodeTrace[nodeTrace.length - 1], | ||
referenceInDocument = getJsonPointerRelationToRoot( | ||
tempRef, | ||
nodeTrace, | ||
version | ||
), | ||
traceToParent = [...this.parents.map((item) => { | ||
return item.key; | ||
}).filter((item) => { | ||
return item !== undefined; | ||
}), this.key], | ||
newValue = Object.assign({}, this.node), | ||
[, local] = tempRef.split(localPointer), | ||
nodeFromData, | ||
refHasContent = false, | ||
parseResult, | ||
newRefInDoc, | ||
inline, | ||
contentFromRemote = remoteRefContentMap.get(tempRef), | ||
nodeTemp = { | ||
fileName: tempRef, | ||
path: tempRef, | ||
content: contentFromRemote | ||
}; | ||
nodeFromData = nodeTemp; | ||
if (nodeFromData && nodeFromData.content) { | ||
parseResult = parseFile(JSON.stringify(nodeFromData.content)); | ||
if (parseResult.result) { | ||
newValue.$ref = referenceInDocument; | ||
refHasContent = true; | ||
nodeFromData.parsed = parseResult; | ||
} | ||
} | ||
this.update({ $ref: tempRef }); | ||
if (nodeTrace.length === 0) { | ||
inline = true; | ||
} | ||
if (_.isNil(globalReferences[tempRef])) { | ||
nodeReferenceDirectory[tempRef] = { | ||
local, | ||
keyInComponents: nodeTrace, | ||
node: newValue, | ||
reference: inline ? newRefInDoc : referenceInDocument, | ||
traceToParent, | ||
parentNodeKey: parentFilename, | ||
mainKeyInTrace: nodeTrace[nodeTrace.length - 1], | ||
refHasContent, | ||
inline | ||
}; | ||
} | ||
mainKeys[componentKey] = tempRef; | ||
if (!added(property.$ref, referencesInNode)) { | ||
referencesInNode.push({ path: pathSolver(property), keyInComponents: nodeTrace, newValue: this.node }); | ||
} | ||
}; | ||
if (hasRemoteReferenceTypeKey) { | ||
handleRemoteURLReference(); | ||
} | ||
} | ||
@@ -393,6 +565,7 @@ }); | ||
* @param {object} globalReferences - The accumulated global refernces from all nodes | ||
* @param {function} remoteRefResolver - The function that would be called to fetch remote ref contents | ||
* @returns {object} - Detect root files result object | ||
*/ | ||
function getNodeContentAndReferences (currentNode, allData, specRoot, version, rootMainKeys, | ||
commonPathFromData, globalReferences) { | ||
async function getNodeContentAndReferences (currentNode, allData, specRoot, version, rootMainKeys, | ||
commonPathFromData, globalReferences, remoteRefResolver) { | ||
let graphAdj = [], | ||
@@ -414,3 +587,3 @@ missingNodes = [], | ||
const { referencesInNode, nodeReferenceDirectory } = getReferences( | ||
const { referencesInNode, nodeReferenceDirectory } = await getReferences( | ||
nodeContent, | ||
@@ -424,3 +597,4 @@ currentNode.fileName !== specRoot.fileName, | ||
allData, | ||
globalReferences | ||
globalReferences, | ||
remoteRefResolver | ||
); | ||
@@ -526,5 +700,7 @@ | ||
* @param {string} version - The current version | ||
* @param {function} remoteRefResolver - The function that would be called to fetch remote ref contents | ||
* @returns {object} The components object related to the file | ||
*/ | ||
function generateComponentsObject (documentContext, rootContent, refTypeResolver, components, version) { | ||
function generateComponentsObject(documentContext, rootContent, | ||
refTypeResolver, components, version, remoteRefResolver) { | ||
let notInLine = Object.entries(documentContext.globalReferences).filter(([, value]) => { | ||
@@ -566,2 +742,3 @@ return value.keyInComponents.length !== 0; | ||
}); | ||
if (isMissingNode) { | ||
@@ -574,2 +751,25 @@ refData.nodeContent = refData.node; | ||
} | ||
else if (!isExtRef(property, '$ref') && isExtURLRef(property, '$ref')) { | ||
let splitPathByHttp = property.$ref.split(httpSeparator), | ||
prefix = splitPathByHttp | ||
.slice(0, splitPathByHttp.length - 1).join(httpSeparator) + | ||
httpSeparator + splitPathByHttp[splitPathByHttp.length - 1] | ||
.split(localPointer)[0], | ||
separatedPaths = [prefix, splitPathByHttp[splitPathByHttp.length - 1].split(localPointer)[1]]; | ||
nodeRef = separatedPaths[0]; | ||
local = separatedPaths[1]; | ||
refData.nodeContent = documentContext.nodeContents[nodeRef]; | ||
const isReferenceRemoteURL = stringIsAValidUrl(nodeRef); | ||
if (isReferenceRemoteURL && _.isFunction(remoteRefResolver)) { | ||
Object.keys(documentContext.nodeContents).forEach((key) => { | ||
if (_.startsWith(key, nodeRef) && !key.split(nodeRef)[1].includes(httpSeparator)) { | ||
refData.nodeContent = documentContext.nodeContents[key]; | ||
} | ||
}); | ||
} | ||
} | ||
else { | ||
@@ -710,5 +910,6 @@ refData.nodeContent = documentContext.nodeContents[nodeRef]; | ||
* @param {string} version - The version we are using | ||
* @param {function} remoteRefResolver - The function that would be called to fetch remote ref contents | ||
* @returns {object} - Detect root files result object | ||
*/ | ||
getBundleContentAndComponents: function (specRoot, allData, origin, version) { | ||
getBundleContentAndComponents: async function (specRoot, allData, origin, version, remoteRefResolver) { | ||
if (origin === BROWSER) { | ||
@@ -730,3 +931,3 @@ path = pathBrowserify; | ||
})); | ||
rootContextData = algorithm.traverseAndBundle(specRoot, (currentNode, globalReferences) => { | ||
rootContextData = await algorithm.traverseAndBundle(specRoot, (currentNode, globalReferences) => { | ||
return getNodeContentAndReferences( | ||
@@ -739,3 +940,4 @@ currentNode, | ||
commonPathFromData, | ||
globalReferences | ||
globalReferences, | ||
remoteRefResolver | ||
); | ||
@@ -751,6 +953,8 @@ }); | ||
rootContextData.nodeContents[specRoot.fileName], | ||
isExtRef, | ||
isExtRemoteRef, | ||
components, | ||
version | ||
version, | ||
remoteRefResolver | ||
); | ||
return { | ||
@@ -757,0 +961,0 @@ fileContent: finalElements.resRoot, |
@@ -32,3 +32,3 @@ class DFS { | ||
traverseAndBundle(node, getAdjacentAndBundle) { | ||
async traverseAndBundle(node, getAdjacentAndBundle) { | ||
let traverseOrder = [], | ||
@@ -39,9 +39,20 @@ stack = [], | ||
nodeContents = {}, | ||
globalReferences = {}; | ||
globalReferences = {}, | ||
hrefsVisited = new Set(); | ||
stack.push(node); | ||
while (stack.length > 0) { | ||
node = stack.pop(); | ||
if (!visited.has(node)) { | ||
if (!visited.has(node) && | ||
/** | ||
* For nodes that are fetched for remote URLs we ensure they | ||
* aren't visited more than once | ||
*/ | ||
(!node.href || (!hrefsVisited.has(node.href))) | ||
) { | ||
traverseOrder.push(node); | ||
visited.add(node); | ||
node.href && hrefsVisited.add(node.href); | ||
let { | ||
@@ -53,3 +64,3 @@ graphAdj, | ||
nodeName | ||
} = getAdjacentAndBundle(node, globalReferences); | ||
} = await getAdjacentAndBundle(node, globalReferences); | ||
nodeContents[nodeName] = nodeContent; | ||
@@ -56,0 +67,0 @@ Object.entries(nodeReferenceDirectory).forEach(([key, data]) => { |
@@ -6,2 +6,3 @@ const slashes = /\//g, | ||
localPointer = '#', | ||
httpSeparator = 'http', | ||
escapedTilde = /~0/g, | ||
@@ -174,2 +175,31 @@ jsonPointerLevelSeparator = '/', | ||
/** | ||
* Determines if a value of a given key property of an object | ||
* is an external reference with key $ref and value that does not start with # | ||
* @param {object} obj - parent object of the $ref property | ||
* @param {string} key - property key to check | ||
* @returns {boolean} - true if the property key is $ref and the value does not start with # | ||
* otherwise false | ||
*/ | ||
function isExtURLRef(obj, key) { | ||
return key === '$ref' && | ||
typeof obj[key] === 'string' && | ||
obj[key] !== undefined && | ||
!obj[key].startsWith(localPointer) && | ||
stringIsAValidUrl(obj[key]); | ||
} | ||
/** | ||
* Determines if a value of a given key property of an object | ||
* is an external reference with key $ref and value that does not start with # | ||
* @param {object} obj - parent object of the $ref property | ||
* @param {string} key - property key to check | ||
* @returns {boolean} - true if the property key is $ref and the value does not start with # | ||
* otherwise false | ||
*/ | ||
function isExtRemoteRef(obj, key) { | ||
return isExtRef(obj, key) || isExtURLRef(obj, key); | ||
} | ||
/** | ||
* Removes the local pointer inside a path | ||
@@ -238,2 +268,4 @@ * aab.yaml#component returns aab.yaml | ||
isExtRef, | ||
isExtURLRef, | ||
isExtRemoteRef, | ||
removeLocalReferenceFromPath, | ||
@@ -244,4 +276,6 @@ isLocalRef, | ||
localPointer, | ||
httpSeparator, | ||
jsonPointerLevelSeparator, | ||
generateObjectName | ||
generateObjectName, | ||
stringIsAValidUrl | ||
}; |
@@ -711,60 +711,2 @@ 'use strict'; | ||
/** | ||
* Used to resolve any kind of OAS3 JSON schema | ||
* | ||
* @param {Object} schema Schema to be resolved | ||
* @returns {Object} Resolved schema for a given OAS | ||
*/ | ||
resolveSchema(schema) { | ||
this.concreteUtils = concreteUtils; | ||
this.specComponents = concreteUtils.getRequiredData(this.openapi); | ||
let resolvedSchema; | ||
try { | ||
resolvedSchema = v2.resolveSchema(this, schema); | ||
} | ||
catch (e) { | ||
return { | ||
result: false, | ||
message: e.message, | ||
error: e | ||
}; | ||
} | ||
return { | ||
result: true, | ||
value: resolvedSchema | ||
}; | ||
} | ||
/** | ||
* Generate faked data from a resolved schema | ||
* | ||
* @param {*} schema Resolved schema to be faked | ||
* @returns {*} fakedValue | ||
*/ | ||
fakeSchema(schema) { | ||
this.concreteUtils = concreteUtils; | ||
this.specComponents = concreteUtils.getRequiredData(this.openapi); | ||
let fakedSchema; | ||
try { | ||
fakedSchema = v2.fakeSchema(this, schema); | ||
} | ||
catch (e) { | ||
return { | ||
result: false, | ||
message: e.message, | ||
error: e | ||
}; | ||
} | ||
return { | ||
result: true, | ||
value: fakedSchema | ||
}; | ||
} | ||
static getOptions(mode, criteria) { | ||
@@ -771,0 +713,0 @@ return getOptions(mode, criteria); |
@@ -18,3 +18,3 @@ /* eslint-disable one-var */ | ||
const { resolvePostmanRequest, resolveSchema, fakeSchema } = require('./schemaUtils'); | ||
const { resolvePostmanRequest } = require('./schemaUtils'); | ||
const { generateRequestItemObject, fixPathVariablesInUrl } = require('./utils'); | ||
@@ -299,15 +299,3 @@ | ||
}); | ||
}, | ||
resolveSchema(context, schema) { | ||
context.schemaCache = context.schemaCache || {}; | ||
return resolveSchema(context, schema); | ||
}, | ||
fakeSchema(context, resolvedSchema) { | ||
context.schemaCache = context.schemaCache || {}; | ||
return fakeSchema(context, resolvedSchema); | ||
} | ||
}; |
@@ -1907,4 +1907,3 @@ const generateAuthForCollectionFromOpenAPI = require('./helpers/collection/generateAuthForCollectionFromOpenAPI'); | ||
resolveRefFromSchema, | ||
resolveSchema, | ||
fakeSchema | ||
resolveSchema | ||
}; |
{ | ||
"name": "openapi-to-postmanv2", | ||
"version": "4.16.0-beta.1", | ||
"version": "4.16.0", | ||
"description": "Convert a given OpenAPI specification to Postman Collection v2.0", | ||
@@ -119,3 +119,3 @@ "homepage": "https://github.com/postmanlabs/openapi-to-postman", | ||
"dependencies": { | ||
"ajv": "8.5.0", | ||
"ajv": "8.11.0", | ||
"ajv-draft-04": "1.0.0", | ||
@@ -140,3 +140,2 @@ "ajv-formats": "2.1.1", | ||
"devDependencies": { | ||
"@stoplight/json-ref-resolver": "3.1.6", | ||
"chai": "4.3.6", | ||
@@ -143,0 +142,0 @@ "editorconfig": "0.15.3", |
Sorry, the diff of this file is too big to display
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
1840311
10
48214
0
16
+ Addedajv@8.11.0(transitive)
- Removedajv@8.5.0(transitive)
Updatedajv@8.11.0