-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rest): add basic parameter type conversion
- Loading branch information
1 parent
de969e4
commit f587cf5
Showing
4 changed files
with
291 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
// Copyright IBM Corp. 2017,2018. All Rights Reserved. | ||
// Node module: @loopback/rest | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import * as assert from 'assert'; | ||
import {ParameterObject} from '@loopback/openapi-spec'; | ||
/** | ||
* Simple converter for parameters | ||
*/ | ||
// tslint:disable-next-line:no-any | ||
export function convert(val: any, param: ParameterObject): any { | ||
if (param.in === 'body') { | ||
param = getBodyDescriptor(param); | ||
} | ||
const type = param.type; | ||
if (val == null) { | ||
if (param.required) { | ||
throw new Error( | ||
`Value is not provided for required parameter ${param.name}`, | ||
); | ||
} | ||
return undefined; | ||
} | ||
switch (type) { | ||
case 'string': | ||
if (param.format === 'date' || param.format === 'date-time') { | ||
return new Date(val); | ||
} | ||
return String(val); | ||
case 'number': | ||
case 'integer': | ||
const num = Number(val); | ||
if (isNaN(num)) { | ||
throw new Error( | ||
`Invalid value ${val} for parameter ${param.name}: ${type}`, | ||
); | ||
} | ||
if (type === 'integer' && !Number.isInteger(num)) { | ||
throw new Error( | ||
`Invalid value ${val} for parameter ${param.name}: ${type}`, | ||
); | ||
} | ||
return num; | ||
case 'boolean': | ||
if (val === 'false') return false; | ||
if (val === 'true') return true; | ||
if (typeof val === 'boolean') return val; | ||
throw new Error( | ||
`Invalid value ${val} for parameter ${param.name}: ${type}`, | ||
); | ||
case 'array': | ||
let items = val; | ||
if (typeof val === 'string') { | ||
switch (param.collectionFormat) { | ||
case 'ssv': // space separated values foo bar. | ||
items = val.split(' '); | ||
break; | ||
case 'tsv': // tab separated values foo\tbar. | ||
items = val.split('\t'); | ||
break; | ||
case 'pipes': // pipe separated values foo|bar. | ||
items = val.split('|'); | ||
break; | ||
case 'csv': // comma separated values foo,bar. | ||
default: | ||
items = val.split(','); | ||
} | ||
} | ||
if (Array.isArray(items)) { | ||
return items.map(i => convert(i, getItemDescriptor(param))); | ||
} | ||
throw new Error( | ||
`Invalid value ${val} for parameter ${param.name}: ${type}`, | ||
); | ||
} | ||
return val; | ||
} | ||
|
||
/** | ||
* Get the body descriptor | ||
* @param param | ||
*/ | ||
function getBodyDescriptor(param: ParameterObject): ParameterObject { | ||
assert(param.in === 'body' && param.schema, 'Parameter location is not body'); | ||
return Object.assign( | ||
{in: param.in, name: param.name, description: param.description}, | ||
param.schema, | ||
); | ||
} | ||
|
||
/** | ||
* Get the array item descriptor | ||
* @param param | ||
*/ | ||
function getItemDescriptor(param: ParameterObject): ParameterObject { | ||
assert(param.type === 'array' && param.items, 'Parameter type is not array'); | ||
return Object.assign( | ||
{in: param.in, name: param.name, description: param.description}, | ||
param.items, | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
// Copyright IBM Corp. 2017,2018. All Rights Reserved. | ||
// Node module: @loopback/rest | ||
// This file is licensed under the MIT License. | ||
// License text available at https://opensource.org/licenses/MIT | ||
|
||
import {convert} from '../..'; | ||
import {expect, ShotRequest, ShotRequestOptions} from '@loopback/testlab'; | ||
import {OperationObject, ParameterObject} from '@loopback/openapi-spec'; | ||
|
||
// tslint:disable:no-any | ||
describe('converter', () => { | ||
function expectToConvert( | ||
param: ParameterObject, | ||
source: any[], | ||
target: any[], | ||
) { | ||
expect(source.map(i => convert(i, param))).to.eql(target); | ||
} | ||
|
||
function expectToFail( | ||
param: ParameterObject, | ||
source: any[], | ||
reason: string | RegExp, | ||
) { | ||
for (const i of source) { | ||
expect(() => convert(i, param)).to.throw(reason); | ||
} | ||
} | ||
|
||
it('converts number parameters', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'balance', | ||
type: 'number', | ||
}; | ||
expectToConvert( | ||
param, | ||
[0, 1.5, '10', '2.5', true, false], | ||
[0, 1.5, 10, 2.5, 1, 0], | ||
); | ||
expectToFail( | ||
param, | ||
['a', 'a1', 'true', {}], | ||
/Invalid value .* for parameter balance\: number/, | ||
); | ||
}); | ||
|
||
it('converts integer parameters', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'id', | ||
type: 'integer', | ||
}; | ||
expectToConvert(param, [0, -1, '10', '-5'], [0, -1, 10, -5]); | ||
expectToFail( | ||
param, | ||
['a', 'a1', 'true', {}, 1.5, '-2.5'], | ||
/Invalid value .* for parameter id\: integer/, | ||
); | ||
}); | ||
|
||
it('converts boolean parameters', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'vip', | ||
type: 'boolean', | ||
}; | ||
expectToConvert( | ||
param, | ||
[true, false, 'true', 'false'], | ||
[true, false, true, false], | ||
); | ||
expectToFail( | ||
param, | ||
['a', 'a1', {}, 1.5, 0, 1, -10], | ||
/Invalid value .* for parameter vip\: boolean/, | ||
); | ||
}); | ||
|
||
it('converts string parameters', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'name', | ||
type: 'string', | ||
}; | ||
expectToConvert( | ||
param, | ||
[true, false, 0, -1, 2.5, '', 'A'], | ||
['true', 'false', '0', '-1', '2.5', '', 'A'], | ||
); | ||
}); | ||
|
||
it('converts date parameters', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'date', | ||
type: 'string', | ||
format: 'date', | ||
}; | ||
const date = new Date(); | ||
expectToConvert(param, [date.toJSON()], [date]); | ||
}); | ||
|
||
describe('string[]', () => { | ||
it('converts csv format', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'nums', | ||
type: 'array', | ||
collectionFormat: 'csv', | ||
items: { | ||
type: 'string', | ||
}, | ||
}; | ||
expectToConvert(param, ['1,2,3', 'ab,c'], [['1', '2', '3'], ['ab', 'c']]); | ||
}); | ||
|
||
it('converts ssv format', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'nums', | ||
type: 'array', | ||
collectionFormat: 'ssv', | ||
items: { | ||
type: 'string', | ||
}, | ||
}; | ||
expectToConvert(param, ['1 2 3', 'ab c'], [['1', '2', '3'], ['ab', 'c']]); | ||
}); | ||
|
||
it('converts pipes format', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'nums', | ||
type: 'array', | ||
collectionFormat: 'pipes', | ||
items: { | ||
type: 'string', | ||
}, | ||
}; | ||
expectToConvert(param, ['1|2|3', 'ab|c'], [['1', '2', '3'], ['ab', 'c']]); | ||
}); | ||
|
||
it('converts tsv format', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'nums', | ||
type: 'array', | ||
collectionFormat: 'tsv', | ||
items: { | ||
type: 'string', | ||
}, | ||
}; | ||
expectToConvert( | ||
param, | ||
['1\t2\t3', 'ab\tc'], | ||
[['1', '2', '3'], ['ab', 'c']], | ||
); | ||
}); | ||
}); | ||
|
||
describe('number[]', () => { | ||
it('converts csv format', () => { | ||
const param: ParameterObject = { | ||
in: 'query', | ||
name: 'nums', | ||
type: 'array', | ||
collectionFormat: 'csv', | ||
items: { | ||
type: 'number', | ||
}, | ||
}; | ||
expectToConvert(param, ['1,2,3', '-10,2.5'], [[1, 2, 3], [-10, 2.5]]); | ||
}); | ||
}); | ||
}); |