Skip to content

Commit

Permalink
feat(intercepts): adds validation
Browse files Browse the repository at this point in the history
  • Loading branch information
rafamel committed Oct 25, 2019
1 parent 321908f commit ef79491
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 10 deletions.
14 changes: 4 additions & 10 deletions packages/intercepts/package-lock.json

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

1 change: 1 addition & 0 deletions packages/intercepts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"typescript": "^3.6.4"
},
"dependencies": {
"ajv": "^6.10.2",
"errorish": "^0.4.0"
},
"peerDependencies": {
Expand Down
1 change: 1 addition & 0 deletions packages/intercepts/src/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './logging';
export * from './validation';
109 changes: 109 additions & 0 deletions packages/intercepts/src/validation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
import {
InterceptImplementation,
intercept,
ErrorTypeImplementation,
error,
ServiceErrorsImplementation,
PublicError
} from '@karmic/core';
import Ajv from 'ajv';
import draft04 from 'ajv/lib/refs/json-schema-draft-04.json';
import { switchMap } from 'rxjs/operators';
import { of, throwError } from 'rxjs';

export interface ValidationOptions {
/**
* If `false`, it won't validate requests.
* If `true`, it will validate requests and use a default `ValidationError` to fail.
* If a `ValidationError` is passed, it will be used to fail instead.
* Default: `true`.
*/
request?: ValidationError | boolean;
/**
* If `false`, it won't validate responses.
* If `true`, it will validate responses and use a default `ValidationError` to fail.
* If a `ValidationError` is passed, it will be used to fail instead.
* Default: `false`.
*/
response?: ValidationError | boolean;
}

export interface ValidationError {
name: string;
type: ErrorTypeImplementation;
}

const ajv = new Ajv({ schemaId: 'id', logger: false });
ajv.addMetaSchema(draft04);

/**
* Returns an intercept that will validate incoming request objects and/or outgoing responses.
*/
export function validation(
options?: ValidationOptions
): InterceptImplementation<any, any, any> {
const opts = Object.assign({ request: true, response: false }, options);

const requestError: ValidationError | null = opts.request
? opts.request === true
? {
name: 'RequestValidationError',
type: error({ code: 'ClientError' })
}
: opts.request
: null;
const responseError: ValidationError | null = opts.response
? opts.response === true
? {
name: 'ResponseValidationError',
type: error({ code: 'ServerError' })
}
: opts.response
: null;

return intercept({
errors: [requestError, responseError].reduce(
(acc: ServiceErrorsImplementation, error) => {
if (error) acc[error.name] = error.type;
return acc;
},
{}
),
factory: (schemas) => {
const validateRequest = ajv.compile(schemas.request);
const validateResponse = ajv.compile(schemas.response);
return (data, context, info, next) => {
if (requestError) {
const valid = validateRequest(data);
if (!valid) {
throw new PublicError(
requestError.name,
requestError.type.code,
null,
requestError.type.description,
true
);
}
}
return responseError
? next(data).pipe(
switchMap((value) => {
const valid = validateResponse(value);
return valid
? of(value)
: throwError(
new PublicError(
responseError.name,
responseError.type.code,
null,
responseError.type.description,
true
)
);
})
)
: next(data);
};
}
});
}

0 comments on commit ef79491

Please sign in to comment.