Skip to content

Commit

Permalink
refactor event validation and now validate events on dispatch (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
matthewgoslett authored Jul 18, 2017
1 parent 1159f43 commit 8de72af
Show file tree
Hide file tree
Showing 10 changed files with 530 additions and 125 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -328,20 +328,21 @@ Your validator must implement the following methods.
```node
class CustomValidator {
/**
* Validates an event.
* Validate an event.
*
* @param {EventInterface} event
* @return {Promise<boolean>}
* @return {Promise<ValidationResult>}
* @example
* validator.validates(event).then((success) => {
* if (success) {
* validator.validate(event).then((result) => {
* if (result.passes) {
* console.log('event validates!');
* } else {
* console.log('event failed validation');
* console.log(result.errors);
* }
* });
*/
validates(event) {
validate(event) {

}
}
Expand Down Expand Up @@ -418,8 +419,9 @@ manager.listenExprFailHandler = (event, expr) => {
};

// hook into validation failures
manager.validationFailHandler = (event, validator) => {
manager.validationFailHandler = (result) => {
// the event failed validation
console.log(result.errors);
};
```

Expand Down
9 changes: 9 additions & 0 deletions changelog.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
# Changelog

## 3.0.0 - 2017-07-18

* EventValidatorInterface->validates() renamed to ->validate()
* EventValidatorInterface->validate() now returns a promise resolving to a ValidationResult instance instead of bool
* The validation fail handler callback now receives a ValidationResult instead of the event and a validator
* dispatch() now returns a promise
* dispatchBatch() now returns a promise
* Events are now validated on dispatch, and will reject the promise with a ValidationResult when validation fails

## 2.0.1 - 2017-07-18

* Add support for translate, listen expr & validation failure callbacks
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@superbalist/js-event-pubsub",
"version": "2.0.1",
"version": "3.0.0",
"description": "An event protocol and implementation over pub/sub",
"main": "lib/index.js",
"scripts": {
Expand All @@ -27,7 +27,7 @@
},
"homepage": "https://github.com/Superbalist/js-event-pubsub#readme",
"dependencies": {
"@superbalist/js-pubsub": "^2.0.0",
"@superbalist/js-pubsub": "^3.0.0",
"ajv": "^5.0.1",
"request": "^2.81.0",
"request-promise-native": "^1.0.3",
Expand Down
77 changes: 68 additions & 9 deletions src/EventManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -148,14 +148,14 @@ class EventManager {
// not using a validator
handler(event);
} else {
this.validator.validates(event).then((success) => {
if (success) {
this.validator.validate(event).then((result) => {
if (result.passes) {
// event passed validation
handler(event);
} else {
// pass to validation fail handler?
if (this.validationFailHandler) {
this.validationFailHandler(event, this.validator);
this.validationFailHandler(result);
}
}
});
Expand Down Expand Up @@ -220,6 +220,7 @@ class EventManager {
*
* @param {string} channel
* @param {EventInterface} event
* @return {Promise<*>}
* @example
* let event = new SimpleEvent('user.created', {
* user: {
Expand All @@ -233,15 +234,38 @@ class EventManager {
* manager->dispatch('events', event);
*/
dispatch(channel, event) {
event = this._prepEventForDispatch(event);
this.adapter.publish(channel, event.toMessage());
return new Promise((resolve, reject) => {
event = this._prepEventForDispatch(event);

let publishAndResolve = () => {
return this.adapter.publish(channel, event.toMessage()).then(resolve);
};

if (this.validator) {
this.validator.validate(event).then((result) => {
if (result.passes) {
publishAndResolve();
} else {
// pass to validation fail handler?
if (this.validationFailHandler) {
this.validationFailHandler(result);
}

reject(result);
}
});
} else {
publishAndResolve();
}
});
}

/**
* Dispatch multiple events.
*
* @param {string} channel
* @param {EventInterface[]} events
* @return {Promise<*>}
* @example
* let events = [
* new SimpleEvent('user.created', {
Expand All @@ -265,11 +289,46 @@ class EventManager {
* manager->dispatchBatch('events', events);
*/
dispatchBatch(channel, events) {
let messages = events.map((event) => {
event = this._prepEventForDispatch(event);
return event.toMessage();
return new Promise((resolve, reject) => {
let validators = [];
let messages = [];

for (let event of events) {
event = this._prepEventForDispatch(event);

messages.push(event.toMessage());

if (this.validator) {
validators.push(this.validator.validate(event));
}
}

Promise.all(validators).then((results) => {
let validates = true;
let firstFailedResult = null;

for (let result of results) {
if (result.fails) {
// when we hit the first event that fails validation,
// we'll break out of here and reject the promise
firstFailedResult = result;
validates = false;
break;
}
}

if (validates) {
this.adapter.publishBatch(channel, messages).then(resolve);
} else {
// pass to validation fail handler?
if (this.validationFailHandler) {
this.validationFailHandler(firstFailedResult);
}

reject(firstFailedResult);
}
});
});
this.adapter.publishBatch(channel, messages);
}
}

Expand Down
11 changes: 6 additions & 5 deletions src/EventValidatorInterface.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,21 @@
*/
class EventValidatorInterface {
/**
* Validates an event.
* Validate an event.
*
* @param {EventInterface} event
* @return {Promise<boolean>}
* @return {Promise<ValidationResult>}
* @example
* validator.validates(event).then((success) => {
* if (success) {
* validator.validate(event).then((result) => {
* if (result.passes) {
* console.log('event validates!');
* } else {
* console.log('event failed validation');
* console.log(result.errors);
* }
* });
*/
validates(event) {
validate(event) {

}
}
Expand Down
43 changes: 43 additions & 0 deletions src/ValidationResult.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use strict';

/**
* ValidationResult Class
*/
class ValidationResult {
/**
* Construct an ValidationResult
*
* @param {EventValidatorInterface} validator
* @param {EventInterface} event
* @param {boolean} passes
* @param {string[]} [errors=[]]
*/
constructor(validator, event, passes, errors = []) {
/**
* @type {EventValidatorInterface}
*/
this.validator = validator;

/**
* @type {EventInterface}
*/
this.event = event;

/**
* @type {boolean}
*/
this.passes = passes;

/**
* @type {boolean}
*/
this.fails = !passes;

/**
* @type {string[]}
*/
this.errors = errors;
}
}

module.exports = ValidationResult;
23 changes: 17 additions & 6 deletions src/validators/JSONSchemaEventValidator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

let Ajv = require('ajv');
let request = require('request-promise-native');
let ValidationResult = require('../ValidationResult');

/**
* JSONSchemaEventValidator Class
Expand All @@ -22,23 +23,33 @@ class JSONSchemaEventValidator {
}

/**
* Validates an event.
* Validate an event.
*
* @param {SchemaEvent} event
* @return {Promise<boolean>}
* @return {Promise<ValidationResult>}
* @example
* validator.validates(event).then((success) => {
* if (success) {
* validator.validate(event).then((result) => {
* if (result.passes) {
* console.log('event validates!');
* } else {
* console.log('event failed validation');
* console.log(result.errors);
* }
* });
*/
validates(event) {
validate(event) {
let _this = this;

return this.ajv.compileAsync({$ref: event.schema})
.then(function(validate) {
return validate(event.toMessage());
if (validate(event.toMessage())) {
return new ValidationResult(_this, event, true);
} else {
let errors = validate.errors.map((error) => {
return error.message;
});
return new ValidationResult(_this, event, false, errors);
}
}
);
}
Expand Down
Loading

0 comments on commit 8de72af

Please sign in to comment.