Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

openapi-diff

Package Overview
Dependencies
Maintainers
4
Versions
36
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

openapi-diff - npm Package Compare versions

Comparing version 0.11.0 to 0.12.0

dist/openapi-diff/spec-differ/diff-finder/find-diffs-in-request-bodies.js

10

CHANGELOG.md

@@ -0,1 +1,11 @@

<a name="0.12.0"></a>
# [0.12.0](https://bitbucket.org/atlassian/openapi-diff/compare/0.11.0...0.12.0) (2018-07-18)
### Features
* add support for request bodies ([5f897d4](https://bitbucket.org/atlassian/openapi-diff/commits/5f897d4))
<a name="0.11.0"></a>

@@ -2,0 +12,0 @@ # [0.11.0](https://bitbucket.org/atlassian/openapi-diff/compare/0.10.0...0.11.0) (2018-07-06)

10

dist/api-types.d.ts

@@ -11,2 +11,4 @@ // tslint:disable:no-namespace

'method.remove' |
'request.body.scope.add' |
'request.body.scope.remove' |
'unclassified.add' |

@@ -18,2 +20,3 @@ 'unclassified.remove';

'method' |
'request.body.scope' |
'unclassified';

@@ -32,4 +35,4 @@

entity: DiffResultEntity;
sourceSpecEntityDetails: DiffResultSpecEntityDetails;
destinationSpecEntityDetails: DiffResultSpecEntityDetails;
sourceSpecEntityDetails: DiffResultSpecEntityDetails[];
destinationSpecEntityDetails: DiffResultSpecEntityDetails[];
source: DiffResultSource;

@@ -41,3 +44,3 @@ details?: any;

export interface DiffResultSpecEntityDetails {
location?: string;
location: string;
value?: any;

@@ -86,2 +89,3 @@ }

'OPENAPI_DIFF_VALIDATE_SWAGGER_2_ERROR' |
'OPENAPI_DIFF_VALIDATE_OPENAPI_3_ERROR' |
'OPENAPI_DIFF_HTTP_CLIENT_ERROR';

@@ -88,0 +92,0 @@

"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
const cli_factory_1 = require("./cli-factory");
const openApiDiff = {
diffSpecs: (options) => __awaiter(this, void 0, void 0, function* () { return cli_factory_1.CliFactory.createOpenApiDiff().diffSpecs(options); })
diffSpecs: (options) => cli_factory_1.CliFactory.createOpenApiDiff().diffSpecs(options)
};
module.exports = openApiDiff;
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -7,8 +15,10 @@ const find_diffs_in_paths_1 = require("./diff-finder/find-diffs-in-paths");

static findDifferences(specs) {
const topLevelXPropertiesDiffs = find_diffs_in_x_properties_1.findDiffsInXProperties(specs.sourceSpec.xProperties, specs.destinationSpec.xProperties, 'xProperties');
const pathDiffs = find_diffs_in_paths_1.findDiffsInPaths(specs.sourceSpec.paths, specs.destinationSpec.paths);
const allDiffs = [...topLevelXPropertiesDiffs, ...pathDiffs];
return Promise.resolve(allDiffs);
return __awaiter(this, void 0, void 0, function* () {
const topLevelXPropertiesDiffs = find_diffs_in_x_properties_1.findDiffsInXProperties(specs.sourceSpec.xProperties, specs.destinationSpec.xProperties, 'xProperties');
const pathDiffs = yield find_diffs_in_paths_1.findDiffsInPaths(specs.sourceSpec.paths, specs.destinationSpec.paths);
const allDiffs = [...topLevelXPropertiesDiffs, ...pathDiffs];
return Promise.resolve(allDiffs);
});
}
}
exports.DiffFinder = DiffFinder;

@@ -8,23 +8,20 @@ "use strict";

};
const createSpecEntityDetails = (parsedProperty) => {
return parsedProperty
? {
location: parsedProperty.originalPath.join('.'),
value: parsedProperty.value
}
: {
location: undefined,
value: undefined
};
};
const createSpecEntityDetails = (parsedProperty) => ({
location: parsedProperty.originalPath.join('.'),
value: parsedProperty.value
});
exports.createDifference = (options) => {
const entity = findEntityForDiff(options.propertyName);
return {
const difference = {
action: options.action,
code: `${entity}.${options.action}`,
destinationSpecEntityDetails: createSpecEntityDetails(options.destinationObject),
destinationSpecEntityDetails: options.destinationSpecOrigins.map(createSpecEntityDetails),
entity,
source: 'openapi-diff',
sourceSpecEntityDetails: createSpecEntityDetails(options.sourceObject)
source: options.source,
sourceSpecEntityDetails: options.sourceSpecOrigins.map(createSpecEntityDetails)
};
if (options.details) {
difference.details = options.details;
}
return difference;
};
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const get_added_keys_from_objects_1 = require("./common/get-added-keys-from-objects");
const get_common_keys_from_objects_1 = require("./common/get-common-keys-from-objects");
const get_removed_keys_from_objects_1 = require("./common/get-removed-keys-from-objects");
const create_difference_1 = require("./create-difference");
const find_diffs_in_request_bodies_1 = require("./find-diffs-in-request-bodies");
const findAddedMethodDifferences = (sourceOperations, destinationOperations) => {

@@ -12,4 +22,6 @@ return get_added_keys_from_objects_1.getAddedKeysFromObjects(sourceOperations, destinationOperations)

action: 'add',
destinationObject: addedDestinationOperation.originalValue,
propertyName: 'method'
destinationSpecOrigins: [addedDestinationOperation.originalValue],
propertyName: 'method',
source: 'openapi-diff',
sourceSpecOrigins: []
});

