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

@seriousme/openapi-schema-validator

Package Overview
Dependencies
Maintainers
1
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@seriousme/openapi-schema-validator - npm Package Compare versions

Comparing version 1.0.0 to 1.0.1

LICENSE.txt

8

CHANGELOG.md

@@ -6,3 +6,9 @@ # Changelog

## [v1.0.0] 30-05-2021
## [v1.0.1] 09-05-2021
### Changed
- Updated dependencies
- Reworked README
- Applied LGTM suggestions
## [v1.0.0] 09-05-2021
Initial version

146

index.js

@@ -1,94 +0,90 @@

import Ajv from 'ajv';
import Ajv2020 from 'ajv/dist/2020.js';
import { createRequire } from 'module';
import JSYaml from 'js-yaml';
import { readFile, stat } from 'fs/promises'
import { resolve } from './resolve.js'
import Ajv from "ajv";
import Ajv2020 from "ajv/dist/2020.js";
import { createRequire } from "module";
import JSYaml from "js-yaml";
import { readFile } from "fs/promises";
import { resolve } from "./resolve.js";
const importJSON = createRequire(import.meta.url);
const openApiVersions = new Set(['2.0', '3.0', '3.1']);
const openApiVersions = new Set(["2.0", "3.0", "3.1"]);
const ajvVersions = {
"http://json-schema.org/draft-07/schema": Ajv,
"https://json-schema.org/draft/2020-12/schema": Ajv2020
}
"http://json-schema.org/draft-07/schema": Ajv,
"https://json-schema.org/draft/2020-12/schema": Ajv2020,
};
const openApiSchemas = {};
function getOpenApiVersion(specification) {
for (const version of openApiVersions) {
const prop = specification[(version == "2.0") ? 'swagger' : 'openapi'];
if (typeof prop === "string" && prop.startsWith(version)) {
return version
}
for (const version of openApiVersions) {
const prop = specification[version == "2.0" ? "swagger" : "openapi"];
if (typeof prop === "string" && prop.startsWith(version)) {
return version;
}
return undefined
}
return undefined;
}
async function getSpecFromData(data) {
if (typeof data === 'object') {
return data;
if (typeof data === "object") {
return data;
}
if (typeof data === "string") {
if (data.match(/\n/)) {
try {
return JSYaml.load(data);
} catch (_) {
return undefined;
}
}
if (typeof data === 'string') {
if (data.match(/\n/)) {
try {
return JSYaml.load(data);
}
catch (_) {
return undefined
};
}
try {
const fileData = await readFile(data, "utf-8");
return JSYaml.load(fileData);
} catch (_) {
return undefined;
}
try {
const fileData = await readFile(data, "utf-8");
return JSYaml.load(fileData);
} catch (_) {
return undefined;
}
}
}
export default class Validator {
constructor(ajvOptions = { strict: false, validateFormats: false }) {
this.ajvOptions = ajvOptions;
return this;
}
constructor(ajvOptions = { strict: false, validateFormats: false }) {
this.ajvOptions = ajvOptions;
return this;
}
resolveRefs(opts={}){
return resolve(this.specification || opts.specification)
}
resolveRefs(opts = {}) {
return resolve(this.specification || opts.specification);
}
static supportedVersions = openApiVersions;
static supportedVersions = openApiVersions;
async validate(data) {
const specification = await getSpecFromData(data);
this.specification = specification;
if (specification === undefined || specification === null) {
return {
valid: false,
errors: "Cannot find JSON, YAML or filename in data"
};
}
const version = getOpenApiVersion(specification);
this.version = version;
if (!version) {
return {
valid: false,
errors: "Cannot find supported swagger/openapi version in specification, version must be a string."
};
}
const schema = await importJSON(`./schemas/v${version}/schema.json`);
const schemaVersion = schema.$schema;
const AjvClass = ajvVersions[schemaVersion];
const ajv = new AjvClass(this.ajvOptions);
const validate = ajv.compile(schema);
const result = {
valid: validate(specification)
};
if (validate.errors) {
result.errors = validate.errors
}
return result
async validate(data) {
const specification = await getSpecFromData(data);
this.specification = specification;
if (specification === undefined || specification === null) {
return {
valid: false,
errors: "Cannot find JSON, YAML or filename in data",
};
}
}
const version = getOpenApiVersion(specification);
this.version = version;
if (!version) {
return {
valid: false,
errors:
"Cannot find supported swagger/openapi version in specification, version must be a string.",
};
}
const schema = await importJSON(`./schemas/v${version}/schema.json`);
const schemaVersion = schema.$schema;
const AjvClass = ajvVersions[schemaVersion];
const ajv = new AjvClass(this.ajvOptions);
const validate = ajv.compile(schema);
const result = {
valid: validate(specification),
};
if (validate.errors) {
result.errors = validate.errors;
}
return result;
}
}
{
"name": "@seriousme/openapi-schema-validator",
"version": "1.0.0",
"version": "1.0.1",
"description": "Validate OpenApi specifications against their JSON schema",

@@ -8,4 +8,3 @@ "main": "index.js",

"dependencies": {
"ajv": "^8.2.0",
"ajv-formats": "^2.0.2",
"ajv": "^8.3.0",
"js-yaml": "^4.1.0"

@@ -23,3 +22,3 @@ },

"c8": "^7.7.2",
"tap": "^15.0.6"
"tap": "^15.0.9"
},

@@ -26,0 +25,0 @@ "directories": {

@@ -5,3 +5,3 @@ # OpenAPI schema validator

[![Language grade: JavaScript](https://img.shields.io/lgtm/grade/javascript/g/seriousme/openapi-schema-validator.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/seriousme/openapi-schema-validator/context:javascript)
[![NPM version](https://img.shields.io/npm/v/openapi-schema-validator.svg)](https://www.npmjs.com/package/@seriousme/openapi-schema-validator)
[![NPM version](https://img.shields.io/npm/v/@seriousme/openapi-schema-validator.svg)](https://www.npmjs.com/package/seriousme/openapi-schema-validator)
![npm](https://img.shields.io/npm/dm/@seriousme/openapi-schema-validator)

@@ -46,6 +46,19 @@

### API
- `<instance>.validate(specification)`
- [`new Validator(ajvOptions)`](#newValidator)
- [`<instance>.validate(specification)`](#validate)
- [`<instance>.version`](#version)
- [`<instance>.resolveRefs(options)`](#resolveRefs)
- [`Validator.supportedVersions`](#supportedVersions)
Thsi function tries to validata a specification against the OpenApi schemas. `specification` can be one of:
<a name="newValidator"></a>
### `new Validator(ajvOptions)`
The constructor returns an instance of `Validator`.
By passing an ajv options object it is possible to influence the behavior of the [AJV schema validator](https://ajv.js.org/).
<a name="validate"></a>
### `<instance>.validate(specification)`
This function tries to validata a specification against the OpenApi schemas. `specification` can be one of:
- a JSON object

@@ -65,3 +78,4 @@ - a JSON object encoded as string

- `<instance>.version`
<a name="version"></a>
### `<instance>.version`

@@ -71,3 +85,4 @@ If validation is succesfull this will return the openApi version found e.g. ("2.0","3.0","3.1).

- `<instance>.resolveRefs(options)`
<a name="resolveRefs"></a>
### `<instance>.resolveRefs(options)`

@@ -79,3 +94,4 @@ This function tries to resolve all internal references. External references are *not* automatically resolved so you need to inline them yourself if required. By default it will use the last specification passed to `<instance>.validate()`

- `Validator.supportedVersions`
<a name="supportedVersions"></a>
### `Validator.supportedVersions`

@@ -86,2 +102,2 @@ This static property returns the OpenApi versions supported by this package as a `Set`. If present, the result of `<instance>.version` is a member of this `Set`.

# License
Licensed under the [MIT license](https://opensource.org/licenses/MIT)
Licensed under the [MIT license](LICENSE.txt)

@@ -1,116 +0,121 @@

import { load, dump } from "js-yaml";
function escapeJsonPointer(str) {
return str.replace(/~/g, "~0").replace(/\//g, "~1");
return str.replace(/~/g, "~0").replace(/\//g, "~1");
}
function unescapeJsonPointer(str) {
return str.replace(/~1/g, "/").replace(/~0/g, "~");
return str.replace(/~1/g, "/").replace(/~0/g, "~");
}
const isObject = obj => (typeof obj === "object" && obj !== null);
const pointerWords = new Set(["$ref", "$id", "$anchor", "$dynamicRef", "$dynamicAnchor", "$schema"]);
const isObject = (obj) => typeof obj === "object" && obj !== null;
const pointerWords = new Set([
"$ref",
"$id",
"$anchor",
"$dynamicRef",
"$dynamicAnchor",
"$schema",
]);
const filtered = raw => Object.fromEntries(
Object.entries(raw).filter(
([key, _]) => (!pointerWords.has(key))
)
);
const filtered = (raw) =>
Object.fromEntries(
Object.entries(raw).filter(([key, _]) => !pointerWords.has(key))
);
function resolveUri(uri, anchors) {
const [prefix, path] = uri.split("#", 2)
const err = new Error(`Can't resolve ${uri}`)
if (path[0] !== "/") {
if (anchors[uri]) {
return anchors[uri];
}
throw err;
const [prefix, path] = uri.split("#", 2);
const err = new Error(`Can't resolve ${uri}`);
if (path[0] !== "/") {
if (anchors[uri]) {
return anchors[uri];
}
throw err;
}
if (!anchors[prefix]) {
throw err;
}
const paths = path.split("/").slice(1);
try {
const result = paths.reduce((o, n) => o[unescapeJsonPointer(n)], anchors[prefix]);
return result;
} catch (_) {
throw err;
}
if (!anchors[prefix]) {
throw err;
}
const paths = path.split("/").slice(1);
try {
const result = paths.reduce(
(o, n) => o[unescapeJsonPointer(n)],
anchors[prefix]
);
return result;
} catch (_) {
throw err;
}
}
export function resolve(tree) {
if (!isObject(tree)) {
return undefined
}
if (!isObject(tree)) {
return undefined;
}
const pointers = {};
pointerWords.forEach(word => pointers[word] = []);
const pointers = {};
pointerWords.forEach((word) => (pointers[word] = []));
function parse(obj, path, id) {
if (!isObject(obj)) {
return
}
if (obj.$id) {
id = obj.$id;
}
for (const prop in obj) {
if (pointerWords.has(prop)) {
pointers[prop].push({ ref: obj[prop], obj, prop, path, id });
}
parse(obj[prop], `${path}/${escapeJsonPointer(prop)}`, id)
}
function parse(obj, path, id) {
if (!isObject(obj)) {
return;
}
// find all refs
parse(tree, "#", "");
if (obj.$id) {
id = obj.$id;
}
for (const prop in obj) {
if (pointerWords.has(prop)) {
pointers[prop].push({ ref: obj[prop], obj, prop, path, id });
}
parse(obj[prop], `${path}/${escapeJsonPointer(prop)}`, id);
}
}
// find all refs
parse(tree, "#", "");
// resolve them
const anchors = { "": tree };
const dynamicAnchors = {};
pointers.$id.forEach(item => {
const { ref, obj, path } = item;
if (anchors[ref]) {
throw new Error(`$id : '${ref}' defined more than once at ${path}`);
}
anchors[ref] = obj
});
// resolve them
const anchors = { "": tree };
const dynamicAnchors = {};
pointers.$id.forEach((item) => {
const { ref, obj, path } = item;
if (anchors[ref]) {
throw new Error(`$id : '${ref}' defined more than once at ${path}`);
}
anchors[ref] = obj;
});
pointers.$anchor.forEach(item => {
const { ref, obj, prop, path, id } = item;
const fullRef = `${id}#${ref}`;
if (anchors[fullRef]) {
throw new Error(`$anchor : '${ref}' defined more than once at '${path}'`);
}
anchors[fullRef] = obj
});
pointers.$anchor.forEach((item) => {
const { ref, obj, path, id } = item;
const fullRef = `${id}#${ref}`;
if (anchors[fullRef]) {
throw new Error(`$anchor : '${ref}' defined more than once at '${path}'`);
}
anchors[fullRef] = obj;
});
pointers.$dynamicAnchor.forEach(item => {
const { ref, obj, prop, path } = item;
if (dynamicAnchors[`#${ref}`]) {
throw new Error(`$dynamicAnchor : '${ref}' defined more than once at '${path}'`);
}
dynamicAnchors[`#${ref}`] = obj;
});
pointers.$dynamicAnchor.forEach((item) => {
const { ref, obj, path } = item;
if (dynamicAnchors[`#${ref}`]) {
throw new Error(
`$dynamicAnchor : '${ref}' defined more than once at '${path}'`
);
}
dynamicAnchors[`#${ref}`] = obj;
});
pointers.$ref.forEach(item => {
const { ref, obj, prop, path, id } = item;
delete obj[prop]
const fullRef = ref[0] !== "#" ? ref : `${id}${ref}`;
const res = filtered(resolveUri(fullRef, anchors))
Object.assign(obj, filtered(resolveUri(fullRef, anchors)));
});
pointers.$ref.forEach((item) => {
const { ref, obj, prop, id } = item;
delete obj[prop];
const fullRef = ref[0] !== "#" ? ref : `${id}${ref}`;
Object.assign(obj, filtered(resolveUri(fullRef, anchors)));
});
pointers.$dynamicRef.forEach(item => {
const { ref, obj, prop, path, id } = item;
if (!dynamicAnchors[ref]){
throw new Error(`Can't resolve $dynamicAnchor : '${ref}'`);
}
delete obj[prop]
Object.assign(obj, filtered(dynamicAnchors[ref]));
});
pointers.$dynamicRef.forEach((item) => {
const { ref, obj, prop } = item;
if (!dynamicAnchors[ref]) {
throw new Error(`Can't resolve $dynamicAnchor : '${ref}'`);
}
delete obj[prop];
Object.assign(obj, filtered(dynamicAnchors[ref]));
});
return tree;
return tree;
}
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