README
@cazoo/events
Used for raising and parsing Cazoo business events. Abstracts away EventBridge, which we are using as an event bus.
Basic Usage
EventClient.put Validates all events and will fail with a validation exception at the first event that fails validation.
import { EventClient, DoNotValidator, parse } from '@cazoo/events';
export const someHandler = async (ev, context) => {
const { name, payload } = parse(ev)
/* Implement your Lambda logic here */
// Raise a new event on the shared EventBridge event bus.
const validator = new DoNotValidator();
const client = new EventClient({ context, validator });
const event = {
name: "orderPlaced",
payload: { foo: 'bar' }
};
await client.put(event)
}
EventClient.putEvents: validates all events and in case of failures raises a ValidationErrorMultiple exception, which maps all failed events with the outcome of their validation (publication is skipped).
import { EventClient, DoNotValidator, parse } from '@cazoo/events';
export const someHandler = async (ev, context) => {
const { name, payload } = parse(ev)
/* Implement your Lambda logic here */
// Raise a new event on the shared EventBridge event bus.
const validator = new DoNotValidator();
const client = new EventClient({ context, validator });
const event = {
name: "orderPlaced",
payload: { foo: 'bar' }
};
await client.putEvents(event)
}
## Targeting a custom event bus
It's possible to use an Environment Variable `CAZOO_EVENTS_TARGET_EVENT_BUS` to use a custom event bus, you can also manually use a
custom event bus using the information below.
```ts
const client = new EventClient({
context: myContext,
eventBusName: "custom-event-bus"
});
Validating target event bus
If you wish to provide a custom event bus as your target, you can also optionally validate the existence of the event bus by using the eventBusValidation boolean as shown below:
const client = new EventClient({
context: myContext,
eventBusName: "custom-event-bus",
eventBusValidation: true
})
IAM Permissions
If you want to use event bus validation, then you will need the events:DescribeEventBus IAM permission on your Lambda.
Note: If eventBusValidation is set, then the eventBusName parameter or the environment variable CAZOO_EVENTS_TARGET_EVENT_BUS must be provided, else an error will throw
Configuring the event bridge client
You can provide your own config for the event bridge client the library uses.
const client = new EventClient({
context: myContext,
clientConfig: { endpoint: 'http://localhost:3213' }
});
If eventBusName parameter is not provided, the default event bus is targeted, instead.
Runtime validation
We now have support for runtime validation of events via the EventClient class. This class will dynamically fetch schemas from the Eventbridge Schema Registry and validate messages before they are raised.
// We use the ARN of the schema registry to look up event schemas
const registryArn = process.env.CAZOO_EVENT_REGISTRY_ARN ||
"arn:aws:schemas:eu-west-1:571578941547:registry/cazoo-events"
const validator = new DynamicSchemaValidator(registryArn)
// We create a client passing a validator and the lambda context
const client = new EventClient({
context,
validator,
});
try {
const result = await client.put({
name: "deliveryCancelled",
payload: {
orderNumber: "abc-123"
}
});
} catch (e) {
console.log(e.message);
console.log(JSON.stringify(e.errors, null, 2))
}
/* Yields the following error
data should NOT have additional properties, data should have required property 'order_number'
[
{
"keyword": "additionalProperties",
"dataPath": "",
"schemaPath": "#/additionalProperties",
"params": {
"additionalProperty": "orderNumber"
},
"message": "should NOT have additional properties"
},
{
"keyword": "required",
"dataPath": "",
"schemaPath": "#/required",
"params": {
"missingProperty": "order_number"
},
"message": "should have required property 'order_number'"
}
]
*/
If no validator is passed, validation will not be enabled.
You can also explicitly disable validation with the DoNotValidator.
const validator = new DoNotValidator()
const client = new EventClient({ context, validator });
// This won't throw
const result = await client.put({
name: "anyOldRubbish",
payload: {
foo: "bar"
}
});
IAM Permissions
Your lambda will require the following IAM permissions to validate schemas.
Serverless
iamRoleStatements:
- Effect: Allow
Action:
- schemas:DescribeSchema
Resource:
- arn:aws:schemas:${AWS::Region}:${AWS::AccountId}:schema/REGISTRY_NAME/SCHEMA_NAME
- ...
Terraform
variable "account_id" {}
variable "region" {}
variable "registry_name" {}
variable "schema_name {}
data "aws_iam_policy_document" "allow_describe_schema" {
statement {
sid = "AllowDescribeSchema"
actions = [
"schemas:DescribeSchema"
]
resources = [
"arn:aws:schemas:${var.region}:${var.account_id}:schema/${var.registry_name}/${var.schema_name}",
...
]
}
}
resource "aws_iam_policy" "allow_describe_schema" {
name = "allow-describe-schema"
path = "/"
policy = data.aws_iam_policy_document.allow_describe_schema.json
}
# attach the policy to your lambda role.
resource "aws_iam_role_policy_attachment" "attach_allow_describe_schema" {
role = aws_iam_role.lambda_execution_role.name
policy_arn = aws_iam_policy.allow_describe_schema.arn
}
Tests
Running the acceptance tests requires authentication with cazoo-dev aws account
Releases
We strictly follow semantic versioning so please consider this when working on this library.
To create a new release follow these steps:
- merge the PR on master
- execute from any local machine, staying on the master/main branch, the
npm version patch/minor/major
The tests will be automatically run, the tag will be pushed and the pipeline will get automatically triggered by the new pushed tag and will deploy to NPM too.