Skip to content

Commit

Permalink
Add validatorAt function in EventValidator
Browse files Browse the repository at this point in the history
This allows us to precache a configured list of URIs
in wikimedia-eventgate instance.

Bug: T217661
  • Loading branch information
ottomata committed Mar 5, 2019
1 parent 9f7b9df commit d048e2d
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 23 deletions.
6 changes: 6 additions & 0 deletions config/config.wikimedia.yaml
Expand Up @@ -66,6 +66,12 @@ services:
# You can alteratively or additionally use a remote HTTP base URI.
#schema_base_uris: [https://raw.githubusercontent.com/wikimedia/mediawiki-event-schemas/master/jsonschema]

# These schema URIs will be 'precached' on service startup.
# They should be resolveable by the URI prefixes in schema_base_uris.
# TODO: we could get this list from a service?
schema_precache_uris:
- /test/event/0.0.2
- /mediawiki/api/request/0.0.1

# If set, this will be appended to every extracted schema_uri if that schema_uri
# does not already end with a file extension.
Expand Down
54 changes: 33 additions & 21 deletions lib/EventValidator.js
Expand Up @@ -161,7 +161,7 @@ class EventValidator {
this.log.info(`Loading schema at ${uri}`);
return this.getSchema(uri)
.then((schema) => {
this.log.debug({ schema }, `Loaded schema at ${uri}`);
this.log.trace({ schema }, `Loaded schema at ${uri}`);

// If this schema has an id that matches metaSchemaIdRegex,
// then it should be added as a meta schema. (We don't need
Expand Down Expand Up @@ -198,51 +198,64 @@ class EventValidator {
}

/**
* Returns a Promise of a validate function for the event.
* If the event's schema URI or $id has been seen before, it should
* Given a schema URI, this will load, compile, and cache the schema there.
* If the schema URI or $id has been seen before, it will
* be loaded from the AJV cache and not recompiled.
* @param {Object} event
* @param {string} uri
* @return {Promise<Function>}
* @throws {EventSchemaLoadError}
*/
validatorFor(event) {
// Extract the schemaUri for this event.
const eventSchemaUri = this.extractSchemaUri(event);
this.log.trace(`Getting validator for event schema ${eventSchemaUri}`);

// If this eventSchemaUri has been seen before, return the already compiled
validatorAt(uri) {
// If this uri has been seen before, return the already compiled
// and cached AJV validator.
const validator = this.ajv.getSchema(eventSchemaUri);
const validator = this.ajv.getSchema(uri);
if (validator) {
return P.resolve(validator);
} else {
// Else load the event's schema from its schema uri and compile it.
return this.loadSchema(eventSchemaUri)
return this.loadSchema(uri)
// Wrap Event schema loading errors in EventSchemaLoadError
.catch((error) => {
throw new EventSchemaLoadError(
`Failed loading schema at ${eventSchemaUri}`,
{ originalError: error, event_schema_uri: eventSchemaUri }
`Failed loading schema at ${uri}`,
{ originalError: error, uri: uri }
);
})
.then((schema) => {
return this.ajv.compileAsync(schema).then((validatorForEvent) => {
this.log.trace(`Compiling validator for schema at ${uri}`);
return this.ajv.compileAsync(schema).then((validatorFn) => {
// Also cache this schema at uri if uri is different than schema.$id
// and this schema uri hasn't yet been added to the AJV cache
if (_.isUndefined(this.ajv.getSchema(eventSchemaUri))) {
if (_.isUndefined(this.ajv.getSchema(uri))) {
this.log.debug(
'Additionally caching schema with $id ' +
`'${validatorForEvent.schema.$id}' by event schema URI ` +
`${eventSchemaUri}`
`'${validatorFn.schema.$id}' by schema URI ` +
`${uri}`
);
this.ajv.addSchema(validatorForEvent.schema, eventSchemaUri);
this.ajv.addSchema(validatorFn.schema, uri);
}
return validatorForEvent;

this.log.trace(
`Compiled and cached validator for schema at ${uri}`
);
return validatorFn;
});
});
}
}

/**
* Returns a Promise of a validate function for the event.
* The event's schema URI will be extracted using the provided extractSchemaUri function.
* @param {Object} event
* @return {Promise<Function>}
* @throws {EventSchemaLoadError}
*/
validatorFor(event) {
// Extract the schemaUri for this event and get a validator for it.
return this.validatorAt(this.extractSchemaUri(event));
}

/**
* Returns a Promise of the validated event using it's JSONSchema at its schema URL,
* Or throws an ValidationError.
Expand All @@ -253,7 +266,6 @@ class EventValidator {
validate(event) {
return this.validatorFor(event)
.then((validateFn) => {

if (!validateFn(event)) {
throw new ValidationError(
`Event failed validation with schema at ${this.extractSchemaUri(event)}`,
Expand Down
9 changes: 8 additions & 1 deletion lib/factories/wikimedia-eventgate.js
@@ -1,7 +1,7 @@
'use strict';

const _ = require('lodash');

const P = require('bluebird');
const uuid = require('cassandra-uuid').TimeUuid;

const {
Expand Down Expand Up @@ -323,6 +323,13 @@ async function makeWikimediaValidate(options, logger) {
'No stream_config_uri was set; any $schema will be allowed in any stream.'
);
}

if (options.schema_precache_uris) {
P.map(options.schema_precache_uris, (uri) => {
logger.info(`Precaching schema at ${uri}`);
return eventValidator.validatorAt(uri);
});
}
}

await init();
Expand Down
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "eventgate",
"version": "1.0.0",
"version": "1.0.1",
"description": "Event intake service - POST JSONSchemaed events, validate, and produce.",
"main": "./app.js",
"scripts": {
Expand Down

0 comments on commit d048e2d

Please sign in to comment.