@@ -24,12 +36,27 @@ });

action: 'remove',
destinationSpecOrigins: [],
propertyName: 'method',
sourceObject: removedSourceOperation.originalValue
source: 'openapi-diff',
sourceSpecOrigins: [removedSourceOperation.originalValue]
});
});
};
exports.findDifferencesInOperations = (sourceOperations, destinationOperations) => {
const findMatchingMethodsDifferences = (sourceOperations, destinationOperations) => __awaiter(this, void 0, void 0, function* () {
const whenDifferencesForAllMatchingMethods = get_common_keys_from_objects_1.getCommonKeysFromObjects(sourceOperations, destinationOperations)
.map((matchingMethod) => {
const matchingSourceOperation = sourceOperations[matchingMethod];
const matchingDestinationOperation = destinationOperations[matchingMethod];
return find_diffs_in_request_bodies_1.findDifferencesInRequestBodies(matchingSourceOperation.requestBody, matchingDestinationOperation.requestBody);
});
const differencesByMethod = yield Promise.all(whenDifferencesForAllMatchingMethods);
return differencesByMethod
.reduce((allDifferences, methodDifferences) => [...allDifferences, ...methodDifferences], []);
});
exports.findDifferencesInOperations = (sourceOperations, destinationOperations) => __awaiter(this, void 0, void 0, function* () {
const matchingMethodsDifferences = yield findMatchingMethodsDifferences(sourceOperations, destinationOperations);
return [
...findAddedMethodDifferences(sourceOperations, destinationOperations),
...findRemovedMethodDifferences(sourceOperations, destinationOperations)
...findRemovedMethodDifferences(sourceOperations, destinationOperations),
...matchingMethodsDifferences
];
};
});
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });

@@ -15,4 +23,6 @@ const get_added_keys_from_objects_1 = require("./common/get-added-keys-from-objects");

action: 'add',
destinationObject: addedDestinationPathItem.originalValue,
propertyName: 'path'
destinationSpecOrigins: [addedDestinationPathItem.originalValue],
propertyName: 'path',
source: 'openapi-diff',
sourceSpecOrigins: []
});

@@ -27,14 +37,16 @@ });

