@getanthill/datastore
Version:
Event-Sourced Datastore
116 lines (97 loc) • 2.73 kB
text/typescript
import type Ajv from 'ajv';
import type { AnyObject, Event } from '../typings';
import util from 'util';
import assert from 'assert';
import * as telemetry from '@getanthill/telemetry';
/**
* Returns available event types
* @param {object} schema Event schemas
* @returns {string[]} Event type available versions
*/
export function getEventTypes(events: AnyObject): string[] {
return Object.keys(events);
}
/**
* Returns available versions for an event type
* @param {object} schema Event schemas
* @param {object} event Object for which the type versions must be returned
* @returns {integer[]} Event type available versions
*/
export function getEventTypeVersions(schema: AnyObject, event: Event) {
const versions = Object.keys(schema.schema.events[event.type]).sort();
return {
v: event.v,
all: versions,
};
}
/**
* Validate an event against a JSON Schema contract
* @param {object} event The object to validate
* @param {object} schema The event schema
* @param {object} validator The JSON Schema validator
* @throws AssertionError if invalid
* @returns {void} -
*/
export function validate(
event: Event,
schema: AnyObject,
validator: Ajv,
throwOnInvalidEvent = true,
) {
if (!schema.schema.events[event.type]) {
const err = new assert.AssertionError({
message: 'Invalid event type',
});
// @ts-ignore
err.details = getEventTypes(schema.schema.events);
throw err;
}
const eventSchema = event.v
? schema.schema.events[event.type][event.v]
: undefined;
if (!eventSchema) {
const err = new assert.AssertionError({
message: 'Invalid event type version',
});
// @ts-ignore
err.details = getEventTypeVersions(schema, event);
throw err;
}
const schemaId = `${event.type}.${event.v}`;
let validate = validator.getSchema(schemaId);
if (!validate) {
validator.addSchema(eventSchema, schemaId);
validate = validator.getSchema(schemaId);
}
const isValid = validate!(event);
if (!isValid && throwOnInvalidEvent === true) {
telemetry.logger.error('[Events] Validation error', {
errors: validate!.errors,
});
const assertionErrorPayload = {
message: 'Event schema validation error',
expected: null,
actual: util.inspect(
{
schema: eventSchema,
event,
errors: validate!.errors,
},
false,
null,
false,
),
};
const err = new assert.AssertionError(assertionErrorPayload);
// @ts-ignore
err.details = validate.errors;
// @ts-ignore
err.details.push({
event,
});
// @ts-ignore
err.event = event;
throw err;
}
return eventSchema;
}