Skip to content

Commit

Permalink
Merge 2702762 into d28ba53
Browse files Browse the repository at this point in the history
  • Loading branch information
thiagobustamante committed Mar 13, 2018
2 parents d28ba53 + 2702762 commit 140b67d
Show file tree
Hide file tree
Showing 70 changed files with 1,775 additions and 926 deletions.
1,294 changes: 1,021 additions & 273 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -75,13 +75,12 @@
"human-interval": "^0.1.6",
"inquirer": "^5.1.0",
"ioredis": "^3.1.4",
"ip-filter": "^2.0.0",
"joi": "13.0.0",
"jsonata": "^1.5.2",
"jsonwebtoken": "^8.2.0",
"lodash": "^4.17.5",
"lodash-deep": "^2.0.0",
"minimatch": "^3.0.4",
"micromatch": "^3.1.9",
"mustache": "^2.3.0",
"on-headers": "^1.0.1",
"os": "^0.1.1",
Expand Down Expand Up @@ -125,7 +124,7 @@
"@types/joi": "^9.0.32",
"@types/jsonwebtoken": "^7.2.5",
"@types/lodash": "4.14.78",
"@types/minimatch": "^3.0.3",
"@types/micromatch": "^3.1.0",
"@types/mocha": "^2.2.48",
"@types/multer": "0.0.32",
"@types/mustache": "^0.8.30",
Expand All @@ -137,6 +136,7 @@
"@types/request": "0.0.31",
"@types/serve-static": "^1.7.31",
"@types/stream-buffers": "^3.0.2",
"@types/tough-cookie": "^2.3.2",
"@types/uuid": "^2.0.29",
"@types/weighted": "0.0.5",
"@types/winston": "0.0.28",
Expand All @@ -151,6 +151,7 @@
"nyc": "^10.2.0",
"rimraf": "^2.6.1",
"source-map-support": "^0.4.14",
"tough-cookie": "^2.3.4",
"ts-node": "^3.3.0",
"tslint": "^5.9.1",
"typescript": "2.7.1",
Expand Down
14 changes: 14 additions & 0 deletions src/admin/config/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use strict';

export class SdkError extends Error {
private statusCode: number;
constructor(message: string, code: number) {
super(message);
this.statusCode = code;
Object.setPrototypeOf(this, SdkError.prototype);
}

get code(): number {
return this.statusCode;
}
}
2 changes: 1 addition & 1 deletion src/admin/config/utils.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

import { SdkError } from '../../error/errors';
import { SdkError } from './errors';