action: 'remove',
destinationSpecOrigins: [],
propertyName: 'path',
sourceObject: removedSourcePathItem.originalValue
source: 'openapi-diff',
sourceSpecOrigins: [removedSourcePathItem.originalValue]
});
});
};
const findMatchingPathDifferences = (sourcePathItems, destinationPathItems) => {
const findMatchingPathDifferences = (sourcePathItems, destinationPathItems) => __awaiter(this, void 0, void 0, function* () {
const matchingPaths = get_common_keys_from_objects_1.getCommonKeysFromObjects(sourcePathItems, destinationPathItems);
return matchingPaths.reduce((allDifferences, matchingPathItem) => {
const differencesInOperations = find_diffs_in_operations_1.findDifferencesInOperations(sourcePathItems[matchingPathItem].operations, destinationPathItems[matchingPathItem].operations);
return [...allDifferences, ...differencesInOperations];
}, []);
};
const whenFindDifferencesInAllOperations = matchingPaths.map((matchingPathItem) => find_diffs_in_operations_1.findDifferencesInOperations(sourcePathItems[matchingPathItem].operations, destinationPathItems[matchingPathItem].operations));
const differencesByOperation = yield Promise.all(whenFindDifferencesInAllOperations);
const flattenDifferences = differencesByOperation.reduce((allDifferences, operationDifferences) => [...allDifferences, ...operationDifferences], []);
return flattenDifferences;
});
const normalizePathItems = (parsedPathItems) => Object.keys(parsedPathItems).reduce((normalizedParsedPathItems, pathName) => {

@@ -46,3 +58,3 @@ const parsedPathItem = parsedPathItems[pathName];

}, {});
exports.findDiffsInPaths = (sourcePathItems, destinationPathItems) => {
exports.findDiffsInPaths = (sourcePathItems, destinationPathItems) => __awaiter(this, void 0, void 0, function* () {
const normalizedSourcePathItems = normalizePathItems(sourcePathItems);

@@ -52,4 +64,4 @@ const normalizedDestinationPathItems = normalizePathItems(destinationPathItems);

const removedPaths = findRemovedPathDifferences(normalizedSourcePathItems, normalizedDestinationPathItems);
const matchingPaths = findMatchingPathDifferences(normalizedSourcePathItems, normalizedDestinationPathItems);
const matchingPaths = yield findMatchingPathDifferences(normalizedSourcePathItems, normalizedDestinationPathItems);
return [...addedPaths, ...removedPaths, ...matchingPaths];
};
});

@@ -14,3 +14,9 @@ "use strict";

if (isAddition) {
return [create_difference_1.createDifference({ sourceObject, destinationObject, propertyName, action: 'add' })];
return [create_difference_1.createDifference({
action: 'add',
destinationSpecOrigins: [destinationObject],
propertyName,
source: 'openapi-diff',
sourceSpecOrigins: []
})];
}

@@ -22,3 +28,9 @@ return [];

if (isDeletion) {
return [create_difference_1.createDifference({ sourceObject, destinationObject, propertyName, action: 'remove' })];
return [create_difference_1.createDifference({
action: 'remove',
destinationSpecOrigins: [],
propertyName,
source: 'openapi-diff',
sourceSpecOrigins: [sourceObject]
})];
}

@@ -32,4 +44,16 @@ return [];

return [
create_difference_1.createDifference({ sourceObject, destinationObject, propertyName, action: 'add' }),
create_difference_1.createDifference({ sourceObject, destinationObject, propertyName, action: 'remove' })
create_difference_1.createDifference({
action: 'add',
destinationSpecOrigins: [destinationObject],
propertyName,
source: 'openapi-diff',
sourceSpecOrigins: [sourceObject]
}),
create_difference_1.createDifference({
action: 'remove',
destinationSpecOrigins: [destinationObject],
propertyName,
source: 'openapi-diff',
sourceSpecOrigins: [sourceObject]
})
];

@@ -36,0 +60,0 @@ }

@@ -8,2 +8,4 @@ "use strict";

'path.remove': 'breaking',
'request.body.scope.add': 'non-breaking',
'request.body.scope.remove': 'breaking',
'unclassified.add': 'unclassified',

@@ -10,0 +12,0 @@ 'unclassified.remove': 'unclassified'

@@ -20,4 +20,4 @@ "use strict";

? swagger2_parser_1.validateAndParseSwagger2Spec(spec, location)
: openapi3_parser_1.parseOpenApi3Spec(spec);
: openapi3_parser_1.validateAndParseOpenApi3Spec(spec, location);
})
};

