Skip to content

Commit

Permalink
support server variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Carmine authored and Carmine committed Apr 1, 2019
1 parent 5ce98f2 commit c493001
Show file tree
Hide file tree
Showing 11 changed files with 186 additions and 140 deletions.
18 changes: 9 additions & 9 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
sudo: required
language: node_js
node_js:
- '10'
- '10'
before_install:
- openssl aes-256-cbc -K $encrypted_5d8f15d35f17_key -iv $encrypted_5d8f15d35f17_iv
-in secrets.zip.enc -out secrets.zip -d
- unzip secrets.zip
- openssl aes-256-cbc -K $encrypted_5d8f15d35f17_key -iv $encrypted_5d8f15d35f17_iv
-in secrets.zip.enc -out secrets.zip -d
- unzip secrets.zip
install:
- npm install
- npm install
script:
- npm run compile
- npm test
- npm run coveralls
- npm run codacy
- npm run compile
- npm run test:coverage
- npm run coveralls
- npm run codacy
env:
global:
secure: ytiwXpn7JDc0fhB2KQa2z/OM9LNyVqf/5XCg6LX1WtXRn3R6qYfqCMBUq+eu/Pvym5SlPUwPwZEzRwSBUBivAhNsSPMNGnzNVyX+OxVruXlaD+Wsx3ROTGnwTpPEh7JUXXpytWmG6IAXwyhOvnhAgILjDFaRPkRNc/9Y1zsRTtFxiaisN/6X4IFxGRdI6OVobLEDKmnUBa2nDw5fZws+hOGWlv7b1NqbzXJ1yrOTH0E/uPsill3aWqXuDSEtv4VhAk0z9Xyw4Bi28XtDNQNSou8LkGmN4QzXHdKz73UgY3RaKQ99PVhyTZz0TrvL54BSB/poUoXnu3ZZODKA9GIrthnHW6rn3u0ovnHRAA+L2CLBTqFbGoctOe3XhnUcDBmcvH3FnE+IcLdSeeEjDO+oGIZdut2af2aPj+rA7Hd76+I/CGEf8osnfZVuLYYWW+semuTCF4rDSI+EPu8Rb3w3POCzskkinONnxdYQpcEw01ltMBUnJxLi7fYVpLeSNsOs5rBMOL+LlbsnG505UvmqaFlzyt7GzvcCXW0hrBAARjq3ADAmtSBVZuzMWebN2X93wjZZ9SLyVtCdVNtCxCwA3X0zYJ1dH6roSNi99gMDG9fxbb+Hhc/0+9+u7Sn2CMvOLy9LiV+lm0MeANDOitRb6OTLuOKlLLjr8lvvJxf9qwE=
19 changes: 18 additions & 1 deletion openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,24 @@ info:
name: Apache 2.0
url: https://www.apache.org/licenses/LICENSE-2.0.html
servers:
- url: http://petstore.swagger.io/v1
- url: /v1
- url: http://{name}.swagger.io:{port}/{version}
variables:
name:
default: petstore
enum:
- petstore
- storeofpets
port:
enum:
- '443'
- '8443'
default: '443'
version:
default: v1
enum:
- v1
- v2
paths:
/pets:
get:
Expand Down
8 changes: 4 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
{
"name": "express-openapi-validator",
"version": "0.3.36",
"version": "0.9.1",
"description": "",
"main": "dist/index.js",
"scripts": {
"compile": "rm -rf dist/ && tsc",
"test": "nyc mocha -r source-map-support/register -r ts-node/register --recursive test/**/*.spec.ts",
"test": "mocha -r source-map-support/register -r ts-node/register --recursive test/**/*.spec.ts",
"test:coverage": "nyc mocha -r source-map-support/register -r ts-node/register --recursive test/**/*.spec.ts",
"coveralls": "cat coverage/lcov.info | coveralls -v",
"codacy": "cat coverage/lcov.info | codacy-coverage"
},
Expand All @@ -20,7 +21,7 @@
"openapi-request-validator": "^3.7.0",
"openapi-schema-validator": "^3.0.3",
"openapi-security-handler": "^2.0.4",
"openapi-types": "1.3.4",
"openapi-types": "^1.3.4",
"path-to-regexp": "^3.0.0",
"ts-log": "^2.1.4"
},
Expand Down
89 changes: 83 additions & 6 deletions src/framework/base.path.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,112 @@
import * as pathToRegexp from 'path-to-regexp';
import { OpenAPIV3 } from 'openapi-types';
import { URL } from 'url';

