Added API for procesing request parameters

* Parameter#getValue takes an http.CientRequest (or similar) and returns
  a wrapper object containing the raw value, the processed value and any
  errors that occured during processing.
This commit is contained in:
Jeremy Whitlock 2015-07-30 14:20:40 -06:00
Родитель f29642d388
Коммит a721204df5
7 изменённых файлов: 2002 добавлений и 40 удалений

2
browser/swagger-core-api-min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

2
browser/swagger-core-api-standalone-min.js поставляемый

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -2,6 +2,8 @@
<dl>
<dt><a href="#Operation">Operation</a></dt>
<dd></dd>
<dt><a href="#ParameterValue">ParameterValue</a></dt>
<dd></dd>
<dt><a href="#Parameter">Parameter</a></dt>
<dd></dd>
<dt><a href="#SwaggerApi">SwaggerApi</a></dt>
@ -33,7 +35,7 @@
### new Operation(api, path, method, ptr, definition, regexp)
The Swagger Operation object.
<strong>Note:</strong> Do not use directly.
**Note:** Do not use directly.
| Param | Type | Description |
@ -83,6 +85,27 @@ Returns a sample value based on the requested code or the default response if no
| --- | --- | --- | --- |
| [code] | <code>number</code> &#124; <code>string</code> | <code>default</code> | The response code |
<a name="ParameterValue"></a>
## ParameterValue
**Kind**: global class
**Properties**
| Name | Type | Description |
| --- | --- | --- |
| errors | <code>Array.&lt;Error&gt;</code> | The error(s) encountered during processing the paramter value |
| raw | <code>\*</code> | The original parameter value *(Does not take default values into account)* |
| value | <code>\*</code> | The processed value *(Takes default values into account and does type coercion and can be an `Error` if there is a problem during coercion.)* |
<a name="new_ParameterValue_new"></a>
### new ParameterValue(parameter, raw)
Object representing a parameter value.
| Param | Type | Description |
| --- | --- | --- |
| parameter | <code>[Parameter](#Parameter)</code> | The Parameter Object |
| raw | <code>\*</code> | The original/raw value |
<a name="Parameter"></a>
## Parameter
**Kind**: global class
@ -91,12 +114,13 @@ Returns a sample value based on the requested code or the default response if no
* [new Parameter(operation, ptr, definition, schema)](#new_Parameter_new)
* [.getSchema()](#Parameter+getSchema) ⇒ <code>object</code>
* [.getSample()](#Parameter+getSample) ⇒ <code>\*</code>
* [.getValue(req)](#Parameter+getValue) ⇒ <code>[ParameterValue](#ParameterValue)</code>
<a name="new_Parameter_new"></a>
### new Parameter(operation, ptr, definition, schema)
The Swagger Parameter object.
<strong>Note:</strong> Do not use directly.
**Note:** Do not use directly.
| Param | Type | Description |
@ -118,6 +142,34 @@ Returns a sample value for the parameter based on its schema;
**Kind**: instance method of <code>[Parameter](#Parameter)</code>
**Returns**: <code>\*</code> - The sample value
<a name="Parameter+getValue"></a>
### parameter.getValue(req) ⇒ <code>[ParameterValue](#ParameterValue)</code>
Returns the parameter value from the request.
**Note:** Below is the list `req` of properties used:
* `body`: Used for `body` and `formData` parameters
* `files`: Used for `formData` parameters whose `type` is `file`
* `header`: Used for `header` parameters
* `query`: Used for `query` parameters
For `path` parameters, we will use the operation's `regexp` property to parse out path parameters using the `url`
property.
*(See: [https://nodejs.org/api/http.html#http_class_http_clientrequest](https://nodejs.org/api/http.html#http_class_http_clientrequest))*
**Kind**: instance method of <code>[Parameter](#Parameter)</code>
**Returns**: <code>[ParameterValue](#ParameterValue)</code> - The parameter value object
**Throws**:
- <code>Error</code> If the `in` value of the parameter's schema is not valid or if the `req` property to retrieve the
parameter is missing.
| Param | Type | Description |
| --- | --- | --- |
| req | <code>object</code> | The http client request *(or equivalent)* |
<a name="SwaggerApi"></a>
## SwaggerApi
**Kind**: global class
@ -135,7 +187,7 @@ Returns a sample value for the parameter based on its schema;
### new SwaggerApi(plugin, definition, resolved, references, options)
The Swagger API object.
<strong>Note:</strong> Do not use directly.
**Note:** Do not use directly.
| Param | Type | Description |
@ -163,8 +215,13 @@ Returns the warnings from the last validate call.
### swaggerApi.getOperation(pathOrReq, [method]) ⇒ <code>[Operation](#Operation)</code>
Returns the operation for the given path and operation.
**Note:** If you pass in an `http.clientRequest` *(or equivalent)*, the `method` and `url` properties are use to
perform the matching. *(See: [https://nodejs.org/api/http.html#http_class_http_clientrequest](https://nodejs.org/api/http.html#http_class_http_clientrequest))*
**Note:** Below is the list of `reqOrPath` properties used when `reqOrPath` is an `http.ClientRequest`
*(or equivalent)*:
* `method`
* `url`
*(See: [https://nodejs.org/api/http.html#http_class_http_clientrequest](https://nodejs.org/api/http.html#http_class_http_clientrequest))*
**Kind**: instance method of <code>[SwaggerApi](#SwaggerApi)</code>
**Returns**: <code>[Operation](#Operation)</code> - The operation for the provided path and method or undefined if there is no operation for that
@ -172,7 +229,7 @@ Returns the operation for the given path and operation.
| Param | Type | Description |
| --- | --- | --- |
| pathOrReq | <code>string</code> &#124; <code>object</code> | The Swagger path string or the http client request |
| pathOrReq | <code>string</code> &#124; <code>object</code> | The Swagger path string or the http client request *(or equivalent)* |
| [method] | <code>string</code> | The Swagger operation method |
<a name="SwaggerApi+getOperations"></a>

Просмотреть файл

@ -28,10 +28,128 @@ var _ = require('lodash-compat');
var debug = require('debug')('swagger-core-api');
var parseUrl = require('url').parse;
var validCollectionFormats = [undefined, 'csv', 'multi', 'pipes', 'ssv', 'tsv'];
var validParameterLocations = ['body', 'formData', 'header', 'path', 'query'];
var validTypes = ['array', 'boolean', 'integer', 'object', 'number', 'string'];
function convertValue (schema, collectionFormat, value) {
var originalValue = value; // Used in error reporting for invalid values
var type = _.isPlainObject(schema) ? (schema.type || 'object') : undefined;
if (validTypes.indexOf(type) === -1) {
throw new TypeError('Invalid \'type\' value: ' + type);
}
// If there is no value, do not convert it
if (_.isUndefined(value)) {
return value;
}
switch (type) {
case 'array':
if (_.isString(value)) {
if (validCollectionFormats.indexOf(collectionFormat) === -1) {
throw new TypeError('Invalid \'collectionFormat\' value: ' + collectionFormat);
}
switch (collectionFormat) {
case 'csv':
case undefined:
value = value.split(',');
break;
case 'multi':
value = [value];
break;
case 'pipes':
value = value.split('|');
break;
case 'ssv':
value = value.split(' ');
break;
case 'tsv':
value = value.split('\t');
break;
// no default
}
}
if (_.isArray(value)) {
value = _.map(value, function (item, index) {
return convertValue(_.isArray(schema.items) ? schema.items[index] : schema.items, collectionFormat, item);
});
} else {
// Assume the value provided was intended to be an array
value = [value];
}
break;
case 'boolean':
if (!_.isBoolean(value)) {
if (value === 'true') {
value = true;
} else if (value === 'false') {
value = false;
} else {
throw new TypeError('Not a valid boolean: ' + value);
}
}
break;
case 'integer':
if (!_.isNumber(value)) {
value = parseInt(value, 10);
if (_.isNaN(value)) {
throw new TypeError('Not a valid integer: ' + originalValue);
}
}
break;
case 'object':
if (!_.isPlainObject(value)) {
if (_.isString(value)) {
value = JSON.parse(value);
} else {
throw new TypeError('Not a valid object: ' + JSON.stringify(originalValue));
}
}
break;
case 'number':
if (!_.isNumber(value)) {
value = parseFloat(value);
if (_.isNaN(value)) {
throw new TypeError('Not a valid number: ' + originalValue);
}
}
break;
case 'string':
if (['date', 'date-time'].indexOf(schema.format) > -1) {
if (_.isString(value)) {
value = new Date(value);
}
if (!_.isDate(value) || value.toString() === 'Invalid Date') {
throw new TypeError('Not a valid ' + schema.format + ' string: ' + originalValue);
}
} else if (!_.isString(value)) {
throw new TypeError('Not a valid string: ' + value);
}
break;
// no default
}
return value;
}
/**
* The Swagger Operation object.
*
* <strong>Note:</strong> Do not use directly.
* **Note:** Do not use directly.
*
* @param {SwaggerApi} api - The Swagger API object
* @param {string} path - The operation path
@ -129,10 +247,77 @@ Operation.prototype.getResponseSample = function (code) {
return sample;
};
/**
* Object representing a parameter value.
*
* @param {Parameter} parameter - The Parameter Object
* @param {*} raw - The original/raw value
*
* @property {Error[]} errors - The error(s) encountered during processing the paramter value
* @property {*} raw - The original parameter value *(Does not take default values into account)*
* @property {*} value - The processed value *(Takes default values into account and does type coercion and can be an
* `Error` if there is a problem during coercion.)*
*
* @constructor
*/
function ParameterValue (parameter, raw) {
var processed = false;
var schema = parameter.computedSchema;
var processedValue;
this.errors = [];
this.raw = raw;
// Use Object.defineProperty for 'value' to allow for lazy processing of the raw value
Object.defineProperty(this, 'value', {
enumerable: true,
get: function () {
if (!processed) {
// Convert/Coerce the raw value from the request object
try {
processedValue = convertValue(schema, parameter.collectionFormat, raw);
} catch (err) {
this.errors.push(err);
}
// If there is still no value and there are no errors, use the default value if available (no coercion)
if (_.isUndefined(processedValue) && this.errors.length === 0) {
if (schema.type === 'array') {
if (_.isArray(schema.items)) {
processedValue = _.reduce(schema.items, function (items, item) {
items.push(item.default);
return items;
}, []);
// If none of the items have a default value reset the processed value to 'undefined'
if (_.all(processedValue, _.isUndefined)) {
processedValue = undefined;
}
} else {
if (!_.isUndefined(schema.items) && !_.isUndefined(schema.items.default)) {
processedValue = [schema.items.default];
}
}
} else {
if (!_.isUndefined(schema.default)) {
processedValue = schema.default;
}
}
}
processed = true;
}
return processedValue;
}
});
}
/**
* The Swagger Parameter object.
*
* <strong>Note:</strong> Do not use directly.
* **Note:** Do not use directly.
*
* @param {Operation} operation - The Swagger Operation object
* @param {string} ptr - The JSON Pointer to the parameter
@ -168,13 +353,97 @@ Parameter.prototype.getSchema = function () {
* @returns {*} The sample value
*/
Parameter.prototype.getSample = function () {
var sample;
return this.operation.api.plugin.getSample(this.computedSchema);
};
if (!_.isUndefined(this.computedSchema)) {
sample = this.operation.api.plugin.getSample(this.computedSchema);
/**
* Returns the parameter value from the request.
*
* **Note:** Below is the list `req` of properties used:
*
* * `body`: Used for `body` and `formData` parameters
* * `files`: Used for `formData` parameters whose `type` is `file`
* * `header`: Used for `header` parameters
* * `query`: Used for `query` parameters
*
* For `path` parameters, we will use the operation's `regexp` property to parse out path parameters using the `url`
* property.
*
* *(See: {@link https://nodejs.org/api/http.html#http_class_http_clientrequest})*
*
* @param {object} req - The http client request *(or equivalent)*
*
* @returns {ParameterValue} The parameter value object
*
* @throws {Error} If the `in` value of the parameter's schema is not valid or if the `req` property to retrieve the
* parameter is missing.
*/
Parameter.prototype.getValue = function (req) {
if (_.isUndefined(req)) {
throw new TypeError('req is required');
} else if (!_.isPlainObject(req)) {
throw new TypeError('req must be an object');
} else if (validParameterLocations.indexOf(this.in) === -1) {
throw new Error('Invalid \'in\' value: ' + this.in);
}
return sample;
var that = this;
var type = this.computedSchema.type || 'object';
var pathMatch;
var value;
switch (this.in) {
case 'body':
value = req.body;
break;
case 'formData':
// For formData, either the value is a file or a property of req.body. req.body as a whole can never be the
// value since the JSON Schema for formData parameters does not allow a type of 'object'.
if (type === 'file') {
if (_.isUndefined(req.files)) {
throw new Error('req.files must be provided for \'formData\' parameters of type \'file\'');
}
value = req.files[this.name];
} else {
if (_.isUndefined(req.body)) {
throw new Error('req.body must be provided for \'formData\' parameters');
}
value = req.body[this.name];
}
break;
case 'header':
if (_.isUndefined(req.headers)) {
throw new Error('req.headers must be provided for \'header\' parameters');
}
value = req.headers[this.name.toLowerCase()];
break;
case 'path':
if (_.isUndefined(req.url)) {
throw new Error('req.url must be provided for \'path\' parameters');
}
// Since we get the raw path parameter value, we need to URI decode it
pathMatch = this.operation.regexp.exec(parseUrl(decodeURIComponent(req.url)).pathname);
if (pathMatch) {
value = pathMatch[_.findIndex(this.operation.regexp.keys, function (key) {
return key.name === that.name;
}) + 1];
}
break;
case 'query':
if (_.isUndefined(req.query)) {
throw new Error('req.query must be provided for \'query\' parameters');
}
value = req.query[this.name];
break;
// no default
}
return new ParameterValue(this, value);
};
/**
@ -190,7 +459,7 @@ Parameter.prototype.getSample = function () {
/**
* The Swagger API object.
*
* <strong>Note:</strong> Do not use directly.
* **Note:** Do not use directly.
*
* @param {object} plugin - The Swagger version plugin
* @param {object} definition - The Swagger definition
@ -245,10 +514,15 @@ SwaggerApi.prototype.getLastWarnings = function () {
/**
* Returns the operation for the given path and operation.
*
* **Note:** If you pass in an `http.clientRequest` *(or equivalent)*, the `method` and `url` properties are use to
* perform the matching. *(See: {@link https://nodejs.org/api/http.html#http_class_http_clientrequest})*
* **Note:** Below is the list of `reqOrPath` properties used when `reqOrPath` is an `http.ClientRequest`
* *(or equivalent)*:
*
* @param {string|object} pathOrReq - The Swagger path string or the http client request
* * `method`
* * `url`
*
* *(See: {@link https://nodejs.org/api/http.html#http_class_http_clientrequest})*
*
* @param {string|object} pathOrReq - The Swagger path string or the http client request *(or equivalent)*
* @param {string} [method] - The Swagger operation method
*
* @returns {Operation} The operation for the provided path and method or undefined if there is no operation for that

Разница между файлами не показана из-за своего большого размера Загрузить разницу