Welcome to your first day at the Cactus Corporation! As you probably know, we've got two main product offerings: a shipping business and a movie streaming service.
To help our users keep track of the state of their account, we've recently launched CactusEvents. These are small webhook payloads that get sent when something changes in the user's account.
Initial user response has been good, but we strive to be the sharpest needle in the cactus and we haven't won yet. Specifically, we want to improve the experience of handling incoming events and are looking for fresh-eyed feedback.
Cactus Corp sends "thin" events to our users, which contain minimal info but provide tools to fetch the full event data. The initial payload looks like this:
{
"id": "evt_123",
"type": "order.shipped",
"related_object": {
"id": "ord_456",
"type": "order"
}
}
Important
Not all events types have a related object. See the table below for more information.
There's a corresponding Event
object you can retrieve (or "pull") from the Cactus API. It's identical as the thin event, but it includes the event's data
:
{
"id": "evt_123",
"type": "order.shipped",
"related_object": {
"id": "ord_456",
"type": "order"
},
"data": {
"shipping_service": "usps"
}
}
We send events related to the the following object types:
interface Order {
id: string;
created: string;
num_items: number;
cost_cents: number;
delivery_date: string;
}
interface Movie {
id: string;
title: string;
release_year: number;
}
With our new V2 event parsing system, we've got classes for every event type. We send you the "pushed" version and you can "pull" the full version. They're identical except the pull version includes the event's Data
type | related object | data properties |
---|---|---|
order.shipped |
Order |
shipping_service |
order.delivery_attempted |
Order |
success , attempt_num , delivery_location |
order.lost |
None | last_seen_city |
movie.started |
Movie |
date |
movie.completed |
Movie |
user , rating |
Each "pushed" event supports the following methods:
{
/**
* returns the full event type corresponding to this push event
*/
pull();
/**
* retrieves the related object by id, if applicable
*/
fetchRelatedObject();
}
The CactusClient
is a convenient way to fetch data from CactusCorp. It handles authentication, retries, etc. It's got the following shape:
{
/**
* @deprecated
*/
parseEventV1(body: string): GenericThinEvent
parseEventV2(body: string): PushedThinEvent
retrieveEvent(id: string): ThinEvent
retrieveOrder(id: string): Order
retrieveMovie(id: string): Movie
}
Today, you'll be using your cactus expertise to help a user fix bugs in their webhook handling code. It's been throwing runtime errors and they're struggling to resolve them. They've also been ignoring a lot of in-editor warnings, so that's probably related.
Please:
- Swap
parseEventV1()
toparseEventV2()
- Resolve any type errors (check w/
npm run --silent typecheck
) - Remove any manual casts (
as ...
) - Resolve any runtime errors (run code w/
npm run --silent part-1
) - Add a new handler branch for the
movie.completed
event. It should print the user's rating and the movie's title. Bear in mind that some data is on the event itself while other data is on the related object.
In the pursuit of even better event handling, we're working on a prototype CactusHandler
that will make event handling even easier. We're focusing on eliminating the manual type casts and abstracting away some of the complexity that comes with handling events.
It's a callback-based design. The handle
function takes the incoming request body and calls the matching callback. For instance:
const handler = new CactusHandler().register("some.event", (thinEvent) => {
console.log("handling", thinEvent.type);
});
handler.handle('{"type": "some.event"}'); // prints "handling some.event"
For part 2, we'd like to migrate our handler from part 1 to this newer style. To do that:
- Migrate the existing
eventHandler
function to a series ofhandler.register(...)
calls - Resolve any type errors (check w/
npm run --silent typecheck
) - Resolve any runtime errors (run code w/
npm run --silent part-2
) - Delete
eventHandler
entirely.
When you're done, we'll talk about this new approach and compare the two.