Fix conversion process for openapi 3.1, adding schemaUtils31X and schemaUtils30X, adding unit tests

This commit is contained in:
Erik Mendoza
2021-12-07 21:52:51 -06:00
parent 7c265fab19
commit 34f31e9931
19 changed files with 897 additions and 92 deletions

View File

@@ -0,0 +1,63 @@
const inputValidation30X = require('./inputValidation'),
schemaUtilsCommon = require('../common/schemaUtilsCommon'),
_ = require('lodash');
module.exports = {
/**
* Parses an OAS string/object as a YAML or JSON
* @param {YAML/JSON} openApiSpec - The OAS 3.x specification specified in either YAML or JSON
* @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error
* @no-unit-test
*/
parseSpec: function (openApiSpec) {
return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation30X);
},
/**
* Get the required elements for conversion from spec parsed data
* @param {object} spec openapi parsed value
* @returns {object} required elements to convert
*/
getRequiredData(spec) {
return {
info: spec.info,
components: spec.components ? spec.components : [],
paths: spec.paths
};
},
/**
* Compares two types and return if they match or not
* @param {string} currentType the type in schema
* @param {string} typeToValidate the type to compare
* @returns {boolean} the result of the comparation
*/
compareTypes(currentType, typeToValidate) {
return currentType === typeToValidate;
},
/**
* This method is to make this module matches with schemaUtilsXXX interface content
* It only returns the provided schema
* @param {object} schema a provided schema
* @returns {object} it returns the same schema
*/
fixExamplesByVersion(schema) {
return schema;
},
/**
* Check if request body type is binary type
* @param {string} bodyType the bodyType provided in a request body content
* @param {object} contentObj The request body content provided in spec
* @returns {boolean} Returns true if content is a binary type
*/
isBinaryContentType (bodyType, contentObj) {
return bodyType &&
!_.isEmpty(_.get(contentObj, [bodyType, 'schema'])) &&
contentObj[bodyType].schema.type === 'string' &&
contentObj[bodyType].schema.format === 'binary';
}
};

View File

@@ -0,0 +1,78 @@
const inputValidation31X = require('./inputValidation31X'),
schemaUtilsCommon = require('../common/schemaUtilsCommon'),
fileUploadTypes = [
'application/octet-stream'
];
module.exports = {
/**
* Parses an OAS 3.1.X string/object as a YAML or JSON
* @param {YAML/JSON} openApiSpec - The OAS 3.1.x specification specified in either YAML or JSON
* @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error
* @no-unit-test
*/
parseSpec: function (openApiSpec) {
return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation31X);
},
inputValidation31X,
/**
* Get the required elements for conversion from spec parsed data
* @param {object} spec openapi parsed value
* @returns {object} required elements to convert
*/
getRequiredData(spec) {
return {
info: spec.info,
paths: spec.paths ? spec.paths : [],
components: spec.components ? spec.components : [],
webhooks: spec.webhooks ? spec.webhooks : []
};
},
/**
* Compares two types and return if they match or not.
* In case that the provided type is an array it checks of the typeToCompare exists in.
* @param {string | array} currentType the type in schema, it could be an array of types
* @param {string} typeToValidate the type to compare
* @returns {boolean} the result of the comparation
*/
compareTypes(currentType, typeToValidate) {
let isTypeMatching = currentType === typeToValidate;
if (Array.isArray(currentType)) {
isTypeMatching = currentType.includes(typeToValidate);
}
return isTypeMatching;
},
/**
* Takes the first element from 'examples' property in a schema and adds a new 'example' property.
* This method is used before faking the schema. (Schema faker uses the example property to fakle the schema)
* @param {object} schema a provided schema
* @returns {object} it returns the schema with a new example property
*/
fixExamplesByVersion(schema) {
if (schema.properties) {
const schemaProperties = Object.keys(schema.properties);
schemaProperties.forEach((property) => {
if (schema.properties[property].examples) {
schema.properties[property].example = schema.properties[property].examples[0];
}
});
}
return schema;
},
/**
* Check if request body type is binary type.
* Open api 3.1 does not need that a binary content has a schema within. It comes as an empty object
* @param {string} bodyType the bodyType provided in a request body content
* @param {object} contentObj The request body content provided in spec
* @returns {boolean} Returns true if content is a binary type
*/
isBinaryContentType (bodyType, contentObj) {
return Object.keys(contentObj[bodyType]).length === 0 && fileUploadTypes.includes(bodyType);
}
};

View File

