Comparing version 0.1.6 to 0.1.7
183
index.js
@@ -32,2 +32,3 @@ /* exported findRefs, isJsonReference, isRemotePointer, pathFromPointer, pathToPointer, resolveRefs */ | ||
each: require('lodash-compat/collection/each'), | ||
indexOf: require('lodash-compat/array/indexOf'), | ||
isArray: require('lodash-compat/lang/isArray'), | ||
@@ -38,4 +39,6 @@ isFunction: require('lodash-compat/lang/isFunction'), | ||
isUndefined: require('lodash-compat/lang/isUndefined'), | ||
map: require('lodash-compat/collection/map'), | ||
keys: require('lodash-compat/object/keys'), | ||
map: require('lodash-compat/collection/map') | ||
}; | ||
var async = require('async'); | ||
var request = require('superagent'); | ||
@@ -45,2 +48,3 @@ var traverse = require('traverse'); | ||
var remoteCache = {}; | ||
var supportedSchemes = ['http', 'https']; | ||
@@ -56,2 +60,23 @@ /** | ||
/** | ||
* Callback used to provide access to altering a remote request prior to the request being made. | ||
* | ||
* @param {object} req - The Superagent request object | ||
* @param {string} ref - The reference being resolved (When applicable) | ||
* | ||
* @callback prepareRequestCallback | ||
*/ | ||
/** | ||
* Callback used to process the content of a reference. | ||
* | ||
* @param {string} content - The content loaded from the file/URL | ||
* @param {string} ref - The reference string (When applicable) | ||
* @param {object} [res] - The Superagent response object (For remote URL requests only) | ||
* | ||
* @returns {object} The JavaScript object representation of the reference | ||
* | ||
* @callback processContentCallback | ||
*/ | ||
/* Internal Functions */ | ||
@@ -63,2 +88,3 @@ | ||
* @param {string} url - The URL to retrieve | ||
* @param {object} options - The options passed to resolveRefs | ||
* @param {resultCallback} done - The result callback | ||
@@ -68,16 +94,31 @@ * | ||
*/ | ||
var getRemoteJson = function getRemoteJson (url, done) { | ||
var getRemoteJson = function getRemoteJson (url, options, done) { | ||
var realUrl = url.split('#')[0]; | ||
var json = remoteCache[realUrl]; | ||
var err; | ||
var userErr; | ||
var realRequest; | ||
if (!_.isUndefined(json)) { | ||
done(err, json); | ||
done(userErr, json); | ||
} else { | ||
request.get(url) | ||
.set('user-agent', 'whitlockjc/json-refs') | ||
.set('Accept', 'application/json') | ||
.end(function (res) { | ||
if (_.isPlainObject(res.body)) { | ||
json = res.body; | ||
realRequest = request.get(url) | ||
.set('user-agent', 'whitlockjc/json-refs'); | ||
if (!_.isUndefined(options.prepareRequest)) { | ||
options.prepareRequest(realRequest, url); | ||
} | ||
realRequest | ||
.buffer() | ||
.end(function (err, res) { | ||
if (err) { | ||
userErr = err; | ||
} else if (res.error) { | ||
userErr = res.error; | ||
} else if (!_.isUndefined(options.processContent)) { | ||
try { | ||
json = options.processContent(res.text, url, res); | ||
} catch (e) { | ||
userErr = e; | ||
} | ||
} else { | ||
@@ -87,3 +128,3 @@ try { | ||
} catch (e) { | ||
err = e; | ||
userErr = e; | ||
} | ||
@@ -94,3 +135,3 @@ } | ||
done(err, json); | ||
done(userErr, json); | ||
}); | ||
@@ -103,2 +144,9 @@ } | ||
/** | ||
* Clears the internal cache of url -> JavaScript object mappings based on previously resolved references. | ||
*/ | ||
module.exports.clearCache = function clearCache () { | ||
remoteCache = {}; | ||
}; | ||
/** | ||
* Returns whether or not the object represents a JSON Reference. | ||
@@ -189,5 +237,3 @@ * | ||
// TODO: Update to work with relative file/path references | ||
return /^https?:\/\//.test(ptr); | ||
return ptr.charAt(0) !== '#'; | ||
}; | ||
@@ -240,2 +286,5 @@ | ||
* @param {object} json - The JSON document having zero or more JSON References | ||
* @param {object} [options] - The options | ||
* @param {prepareRequestCallback} [options.prepareRequest] - The callback used to prepare a request | ||
* @param {processContentCallback} [options.processContent] - The callback used to process a reference's content | ||
* @param {resultCallback} done - The result callback | ||
@@ -245,3 +294,8 @@ * | ||
*/ | ||
var resolveRefs = module.exports.resolveRefs = function resolveRefs (json, done) { | ||
var resolveRefs = module.exports.resolveRefs = function resolveRefs (json, options, done) { | ||
if (arguments.length < 3) { | ||
done = arguments[1]; | ||
options = {}; | ||
} | ||
if (_.isUndefined(json)) { | ||
@@ -251,2 +305,4 @@ throw new Error('json is required'); | ||
throw new Error('json must be an object'); | ||
} else if (!_.isPlainObject(options)) { | ||
throw new Error('options must be an object'); | ||
} else if (_.isUndefined(done)) { | ||
@@ -258,3 +314,9 @@ throw new Error('done is required'); | ||
var isAsync = false; | ||
// Validate the options | ||
if (!_.isUndefined(options.prepareRequest) && !_.isFunction(options.prepareRequest)) { | ||
throw new Error('options.prepareRequest must be a function'); | ||
} else if (!_.isUndefined(options.processContent) && !_.isFunction(options.processContent)) { | ||
throw new Error('options.processContent must be a function'); | ||
} | ||
var refs = findRefs(json); | ||
@@ -274,2 +336,3 @@ var removeCircular = function removeCircular (jsonT) { | ||
}; | ||
var metadata = {}; | ||
var cJsonT; | ||
@@ -280,26 +343,64 @@ | ||
var replaceReference = function (to, from, ref, refPtr) { | ||
var refMetadata = { | ||
ref: ref | ||
}; | ||
var missing = false; | ||
var parentPath; | ||
var refPath; | ||
var value; | ||
ref = ref.indexOf('#') === -1 ? | ||
'#' : | ||
ref.substring(ref.indexOf('#')); | ||
refPath = pathFromPointer(refPtr); | ||
parentPath = refPath.slice(0, refPath.length - 1); | ||
if (parentPath.length === 0) { | ||
missing = !_.isUndefined(from.value); | ||
value = from.value; | ||
to.value = value; | ||
} else { | ||
missing = !from.has(pathFromPointer(ref)); | ||
value = from.get(pathFromPointer(ref)); | ||
to.set(parentPath, value); | ||
} | ||
if (!missing) { | ||
refMetadata.value = value; | ||
} | ||
metadata[refPtr] = refMetadata; | ||
}; | ||
var remoteRefs = {}; | ||
_.each(refs, function (ref, refPtr) { | ||
var refPath = pathFromPointer(refPtr); | ||
var parentPath = refPath.slice(0, refPath.length - 1); | ||
if (isRemotePointer(ref)) { | ||
isAsync = true; | ||
remoteRefs[refPtr] = ref; | ||
} else { | ||
replaceReference(cJsonT, cJsonT, ref, refPtr); | ||
} | ||
}); | ||
getRemoteJson(ref, function (err, json) { | ||
async.map(_.keys(remoteRefs), function (refPtr, callback) { | ||
var ref = remoteRefs[refPtr]; | ||
var scheme = ref.split(':')[0]; | ||
// Do not process relative references or references to unsupported resources | ||
if (ref.charAt(0) === '.' || _.indexOf(supportedSchemes, scheme) === -1) { | ||
callback(); | ||
} else { | ||
getRemoteJson(ref, options, function (err, json) { | ||
if (err) { | ||
done(err); | ||
callback(err); | ||
} else { | ||
resolveRefs(json, function (err, json) { | ||
resolveRefs(json, options, function (err, json) { | ||
delete remoteRefs[refPtr]; | ||
if (err) { | ||
done(err); | ||
callback(err); | ||
} else { | ||
if (parentPath.length === 0) { | ||
cJsonT.value = json; | ||
} else { | ||
cJsonT.set(parentPath, traverse(json).get(pathFromPointer(ref.indexOf('#') === -1 ? | ||
'#' : | ||
ref.substring(ref.indexOf('#'))))); | ||
} | ||
replaceReference(cJsonT, traverse(json), ref, refPtr); | ||
done(undefined, removeCircular(cJsonT)); | ||
callback(); | ||
} | ||
@@ -309,17 +410,13 @@ }); | ||
}); | ||
} | ||
}, function (err) { | ||
if (err) { | ||
done(err); | ||
} else { | ||
if (parentPath.length === 0) { | ||
cJsonT.value = json; | ||
} else { | ||
cJsonT.set(parentPath, cJsonT.get(pathFromPointer(ref))); | ||
} | ||
done(undefined, removeCircular(cJsonT), metadata); | ||
} | ||
}); | ||
if (!isAsync) { | ||
done(undefined, removeCircular(cJsonT)); | ||
} | ||
} else { | ||
done(undefined, json); | ||
done(undefined, json, metadata); | ||
} | ||
}; |
{ | ||
"name": "json-refs", | ||
"version": "0.1.6", | ||
"version": "0.1.7", | ||
"description": "Various utilities for JSON References (http://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03).", | ||
@@ -40,5 +40,7 @@ "main": "index.js", | ||
"uglifyify": "~3.0.1", | ||
"vinyl-source-stream": "~1.0.0" | ||
"vinyl-source-stream": "~1.0.0", | ||
"yamljs": "^0.2.1" | ||
}, | ||
"dependencies": { | ||
"async": "^0.9.0", | ||
"lodash-compat": "^3.5.0", | ||
@@ -45,0 +47,0 @@ "superagent": "~0.21.0", |
@@ -117,3 +117,3 @@ # json-refs | ||
## `resolveRefs (json, done)` | ||
## `resolveRefs (json, options, done)` | ||
@@ -123,3 +123,7 @@ **Arguments** | ||
* `json {object}`: The JavaScript object containing zero or more JSON References | ||
* `done {function}`: An error-first callback to be called with the fully-resolved object | ||
* `[options] {object}`: The options | ||
* `[options.prepareRequest] {function}`: The callback used to prepare a request | ||
* `[options.processContent] {function}`: The callback used to process the remote request content | ||
* `done {function}`: An error-first callback to be called with the fully-resolved object and metadata for the reference | ||
resolution | ||
@@ -130,8 +134,68 @@ **Response** | ||
argument. If there is no `Error`, the first argument is `undefined` and the second argument is an `object` whose value | ||
is the fully resolved document. | ||
is the fully resolved document. The third argument is an `object` whose value is the reference resolution metadata. | ||
Its keys are the location of the reference and it's values are as follows: | ||
* `ref {string}`: The reference value as it existed in the original document | ||
* `[value] {*}`: The resolved value of the reference, if there is one. If this property was set, this means that the | ||
reference was resolvable and it resolved to an explicit value. If this property is not set, that means the reference | ||
was unresolvable. A value of `undefined` means that the reference was resolvable to an actual value of `undefined` and | ||
is not indicative of an unresolvable reference. | ||
* ` | ||
##Usage | ||
**Note:** If you need to alter your request in any way, for example to add specific headers to the request or to add | ||
authentication to the request or any other situation in which the request might need to be altered, you will need to use | ||
the `options.prepareRequest` callback. Here is a simple example that uses `options.prepareRequest` to make a secure | ||
request using an Basic Authentication _(The example is written for Node.js but the actual business logic in how | ||
`resolveRefs` is called sould be the same in the browser)_: | ||
```js | ||
var jsonRefs = require('json-refs'); | ||
var json = { | ||
name: 'json-refs', | ||
owner: { | ||
$ref: 'https://api.github.com/repos/whitlockjc/json-refs#/owner' | ||
} | ||
}; | ||
jsonRefs.resolveRefs(json, { | ||
prepareRequest: function (req) { | ||
// Add the 'Basic Authentication' credentials | ||
req.auth('whitlockjc', 'MY_GITHUB_PASSWORD'); | ||
// Add the 'X-API-Key' header for an API Key based authentication | ||
// req.set('X-API-Key', 'MY_API_KEY'); | ||
} | ||
}, function (err, rJson, metadata) { | ||
if (err) throw err; | ||
console.log(JSON.stringify(rJson)); // {name: 'json-refs', owner: {/* GitHub Repository Owner Information */}} | ||
console.log(JSON.stringify(metadata)); // {'#/owner/$ref': {ref: 'https://api.github.com/repos/whitlockjc/json-refs#/owner', value: {/*GitHub Repository Onwer Information */}}} | ||
}); | ||
``` | ||
**Note:** If you need to pre-process the content of your remote requets, like to support data not explicitly supported | ||
by Superagent, you can use the `options.processContent` callback. Here is a simple example that uses | ||
`options.processContent` to retrieve a YAML resource: | ||
```js | ||
var jsonRefs = require('json-resf'); | ||
var YAML = require('yamljs'); | ||
jsonRefs.resolveRefs({ | ||
$ref: 'http://somehost/somefile.yaml' | ||
}, { | ||
processContent: function (content) { | ||
return YAML.parse(content); | ||
} | ||
}, function (err, rJson, metadata) { | ||
if (err) throw err; | ||
console.log(JSON.stringify(rJson)); // Document should be JSON equivalent of your YAML document | ||
}); | ||
``` | ||
###Node.js | ||
```js | ||
var jsonRefs = require('json-refs'); | ||
var json = { | ||
@@ -143,6 +207,7 @@ name: 'json-refs', | ||
}; | ||
jsonRefs.resolveRefs(json, function (err, rJson) { | ||
jsonRefs.resolveRefs(json, function (err, rJson, metadata) { | ||
if (err) throw err; | ||
console.log(JSON.stringify(rJson)); // {name: 'json-refs', owner: {/* GitHub Repository Owner Information */}} | ||
console.log(JSON.stringify(metadata)); // {'#/owner/$ref': {ref: 'https://api.github.com/repos/whitlockjc/json-refs#/owner', value: {/*GitHub Repository Onwer Information */}}} | ||
}); | ||
@@ -149,0 +214,0 @@ ``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
23047
358
274
4
11
+ Addedasync@^0.9.0