Skip to content

Commit

Permalink
Initial version
Browse files Browse the repository at this point in the history
  • Loading branch information
seriousme committed May 9, 2021
0 parents commit a295ed2
Show file tree
Hide file tree
Showing 44 changed files with 17,538 additions and 0 deletions.
86 changes: 86 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage
.nyc_output

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directories
node_modules
jspm_packages

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# =========================
# Operating System Files
# =========================

# OSX
# =========================

.DS_Store
.AppleDouble
.LSOverride

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# Windows
# =========================

# Windows image file caches
Thumbs.db
ehthumbs.db

# Folder config file
Desktop.ini

# Recycle Bin used on file shares
$RECYCLE.BIN/

# Windows Installer files
*.cab
*.msi
*.msm
*.msp

# Windows shortcuts
*.lnk
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Changelog

## [Unreleased]
### Changed

## [v1.0.0] 30-05-2021
Initial version
81 changes: 81 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
# OpenAPI schema validator
[![CI status](https://github.com/seriousme/openapi-schema-validator/workflows/Node.js%20CI/badge.svg)](https://github.com/seriousme/openapi-schema-validator/actions?query=workflow%3A%22Node.js+CI%22)
[![Coverage Status](https://coveralls.io/repos/github/seriousme/openapi-schema-validator/badge.svg?branch=master)](https://coveralls.io/github/seriousme/openapi-schema-validator?branch=master)
[![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](https://img.shields.io/npm/dm/@seriousme/openapi-schema-validator)



A JSON schema validator for [OpenAPI](https://www.openapis.org/) specifications, it currently supports:
- [2.0](https://spec.openapis.org/oas/v2.0)
- [3.0.x](https://spec.openapis.org/oas/v3.0.3)
- [3.1.x](https://spec.openapis.org/oas/v3.1.0)

<a name="install"></a>
## Install
```
npm i @seriousme/openapi-schema-validator --save
```

<a name="Usage"></a>
### Usage

```javascript
import Validator from "openapi-schema-validator";

console.log(Validator.supportedVersions.has("3.1"))
// prints true

const validator = new Validator();
const res = await validator.validate('./petstore.json');
if (res.valid){
console.log("Specification matches schema for version", validator.version);
const schema = validator.resolveRefs());
// schema now contains a Javascript object containing the dereferenced schema
} else {
console.log("Specification does not match Schema");
console.log(res.errors);
}

```

<a name="Usage"></a>
### API
- `<instance>.validate(specification)`

Thsi function tries to validata a specification against the OpenApi schemas. `specification` can be one of:

- a JSON object
- a JSON object encoded as string
- a YAML string
- a filename

External references are *not* automatically resolved so you need to inline them yourself if required.
The result is an object:
```
{
valid: <boolean>,
errors: <any> // only present if valid is false
}
```

- `<instance>.version`

If validation is succesfull this will return the openApi version found e.g. ("2.0","3.0","3.1).
The openApi specification only specifies major/minor versions as separate schemas. So "3.0.3" results in "3.0".

- `<instance>.resolveRefs(options)`

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()`
but you can explicity pass a specification by passing `{specification:<object>}` as options.
The result is an `object` where all references have been resolved.
Resolution of references is `shallow` This should normally not be a problem for this use case.

- `Validator.supportedVersions`

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`.

<a name="license"></a>
# License
Licensed under the [MIT license](https://opensource.org/licenses/MIT)
16 changes: 16 additions & 0 deletions index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export default class Validator {
static supportedVersions: Set<string>;
constructor(ajvOptions?: {
strict: boolean;
validateFormats: boolean;
});
resolveRefs(opts?: {
specification?: object
}): object;
validate(schema: object | string): Promise<{
valid: boolean;
errors?: any
}>;
specification: object;
version: string;
}
94 changes: 94 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
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'


const importJSON = createRequire(import.meta.url);

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
}

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
}
}
return undefined
}

async function getSpecFromData(data) {
if (typeof data === 'object') {
return data;
}
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;
}
}
}


export default class Validator {
constructor(ajvOptions = { strict: false, validateFormats: false }) {
this.ajvOptions = ajvOptions;
return this;
}

resolveRefs(opts={}){
return resolve(this.specification || opts.specification)
}

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
}
}
Loading

0 comments on commit a295ed2

Please sign in to comment.