@@ -1,17 +0,0 @@
const inputValidation31X = require('./inputValidation31X'),
schemaUtilsCommon = require('../common/schemaUtilsCommon');
module.exports = {
/**
* Parses an OAS 3.1.X string/object as a YAML or JSON
* @param {YAML/JSON} openApiSpec - The OAS 3.1.x specification specified in either YAML or JSON
* @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error
* @no-unit-test
*/
parseSpec: function (openApiSpec) {
return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation31X);
},
inputValidation31X
};

View File

@@ -40,6 +40,24 @@ function getSpecVersion({ type, data }) {
return version;
}
/**
*
* @param {string} specVersion - the OAS specification version
* @returns {NodeRequire} the schema utils according to version
*/
function getConcreteSchemaUtils({ type, data }) {
const specVersion = getSpecVersion({ type, data });
let concreteUtils = {};
if (specVersion === DEFAULT_SPEC_VERSION) {
concreteUtils = require('../30XUtils/schemaUtils30X');
}
else {
concreteUtils = require('../31XUtils/schemaUtils31X');
}
return concreteUtils;
}
module.exports = {
getSpecVersion
getSpecVersion,
getConcreteSchemaUtils
};

View File

@@ -32,7 +32,8 @@ const _ = require('lodash'),
'regex',
'uuid',
'json-pointer'
];
],
DEFAULT_SCHEMA_UTILS = require('./30XUtils/schemaUtils30X');
module.exports = {
/**
@@ -130,7 +131,12 @@ module.exports = {
resolveFor = 'CONVERSION', resolveTo = 'schema', stack = 0, seenRef = {}, stackLimit = 10) {
var resolvedSchema, prop, splitRef,
ERR_TOO_MANY_LEVELS = '<Error: Too many levels of nesting to fake this schema>';
let concreteUtils = DEFAULT_SCHEMA_UTILS;
if (components) {
concreteUtils = components.hasOwnProperty('concreteUtils') ?
components.concreteUtils :
DEFAULT_SCHEMA_UTILS;
}
stack++;
schemaResolutionCache = schemaResolutionCache || {};
@@ -244,7 +250,7 @@ module.exports = {
}
return { value: 'reference ' + schema.$ref + ' not found in the OpenAPI spec' };
}
if (schema.type === 'object' || schema.hasOwnProperty('properties')) {
if (concreteUtils.compareTypes(schema.type, 'objects') || schema.hasOwnProperty('properties')) {
// go through all props
schema.type = 'object';
if (schema.hasOwnProperty('properties')) {
@@ -281,7 +287,7 @@ module.exports = {
schema.default = '<object>';
}
}
else if (schema.type === 'array' && schema.items) {
else if (concreteUtils.compareTypes(schema.type, 'array') && schema.items) {
/*
For VALIDATION - keep minItems and maxItems properties defined by user in schema as is
FOR CONVERSION -

View File

@@ -15,8 +15,7 @@ const async = require('async'),
defaultOptions = require('../lib/options.js').getOptions('use'),
{ Node, Trie } = require('./trie.js'),
{ validateSchema } = require('./ajvValidation'),
inputValidation = require('./inputValidation'),
schemaUtilsCommon = require('./common/schemaUtilsCommon'),
inputValidation = require('./30XUtils/inputValidation'),
SCHEMA_FORMATS = {
DEFAULT: 'default', // used for non-request-body data and json
XML: 'xml' // used for request-body XMLs
@@ -93,7 +92,8 @@ const async = require('async'),
'array': (d) => Array.isArray(d),
'object': (d) => typeof d === 'object' && !Array.isArray(d)
},
crypto = require('crypto');
crypto = require('crypto'),
DEFAULT_SCHEMA_UTILS = require('./30XUtils/schemaUtils30X');
/* eslint-enable */
// See https://github.com/json-schema-faker/json-schema-faker/tree/master/docs#available-options
@@ -137,9 +137,16 @@ function safeSchemaFaker(oldSchema, resolveTo, resolveFor, parameterSourceOption
var prop, key, resolvedSchema, fakedSchema,
schemaResolutionCache = _.get(schemaCache, 'schemaResolutionCache', {}),
schemaFakerCache = _.get(schemaCache, 'schemaFakerCache', {});
let concreteUtils = DEFAULT_SCHEMA_UTILS;
if (components) {
concreteUtils = components.hasOwnProperty('concreteUtils') ?
components.concreteUtils :
DEFAULT_SCHEMA_UTILS;
}
resolvedSchema = deref.resolveRefs(oldSchema, parameterSourceOption, components, schemaResolutionCache,
resolveFor, resolveTo, 0, {}, stackLimit);
resolvedSchema = concreteUtils.fixExamplesByVersion(resolvedSchema);
key = JSON.stringify(resolvedSchema);
if (resolveTo === 'schema') {
@@ -441,16 +448,6 @@ module.exports = {
return variables;
},
/**
* Parses an OAS string/object as a YAML or JSON
* @param {YAML/JSON} openApiSpec - The OAS 3.x specification specified in either YAML or JSON
* @returns {Object} - Contains the parsed JSON-version of the OAS spec, or an error
* @no-unit-test
*/
parseSpec: function (openApiSpec) {
return schemaUtilsCommon.parseSpec(openApiSpec, inputValidation);
},
/**
* Returns params applied to specific operation with resolved references. Params from parent
* blocks (collection/folder) are merged, so that the request has a flattened list of params needed.
@@ -1686,6 +1683,14 @@ module.exports = {
enumValue,
formHeaders = [];
let concreteUtils = DEFAULT_SCHEMA_UTILS;
if (components) {
concreteUtils = components.hasOwnProperty('concreteUtils') ?
components.concreteUtils :
DEFAULT_SCHEMA_UTILS;
}
// @TODO: how do we support multiple content types
contentObj = requestBody.content;
@@ -1862,10 +1867,7 @@ module.exports = {
}
if (
bodyType &&
!_.isEmpty(_.get(contentObj, [bodyType, 'schema'])) &&
contentObj[bodyType].schema.type === 'string' &&
contentObj[bodyType].schema.format === 'binary'
concreteUtils.isBinaryContentType(bodyType, contentObj)
) {
updateOptions = {
mode: 'file'
@@ -2152,7 +2154,8 @@ module.exports = {
* @returns {Object} postman request Item
* @no-unit-test
*/
convertRequestToItem: function(openapi, operationItem, components, options, schemaCache, variableStore) {
convertRequestToItem: function(openapi, operationItem, components,
options, schemaCache, variableStore) {
options = _.merge({}, defaultOptions, options);
var reqName,
pathVariables = openapi.baseUrlVariables,

View File

@@ -2,6 +2,7 @@
// This is the default collection name if one can't be inferred from the OpenAPI spec
const COLLECTION_NAME = 'Imported from OpenAPI 3.0',
{ getConcreteSchemaUtils } = require('./common/versionUtils.js'),
BROWSER = 'browser',
Ajv = require('ajv'),
async = require('async'),
@@ -22,30 +23,27 @@ const COLLECTION_NAME = 'Imported from OpenAPI 3.0',
// This provides the base class for
// errors with the input OpenAPI spec
OpenApiErr = require('./error.js'),
DEFAULT_SPEC_VERSION = '3.0',
{
getSpecVersion
} = require('./common/versionUtils.js');
schemaUtils = require('./schemaUtils');
let path = require('path'),
schemaUtils,
concreteUtils,
pathBrowserify = require('path-browserify');
/**
*
* @param {string} specVersion - the OAS specification version
* @returns {NodeRequire} the schema utils according to version
*/
function getConcreteSchemaUtils(specVersion) {
let schemaUtils = {};
if (specVersion === DEFAULT_SPEC_VERSION) {
schemaUtils = require('./schemaUtils');
}
else {
schemaUtils = require('./schemaUtils31X');
}
return schemaUtils;
}
// /**
// *
// * @param {string} specVersion - the OAS specification version
// * @returns {NodeRequire} the schema utils according to version
// */
// function getConcreteSchemaUtils(specVersion) {
// let concreteUtils = {};
// if (specVersion === DEFAULT_SPEC_VERSION) {
// concreteUtils = require('./30XUtils/schemaUtils30X');
// }
// else {
// concreteUtils = require('./31XUtils/schemaUtils31X');
// }
// return concreteUtils;
// }
class SchemaPack {
constructor (input, options = {}) {
@@ -66,10 +64,9 @@ class SchemaPack {
);
// hardcoding this option - not exposed to users yet
this.computedOptions.schemaFaker = true;
let indentCharacter = this.computedOptions.indentCharacter,
specVersion = getSpecVersion(input);
let indentCharacter = this.computedOptions.indentCharacter;
this.computedOptions.indentCharacter = indentCharacter === 'tab' ? '\t' : ' ';
schemaUtils = getConcreteSchemaUtils(specVersion);
concreteUtils = getConcreteSchemaUtils(input);
this.validate();
}
@@ -125,7 +122,7 @@ class SchemaPack {
return this.validationResult;
}
specParseResult = schemaUtils.parseSpec(json);
specParseResult = concreteUtils.parseSpec(json);
if (!specParseResult.result) {
// validation failed
@@ -258,7 +255,7 @@ class SchemaPack {
analysis,
generatedStore = {},
collectionJSON,
componentsAndPaths,
specComponentsAndUtils,
authHelper,
schemaCache = {
schemaResolutionCache: this.schemaResolutionCache,
@@ -270,10 +267,12 @@ class SchemaPack {
}
// this cannot be attempted before validation
componentsAndPaths = {
components: this.openapi.components,
paths: this.openapi.paths
};
specComponentsAndUtils = { concreteUtils };
Object.assign(specComponentsAndUtils, concreteUtils.getRequiredData(this.openapi));
// {
// components: this.openapi.components,
// paths: this.openapi.paths
// };
// create and sanitize basic spec
openapi = this.openapi;
@@ -330,10 +329,24 @@ class SchemaPack {
// For paths, All operations are grouped based on corresponding paths
try {
if (options.folderStrategy === 'tags') {
schemaUtils.addCollectionItemsUsingTags(openapi, generatedStore, componentsAndPaths, options, schemaCache);
schemaUtils.addCollectionItemsUsingTags(
openapi,
generatedStore,
specComponentsAndUtils,
options,
schemaCache,
concreteUtils
);
}
else {
schemaUtils.addCollectionItemsUsingPaths(openapi, generatedStore, componentsAndPaths, options, schemaCache);
schemaUtils.addCollectionItemsUsingPaths(
openapi,
generatedStore,
specComponentsAndUtils,
options,
schemaCache,
concreteUtils
);
}
}
catch (e) {

View File

@@ -0,0 +1,31 @@
{
"openapi": "3.1.0",
"info": {
"title": "Non-oAuth Scopes example",
"version": "1.0.0"
},
"paths": {
"/users": {
"get": {
"security": [
{
"bearerAuth": [
"read:users",
"public"
]
}
]
}
}
},
"components": {
"securitySchemes": {
"bearerAuth": {
"type": "http",
"scheme": "bearer",
"bearerFormat": "jwt",
"description": "note: non-oauth scopes are not defined at the securityScheme level"
}
}
}
}

View File

@@ -0,0 +1,247 @@
{
"openapi": "3.1.0",
"info": {
"version": "1.0.0",
"title": "Swagger Petstore",
"license": {
"name": "MIT"
}
},
"servers": [
{
"url": "http://petstore.swagger.io/v1"
}
],
"paths": {
"/pets": {
"get": {
"summary": "List all pets",
"operationId": "listPets",
"tags": [
"pets"
],
"parameters": [
{
"name": "limit",
"in": "header",
"description": "How many items to return at one time (max 100)",
"required": false,
"schema": {
"type": "integer",
"format": "int32"
}
},
{
"name": "variable",
"in": "query",
"description": "random variable",
"style": "form",
"explode": false,
"schema": {
"type": "array",
"items": {
"type": "string"
}
}
},
{
"name": "variable2",
"in": "query",
"description": "another random variable",
"style": "spaceDelimited",
"schema": {
"type": "array",
"items": {
"type": "integer",
"format": "int64"
}
}
}
],
"responses": {
"200": {
"description": "An paged array of pets",
"headers": {
"x-next": {
"description": "A link to the next page of responses",
"schema": {
"type": "string"
}
}
},
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pets"
}
}
}
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
},
"post": {
"summary": "Create a pet",
"operationId": "createPets",
"tags": [
"pets"
],
"responses": {
"201": {
"description": "Null response"
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
},
"parameters": [
{
"name": "limit_2",
"in": "headers",
"description": "How many items to return at one time (max 100)",
"required": false,
"schema": {
"type": "integer",
"format": "int32"
}
}
]
},
"/pets/{petId}": {
"get": {
"summary": "Info for a specific pet",
"operationId": "showPetById",
"tags": [
"pets"
],
"parameters": [
{
"name": "petId",
"in": "path",
"required": true,
"description": "The id of the pet to retrieve",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "Expected response to a valid request",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Pet"
}
}
}
},
"default": {
"description": "unexpected error",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/Error"
}
}
}
}
}
}
},
"/pet/{petId}/uploadImage": {
"post": {
"tags": [
"pet",
"cat"
],
"summary": "uploads an image",
"description": "",
"operationId": "uploadFile",
"parameters": [
{
"name": "petId",
"in": "path",
"description": "ID of pet to update",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
}
],
"requestBody": {
"content": {
"application/octet-stream": {}
}
}
}
}
},
"components": {
"schemas": {
"Pet": {
"required": [
"id",
"name"
],
"properties": {
"id": {
"type": "integer",
"format": "int64",
"examples": [
111111000111111
]
},
"name": {
"type": "string",
"examples": [
"this is my fisrt example name in pet"
]
},
"tag": {
"type": "string"
}
}
},
"Pets": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Pet"
}
},
"Error": {
"required": [
"code",
"message"
],
"properties": {
"code": {
"type": "integer",
"format": "int32"
},
"message": {
"type": "string"
}
}
}
}
}
}

View File

@@ -0,0 +1,121 @@
const { expect } = require('chai'),
{
parseSpec, getRequiredData, compareTypes, fixExamplesByVersion, isBinaryContentType
} = require('../../../lib/30XUtils/schemaUtils30X'),
fs = require('fs'),
valid30xFolder = './test/data/valid_openapi',
invalid30xFolder = './test/data/invalid_openapi';
describe('parseSpec method', function () {
it('should return true and a parsed specification', function () {
let fileContent = fs.readFileSync(valid30xFolder + '/petstore.json', 'utf8');
const parsedSpec = parseSpec(fileContent);
expect(parsedSpec.result).to.be.true;
expect(parsedSpec.openapi.openapi).to.equal('3.0.0');
});
it('should return false and invalid format message when input content is sent', function () {
let fileContent = fs.readFileSync(invalid30xFolder + '/empty-spec.yaml', 'utf8');
const parsedSpec = parseSpec(fileContent);
expect(parsedSpec.result).to.be.false;
expect(parsedSpec.reason).to.equal('Invalid format. Input must be in YAML or JSON format.');
});
it('should return false and Spec must contain info object', function () {
let fileContent = fs.readFileSync(invalid30xFolder + '/invalid-no-info.yaml', 'utf8');
const parsedSpec = parseSpec(fileContent);
expect(parsedSpec.result).to.be.false;
expect(parsedSpec.reason).to.equal('Specification must contain an Info Object for the meta-data of the API');
});
});
describe('getRequiredData method', function() {
it('Should return all required data from file', function() {
const fileContent = fs.readFileSync(valid30xFolder + '/petstore.json', 'utf8'),
requiredData = getRequiredData(JSON.parse(fileContent));
expect(requiredData).to.be.an('object')
.and.to.have.all.keys('info', 'paths', 'components');
});
});
describe('compareTypes method', function() {
it('Should match type in spec with type to compare when type in spec is a string when they are equal', function() {
const typeInSpec = 'string',
typeToCompare = 'string',
matchTypes = compareTypes(typeInSpec, typeToCompare);
expect(matchTypes).to.be.true;
});
it('Should not match typeInSpec with typeToCompare when ' +
'typeInSpec is an array that includes the typeInSpec value', function() {
const typeInSpec = ['string'],
typeToCompare = 'string',
matchTypes = compareTypes(typeInSpec, typeToCompare);
expect(matchTypes).to.be.false;
});
it('Should not match type in spec with type to compare when ' +
'type in spec is a string when they are different', function() {
const typeInSpec = 'integer',
typeToCompare = 'string',
matchTypes = compareTypes(typeInSpec, typeToCompare);
expect(matchTypes).to.be.false;
});
});
describe('fixExamplesByVersion method', function() {
it('Should return the same schema than the provided', function() {
const providedSchema = {
required: [
'id',
'name'
],
type: 'object',
properties: {
id: {
type: 'integer'
},
name: {
type: 'string',
example: 'this is my fisrt example name in pet'
},
tag: {
type: 'string'
}
}
},
fixedSchemaWithExample = fixExamplesByVersion(providedSchema);
expect(JSON.stringify(fixedSchemaWithExample)).to.be.equal(JSON.stringify(providedSchema));
});
});
describe('isBinaryContentType method', function() {
it('Should be true if content type is binary type without schema', function() {
const bodyType = 'application/octet-stream',
contentObject = {
'application/octet-stream': {
'schema': {
'type': 'string',
'format': 'binary'
}
}
},
isBinary = isBinaryContentType(bodyType, contentObject);
expect(isBinary).to.be.true;
});
it('Should be false if content type is not binary type', function() {
const bodyType = 'application/json',
contentObject = {
'application/json': {
'schema': {
'type': 'string',
'example': 'OK'
}
}
},
isBinary = isBinaryContentType(bodyType, contentObject);
expect(isBinary).to.be.false;
});
});

View File

@@ -1,7 +1,7 @@
const { expect } = require('chai'),
{
validateSpec
} = require('../../../lib/31Xsupport/inputValidation31X'),
} = require('../../../lib/31XUtils/inputValidation31X'),
correctMockedEntryWH = {
openapi: '3.1.0',
info: {

View File

@@ -1,7 +1,7 @@
const { expect } = require('chai'),
{
parseSpec
} = require('../../../lib/31Xsupport/schemaUtils31X'),
parseSpec, getRequiredData, compareTypes, fixExamplesByVersion, isBinaryContentType
} = require('../../../lib/31XUtils/schemaUtils31X'),
fs = require('fs'),
valid31xFolder = './test/data/valid_openapi31X',
invalid31xFolder = './test/data/invalid_openapi31X';
@@ -30,3 +30,130 @@ describe('parseSpec method', function () {
});
});
describe('getRequiredData method', function() {
it('Should return all required data from file', function() {
const fileContent = fs.readFileSync(valid31xFolder + '/petstore.json', 'utf8'),
requiredData = getRequiredData(JSON.parse(fileContent));
expect(requiredData).to.be.an('object')
.and.to.have.all.keys('info', 'paths', 'webhooks', 'components');
expect(requiredData.webhooks).to.be.an('array').with.length(0);
});
});
describe('compareTypes method', function() {
it('Should match type in spec with type to compare when type in spec is a string when they are equal', function() {
const typeInSpec = 'string',
typeToCompare = 'string',
matchTypes = compareTypes(typeInSpec, typeToCompare);
expect(matchTypes).to.be.true;
});
it('Should match type in spec with type to compare when type in spec is an array when they are equal', function() {
const typeInSpec = ['string'],
typeToCompare = 'string',
matchTypes = compareTypes(typeInSpec, typeToCompare);
expect(matchTypes).to.be.true;
});
it('Should match type in spec with type to compare when ' +
'type in spec is an array with multiple types when they are equal', function() {
const typeInSpec = ['string', 'null'],
typeToCompare = 'string',
matchTypes = compareTypes(typeInSpec, typeToCompare);
expect(matchTypes).to.be.true;
});
it('Should match type in spec with type to compare when ' +
'type in spec is a string when they are different', function() {
const typeInSpec = 'integer',
typeToCompare = 'string',
matchTypes = compareTypes(typeInSpec, typeToCompare);
expect(matchTypes).to.be.false;
});
it('Should not match type in spec with type to compare when' +
'type in spec is an array when they are different', function() {
const typeInSpec = ['integer'],
typeToCompare = 'string',
matchTypes = compareTypes(typeInSpec, typeToCompare);
expect(matchTypes).to.be.false;
});
});
describe('fixExamplesByVersion method', function() {
it('Should take the first element from examples and add it as example', function() {
const providedSchema = {
required: [
'id',
'name'
],
type: 'object',
properties: {
id: {
type: 'integer'
},
name: {
type: 'string',
examples: [
'this is my fisrt example name in pet'
]
},
tag: {
type: 'string'
}
}
},
expectedSchemaAfterFix = {
required: [
'id',
'name'
],
type: 'object',
properties: {
id: {
type: 'integer'
},
name: {
type: 'string',
examples: [
'this is my fisrt example name in pet'
],
example: 'this is my fisrt example name in pet'
},
tag: {
type: 'string'
}
}
},
fixedSchemaWithExample = fixExamplesByVersion(providedSchema);
expect(JSON.stringify(fixedSchemaWithExample)).to.be.equal(JSON.stringify(expectedSchemaAfterFix));
});
});
describe('isBinaryContentType method', function() {
it('Should be true if content type is binary type without schema', function() {
const bodyType = 'application/octet-stream',
contentObject = {
'application/octet-stream': {}
},
isBinary = isBinaryContentType(bodyType, contentObject);
expect(isBinary).to.be.true;
});
it('Should be false if content type is not binary type', function() {
const bodyType = 'application/json',
contentObject = {
'application/json': {
'schema': {
'type': 'string',
'examples': [
'OK'
]
}
}
},
isBinary = isBinaryContentType(bodyType, contentObject);
expect(isBinary).to.be.false;
});
});

View File

@@ -0,0 +1,51 @@
const { SchemaPack } = require('../../..');
const expect = require('chai').expect,
fs = require('fs'),
path = require('path'),
OPENAPI_31_FOLDER = '../../data/valid_openapi31X',
OPENAPI_30_FOLDER = '../../data/valid_openapi';
describe('Testing openapi 3.1 schema pack convert', function() {
// it('Should convert from openapi 3.1 spec to postman collection', function() {
// const fileSource = path.join(__dirname, OPENAPI_30_FOLDER + '/petstore-detailed.yaml'),
// fileData = fs.readFileSync(fileSource, 'utf8'),
// input = {
// type: 'string',
// data: fileData
// },
// converter = new SchemaPack(input);
// converter.convert((err, result) => {
// expect(err).to.be.null;
// });
// });
it('Should convert from openapi 3.1 spec to postman collection -- petstore modifyed', function() {
const fileSource = path.join(__dirname, OPENAPI_31_FOLDER + '/petstore.json'),
fileData = fs.readFileSync(fileSource, 'utf8'),
input = {
type: 'string',
data: fileData
},
converter = new SchemaPack(input);
converter.convert((err, result) => {
expect(err).to.be.null;
});
});
// it('Should convert from openapi 3.0 spec to postman collection', function() {
// const fileSource = path.join(__dirname, OPENAPI_30_FOLDER + '/petstore.json'),
// fileData = fs.readFileSync(fileSource, 'utf8'),
// input = {
// type: 'json',
// data: fileData
// },
// converter = new SchemaPack(input);
// converter.convert((err, result) => {
// expect(err).to.be.null;
// });
// });
});

View File

@@ -13,6 +13,7 @@ describe('CONVERT FUNCTION TESTS ', function() {
var testSpec = path.join(__dirname, VALID_OPENAPI_PATH + '/test.json'),
testSpec1 = path.join(__dirname, VALID_OPENAPI_PATH + '/test1.json'),
test31SpecDir = path.join(__dirname, '../data/valid_openapi31X/'),
issue133 = path.join(__dirname, VALID_OPENAPI_PATH + '/issue#133.json'),
issue160 = path.join(__dirname, VALID_OPENAPI_PATH, '/issue#160.json'),
unique_items_schema = path.join(__dirname, VALID_OPENAPI_PATH + '/unique_items_schema.json'),
@@ -640,6 +641,27 @@ describe('CONVERT FUNCTION TESTS ', function() {
}
});
});
it('Should return meta data from a valid 3.1 file', function(done) {
var openapi = fs.readFileSync(test31SpecDir + 'non-oauth.json', 'utf8');
Converter.getMetaData({ type: 'json', data: openapi }, (err, status) => {
if (err) {
expect.fail(null, null, err);
}
if (status.result) {
expect(status.result).to.be.eq(true);
expect(status.name).to.be.equal('Non-oAuth Scopes example');
expect(status.output[0].name).to.be.equal('Non-oAuth Scopes example');
expect(status.output[0].type).to.be.equal('collection');
done();
}
else {
expect.fail(null, null, status.reason);
done();
}
});
});
it('Should return validation result for an invalid file', function(done) {
var invalidNoInfo = path.join(__dirname, INVALID_OPENAPI_PATH + '/invalid-no-info.yaml'),
openapi = fs.readFileSync(invalidNoInfo, 'utf8');

View File

@@ -1,6 +1,7 @@
var expect = require('chai').expect,
_ = require('lodash'),
deref = require('../../lib/deref.js');
deref = require('../../lib/deref.js'),
schemaUtils30X = require('./../../lib/30XUtils/schemaUtils30X');
describe('DEREF FUNCTION TESTS ', function() {
describe('resolveRefs Function', function () {
@@ -90,7 +91,8 @@ describe('DEREF FUNCTION TESTS ', function() {
type: 'object'
}
}
}
},
concreteUtils: schemaUtils30X
},
parameterSource = 'REQUEST',
// deref.resolveRefs modifies the input schema and components so cloning to keep tests independent of each other
@@ -170,7 +172,8 @@ describe('DEREF FUNCTION TESTS ', function() {
}
}
}
}
},
concreteUtils: schemaUtils30X
},
parameterSource = 'REQUEST',
schemaResolutionCache = {},
@@ -213,7 +216,11 @@ describe('DEREF FUNCTION TESTS ', function() {
// check for supported formats
_.forEach(allSupportedFormats, (supportedFormat) => {
output = deref.resolveRefs(schema, parameterSource,
{ components: { schemas: { schemaWithFormat: { type: 'string', format: supportedFormat } } } });
{
components:
{ schemas: { schemaWithFormat: { type: 'string', format: supportedFormat } } },
concreteUtils: schemaUtils30X
});
expect(output.type).to.equal('string');
expect(output.format).to.equal(supportedFormat);
@@ -222,7 +229,10 @@ describe('DEREF FUNCTION TESTS ', function() {
// check for not supported formats
_.forEach(nonSupportedFormats, (nonSupportedFormat) => {
output = deref.resolveRefs(schema, parameterSource,
{ components: { schemas: { schemaWithFormat: nonSupportedFormat } } });
{
components: { schemas: { schemaWithFormat: nonSupportedFormat } },
concreteUtils: schemaUtils30X
});
expect(output.type).to.equal(nonSupportedFormat.type);
expect(output.format).to.be.undefined;
@@ -240,7 +250,7 @@ describe('DEREF FUNCTION TESTS ', function() {
parameterSource = 'REQUEST',
output;
output = deref.resolveRefs(schema, parameterSource, {});
output = deref.resolveRefs(schema, parameterSource, { concreteUtils: schemaUtils30X });
expect(output.type).to.equal('string');
expect(output.format).to.be.undefined;
expect(output.pattern).to.eql(schema.pattern);
@@ -299,7 +309,8 @@ describe('DEREF FUNCTION TESTS ', function() {
}
}
}
}
},
concreteUtils: schemaUtils30X
},
parameterSource = 'REQUEST',
schemaResoltionCache = {},
@@ -359,7 +370,14 @@ describe('DEREF FUNCTION TESTS ', function() {
}
];
expect(deref.resolveAllOf(allOfschema, 'REQUEST', {}, {}, null, 'example')).to.deep.include({
expect(deref.resolveAllOf(
allOfschema,
'REQUEST',
{ concreteUtils: schemaUtils30X },
{},
null,
'example'
)).to.deep.include({
type: 'object',
properties: {
source: {

View File

@@ -1,7 +1,7 @@
const expect = require('chai').expect,
path = require('path'),
fs = require('fs'),
inputValidation = require('../../lib/inputValidation'),
inputValidation = require('../../lib/30XUtils/inputValidation'),
parse = require('../../lib/parse.js');
describe('PARSE FUNCTION TESTS', function() {

View File

@@ -6,7 +6,8 @@ var expect = require('chai').expect,
crypto = require('crypto'),
hash = (input) => {
return crypto.createHash('sha1').update(input).digest('base64');
};
},
concreteUtils = require('./../../lib/30XUtils/schemaUtils30X');
/* Utility function Unit tests */
describe('UTILITY FUNCTION TESTS', function() {
@@ -69,7 +70,7 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () {
resolveTo = 'schema',
resolveFor = 'CONVERSION';
expect(SchemaUtils.safeSchemaFaker(schema, resolveTo, resolveFor, parameterSource, { components }))
expect(SchemaUtils.safeSchemaFaker(schema, resolveTo, resolveFor, parameterSource, { components, concreteUtils }))
.to.equal('<string>');
done();
});
@@ -100,7 +101,13 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () {
resolveTo = 'schema',
resolveFor = 'CONVERSION',
result = SchemaUtils.safeSchemaFaker(schema, resolveTo, resolveFor, parameterSource, { components }),
result = SchemaUtils.safeSchemaFaker(
schema,
resolveTo,
resolveFor,
parameterSource,
{ components, concreteUtils }
),
tooManyLevelsString = result[0].c.value;
expect(result).to.not.equal(null);
@@ -139,7 +146,13 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () {
parameterSource = 'REQUEST',
resolveTo = 'schema',
resolveFor = 'CONVERSION',
fakedSchema = SchemaUtils.safeSchemaFaker(schema, resolveTo, resolveFor, parameterSource, { components });
fakedSchema = SchemaUtils.safeSchemaFaker(
schema,
resolveTo,
resolveFor,
parameterSource,
{ components, concreteUtils }
);
expect(fakedSchema.value).to.equal('reference #/components/schem2 not found in the OpenAPI spec');
done();
@@ -163,14 +176,20 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () {
parameterSource = 'REQUEST',
resolveTo = 'schema',
resolveFor = 'CONVERSION',
resolvedSchema = deref.resolveRefs(schema, parameterSource, { components }, {}, resolveFor, resolveTo),
resolvedSchema = deref.resolveRefs(schema,
parameterSource,
{ components, concreteUtils },
{},
resolveFor,
resolveTo
),
schemaCache = {
schemaFakerCache: {},
schemaResolutionCache: {}
},
key = hash('resolveToSchema ' + JSON.stringify(resolvedSchema)),
fakedSchema = SchemaUtils.safeSchemaFaker(schema, resolveTo, resolveFor, parameterSource,
{ components }, 'default', ' ', schemaCache);
{ components, concreteUtils }, 'default', ' ', schemaCache);
expect(schemaCache.schemaFakerCache).to.have.property(key);
expect(schemaCache.schemaFakerCache[key]).to.equal(fakedSchema);
@@ -204,11 +223,16 @@ describe('SCHEMA UTILITY FUNCTION TESTS ', function () {
schemaFakerCache: {},
schemaResolutionCache: {}
},
resolvedSchema = deref.resolveRefs(schema, parameterSource, { components }, schemaCache.schemaResolutionCache,
resolveFor, resolveTo),
resolvedSchema = deref.resolveRefs(schema,
parameterSource,
{ components, concreteUtils },
schemaCache.schemaResolutionCache,
resolveFor,
resolveTo
),
key = hash('resolveToExample ' + JSON.stringify(resolvedSchema)),
fakedSchema = SchemaUtils.safeSchemaFaker(schema, resolveTo, resolveFor, parameterSource,
{ components }, 'default', ' ', schemaCache);
{ components, concreteUtils }, 'default', ' ', schemaCache);
expect(schemaCache.schemaFakerCache).to.have.property(key);
expect(schemaCache.schemaFakerCache[key]).to.equal(fakedSchema);