Skip to content

Commit

Permalink
Add option to model implicit default response
Browse files Browse the repository at this point in the history
  • Loading branch information
Vlad Barosan committed Mar 15, 2018
1 parent 4d1ec11 commit 2fbf9d8
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 31 deletions.
6 changes: 5 additions & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
### 03/15/2018 0.4.35
- Validate default responses. [#228](https://github.com/Azure/oav/issues/228)
- Add option to model an implicit default cloud error response. [#224](https://github.com/Azure/oav/issues/224)

### 03/08/2018 0.4.34
- Updating default output level to info.
- Fixing issue #225, where output verbosity wasn't respected in one case.
- Fixing issue #225, where output verbosity wasn't respected in one case.

### 02/28/2018 0.4.33
- Don't validate pure objects.
Expand Down
40 changes: 40 additions & 0 deletions lib/util/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -758,3 +758,43 @@ function isPropertyRequired(propName, model) {
* Contains the reverse mapping of http.STATUS_CODES
*/
exports.statusCodeStringToStatusCode = lodash.invert(lodash.mapValues(http.STATUS_CODES, value => { return value.replace(/ |-/g, "").toLowerCase(); }));


/**
* Models an ARM cloud error.
*/
exports.CloudError = {
description: "Error response describing why the operation failed.",
schema: {
properties: {
error: {
properties: {
code: {
type: "string",
description: "An identifier for the error. Codes are invariant and are intended to be consumed programmatically."
},
message: {
type: "string",
description: "A message describing the error, intended to be suitable for display in a user interface."
},
target: {
type: "string",
description: "The target of the particular error. For example, the name of the property in error."
},
details: {
type: "array",
items: { type: "object" },
description: "A list of additional details about the error."
},
additionalInfo: {
type: "array",
items: { type: "object" },
description: "A list of additional info about an error."
}
},
additionalProperties: false
}
},
additionalProperties: false
}
};
11 changes: 10 additions & 1 deletion lib/validators/liveValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ class LiveValidator {
*
* @param {string} [options.directory] The directory where to clone github repository or from where to find swaggers. Defaults to "repo" under user directory.
*
* @param {string} [options.shouldModelImplicitDefaultResponse] Specifies if to model a default response for operations even if it is not specified in the specs.
*
* @returns {object} CacheBuilder Returns the configured CacheBuilder object.
*/
constructor(options) {
Expand Down Expand Up @@ -141,7 +143,14 @@ class LiveValidator {
return () => {
log.info(`Building cache from: "${swaggerPath}"`);

let validator = new SpecValidator(swaggerPath, null, { isPathCaseSensitive: this.options.isPathCaseSensitive });
let validator = new SpecValidator(
swaggerPath,
null,
{
shouldModelImplicitDefaultResponse: this.options.shouldModelImplicitDefaultResponse,
isPathCaseSensitive: this.options.isPathCaseSensitive
});

return validator.initialize().then((api) => {
let operations = api.getOperations();
let apiVersion = api.info.version.toLowerCase();
Expand Down
44 changes: 36 additions & 8 deletions lib/validators/specResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class SpecResolver {
*
* @param {object} [options.shouldResolveNullableTypes] Should we allow null values to match any type? Default: true
*
* @param {object} [options.shouldModelImplicitDefaultResponse] Should we model a default response even if it is not defined? Default: false
*
* @return {object} An instance of the SpecResolver class.
*/
constructor(specPath, specInJson, options) {
Expand Down Expand Up @@ -93,6 +95,9 @@ class SpecResolver {
if (options.shouldResolveNullableTypes === null || options.shouldResolveNullableTypes === undefined) {
options.shouldResolveNullableTypes = options.shouldResolveAllOf;
}
if (options.shouldModelImplicitDefaultResponse === null || options.shouldModelImplicitDefaultResponse === undefined) {
options.shouldModelImplicitDefaultResponse = false;
}
this.options = options;
}

Expand Down Expand Up @@ -169,7 +174,13 @@ class SpecResolver {
} else {
return Promise.resolve(self);
}
}).catch(function (err) {
}).then(() => {
if (self.options.shouldModelImplicitDefaultResponse) {
return self.modelImplicitDefaultResponse();
} else {
return Promise.resolve(self);
}
}).catch((err) => {
let e = {
message: `An Error occurred while resolving relative references and allOf in model definitions in the swagger spec: "${self.specPath}".`,
code: ErrorCodes.ResolveSpecError.name,
Expand Down Expand Up @@ -216,9 +227,9 @@ class SpecResolver {
}

let allRefsRemoteRelative = JsonRefs.findRefs(doc, options);
let promiseFactories = utils.getKeys(allRefsRemoteRelative).map(function (refName) {
let promiseFactories = utils.getKeys(allRefsRemoteRelative).map((refName) => {
let refDetails = allRefsRemoteRelative[refName];
return function () { return self.resolveRelativeReference(refName, refDetails, doc, docPath); };
return () => { return self.resolveRelativeReference(refName, refDetails, doc, docPath); };
});
if (promiseFactories.length) {
return utils.executePromisesSequentially(promiseFactories);
Expand Down Expand Up @@ -271,7 +282,7 @@ class SpecResolver {
docPath = utils.joinPath(docDir, parsedReference.filePath);
}

return utils.parseJson(docPath).then(function (result) {
return utils.parseJson(docPath).then((result) => {
if (!parsedReference.localReference) {
//Since there is no local reference we will replace the key in the object with the parsed
//json (relative) file it is refering to.
Expand Down Expand Up @@ -344,7 +355,7 @@ class SpecResolver {
let spec = self.specInJson;
let definitions = spec.definitions;
let modelNames = utils.getKeys(definitions);
modelNames.map(function (modelName) {
modelNames.map((modelName) => {
let model = definitions[modelName];
let modelRef = '/definitions/' + modelName;
return self.resolveAllOfInModel(model, modelRef);
Expand All @@ -370,7 +381,7 @@ class SpecResolver {

if (!self.resolvedAllOfModels[modelRef]) {
if (model && model.allOf) {
model.allOf.map(function (item) {
model.allOf.map((item) => {
let referencedModel = item;
let ref = item['$ref'];
let slicedRef = ref ? ref.slice(1) : undefined;
Expand Down Expand Up @@ -435,7 +446,7 @@ class SpecResolver {
let spec = self.specInJson;
let definitions = spec.definitions;
let modelNames = utils.getKeys(definitions);
modelNames.map(function (modelName) {
modelNames.map((modelName) => {
if (definitions[modelName].allOf) {
delete definitions[modelName].allOf;
}
Expand All @@ -460,7 +471,7 @@ class SpecResolver {
if (!modelNames) {
modelNames = utils.getKeys(definitions);
}
modelNames.forEach(function iterator(modelName) {
modelNames.forEach(modelName => {
let model = definitions[modelName];
if (model) {
if (force || (!model.additionalProperties && (!(!model.properties || (model.properties && utils.getKeys(model.properties).length === 0))))) {
Expand Down Expand Up @@ -590,6 +601,23 @@ class SpecResolver {
return Promise.resolve(self);
}

/**
* Models a default response as a Cloud Error if none is specified in the api spec.
*/
modelImplicitDefaultResponse() {
let self = this;
let spec = self.specInJson;
for (let pathObj of utils.getValues(spec.paths)) {
for (let operation of utils.getValues(pathObj)) {

// going through responses
if (operation.responses && !operation.responses.default) {
operation.responses.default = utils.CloudError;
}
}
}
}

/**
* Resolves the discriminator by replacing all the references to the parent model with a oneOf array containing
* references to the parent model and all its child models. It also modifies the discriminator property in
Expand Down
40 changes: 20 additions & 20 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "oav",
"version": "0.4.34",
"version": "0.4.35",
"author": {
"name": "Microsoft Corporation",
"email": "[email protected]",
Expand Down

0 comments on commit 2fbf9d8

Please sign in to comment.