-
Notifications
You must be signed in to change notification settings - Fork 38
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Adding OpenAPI v3.0 Support #57
Open
philsturgeon
wants to merge
6
commits into
subeeshcbabu-zz:master
Choose a base branch
from
philsturgeon:upgrade-to-openapi3
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 |
---|---|---|
|
@@ -18,6 +18,7 @@ html-report | |
xunit.xml | ||
node_modules | ||
npm-debug.log | ||
package-lock.json | ||
|
||
.project | ||
.idea | ||
|
File renamed without changes.
File renamed without changes.
File renamed without changes.
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,28 @@ | ||
'use strict'; | ||
const Moment = require('moment'); | ||
const Chance = require('chance').Chance(); | ||
const Randexp = require('randexp').randexp; | ||
|
||
const date = () => Moment().format('YYYY-MM-DD'); | ||
const dataTime = () => Moment().toISOString(); | ||
const url = () => Chance.url(); | ||
const email = () => Chance.email(); | ||
const phone = () => Chance.phone(); | ||
const guid = () => Chance.guid(); | ||
const ipv4 = () => Chance.ip(); | ||
const ipv6 = () => Chance.ipv6(); | ||
const hostname = () => Randexp(/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/gm); | ||
|
||
module.exports = { | ||
date, | ||
'date-time': dataTime, | ||
uri: url, | ||
url, | ||
email, | ||
phone, | ||
uuid: guid, | ||
guid, | ||
ipv4, | ||
ipv6, | ||
hostname | ||
}; |
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,239 @@ | ||
'use strict'; | ||
const Chance = require('chance').Chance(); | ||
const Format = require('./format'); | ||
const Randexp = require('randexp').randexp; | ||
|
||
const mock = (node, useExample) => { | ||
let mock; | ||
|
||
// Parameters and responses live inside the schema keyword now | ||
const schema = node.schema; | ||
|
||
if (schema) { | ||
let type = schema.type || findType(schema); | ||
let example = schema.examples || schema.example || schema.default; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Added default as an example, which the spec says can be used if no examples exist. This |
||
/** | ||
* Get the mock generator from the `type` of the schema | ||
*/ | ||
if (example && useExample) { | ||
mock = example; | ||
} else { | ||
const generator = Generators[type]; | ||
if (generator) { | ||
mock = generator.call(null, schema, useExample); | ||
} | ||
} | ||
} | ||
return mock; | ||
}; | ||
|
||
const objectMock = ({ properties, additionalProperties }, useExample ) => { | ||
let mockObj = {}; | ||
if (properties) { | ||
Object.keys(properties).forEach(function (key) { | ||
mockObj[key] = mock(properties[key], useExample); | ||
}); | ||
/** | ||
* In the absense of `properties`, check if `additionalProperties` is defined or not. | ||
* (If additionalProperties is an object, that object is a schema that will be used to validate | ||
* any additional properties not listed in properties.) | ||
* | ||
* If present, use this to generate mocks. | ||
*/ | ||
} else if (additionalProperties) { | ||
//Create a random property | ||
mockObj[Chance.word()] = mock(additionalProperties, useExample); | ||
} | ||
return mockObj; | ||
}; | ||
/** | ||
* Generates a mock `array` data of `items` | ||
* Supports: `minItems` and `maxItems` | ||
* TODO: Implement `uniqueItems` | ||
*/ | ||
const arrayMock = ({ items, minItems, maxItems }, useExample) => { | ||
let min; | ||
let max; | ||
let numItems; | ||
let arr = []; | ||
|
||
if (items) { | ||
//Use the min as the base | ||
min = minItems || 1; | ||
if (maxItems) { | ||
//If min is greater than max, use min as max. | ||
max = (maxItems < min) ? min : maxItems; | ||
} else { | ||
//If max is not defined, use min as max. | ||
max = min; | ||
} | ||
//Find the number of items with min and max boundary parameters. | ||
numItems = Chance.integer({ | ||
min: min, | ||
max: max | ||
}); | ||
for (let i = 0; i < numItems; i++) { | ||
arr.push(mock(items, useExample)); | ||
} | ||
} | ||
return arr; | ||
}; | ||
/** | ||
* Generates a mock `integer` value | ||
* Supports `minimum`, `maximum`, `exclusiveMinimum` and `exclusiveMaximum` | ||
* TODO - Validate `minimum` and `maximum` values | ||
*/ | ||
const integerMock = schema => { | ||
let opts = {}; | ||
let intmock; | ||
|
||
/** | ||
* If `enum` is defined for the property | ||
*/ | ||
if (schema.enum && schema.enum.length > 0) { | ||
return enumMock(schema); | ||
} | ||
|
||
if (Number.isInteger(schema.minimum)) { | ||
opts.min = (schema.exclusiveMinimum) ? schema.minimum + 1 : schema.minimum; | ||
} | ||
if (Number.isInteger(schema.maximum)) { | ||
opts.max = (schema.exclusiveMaximum) ? schema.maximum - 1 : schema.maximum; | ||
} | ||
//Generate a number that is multiple of schema.multipleOf | ||
if (Number.isInteger(schema.multipleOf) && schema.multipleOf > 0) { | ||
//Use the min/muplilier as the min number | ||
//Use default min as 1 if min is not properly set. | ||
opts.min = (Number.isInteger(opts.min)) ? (Math.ceil(opts.min / schema.multipleOf)) : 1; | ||
//Use the max/muplilier as the new max value | ||
//Use a default - min + 10 - if max value is not properly set. | ||
opts.max = (Number.isInteger(opts.max)) ? (Math.floor(opts.max / schema.multipleOf)) : (opts.min + 10); | ||
intmock = Chance.integer(opts); | ||
intmock = intmock * schema.multipleOf; | ||
} else { | ||
intmock = Chance.integer(opts); | ||
} | ||
return intmock; | ||
}; | ||
|
||
/** | ||
* Generates a mock `number` value | ||
* Supports `minimum`, `maximum`, `exclusiveMinimum` and `exclusiveMaximum` | ||
* TODO - Validate `minimum` and `maximum` values | ||
*/ | ||
const numberMock = schema => { | ||
let opts = {}; | ||
let nummock; | ||
|
||
/** | ||
* If `enum` is defined for the property | ||
*/ | ||
if (schema.enum && schema.enum.length > 0) { | ||
return enumMock(schema); | ||
} | ||
|
||
if (Number.isFinite(schema.minimum)) { | ||
opts.min = (schema.exclusiveMinimum) ? schema.minimum + 0.1 : schema.minimum; | ||
} | ||
if (Number.isFinite(schema.maximum)) { | ||
opts.max = (schema.exclusiveMaximum) ? schema.maximum - 0.1 : schema.maximum ; | ||
} | ||
//Generate a number that is multiple of schema.multipleOf | ||
if (Number.isFinite(schema.multipleOf) && schema.multipleOf > 0) { | ||
//Use the min/muplilier as the min number | ||
//Use default min as 1 if min is not properly set | ||
opts.min = (Number.isFinite(opts.min)) ? (Math.ceil(opts.min / schema.multipleOf)) : 1; | ||
//Use the max/muplilier as the new max value | ||
//Use a default - min + 10 - if max value is not properly set. | ||
opts.max = (Number.isFinite(opts.max)) ? (Math.floor(opts.max / schema.multipleOf)) : (opts.min + 10); | ||
|
||
nummock = Chance.integer(opts); | ||
nummock = nummock * schema.multipleOf; | ||
} else { | ||
nummock = Chance.floating(opts); | ||
} | ||
return nummock; | ||
}; | ||
|
||
const booleanMock = schema => { | ||
/** | ||
* If `enum` is defined for the property | ||
*/ | ||
if (schema.enum && schema.enum.length > 0) { | ||
return enumMock(schema); | ||
} | ||
return Chance.bool(); | ||
}; | ||
/** | ||
* Geneartes a mock `string` value | ||
* Supports: `minLength`, `maxLength`, `enum`, `date`, and `date-time` | ||
* | ||
*/ | ||
const stringMock = schema => { | ||
let mockStr; | ||
let opts = {}; | ||
let minLength = schema.minLength || 1; | ||
let maxLength = schema.maxLength || minLength + 10; | ||
opts.min = minLength; | ||
opts.max = maxLength; | ||
|
||
if (schema.enum && schema.enum.length > 0) { | ||
/** | ||
* If `enum` is defined for the property | ||
*/ | ||
mockStr = enumMock(schema); | ||
} else if (schema.pattern) { | ||
/** | ||
* If `pattern` is defined for the property | ||
*/ | ||
mockStr = Randexp(schema.pattern); | ||
} else if(Format[schema.format]) { | ||
/** | ||
* If a `format` is defined for the property | ||
*/ | ||
mockStr = Format[schema.format].call(null, schema); | ||
} else { | ||
mockStr = Chance.string({ | ||
length: Chance.integer(opts), | ||
alpha: true //Use only alpha characters | ||
}); | ||
} | ||
|
||
return mockStr; | ||
}; | ||
|
||
const enumMock = schema => { | ||
let len = schema.enum.length; | ||
let opts = { | ||
min: 0, | ||
max: len - 1 | ||
}; | ||
return schema.enum[Chance.integer(opts)]; | ||
}; | ||
|
||
const fileMock = () => { | ||
return Chance.file(); | ||
}; | ||
|
||
//Find out the type based on schema props | ||
//(This is not a complete list or full proof solution) | ||
const findType = schema => { | ||
let type = 'object';// Use 'object' as the default type | ||
if (schema.pattern) { | ||
type = 'string'; | ||
} else if (schema.items) { | ||
type = 'array'; | ||
} | ||
return type; | ||
}; | ||
|
||
const Generators = module.exports = { | ||
object: objectMock, | ||
array: arrayMock, | ||
string: stringMock, | ||
integer: integerMock, | ||
number: numberMock, | ||
boolean: booleanMock, | ||
file: fileMock, | ||
mock | ||
}; |
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,74 @@ | ||
const Generators = require('./index'); | ||
|
||
const defaultStyle = { | ||
query: 'form', | ||
path: 'simple', | ||
header: 'simple', | ||
cookie: 'form' | ||
}; | ||
|
||
const arrayStyles = { | ||
// matrix: val => ';' + val.join(','), | ||
// label: val => '.' + val.join('.'), | ||
// form: val => val.join('\t'), | ||
simple: val => val.join(','), | ||
spaceDelimited: val => val.join('%20'), | ||
pipeDelimited: val => val.join('|'), | ||
// deepObject: val => val.join('|'), | ||
}; | ||
|
||
/** | ||
* TODO : Handle type `file` | ||
*/ | ||
const queryMock = param => { | ||
// Describes how the parameter value will be serialized depending on the type of the parameter | ||
// value. Default values (based on value of in): for query - form; for path - simple; for header | ||
// - simple; for cookie - form. | ||
if (typeof param.style !== 'string') { | ||
param.style = defaultStyle[param.in]; | ||
} | ||
|
||
// When this is true, parameter values of type array or object generate separate parameters for | ||
// each value of the array or key-value pair of the map. For other types of parameters this property | ||
// has no effect. When style is form, the default value is true. For all other styles, the default | ||
// value is false. | ||
if (typeof param.explode === 'undefined') { | ||
param.explode = param.style == 'form'; | ||
} | ||
|
||
let value = Generators.mock(param); | ||
|
||
if (param.type === 'array') { | ||
const collector = arrayStyles[param.style] || arrayStyles.simple; | ||
|
||
console.log({collector}) | ||
console.log({value}) | ||
value = collector(value, param.explode); | ||
console.log({value}) | ||
|
||
} | ||
|
||
// TODO Support object collection too https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.1.md#style-values | ||
if (param.type === 'object') { | ||
} | ||
|
||
return { | ||
name: param.name, | ||
value: value | ||
}; | ||
}; | ||
|
||
const bodyMock = param => { | ||
return { | ||
name: param.name, | ||
value: Generators.mock(param.schema) | ||
}; | ||
}; | ||
|
||
module.exports = { | ||
query: queryMock, | ||
path: queryMock, | ||
formData: queryMock, | ||
header: queryMock, | ||
body: bodyMock | ||
}; |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Notice I'm calling this node instead of schema, as the schema is actually
node.schema
now.