export function checkStatus(response: any, status: number) {
if (response.status !== status) {
Expand Down
106 changes: 105 additions & 1 deletion src/config/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Proxy, proxyValidatorSchema } from './proxy';
import { Group, groupValidatorSchema } from './group';
import { ApiCircuitBreakerConfig, apiCircuitBreakerConfigValidatorSchema } from './circuit-breaker';
import { ApiFilter, apiFilterSchema } from './filter';
import { ValidationError } from '../error/errors';
import { ValidationError } from './errors';
import { MiddlewareConfig, middlewareConfigValidatorSchema } from './middleware';
import { ObjectID } from 'bson';

Expand Down Expand Up @@ -90,6 +90,48 @@ export interface ApiConfig {
* ```
*/
filter?: Array<ApiFilter>;
/**
* Add interceptors to the request pipeline. An Interceptor is a function that receives
* the request or the response object and can modify these objects.
*
* You can define two types of interceptors: Request Interceptors or Response Interceptors.
*
* Example of a request interceptor:
* ```
* module.exports = function(proxyReq) {
* // you can update headers
* proxyReq.headers['Content-Type'] = 'text/html';
* // you can change the method
* proxyReq.method = 'GET';
* // you can munge the bodyContent.
* proxyReq.bodyContent = proxyReq.bodyContent.replace(/losing/, 'winning!');
* return proxyReq;
* };
* ```
*
* Example of a response interceptor:
* ```
* module.exports = function(body, headers, request) {
* data = JSON.parse(body.toString('utf8'));
* return {body: data};
* };
* ```
*
* Each interceptor must be defined on its own .js file (placed on middleware/interceptor/[request | response] folder)
* and the fileName must match: <interceptorName>.js.
*
* So, the above request interceptor should be saved in a file called
* middleware/interceptor/request/myRequestInterceptor.js and configured as:
*
* ```
* interceptor:{
* request: [{middleware{ name: "myRequestInterceptor"} }]
* }
* ```
*
* If more than one request or response interceptor are defined, they are executed in declaration order.
*/
interceptor?: Interceptors;
/**
* Configure how to handle errors during API pipeline.
*/
Expand All @@ -98,8 +140,67 @@ export interface ApiConfig {
* Disable all stats recording for this API
*/
disableStats?: boolean;
/**
* Allows you to control when to parse the request body. Just enable it if you need to access the ```request.body```
* inside a proxy middleware, like a ```filter``` or ```interceptor```. You can inform the expected
* types of body you are expecting. [json, urlencoded, raw]
*/
parseReqBody?: string | Array<string> | boolean;
/**
* Allows you to control when to parse the cookies. Just enable it if you need to access the ```request.cookies```
* inside a proxy middleware, like a ```filter``` or ```interceptor```.
*/
parseCookies?: boolean;
}

/**
* Add interceptors to the request pipeline. An Interceptor is a function that receives
* the request or the response object and can modify these objects.
*/
export interface Interceptors {
/**
* A list of request interceptors
*/
request?: Array<Interceptor>;
/**
* A list of response interceptors
*/
response?: Array<Interceptor>;
}

/**
* An Interceptor is a function that receives
* the request or the response object and can modify these objects.
*/
export interface Interceptor {
/**
* The interceptor to be used.
*/
middleware: MiddlewareConfig;
/**
* A list of groups that should be filtered by this filter. If not provided, everything
* will be filtered.
* Defaults to *.
*/
group?: Array<string>;
}

export interface ResponseInterceptorResult {
body?: any;
removeHeaders?: Array<string>;
updateHeaders?: any;
}

const interceptorSchema = Joi.object().keys({
group: Joi.alternatives([Joi.array().items(Joi.string()), Joi.string()]),
middleware: middlewareConfigValidatorSchema.required()
});

const interceptorsSchema = Joi.object().keys({
request: Joi.alternatives([Joi.array().items(interceptorSchema), interceptorSchema]),
response: Joi.alternatives([Joi.array().items(interceptorSchema), interceptorSchema])
}).min(1);

export let apiConfigValidatorSchema = Joi.object().keys({
authentication: Joi.alternatives([Joi.array().items(apiAuthenticationValidatorSchema), apiAuthenticationValidatorSchema]),
cache: Joi.alternatives([Joi.array().items(apiCacheConfigValidatorSchema), apiCacheConfigValidatorSchema]),
Expand All @@ -111,7 +212,10 @@ export let apiConfigValidatorSchema = Joi.object().keys({
filter: Joi.alternatives([Joi.array().items(apiFilterSchema), apiFilterSchema]),
group: Joi.alternatives([Joi.array().items(groupValidatorSchema), groupValidatorSchema]),
id: Joi.string(),
interceptor: interceptorsSchema,
name: Joi.string().min(3).required(),
parseCookies: Joi.boolean(),
parseReqBody: Joi.alternatives([Joi.string().valid('json', 'urlencoded', 'raw'), Joi.array().items(Joi.string().valid('json', 'urlencoded', 'raw')), Joi.boolean()]),
path: Joi.string().regex(/^[A-Za-z\-\/0-9_\.]+$/i).required(),
proxy: proxyValidatorSchema.required(),
throttling: Joi.alternatives([Joi.array().items(apiThrottlingConfigValidatorSchema), apiThrottlingConfigValidatorSchema]),
Expand Down
2 changes: 1 addition & 1 deletion src/config/authentication.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import * as Joi from 'joi';
import { MiddlewareConfig, middlewareConfigValidatorSchema } from './middleware';
import { ValidationError } from '../error/errors';
import { ValidationError } from './errors';

/**
* Configure Authentication for APIs.
Expand Down
2 changes: 1 addition & 1 deletion src/config/config-package.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import * as Joi from 'joi';
import { GatewayConfig, gatewayConfigValidatorSchema } from './gateway';
import { ApiConfig, apiConfigValidatorSchema } from './api';
import { ValidationError } from '../error/errors';
import { ValidationError } from './errors';

/**
* The Server config descriptor.
Expand Down
46 changes: 1 addition & 45 deletions src/error/errors.ts → src/config/errors.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

import { HttpError, Errors } from 'typescript-rest';
import { Errors } from 'typescript-rest';
import * as Joi from 'joi';
import * as _ from 'lodash';

Expand Down Expand Up @@ -46,47 +46,3 @@ export class ValidationError extends Errors.ForbidenError {
return result.join('\n');
}
}

export class UnauthorizedError extends Errors.UnauthorizedError {
constructor(message?: string) {
super(message);

Object.setPrototypeOf(this, UnauthorizedError.prototype);
}
}

export class NotFoundError extends Errors.NotFoundError {
constructor(message?: string) {
super(message);

Object.setPrototypeOf(this, NotFoundError.prototype);
}
}

export class UnavailableError extends Error {
statusCode: number = 503;
constructor(message: string) {
super(message);
Object.setPrototypeOf(this, UnavailableError.prototype);
}
}

export class ProxyError extends HttpError {
constructor(message: string, statusCode: number) {
super('gatewayError', statusCode, message);
Object.setPrototypeOf(this, ProxyError.prototype);
}
}

export class SdkError extends Error {
private statusCode: number;
constructor(message: string, code: number) {
super(message);
this.statusCode = code;
Object.setPrototypeOf(this, SdkError.prototype);
}

get code(): number {
return this.statusCode;
}
}
2 changes: 1 addition & 1 deletion src/config/gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { AuthenticationConfig, authenticationValidatorSchema } from './authentic
import { CacheConfig, cacheConfigValidatorSchema } from './cache';
import { CircuitBreakerConfig, circuitBreakerConfigValidatorSchema } from './circuit-breaker';
import { ThrottlingConfig, throttlingConfigValidatorSchema } from './throttling';
import { ValidationError } from '../error/errors';
import { ValidationError } from './errors';

/**
* The Server config descriptor.
Expand Down
28 changes: 28 additions & 0 deletions src/config/jsonata.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';

import * as Joi from 'joi';
import { ValidationError } from './errors';

/**
* A [jsonata](https://www.npmjs.com/package/jsonata) expression, used by core interceptors
* to transform responses.
*/
export interface JSONAtaExpression {
/**
* The jsonata expressio
*/
expression: string;
}

const jsonataExpressionSchema = Joi.object().keys({
expression: Joi.string().required()
});

export function validateJsonAtaExpression(config: JSONAtaExpression) {
const result = Joi.validate(config, jsonataExpressionSchema);
if (result.error) {
throw new ValidationError(result.error);
} else {
return result.value;
}
}

0 comments on commit 140b67d

Please sign in to comment.