@@ -6,9 +6,12 @@ "use strict";

};
exports.parseXPropertiesInObject = (object) => {
exports.parseXPropertiesInObject = (object, pathBuilder) => {
return Object.keys(object)
.filter(isXProperty)
.reduce((xProperties, currentKey) => {
xProperties[currentKey] = { originalPath: [currentKey], value: object[currentKey] };
xProperties[currentKey] = {
originalPath: pathBuilder.withChild(currentKey).build(),
value: object[currentKey]
};
return xProperties;
}, {});
};
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const SwaggerParser = require("swagger-parser");
const open_api_diff_error_impl_1 = require("../../common/open-api-diff-error-impl");
const parse_x_properties_1 = require("./common/parse-x-properties");
const path_builder_1 = require("./common/path-builder");
const typeCheckedOpenApi3Methods = {

@@ -15,11 +26,37 @@ delete: undefined,

const isOpenApi3Method = (propertyName) => Object.keys(typeCheckedOpenApi3Methods).indexOf(propertyName) >= 0;
const parseOperations = (pathItemObject, pathItemOriginalPath) => {
const isRequestBodyObject = (requestBody) => !!requestBody && !!requestBody.content;
const parsedRequestBodyJsonSchema = (requestBodyObject, pathBuilder) => {
if (isRequestBodyObject(requestBodyObject) && requestBodyObject.content['application/json']) {
return {
originalPath: pathBuilder.withChild('content').withChild('application/json').withChild('schema').build(),
value: requestBodyObject.content['application/json'].schema
};
}
return undefined;
};
const parsedRequestBody = (requestBody, pathBuilder) => {
const originalPath = pathBuilder.withChild('requestBody');
return {
jsonSchema: parsedRequestBodyJsonSchema(requestBody, originalPath),
originalValue: {
originalPath: originalPath.build(),
value: requestBody
}
};
};
const parseOperations = (pathItemObject, pathBuilder) => {
return Object.keys(pathItemObject)
.filter(isOpenApi3Method)
.reduce((accumulator, method) => {
const operationObject = pathItemObject[method];
const originalPath = pathBuilder.withChild(method);
const requestBody = operationObject
? parsedRequestBody(operationObject.requestBody, originalPath)
: parsedRequestBody(undefined, originalPath);
accumulator[method] = {
originalValue: {
originalPath: [...pathItemOriginalPath, method],
value: pathItemObject[method]
}
originalPath: originalPath.build(),
value: operationObject
},
requestBody
};

@@ -29,9 +66,9 @@ return accumulator;

};
const parsePaths = (paths) => Object.keys(paths).reduce((accumulator, pathName) => {
const parsePaths = (paths, pathBuilder) => Object.keys(paths).reduce((accumulator, pathName) => {
const pathItemObject = paths[pathName];
const originalPath = ['paths', pathName];
const originalPath = pathBuilder.withChild(pathName);
accumulator[pathName] = {
operations: parseOperations(pathItemObject, originalPath),
originalValue: {
originalPath,
originalPath: originalPath.build(),
value: pathItemObject

@@ -43,10 +80,24 @@ },

}, {});
const validateOpenApi3 = (openApi3Spec) => openApi3Spec;
exports.parseOpenApi3Spec = (openApi3Spec) => {
const validatedOpenApi3Spec = validateOpenApi3(openApi3Spec);
const validateOpenApi3 = (openApi3Spec, location) => __awaiter(this, void 0, void 0, function* () {
try {
const options = {
dereference: { circular: false }
};
return yield SwaggerParser.validate(openApi3Spec, options);
}
catch (error) {
throw new open_api_diff_error_impl_1.OpenApiDiffErrorImpl('OPENAPI_DIFF_VALIDATE_OPENAPI_3_ERROR', `Validation errors in ${location}`, error);
}
});
const parseOpenApi3Spec = (spec) => {
const pathBuilder = path_builder_1.PathBuilder.createRootPathBuilder();
return {
format: 'openapi3',
paths: parsePaths(validatedOpenApi3Spec.paths),
xProperties: parse_x_properties_1.parseXPropertiesInObject(validatedOpenApi3Spec)
paths: parsePaths(spec.paths, pathBuilder.withChild('paths')),
xProperties: parse_x_properties_1.parseXPropertiesInObject(spec, pathBuilder)
};
};
exports.validateAndParseOpenApi3Spec = (openApi3Spec, location) => __awaiter(this, void 0, void 0, function* () {
const validatedOpenApi3Spec = yield validateOpenApi3(openApi3Spec, location);
return parseOpenApi3Spec(validatedOpenApi3Spec);
});

@@ -14,2 +14,3 @@ "use strict";

const parse_x_properties_1 = require("./common/parse-x-properties");
const path_builder_1 = require("./common/path-builder");
const typeCheckedSwagger2Methods = {

@@ -25,11 +26,48 @@ delete: undefined,

const isSwagger2Method = (propertyName) => Object.keys(typeCheckedSwagger2Methods).indexOf(propertyName) >= 0;
const parseOperations = (pathItemObject, pathItemOriginalPath) => {
const findBodyParameterAndIndex = (parameters) => {
let bodyParameterIndex = -1;
const bodyParameter = parameters.find((parameter, index) => {
const isBody = parameter.in === 'body';
bodyParameterIndex = index;
return isBody;
});
return { bodyParameter, index: bodyParameterIndex };
};
function toParsedRequestBody(bodyParameter, pathBuilder) {
return {
jsonSchema: {
originalPath: pathBuilder.withChild('schema').build(),
value: bodyParameter.schema
},
originalValue: {
originalPath: pathBuilder.build(),
value: bodyParameter
}
};
}
const parseBodyParameter = (parameters, pathBuilder) => {
const { bodyParameter, index } = findBodyParameterAndIndex(parameters);
if (bodyParameter) {
return toParsedRequestBody(bodyParameter, pathBuilder.withChild(`${index}`));
}
return {
originalValue: {
originalPath: pathBuilder.build(),
value: undefined
}
};
};
const parseOperations = (pathItemObject, pathBuilder) => {
return Object.keys(pathItemObject)
.filter(isSwagger2Method)
.reduce((accumulator, method) => {
const operation = pathItemObject[method];
const parameters = operation ? operation.parameters || [] : [];
const operationPath = pathBuilder.withChild(method);
accumulator[method] = {
originalValue: {
originalPath: [...pathItemOriginalPath, method],
originalPath: operationPath.build(),
value: pathItemObject[method]
}
},
requestBody: parseBodyParameter(parameters, operationPath.withChild('parameters'))
};

@@ -39,9 +77,9 @@ return accumulator;

};
const parsePaths = (paths) => Object.keys(paths).reduce((accumulator, pathName) => {
const parsePaths = (paths, pathBuilder) => Object.keys(paths).reduce((accumulator, pathName) => {
const pathItemObject = paths[pathName];
const originalPath = ['paths', pathName];
const originalPath = pathBuilder.withChild(pathName);
accumulator[pathName] = {
operations: parseOperations(pathItemObject, originalPath),
originalValue: {
originalPath,
originalPath: originalPath.build(),
value: paths[pathName]

@@ -54,6 +92,7 @@ },

const parseSwagger2Spec = (swagger2Spec) => {
const pathBuilder = path_builder_1.PathBuilder.createRootPathBuilder();
return {
format: 'swagger2',
paths: parsePaths(swagger2Spec.paths),
xProperties: parse_x_properties_1.parseXPropertiesInObject(swagger2Spec)
paths: parsePaths(swagger2Spec.paths, pathBuilder.withChild('paths')),
xProperties: parse_x_properties_1.parseXPropertiesInObject(swagger2Spec, pathBuilder)
};

@@ -63,3 +102,4 @@ };

try {
return yield SwaggerParser.validate(document);
const options = { dereference: { circular: false } };
return yield SwaggerParser.validate(document, options);
}

@@ -66,0 +106,0 @@ catch (error) {

{
"name": "openapi-diff",
"version": "0.11.0",
"version": "0.12.0",
"description": "A CLI tool to identify differences between Swagger/OpenAPI specs.",

@@ -64,2 +64,3 @@ "bin": {

"js-yaml": "^3.10.0",
"json-schema-diff": "^0.8.1",
"lodash": "^4.17.4",

@@ -66,0 +67,0 @@ "openapi3-ts": "^0.6.2",

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc