зеркало из https://github.com/Azure/sway.git
[S1007882] Support x-ms-mutability in response validation (#83)
Adjust logic in Z-schema custom validator to check if a write-only property (x-ms-mutability) is in the response. If so, report a WRITE_ONLY_PROPERTY_NOT_ALLOWED_IN_RESPONSE error. Added unit tests for x-ms-secret and x-ms-mutability
This commit is contained in:
Родитель
906679ca6b
Коммит
77ab4bcbe5
|
@ -205,6 +205,81 @@ function shouldSkipValidate (options, errors) {
|
|||
});
|
||||
}
|
||||
|
||||
function isPropertyWriteOnly (xMsMutability) {
|
||||
return xMsMutability && xMsMutability.indexOf('read') === -1 && (xMsMutability.indexOf('create') != -1 || xMsMutability.indexOf('update') != -1);
|
||||
}
|
||||
|
||||
function checkSecretPropertyInResponse (validateOptions, schema, json, report) {
|
||||
if (shouldSkipValidate(validateOptions, ['SECRET_PROPERTY'])) {
|
||||
return
|
||||
}
|
||||
|
||||
var isResponse = validateOptions && validateOptions.isResponse
|
||||
var xMsSecret = schema && schema['x-ms-secret']
|
||||
|
||||
if (isResponse && schema && xMsSecret && json !== undefined) {
|
||||
let errorMessage = 'Secret property `"{0}": ';
|
||||
|
||||
if (schema && schema.type === 'string' && typeof json === 'string') {
|
||||
errorMessage += '"{1}"';
|
||||
} else {
|
||||
errorMessage += '{1}';
|
||||
}
|
||||
let propertyName = '';
|
||||
|
||||
if (schema.title && typeof schema.title === 'string') {
|
||||
try {
|
||||
let result = JSON.parse(schema.title);
|
||||
|
||||
if (Array.isArray(result.path) && result.path.length) {
|
||||
propertyName = result.path[result.path.length - 1];
|
||||
}
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
errorMessage += '`, cannot be sent in the response.';
|
||||
report.addCustomError('SECRET_PROPERTY', errorMessage, [propertyName, json], null, schema);
|
||||
}
|
||||
}
|
||||
|
||||
function checkWriteOnlyPropertyInResponse (validateOptions, schema, json, report) {
|
||||
// Check if there's a write-only property in the response.
|
||||
// Write-only property definition: Property with 'x-ms-mutability' that does NOT have ['read'] and only has 'create' or 'update' or both
|
||||
|
||||
if (shouldSkipValidate(validateOptions, ['WRITEONLY_PROPERTY_NOT_ALLOWED_IN_RESPONSE'])) {
|
||||
return
|
||||
}
|
||||
|
||||
var isResponse = validateOptions && validateOptions.isResponse
|
||||
var xMsMutability = schema && schema['x-ms-mutability']
|
||||
|
||||
if (isResponse && schema && isPropertyWriteOnly(xMsMutability) && json !== undefined) {
|
||||
let errorMessage = 'Write-only property `"{0}": ';
|
||||
|
||||
if (schema && schema.type === 'string' && typeof json === 'string') {
|
||||
errorMessage += '"{1}"';
|
||||
} else {
|
||||
errorMessage += '{1}';
|
||||
}
|
||||
let propertyName = '';
|
||||
|
||||
if (schema.title && typeof schema.title === 'string') {
|
||||
try {
|
||||
let result = JSON.parse(schema.title);
|
||||
|
||||
if (Array.isArray(result.path) && result.path.length) {
|
||||
propertyName = result.path[result.path.length - 1];
|
||||
}
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
errorMessage += '`, is not allowed in the response.';
|
||||
report.addCustomError('WRITEONLY_PROPERTY_NOT_ALLOWED_IN_RESPONSE', errorMessage, [propertyName, json], null, schema);
|
||||
}
|
||||
}
|
||||
|
||||
function readOnlyValidator (report, schema, json) {
|
||||
// http://json-schema.org/latest/json-schema-validation.html#rfc.section.10.3
|
||||
if (shouldSkipValidate(this.validateOptions, ['READONLY_PROPERTY_NOT_ALLOWED_IN_REQUEST'])) {
|
||||
|
@ -248,45 +323,8 @@ function readOnlyValidator (report, schema, json) {
|
|||
}
|
||||
|
||||
function customValidatorFn (report, schema, json) {
|
||||
if (shouldSkipValidate(this.validateOptions, ['SECRET_PROPERTY'])) {
|
||||
return
|
||||
}
|
||||
|
||||
var isResponse = this.validateOptions && this.validateOptions.isResponse
|
||||
var xMsSecret = schema && schema['x-ms-secret']
|
||||
|
||||
if (isResponse && schema && xMsSecret && json !== undefined) {
|
||||
let errorMessage = 'Secret property `"{0}": '
|
||||
|
||||
if (schema && schema.type === 'string' && typeof json === 'string') {
|
||||
errorMessage += '"{1}"'
|
||||
} else {
|
||||
errorMessage += '{1}'
|
||||
}
|
||||
|
||||
let propertyName = ''
|
||||
|
||||
if (schema.title && typeof schema.title === 'string') {
|
||||
try {
|
||||
let result = JSON.parse(schema.title)
|
||||
|
||||
if (Array.isArray(result.path) && result.path.length) {
|
||||
propertyName = result.path[result.path.length - 1]
|
||||
}
|
||||
} catch (err) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
errorMessage += '`, cannot be sent in the response.'
|
||||
report.addCustomError(
|
||||
'SECRET_PROPERTY',
|
||||
errorMessage,
|
||||
[propertyName, json],
|
||||
null,
|
||||
schema
|
||||
)
|
||||
}
|
||||
checkWriteOnlyPropertyInResponse(this.validateOptions, schema, json, report);
|
||||
checkSecretPropertyInResponse(this.validateOptions, schema, json, report);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "yasway",
|
||||
"version": "1.8.5",
|
||||
"version": "1.8.6",
|
||||
"description": "A library that simplifies Swagger integrations.",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
|
|
|
@ -680,6 +680,18 @@ definitions:
|
|||
- "available"
|
||||
- "pending"
|
||||
- "sold"
|
||||
secret:
|
||||
type: "string"
|
||||
x-ms-secret: ["secret"]
|
||||
writeOnly:
|
||||
type: "string"
|
||||
x-ms-mutability: ["create", "update"]
|
||||
createOnly:
|
||||
type: "string"
|
||||
x-ms-mutability: ["create"]
|
||||
updateOnly:
|
||||
type: "string"
|
||||
x-ms-mutability: ["update"]
|
||||
type: "object"
|
||||
xml:
|
||||
name: "Pet"
|
||||
|
|
|
@ -129,6 +129,30 @@ describe('Response', function () {
|
|||
photoUrls: []
|
||||
};
|
||||
|
||||
var writeOnlyPet = {
|
||||
name: 'Test Pet',
|
||||
photoUrls: [],
|
||||
writeOnly: 'writeonly'
|
||||
};
|
||||
|
||||
var createOnlyPet = {
|
||||
name: 'Test Pet',
|
||||
photoUrls: [],
|
||||
createOnly: 'createonly'
|
||||
}
|
||||
|
||||
var updateOnlyPet = {
|
||||
name: 'Test Pet',
|
||||
photoUrls: [],
|
||||
updateOnly: 'updateonly'
|
||||
}
|
||||
|
||||
var secretPet = {
|
||||
name: 'Test Pet',
|
||||
photoUrls: [],
|
||||
secret: 'password'
|
||||
}
|
||||
|
||||
describe('validate Content-Type', function () {
|
||||
describe('operation level produces', function () {
|
||||
var cSway;
|
||||
|
@ -624,6 +648,150 @@ describe('Response', function () {
|
|||
})
|
||||
.then(done, done);
|
||||
});
|
||||
|
||||
it('Test if response has secret property marked with x-ms-secret', function (done) {
|
||||
var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc);
|
||||
|
||||
Sway.create({
|
||||
definition: cSwaggerDoc
|
||||
})
|
||||
.then(function (api) {
|
||||
var results = api.getOperation('/pet/{petId}', 'get').validateResponse({
|
||||
body: secretPet,
|
||||
encoding: 'utf-8',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
statusCode: 200
|
||||
});
|
||||
|
||||
assert.deepEqual(results.errors, [
|
||||
{
|
||||
code: 'INVALID_RESPONSE_BODY',
|
||||
errors: [
|
||||
{
|
||||
code: 'SECRET_PROPERTY',
|
||||
message: 'Secret property `"": "password"`, cannot be sent in the response.',
|
||||
params: ['', 'password'],
|
||||
path: ['secret']
|
||||
}
|
||||
],
|
||||
message: 'Invalid body: Secret property `"": "password"`, cannot be sent in the response.',
|
||||
path: []
|
||||
}
|
||||
]);
|
||||
assert.equal(results.warnings.length, 0);
|
||||
})
|
||||
.then(done, done);
|
||||
});
|
||||
|
||||
it('Test if response has WRITE only property marked with x-ms-mutability', function (done) {
|
||||
var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc);
|
||||
|
||||
Sway.create({
|
||||
definition: cSwaggerDoc
|
||||
})
|
||||
.then(function (api) {
|
||||
var results = api.getOperation('/pet/{petId}', 'get').validateResponse({
|
||||
body: writeOnlyPet,
|
||||
encoding: 'utf-8',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
statusCode: 200
|
||||
});
|
||||
|
||||
assert.deepEqual(results.errors, [
|
||||
{
|
||||
code: 'INVALID_RESPONSE_BODY',
|
||||
errors: [
|
||||
{
|
||||
code: 'WRITEONLY_PROPERTY_NOT_ALLOWED_IN_RESPONSE',
|
||||
message: 'Write-only property `"": "writeonly"`, is not allowed in the response.',
|
||||
params: ['', 'writeonly'],
|
||||
path: ['writeOnly']
|
||||
}
|
||||
],
|
||||
message: 'Invalid body: Write-only property `"": "writeonly"`, is not allowed in the response.',
|
||||
path: []
|
||||
}
|
||||
]);
|
||||
assert.equal(results.warnings.length, 0);
|
||||
})
|
||||
.then(done, done);
|
||||
});
|
||||
|
||||
it('Test if response has CREATE only property marked with x-ms-mutability', function (done) {
|
||||
var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc);
|
||||
|
||||
Sway.create({
|
||||
definition: cSwaggerDoc
|
||||
})
|
||||
.then(function (api) {
|
||||
var results = api.getOperation('/pet/{petId}', 'get').validateResponse({
|
||||
body: createOnlyPet,
|
||||
encoding: 'utf-8',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
statusCode: 200
|
||||
});
|
||||
|
||||
assert.deepEqual(results.errors, [
|
||||
{
|
||||
code: 'INVALID_RESPONSE_BODY',
|
||||
errors: [
|
||||
{
|
||||
code: 'WRITEONLY_PROPERTY_NOT_ALLOWED_IN_RESPONSE',
|
||||
message: 'Write-only property `"": "createonly"`, is not allowed in the response.',
|
||||
params: ['', 'createonly'],
|
||||
path: ['createOnly']
|
||||
}
|
||||
],
|
||||
message: 'Invalid body: Write-only property `"": "createonly"`, is not allowed in the response.',
|
||||
path: []
|
||||
}
|
||||
]);
|
||||
assert.equal(results.warnings.length, 0);
|
||||
})
|
||||
.then(done, done);
|
||||
});
|
||||
|
||||
it('Test if response has UPDATE only property marked with x-ms-mutability', function (done) {
|
||||
var cSwaggerDoc = _.cloneDeep(helpers.swaggerDoc);
|
||||
|
||||
Sway.create({
|
||||
definition: cSwaggerDoc
|
||||
})
|
||||
.then(function (api) {
|
||||
var results = api.getOperation('/pet/{petId}', 'get').validateResponse({
|
||||
body: updateOnlyPet,
|
||||
encoding: 'utf-8',
|
||||
headers: {
|
||||
'content-type': 'application/json'
|
||||
},
|
||||
statusCode: 200
|
||||
});
|
||||
|
||||
assert.deepEqual(results.errors, [
|
||||
{
|
||||
code: 'INVALID_RESPONSE_BODY',
|
||||
errors: [
|
||||
{
|
||||
code: 'WRITEONLY_PROPERTY_NOT_ALLOWED_IN_RESPONSE',
|
||||
message: 'Write-only property `"": "updateonly"`, is not allowed in the response.',
|
||||
params: ['', 'updateonly'],
|
||||
path: ['updateOnly']
|
||||
}
|
||||
],
|
||||
message: 'Invalid body: Write-only property `"": "updateonly"`, is not allowed in the response.',
|
||||
path: []
|
||||
}
|
||||
]);
|
||||
assert.equal(results.warnings.length, 0);
|
||||
})
|
||||
.then(done, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Загрузка…
Ссылка в новой задаче