interface ServerUrlVariables {
[key: string]: ServerUrlValues;
}
interface ServerUrlValues {
enum: string[];
default?: string;
}

export default class BasePath {
public readonly variables: { [key: string]: { enum: string[] } } = {};
public readonly variables: ServerUrlVariables = {};
public readonly path: string = '';
private allPaths: string[] = null;

constructor(server: OpenAPIV3.ServerObject) {
// break the url into parts
// baseUrl param added to make the parsing of relative paths go well
const serverUrl = new URL(server.url, 'http://localhost');
let urlPath = decodeURI(serverUrl.pathname).replace(/\/$/, '');
let urlPath = this.findUrlPath(server.url);
if (/{\w+}/.test(urlPath)) {
// has variable that we need to check out
urlPath = urlPath.replace(/{(\w+)}/g, (substring, p1) => `:${p1}`);
}
this.path = urlPath;
for (const variable in server.variables) {
if (server.variables.hasOwnProperty(variable)) {
this.variables[variable] = { enum: server.variables[variable].enum };
const v = server.variables[variable];
const enums = v.enum || [];
if (enums.length === 0 && v.default) enums.push(v.default);

this.variables[variable] = {
enum: enums,
default: v.default,
};
}
}
}

public hasVariables() {
public hasVariables(): boolean {
return Object.keys(this.variables).length > 0;
}

public all(): string[] {
if (!this.hasVariables()) return [this.path];
if (this.allPaths) return this.allPaths;
// TODO performance optimization
// ignore variables that are not part of path params
const allParams = Object.entries(this.variables).reduce((acc, v) => {
const [key, value] = v;
const params = value.enum.map(e => ({
[key]: e,
}));
acc.push(params);
return acc;
}, []);

const allParamCombos = cartesian(...allParams);
const toPath = pathToRegexp.compile(this.path);
const paths = new Set();
for (const combo of allParamCombos) {
paths.add(toPath(combo));
}
this.allPaths = Array.from(paths);
return this.allPaths;
}

public static fromServers(servers: OpenAPIV3.ServerObject[]) {
if (!servers) {
return [new BasePath({ url: '' })];
}
return servers.map(server => new BasePath(server));
}

private findUrlPath(u) {
const findColonSlashSlash = p => {
const r = /:\/\//.exec(p);
if (r) return r.index;
return -1;
};
const findFirstSlash = p => {
const r = /\//.exec(p);
if (r) return r.index;
return -1;
};

const fcssIdx = findColonSlashSlash(u);
const startSearchIdx = fcssIdx !== -1 ? fcssIdx + 3 : 0;
const startPathIdx = findFirstSlash(u.substring(startSearchIdx));
if (startPathIdx === -1) return '/';

const pathIdx = startPathIdx + startSearchIdx;
return u.substring(pathIdx);
}
}

function cartesian(...arg) {
const r = [],
max = arg.length - 1;
function helper(obj, i) {
const values = arg[i];
for (var j = 0, l = values.length; j < l; j++) {
const a = { ...obj };
const key = Object.keys(values[j])[0];
a[key] = values[j][key];
if (i == max) r.push(a);
else helper(a, i + 1);
}
}
helper({}, 0);
return r;
}
67 changes: 20 additions & 47 deletions src/framework/index.ts
Original file line number Diff line number Diff line change
@@ -1,51 +1,24 @@
// import fsRoutes from 'fs-routes';
// import OpenAPIDefaultSetter from 'openapi-default-setter';
// import OpenAPIRequestCoercer from 'openapi-request-coercer';
// import OpenAPIRequestValidator from 'openapi-request-validator';
// import OpenAPIResponseValidator from 'openapi-response-validator';
import OpenAPISchemaValidator from 'openapi-schema-validator';
import OpenAPISecurityHandler from 'openapi-security-handler';
import { OpenAPI, OpenAPIV2, OpenAPIV3 } from 'openapi-types';
import { OpenAPIV2, OpenAPIV3 } from 'openapi-types';
import BasePath from './base.path';
import {
ConsoleDebugAdapterLogger,
IOpenAPIFramework,
OpenAPIFrameworkAPIContext,
OpenAPIFrameworkArgs,
OpenAPIFrameworkConstructorArgs,
// OpenAPIFrameworkOperationContext,
OpenAPIFrameworkPathContext,
OpenAPIFrameworkPathObject,
OpenAPIFrameworkVisitor,
} from './types';
import {
// addOperationTagToApiDoc,
// allowsCoercionFeature,
// allowsDefaultsFeature,
// allowsFeatures,
// allowsResponseValidationFeature,
// allowsValidationFeature,
assertRegExpAndSecurity,
// byDefault,
// byDirectory,
// byMethods,
// byRoute,
// byString,
copy,
// getAdditionalFeatures,
getBasePathsFromServers,
// getMethodDoc,
// getSecurityDefinitionByPath,
loadSpecFile,
handleYaml,
// injectDependencies,
// METHOD_ALIASES,
// resolveParameterRefs,
// resolveResponseRefs,
sortApiDocTags,
// sortOperationDocTags,
// toAbsolutePath,
// withNoDuplicates,
} from './util';

export {
Expand All @@ -63,18 +36,18 @@ export default class OpenAPIFramework implements IOpenAPIFramework {
public readonly featureType;
public readonly loggingPrefix;
public readonly name;
private customFormats;
private dependencies;
private enableObjectCoercion;
private errorTransformer;
private externalSchemas;
// private customFormats;
// private dependencies;
// private enableObjectCoercion;
// private errorTransformer;
// private externalSchemas;
private originalApiDoc;
private operations;
private paths;
private pathsIgnore;
// private operations;
// private paths;
// private pathsIgnore;
private pathSecurity;
private routesGlob;
private routesIndexFileRegExp;
// private routesGlob;
// private routesIndexFileRegExp;
private securityHandlers;
private validateApiDoc;
private validator;
Expand Down Expand Up @@ -124,7 +97,7 @@ export default class OpenAPIFramework implements IOpenAPIFramework {
}
});

this.enableObjectCoercion = !!args.enableObjectCoercion;
// this.enableObjectCoercion = !!args.enableObjectCoercion;
this.originalApiDoc = handleYaml(loadSpecFile(args.apiDoc));
if (!this.originalApiDoc) {
throw new Error(`spec could not be read at ${args.apiDoc}`);
Expand All @@ -145,17 +118,17 @@ export default class OpenAPIFramework implements IOpenAPIFramework {
(this.apiDoc as OpenAPIV2.Document).swagger,
extensions: this.apiDoc[`x-${this.name}-schema-extension`],
});
this.customFormats = args.customFormats;
this.dependencies = args.dependencies;
this.errorTransformer = args.errorTransformer;
this.externalSchemas = args.externalSchemas;
this.operations = args.operations;
this.pathsIgnore = args.pathsIgnore;
// this.customFormats = args.customFormats;
// this.dependencies = args.dependencies;
// this.errorTransformer = args.errorTransformer;
// this.externalSchemas = args.externalSchemas;
// this.operations = args.operations;
// this.pathsIgnore = args.pathsIgnore;
this.pathSecurity = Array.isArray(args.pathSecurity)
? args.pathSecurity
: [];
this.routesGlob = args.routesGlob;
this.routesIndexFileRegExp = args.routesIndexFileRegExp;
// this.routesGlob = args.routesGlob;
// this.routesIndexFileRegExp = args.routesIndexFileRegExp;
this.securityHandlers = args.securityHandlers;
this.pathSecurity.forEach(assertRegExpAndSecurity.bind(null, this));

Expand Down
Loading

0 comments on commit c493001

Please sign in to comment.