Skip to content

Commit

Permalink
fix(arrest): Plenty of fixes and more tests
Browse files Browse the repository at this point in the history
  • Loading branch information
0xfede committed Mar 14, 2017
1 parent febce40 commit ccebf6b
Show file tree
Hide file tree
Showing 9 changed files with 294 additions and 64 deletions.
8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "arrest",
"version": "0.0.0-development",
"version": "2.3.0-beta.35",
"description": "REST framework for Node.js, Express and MongoDB",
"main": "dist/index.js",
"typings": "dist/index.d.ts",
Expand Down Expand Up @@ -82,8 +82,8 @@
"debug": "2.6.1",
"eredita": "1.0.1",
"express": "4.14.1",
"jsonpolice": "3.5.1",
"jsonref": "3.4.0",
"jsonpolice": "4.0.1",
"jsonref": "3.5.0",
"lodash": "4.17.4",
"mongodb": "2.2.24",
"rql": "0.3.3",
Expand All @@ -94,7 +94,7 @@
"path": "node_modules/cz-conventional-changelog"
},
"ghooks": {
"pre-commit": "npm run build && npm run cover && npm run check-coverage"
"_pre-commit": "npm run build && npm run cover && npm run check-coverage"
}
},
"engines": {
Expand Down
85 changes: 46 additions & 39 deletions src/api.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as _ from 'lodash';
import * as semver from 'semver';
import * as jr from 'jsonref';
import * as jp from 'jsonpolice';
import { Router, RouterOptions, RequestHandler, Request, Response, NextFunction } from 'express';
import { Eredita } from 'eredita';
import Logger from './debug';
Expand Down Expand Up @@ -160,7 +161,7 @@ export class API implements Swagger {
constructor(info:Swagger, registry:SchemaRegistry = new SchemaRegistry()) {
delete info.paths;
delete info.tags;
Object.assign(this, (new Eredita(info, new Eredita(_default_swagger))).mergePath());
Object.assign(this, (new Eredita(info, new Eredita(_.cloneDeep(_default_swagger)))).mergePath());
if (!semver.valid(this.info.version)) {
throw new Error('invalid_version');
}
Expand Down Expand Up @@ -199,19 +200,20 @@ export class API implements Swagger {

router(options?: RouterOptions): Promise<Router> {
if (!this[__router]) {
this[__router] = new Promise(resolve => {
let r = Router(options);
let originalSwagger = JSON.parse(JSON.stringify(this));
r.get('/swagger.json', (req: Request, res: Response, next: NextFunction) => {
let out = _.cloneDeep(originalSwagger);
if (!req.headers['host']) {
next(API.newError(400, 'Bad Request', 'Missing Host header in the request'));
} else {
out.host = req.headers['host'];
out.basePath = req.baseUrl;
let proto = this.schemes && this.schemes.length ? this.schemes[0] : 'http';
out.id = proto + '://' + out.host + out.basePath + '/swagger.json#';
_.each(originalSwagger.securityDefinitions, function (i, k) {
let r = Router(options);
//console.log(this.paths['/tests/a'].post.parameters);
let originalSwagger:Swagger = _.cloneDeep(this) as Swagger;
r.get('/swagger.json', (req: Request, res: Response, next: NextFunction) => {
let out:any = _.cloneDeep(originalSwagger);
if (!req.headers['host']) {
next(API.newError(400, 'Bad Request', 'Missing Host header in the request'));
} else {
out.host = req.headers['host'];
out.basePath = req.baseUrl;
let proto = this.schemes && this.schemes.length ? this.schemes[0] : 'http';
out.id = proto + '://' + out.host + out.basePath + '/swagger.json#';
if (originalSwagger.securityDefinitions) {
_.each(originalSwagger.securityDefinitions, (i:any, k) => {
if (k) {
if (i.authorizationUrl) {
out.securityDefinitions[k].authorizationUrl = jr.normalizeUri(i.authorizationUrl, out.id, true);
Expand All @@ -221,29 +223,34 @@ export class API implements Swagger {
}
}
});
res.json(out);
}
});
if (this[__schemas]) {
r.get('/schemas/:id', (req: Request, res: Response, next: NextFunction) => {
if (this[__schemas][req.params.id]) {
(this[__schemas][req.params.id] as RequestHandler)(req, res, next);
} else {
next();
}
});
res.json(out);
}
resolve(this.registry.resolve(this).then(() => {
let promises: Promise<Router>[] = [];
this.resources.forEach((resource: Resource) => {
promises.push(resource.router(r, options));
});
return Promise.all(promises).then(() => {
r.use(API.handleError);
return r;
});
}));
});
if (this[__schemas]) {
r.get('/schemas/:id', (req: Request, res: Response, next: NextFunction) => {
if (this[__schemas][req.params.id]) {
res.json(this[__schemas][req.params.id]);
} else {
next();
}
});
}
let p:Promise<any> = Promise.resolve(true);
for (let i in this[__schemas]) {
p = p.then(() => this.registry.create(this[__schemas][i]));
};
p = p.then(() => this.registry.resolve(this)).then(() => {
let promises: Promise<Router>[] = [];
this.resources.forEach((resource: Resource) => {
promises.push(resource.router(r, options));
});
return Promise.all(promises).then(() => {
r.use(API.handleError);
return r;
});
});
this[__router] = p.then(() => r);
}
return this[__router];
}
Expand All @@ -269,14 +276,14 @@ export class API implements Swagger {
});
}
}
registerSchema(id:string, handler:RequestHandler): this {
registerSchema(id:string, schema:Swagger.Schema) {
if (!this[__schemas]) {
this[__schemas] = {};
this.addTag(_default_schema_tag);
this.addOperation('/schemas/{id}', 'get', _default_schema_operation);
this.addTag(_.cloneDeep(_default_schema_tag));
this.addOperation('/schemas/{id}', 'get', _.cloneDeep(_default_schema_operation));
}
this[__schemas][id] = handler;
return this;
this[__schemas][id] = schema;
this.registry.register(`schemas/${id}`, schema);
}

static newError(code: number, message?: string, info?: any, err?: any): RESTError {
Expand Down
6 changes: 3 additions & 3 deletions src/mongo/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ export abstract class MongoOperation extends Operation {
});
}
get requestSchema(): any {
return this.resource.schema;
return this.resource.requestSchema;
}
get responseSchema(): any {
return this.resource.schema;
return this.resource.responseSchema;
}

protected getCollectionOptions(): mongo.DbCollectionOptions {
Expand Down Expand Up @@ -471,7 +471,7 @@ export class RemoveMongoOperation extends MongoOperation {
return job;
}
runOperation(job:MongoJob): MongoJob | Promise<MongoJob> {
// TODO remove when mongo typings include a propery type
// TODO remove when mongo typings include a proper type
let opts = job.opts as { w?: number | string, wtimmeout?: number, j?: boolean, bypassDocumentValidation?: boolean };
return job.coll.deleteOne(job.query, opts).then(result => {
if (result.deletedCount != 1) {
Expand Down
6 changes: 6 additions & 0 deletions src/mongo/resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ export class MongoResource extends Resource {
get schema(): any {
return {};
}
get requestSchema(): any {
return this.schema;
}
get responseSchema(): any {
return this.schema;
}

static defaultRoutes():Routes {
return {
Expand Down
4 changes: 2 additions & 2 deletions src/operation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ export abstract class Operation implements Swagger.Operation {
validators.push(this.api.registry.create(parameter).then((schema: jp.Schema) => {
return function (req: Request) {
if (typeof req[key][parameter.name] === 'undefined' && required === true) {
jp.fireValidationError(key + '.' + parameter.name, (schema as any).scope, 'required');
throw new jp.ValidationError(key + '.' + parameter.name, (schema as any).scope, 'required');
} else {
let out = schema.validate(req[key][parameter.name], key + '.' + parameter.name);
if (typeof out !== 'undefined') {
Expand Down Expand Up @@ -158,7 +158,7 @@ export abstract class Operation implements Swagger.Operation {
if (_.isEqual(req.body, {}) && (!parseInt(req.headers['content-length']))) {
if (params.body[0].required === true) {
// TODO maybe scope shouldn't be protected
jp.fireValidationError('body', (schema as any).scope, 'required');
throw new jp.ValidationError('body', (schema as any).scope, 'required');
}
} else {
schema.validate(req.body, 'body');
Expand Down
11 changes: 9 additions & 2 deletions src/schema.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as request from 'request';
import * as jr from 'jsonref';
import * as jp from 'jsonpolice';
import { RESTError } from './error'

export class SchemaRegistry {
private opts:jr.ParseOptions;
Expand All @@ -23,18 +24,24 @@ export class SchemaRegistry {
if (err) {
reject(err);
} else if (response.statusCode !== 200) {
reject(response.statusCode);
reject(new RESTError(response.statusCode || 500));
} else {
resolve(data);
}
});
});
}

resolve(dataOrUri:any): Promise<any> {
return jr.parse(dataOrUri, this.opts);
}
create(dataOrUri:any): Promise<jp.Schema> {
return jp.create(dataOrUri, this.opts);
}
register(id, data:any) {
data.id = jr.normalizeUri(id);
if (!this.opts.store) {
this.opts.store = {};
}
this.opts.store[data.id] = data;
}
}
Loading

0 comments on commit ccebf6b

Please sign in to comment.