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

fury-adapter-oas3-parser

Package Overview
Dependencies
Maintainers
1
Versions
23
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

fury-adapter-oas3-parser - npm Package Compare versions

Comparing version 0.4.0 to 0.4.1

lib/parser/oas/parseParameters.js

22

CHANGELOG.md
# Fury OAS3 Parser Changelog
## 0.4.1 (28-01-19)
### Enhancements
- Path and query parameters are not supported in 'Operation Object'
### Bug Fixes
- Unsupported properties in 'Parameter Object' will now emit an unsupported
warning, previously using unsupported properties was emitting a warning that
the properties were invalid.
- The parser will no longer error out for unsupported parameter 'in' values,
instead an unsupported warning will be emitted.
- Parameter names containing unreserved URI Template characters (`-`, `.`, `_`,
and `~`) are now supported.
- Referencing (`$ref`) a parameter that couldn't be parsed (due to the
parameter failing validation) will no longer cause a cryptic error that the
referenced object was not defined.
## 0.4.0 (25-01-19)

@@ -4,0 +26,0 @@

26

lib/parser/annotations.js
const R = require('ramda');
const { isMember, isObject } = require('../predicates');

@@ -44,26 +43,2 @@ function createAnnotation(annotationClass, namespace, message, element) {

function validateObjectContainsRequiredKeys(namespace, path, requiredKeys, object) {
if (!isObject(object)) {
return new namespace.elements.ParseResult([object]);
}
// FIXME Can be simplified once https://github.com/refractproject/minim/issues/201 is completed
const hasMember = (key) => {
const findKey = R.allPass([isMember, member => member.key.toValue() === key]);
const matchingMembers = object.content.filter(findKey);
return matchingMembers.length > 0;
};
const missingKeys = R.reject(hasMember, requiredKeys);
const errorFromKey = key => createError(namespace, `'${path}' is missing required property '${key}'`, object);
if (missingKeys.length > 0) {
return new namespace.elements.ParseResult(
R.map(errorFromKey, missingKeys)
);
}
return new namespace.elements.ParseResult([object]);
}
module.exports = {

@@ -79,3 +54,2 @@ createError,

createMemberValueNotBooleanError: R.curry(createMemberValueNotBooleanError),
validateObjectContainsRequiredKeys: R.curry(validateObjectContainsRequiredKeys),
};

100

lib/parser/oas/parseComponentsObject.js

@@ -19,3 +19,50 @@ const R = require('ramda');

const valueIsObject = R.compose(isObject, getValue);
/**
* Is the given parse result empty (i.e, contains no elements other than annotations)
*
* @param parseResult {ParseResult}
* @returns boolean
*/
const isParseResultEmpty = parseResult => R.reject(isAnnotation, parseResult).isEmpty;
/**
* Parse a component member
*
* This function takes another parser that is capable of parsing the specific
* component type (schema parser, parameter parser etc) and parses a
* component using the given parser. It will return a parse result of member.
*
* In the cases that the given member cannot be parsed, it will result in
* returning a member element to represent the key without a value.
*
* @param context
* @param parser {function}
* @param member {Member} - Member element to represent the component,
* member key contains the reusable component name, value represents
* the reusable component
*
* @returns ParseResult
*/
const parseComponentMember = R.curry((context, parser, member) => {
// Create a Member Element with `member.key` as the key
const Member = R.constructN(2, context.namespace.elements.Member)(member.key);
const parseResult = pipeParseResult(context.namespace,
parser(context),
Member)(member.value);
if (isParseResultEmpty(parseResult)) {
// parse result does not contain a member, that's because parsing a
// component has failed. We want to store the member without value in
// this case so that we can correctly know if a component with the name
// existed during dereferencing.
parseResult.unshift(Member(undefined));
}
return parseResult;
});
/**
* Parse Components Object

@@ -32,32 +79,35 @@ *

const validateIsObject = key => R.unless(isObject,
createWarning(namespace, `'${name}' '${key}' is not an object`));
const createMemberValueNotObjectWarning = member => createWarning(namespace,
`'${name}' '${member.key.toValue()}' is not an object`, member.value);
const validateIsObject = R.unless(valueIsObject, createMemberValueNotObjectWarning);
const parseSchemaMember = member => pipeParseResult(namespace,
R.compose(parseSchemaObject(context), getValue),
(dataStructure) => {
// eslint-disable-next-line no-param-reassign
dataStructure.content.id = member.key.clone();
return dataStructure;
})(member);
/**
* Parses a member representing a component object (such as an object
* representing the parameter components)
*
* @param parser {function}
* @param member {Member}
*
* @returns ParseResult
*/
const parseComponentObjectMember = (parser) => {
const parseMember = parseComponentMember(context, parser);
const parseSchemasObject = pipeParseResult(namespace,
validateIsObject('schemas'),
parseObject(context, name, parseSchemaMember));
const parseParametersObjectMember = (member) => {
// Create a Member Element with `member.key` as the key
const Member = R.constructN(2, namespace.elements.Member)(member.key);
const parseResult = parseParameterObject(context, member.value);
// Wrap non-annotation elements in member element
return R.map(R.unless(isAnnotation, Member), parseResult);
return pipeParseResult(context.namespace,
validateIsObject,
R.compose(parseObject(context, name, parseMember), getValue));
};
const parseParametersObject = pipeParseResult(namespace,
validateIsObject('parameters'),
parseObject(context, name, parseParametersObjectMember));
// eslint-disable-next-line no-param-reassign
const setDataStructureId = (dataStructure, key) => { dataStructure.content.id = key.clone(); };
const parseSchemas = pipeParseResult(namespace,
parseComponentObjectMember(parseSchemaObject),
(object) => {
object.forEach(setDataStructureId);
return object;
});
const parseMember = R.cond([
[hasKey('schemas'), R.compose(parseSchemasObject, getValue)],
[hasKey('parameters'), R.compose(parseParametersObject, getValue)],
[hasKey('schemas'), parseSchemas],
[hasKey('parameters'), parseComponentObjectMember(parseParameterObject)],
[isUnsupportedKey, createUnsupportedMemberWarning(namespace, name)],

@@ -72,3 +122,3 @@

return parseObject(context, name, parseMember, element);
return parseObject(context, name, parseMember)(element);
}

@@ -75,0 +125,0 @@

@@ -6,3 +6,2 @@ const R = require('ramda');

createInvalidMemberWarning,
validateObjectContainsRequiredKeys,
} = require('../annotations');

@@ -52,4 +51,3 @@ const {

R.unless(isObject, createError(namespace, `'${name}' is not an object`)),
validateObjectContainsRequiredKeys(namespace, name, requiredKeys),
parseObject(context, name, parseMember),
parseObject(context, name, parseMember, requiredKeys),
(info) => {

@@ -56,0 +54,0 @@ const api = new namespace.elements.Category();

@@ -0,1 +1,3 @@

/* eslint-disable no-underscore-dangle */
const R = require('ramda');

@@ -9,3 +11,2 @@

createInvalidMemberWarning,
validateObjectContainsRequiredKeys,
} = require('../annotations');

@@ -31,2 +32,41 @@ const pipeParseResult = require('../../pipeParseResult');

function filterSourceMaps(result) {
if (isAnnotation(result)) {
return result;
}
if (result) {
if (!result.element) {
return result;
}
if (result._attributes) {
result.attributes.remove('sourceMap');
result.attributes.forEach((value, key, member) => {
filterSourceMaps(member);
});
}
if (result._meta) {
result.meta.forEach((value, key, member) => {
filterSourceMaps(member);
});
}
if (result.content) {
if (Array.isArray(result.content)) {
result.content.forEach((value) => {
filterSourceMaps(value);
});
}
if (result.content.key) {
filterSourceMaps(result.content.key);
if (result.content.value) {
filterSourceMaps(result.content.value);
}
}
if (result.content.element) {
filterSourceMaps(result.content);
}
}
}
return result;
}
function parseOASObject(context, object) {

@@ -68,4 +108,3 @@ const { namespace } = context;

const parseOASObject = pipeParseResult(namespace,
validateObjectContainsRequiredKeys(namespace, name, requiredKeys),
parseObject(context, name, parseMember),
parseObject(context, name, parseMember, requiredKeys),
(object) => {

@@ -94,5 +133,6 @@ const api = object.get('info');

return parseOASObject(object);
return context.options.generateSourceMap ? parseOASObject(object) : filterSourceMaps(parseOASObject(object));
}
module.exports = R.curry(parseOASObject);

@@ -6,3 +6,2 @@ const R = require('ramda');

createInvalidMemberWarning,
validateObjectContainsRequiredKeys,
createIdentifierNotUniqueWarning,

@@ -15,2 +14,3 @@ } = require('../annotations');

const parseResponsesObject = require('./parseResponsesObject');
const parseParameterObjects = require('./parseParameterObjects');

@@ -20,4 +20,3 @@ const name = 'Operation Object';

const unsupportedKeys = [
'tags', 'externalDocs', 'parameters', 'requestBody',
'callbacks', 'deprecated', 'security',
'tags', 'externalDocs', 'requestBody', 'callbacks', 'deprecated', 'security',
];

@@ -39,2 +38,25 @@ const isUnsupportedKey = R.anyPass(R.map(hasKey, unsupportedKeys));

function hrefVariablesFromParameters(namespace, parameters) {
const path = parameters.get('path')
? parameters.get('path')
: new namespace.elements.HrefVariables();
const query = parameters.get('query')
? parameters.get('query')
: new namespace.elements.HrefVariables();
if (!path.isEmpty || !query.isEmpty) {
return path.concat(query);
}
return undefined;
}
function hrefFromParameters(path, queryParameters) {
const href = path.clone();
const queryString = queryParameters.keys().join(',');
href.content += `{?${queryString}}`;
return href;
}
/**

@@ -44,2 +66,3 @@ * Parse Operation Object

* @param namespace {Namespace}
* @param path {StringElement}
* @param element {Element}

@@ -50,3 +73,3 @@ * @returns ParseResult<Transition>

*/
function parseOperationObject(context, member) {
function parseOperationObject(context, path, member) {
const { namespace } = context;

@@ -69,2 +92,3 @@

[hasKey('responses'), R.compose(parseResponsesObject(context), getValue)],
[hasKey('parameters'), R.compose(parseParameterObjects(context, name), getValue)],

@@ -81,4 +105,3 @@ [isUnsupportedKey, createUnsupportedMemberWarning(namespace, name)],

const parseOperation = pipeParseResult(namespace,
validateObjectContainsRequiredKeys(namespace, name, requiredKeys),
parseObject(context, name, parseMember),
parseObject(context, name, parseMember, requiredKeys),
(operation) => {

@@ -98,2 +121,12 @@ const transition = new namespace.elements.Transition();

const parameters = operation.get('parameters');
if (parameters) {
const queryParameters = parameters.get('query');
if (queryParameters) {
transition.href = hrefFromParameters(path, queryParameters);
}
transition.hrefVariables = hrefVariablesFromParameters(namespace, parameters);
}
const transactions = createTransactions(namespace, member, operation);

@@ -100,0 +133,0 @@ transition.content = transition.content.concat(transactions);

const R = require('ramda');
const { isExtension, hasKey } = require('../../predicates');
const { isExtension, hasKey, getValue } = require('../../predicates');
const {
createError,
createWarning,
createUnsupportedMemberWarning,
createInvalidMemberWarning,
validateObjectContainsRequiredKeys,
} = require('../annotations');

@@ -17,4 +17,4 @@ const pipeParseResult = require('../../pipeParseResult');

const unsupportedKeys = [
// FIXME Only contains "fixed" fields from spec
'deprecated', 'allowEmptyValue',
'deprecated', 'allowEmptyValue', 'style', 'explode', 'allowReserved',
'schema', 'example', 'examples', 'content',
];

@@ -31,5 +31,5 @@ const isUnsupportedKey = R.anyPass(R.map(hasKey, unsupportedKeys));

const unreservedCharacterRegex = /^[A-z0-9\\.\\_\\~\\-]+$/;
function nameContainsReservedCharacter(member) {
return !/^[A-z0-9]+$/.test(member.value.toValue());
return !unreservedCharacterRegex.test(member.value.toValue());
}

@@ -49,9 +49,12 @@

const validateIn = R.unless(isValidInValue, createError(namespace,
"'Parameter Object' 'in' must be either 'query, 'header', 'path' or 'cookie'"));
const createInvalidInError = R.compose(
createError(namespace, `'${name}' 'in' must be either 'query, 'header', 'path' or 'cookie'`),
getValue
);
const validateIn = R.unless(isValidInValue, createInvalidInError);
// FIXME: The following should not be an error
const isSupportedIn = R.anyPass([hasValue('path'), hasValue('query')]);
const ensureSupportedIn = R.unless(isSupportedIn, createError(namespace,
"Only 'in' values of 'path' and 'query' are supported at the moment"));
const createUnsupportedInWarning = member => createWarning(namespace,
`'${name}' 'in' ${member.value.toValue()} is unsupported`, member.value);
const ensureSupportedIn = R.unless(isSupportedIn, createUnsupportedInWarning);

@@ -63,4 +66,6 @@ const parseIn = pipeParseResult(namespace,

const createUnsupportedNameError = createError(namespace,
`'${name}' 'name' contains unsupported characters. Only alphanumeric characters are currently supported`);
const createUnsupportedNameError = R.compose(
createError(namespace, `'${name}' 'name' contains unsupported characters. Only alphanumeric characters are currently supported`),
getValue
);
const validateName = R.when(nameContainsReservedCharacter, createUnsupportedNameError);

@@ -87,4 +92,3 @@ const parseName = pipeParseResult(namespace,

const parseParameter = pipeParseResult(namespace,
validateObjectContainsRequiredKeys(namespace, name, requiredKeys),
parseObject(context, name, parseMember),
parseObject(context, name, parseMember, requiredKeys),
(parameter) => {

@@ -91,0 +95,0 @@ const member = new namespace.elements.Member(parameter.get('name'));

@@ -38,8 +38,2 @@ const R = require('ramda');

function createErrorForMissingPathParameter(namespace, path, variable) {
// FIXME: This shouldn't be an error
const message = `Path '${path.toValue()}' contains variable '${variable}' which is not declared in the parameters section of the '${name}'`;
return createError(namespace, message, path);
}
function createErrorForMissingPathVariable(namespace, path, variable) {

@@ -50,32 +44,2 @@ return createError(namespace, `Path '${path.toValue()}' is missing path variable '${variable}'. Add '{${variable}}' to the path`, path);

/**
* Validates that there is a href variable for each path variable in the given path
* @param namespace
* @param path {StringElement}
* @param pathItem {ObjectElement}
* @retuns ParseResult<ObjectElement>
*/
function validatePathForMissingHrefVariables(namespace, path, pathItem) {
const pathVariables = extractPathVariables(path.toValue());
const parameters = pathItem.get('parameters')
? pathItem.get('parameters')
: new namespace.elements.Object();
const hrefVariables = parameters.get('path')
? parameters.get('path')
: new namespace.elements.HrefVariables();
const missingParameters = hrefVariables
? pathVariables.filter(name => !hrefVariables.getMember(name))
: pathVariables;
if (missingParameters.length > 0) {
const toError = R.curry(createErrorForMissingPathParameter)(namespace, path);
return new namespace.elements.ParseResult(missingParameters.map(toError));
}
return new namespace.elements.ParseResult([pathItem]);
}
/**
* Validates that each href variable is found within the given path

@@ -163,3 +127,3 @@ * @param namespace

[hasKey('parameters'), R.curry(parseParameters)(context, member.key)],
[isHttpMethodKey, parseOperationObject(context)],
[isHttpMethodKey, parseOperationObject(context, member.key)],

@@ -180,3 +144,2 @@ // FIXME Parse $ref

parseObject(context, name, parseMember),
R.curry(validatePathForMissingHrefVariables)(namespace, member.key),
(pathItem) => {

@@ -183,0 +146,0 @@ const resource = new namespace.elements.Resource();

@@ -6,3 +6,2 @@ const R = require('ramda');

createInvalidMemberWarning,
validateObjectContainsRequiredKeys,
} = require('../annotations');

@@ -23,3 +22,5 @@ const { isExtension, hasKey } = require('../../predicates');

* @param element {Element}
* @returns ParseResult
* @returns ParseResult - A parse result containing error if the referenced
* object was not found, the referenced element, or if the referenced
* element was not successfully parsed, an empty parse result.
*

@@ -56,3 +57,3 @@ * @see https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md#referenceObject

const element = component.get(referenceParts[3]);
const element = component.getMember(referenceParts[3]);
if (!element) {

@@ -62,3 +63,7 @@ return createError(namespace, `'${ref.toValue()}' is not defined`, ref);

return element;
if (element.value) {
return element.value;
}
return new namespace.elements.ParseResult([]);
};

@@ -73,4 +78,3 @@

const parseReference = pipeParseResult(namespace,
validateObjectContainsRequiredKeys(namespace, name, requiredKeys),
parseObject(context, name, parseMember),
parseObject(context, name, parseMember, requiredKeys),
object => object.get('$ref'),

@@ -77,0 +81,0 @@ parseRef);

@@ -10,3 +10,2 @@ const R = require('ramda');

createInvalidMemberWarning,
validateObjectContainsRequiredKeys,
} = require('../annotations');

@@ -80,4 +79,3 @@ const parseObject = require('../parseObject');

const parseResponse = pipeParseResult(namespace,
validateObjectContainsRequiredKeys(namespace, name, requiredKeys),
parseObject(context, name, parseMember),
parseObject(context, name, parseMember, requiredKeys),
(responseObject) => {

@@ -84,0 +82,0 @@ // Try to fecth responses from the media type parsing

@@ -33,5 +33,5 @@ const R = require('ramda');

return new namespace.elements.ParseResult([]);
return new namespace.elements.ParseResult([openapi]);
}
module.exports = R.curry(parseOpenAPI);

@@ -5,3 +5,4 @@ const R = require('ramda');

} = require('../predicates');
const { createWarning } = require('./annotations');
const { createError, createWarning } = require('./annotations');
const pipeParseResult = require('../pipeParseResult');

@@ -32,2 +33,32 @@ /*

// FIXME Can be simplified once https://github.com/refractproject/minim/issues/201 is completed
const hasMember = R.curry((object, key) => {
const findKey = R.allPass([isMember, member => member.key.toValue() === key]);
const matchingMembers = object.content.filter(findKey);
return matchingMembers.length > 0;
});
const validateObjectContainsRequiredKeys = R.curry((namespace, path, requiredKeys, object) => {
const missingKeys = R.reject(hasMember(object), requiredKeys);
const errorFromKey = key => createError(namespace, `'${path}' is missing required property '${key}'`, object);
if (missingKeys.length > 0) {
return new namespace.elements.ParseResult(
R.map(errorFromKey, missingKeys)
);
}
return new namespace.elements.ParseResult([object]);
});
const validateObjectContainsRequiredKeysNoError = R.curry((namespace, requiredKeys, object) => {
const missingKeys = R.reject(hasMember(object), requiredKeys);
if (missingKeys.length > 0) {
return new namespace.elements.ParseResult();
}
return new namespace.elements.ParseResult([object]);
});
/**

@@ -64,3 +95,5 @@ * A callback for transforming a member element

* @param namespace
* @param name {string} - The human readable name of the element. Used for annotation messages.
* @param transform {transformMember} - The callback to transform a member
* @param requiredKeys {string[]} - The callback to transform a member
* @param object {ObjectElement} - The object containing members to transform

@@ -70,11 +103,5 @@ *

*/
function parseObject(context, name, parseMember, object) {
function parseObject(context, name, parseMember, requiredKeys) {
const { namespace } = context;
if (!isObject(object)) {
return new namespace.elements.ParseResult([
createWarning(namespace, `'${name}' is not an object`, object),
]);
}
// Create a member from a key and value

@@ -119,6 +146,10 @@ const createMember = R.constructN(2, namespace.elements.Member);

return validateMembers(object);
return pipeParseResult(namespace,
R.unless(isObject, createWarning(namespace, `'${name}' is not an object`)),
validateObjectContainsRequiredKeys(namespace, name, requiredKeys || []),
validateMembers,
validateObjectContainsRequiredKeysNoError(namespace, requiredKeys || []));
}
module.exports = R.curry(parseObject);
module.exports = parseObject;
{
"name": "fury-adapter-oas3-parser",
"version": "0.4.0",
"version": "0.4.1",
"description": "Open API Specification 3 API Elements Parser",

@@ -5,0 +5,0 @@ "author": "Apiary.io <support@apiary.io>",

@@ -70,3 +70,3 @@ # OpenAPI Support

| operationId | ✓ |
| parameters | [✕](https://github.com/apiaryio/api-elements.js/issues/65) |
| parameters | [~](https://github.com/apiaryio/api-elements.js/issues/65) |
| requestBody | ✕ |

@@ -73,0 +73,0 @@ | responses | [~](#responses-object) |

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