зеркало из https://github.com/Azure/sway.git
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:
Родитель
f29642d388
Коммит
a721204df5
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
69
docs/API.md
69
docs/API.md
|
@ -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> | <code>string</code> | <code>default</code> | The response code |
|
||||
|
||||
<a name="ParameterValue"></a>
|
||||
## ParameterValue
|
||||
**Kind**: global class
|
||||
**Properties**
|
||||
|
||||
| Name | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| errors | <code>Array.<Error></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> | <code>object</code> | The Swagger path string or the http client request |
|
||||
| pathOrReq | <code>string</code> | <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>
|
||||
|
|
294
lib/types.js
294
lib/types.js
|
@ -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
|
||||
|
|
1083
test/test-2.0.js
1083
test/test-2.0.js
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
Загрузка…
Ссылка в новой задаче