Skip to content

Releases: event-driven-io/emmett

0.20.2

25 Oct 18:19
Compare
Choose a tag to compare

📝 What's Changed

Full Changelog: 0.20.1...0.20.2

0.20.1

23 Oct 12:30
Compare
Choose a tag to compare

📝 What's Changed

  • Fix CommandHandler type definition for handleOptions By accident the 0.20.0 provided regression for the event store customisation if you're building your own based on Emmett's abstractions by @alex-laycalvert in 123

Full Changelog: 0.20.0...0.20.1

0.20.0

11 Oct 13:30
3f1c6c4
Compare
Choose a tag to compare

🚀 What's New

  • [BREAKING] Command handling idempotency improvements. This will allow automatic retries, e.g. for optimistic concurrency check, and also ignoring the command by returning an empty array. All of that should give easier handling for idempotent handlers. by @oskardudycz in 119
  • Added Support for an async decide / handle function for CommandHandler Kids, be careful with that! by @alex-laycalvert in 99
  • Added streaming capabilities. That's the foundational work for event subscriptions and event streaming through WebStreams. by @oskardudycz in 76

📝 What's Changed

Full Changelog: 0.19.1...0.20.0

0.19.1

06 Oct 11:20
Compare
Choose a tag to compare

📝 What's Changed

Full Changelog: 0.19.0...0.19.1

0.19.0

02 Oct 06:39
Compare
Choose a tag to compare

🚀 What's New

  • Bumped Pongo to 0.15.0 by @oskardudycz in 115, 117. Check detailed log of changes in Pongo 0.14.0 and 0.15.0 release notes.
  • Added Initial Emmett CLI. The migration support will come tho in the follow up release. by @oskardudycz in 116

Full Changelog: 0.18.0...0.19.0

0.18.0

27 Aug 19:25
Compare
Choose a tag to compare

🚀 What's New

  • Added default implementation of PostgreSQL event store session It automatically reuses a single connection inside the same command handler. That cut the time to at least a half for command handling processing. by @oskardudycz in #110
  • Added also capability to migrate, return string and print schema for PostgreSQL event store. You can do it with:
// migrate
await eventStore.schema.migrate();

// print
eventStore.schema.print();

// return
const sql = evenStore.schema.sql();

There'll be a follow-up release with the command line utility. by @oskardudycz in #110

##📝 What's Changed

  • Updated to Pongo 0.13.0 and other peer dependencies This is technically a breaking change, as some dependencies were bumped by a major version. by @oskardudycz in #113
  • Updated TS config to fix debugging by @oskardudycz in #114

Full Changelog: 0.17.0...0.18.0

0.17.0

13 Aug 09:27
Compare
Choose a tag to compare

🚀 What's New

1. Added idempotence check tests for PostgreSQL projections. Now you can simulate retries, ensuring that your projection handling is idempotent. You can do it by specifying when options, passing the number of times, e.g. { numberOfTimes: 2 }

See full example:

void test('with idempotency check', () => {
  const couponId = uuid();

  return given(
    eventsInStream<ProductItemAdded>(shoppingCartId, [
      {
        type: 'ProductItemAdded',
        data: {
          productItem: { price: 100, productId: 'shoes', quantity: 100 },
        },
      },
    ]),
  )
    .when(
      newEventsInStream(shoppingCartId, [
        {
          type: 'DiscountApplied',
          data: { percent: 10, couponId },
        },
      ]),
      { numberOfTimes: 2 },
    )
    .then(
      expectPongoDocuments
        .fromCollection<ShoppingCartShortInfo>(
          shoppingCartShortInfoCollectionName,
        )
        .withId(shoppingCartId)
        .toBeEqual({
          productItemsCount: 100,
          totalAmount: 9000,
          appliedDiscounts: [couponId],
        }),
    );
})

Note: This is not needed for inline projections as they will be either applied or not, but it will be more apt for async projections.

by @oskardudycz in 106

2. Added initialState option to Pongo projections to allow getting a non-nullable state

It's optional, but you can do it by:

const shoppingCartShortInfoProjection = pongoSingleStreamProjection({
  collectionName: shoppingCartShortInfoCollectionName,
  evolve,
  canHandle: ['ProductItemAdded', 'DiscountApplied'],
  initialState: () => ({
    productItemsCount: 0,
    totalAmount: 0,
    appliedDiscounts: [],
  }),
});

Then your evolve function will require to have the signature taking non-nullable state, e.g.:

const evolve = (
  document: ShoppingCartShortInfo,
  { type, data: event }: ProductItemAdded | DiscountApplied,
): ShoppingCartShortInfo => {
  switch (type) {
    case 'ProductItemAdded':
      return {
        ...document,
        totalAmount:
          document.totalAmount +
          event.productItem.price * event.productItem.quantity,
        productItemsCount:
          document.productItemsCount + event.productItem.quantity,
      };
    case 'DiscountApplied':
      // idempotence check
      if (document.appliedDiscounts.includes(event.couponId)) return document;

      return {
        ...document,
        totalAmount: (document.totalAmount * (100 - event.percent)) / 100,
        appliedDiscounts: [...document.appliedDiscounts, event.couponId],
      };
  }
};

by @oskardudycz in 106

3. Extended typing for PostgreSQL projections to include global store position in event metadata

PostgreSQL event store maintains the global position, so it's worth reflecting that through the metadata.

Full Changelog: 0.16.0...0.17.0

0.16.0

09 Aug 18:05
Compare
Choose a tag to compare

🚀 What's New

1. Added PostgreSQL and Pongo Projection specification for integration testing

Now you can write your tests as:

const given = PostgreSQLProjectionSpec.for({
  projection: shoppingCartShortInfoProjection,
  connectionString,
});

test('with empty given and when eventsInStream', 
  given(
    eventsInStream<ProductItemAdded>(shoppingCartId, [
      {
        type: 'ProductItemAdded',
        data: {
          productItem: { price: 100, productId: 'shoes', quantity: 100 },
        },
      },
    ]),
 )
  .when(
    newEventsInStream(shoppingCartId, [
      {
        type: 'DiscountApplied',
        data: { percent: 10 },
      },
    ]),
  )
  .then(
    expectPongoDocuments
      .fromCollection(shoppingCartShortInfoCollectionName)
      .withId(shoppingCartId)
      .toBeEqual({
        productItemsCount: 100,
        totalAmount: 9000,
      }),
    );
});

There are also other helpers; you can also check raw projections using helpers like assertSQLQueryResultMatches.

by @oskardudycz in #105

2. Refactored Postgres projections registrations

  • Separated projection definition from registration. Thanks to that, projections will be able to switch their type in the future or be used in both (e.g., for rebuilds).
  • Refactored projections to take params object instead of a list of parameters. That will cut the number of permutations and make it easier to define them
  • Renamed pongoSingleProjection to pongoSingleStreamProjection to keep naming consistency.

Now, registration will look like this (notice projections.inline helper):

import { projections } from '@event-driven-io/emmett';

const eventStore = getPostgreSQLEventStore(connectionString, {
  projections: projections.inline(
    [shoppingCartShortInfoProjection, customProjection]
  ),
});

And Pongo projection definition as:

const shoppingCartShortInfoProjection = pongoSingleStreamProjection({
  collectionName: 'shoppingCartsShortInfo',
  evolve,
  canHandle: ['ProductItemAdded', 'DiscountApplied'],
});

by @oskardudycz in #104

📝 What’s Changed

Full Changelog: 0.15.0...0.16.0

0.15.0

07 Aug 12:57
Compare
Choose a tag to compare

🚀 What's New

1. Refactored PostgreSQL event store to benefit from the latest Pongo and Dumbo improvements

Now, it's possible to inject ambient connections and use non-pooled event stores in the same way as in Pongo.

To run a non-pooled setup (e.g. for Cloudflare Hyperdrive or Supabase Supavisor) you need to do the following:

const eventStore = getPostgreSQLEventStore(connectionString, {
  projections: [shoppingCartShortInfoProjection, customProjection],
  connectionOptions: { pooled: false },
});

Now you can also use custom projection as:

import { sql } from '@event-driven-io/dumbo';
import { postgreSQLInlineProjection, sql } from '@event-driven-io/emmett-postgresql';

const customProjection = postgreSQLInlineProjection<ShoppingCartEvent>({
  name: 'customProjection',
  canHandle: ['ProductItemAdded'],
  handle: (events, { execute }) => 
    execute.batchCommand(
      events.map(({ shoppingCartId, productId, quantity }) =>
        sql('INSERT INTO product_items VALUES(%s, %L, %L)', productId, quantity));
      )
    ),
});

by @oskardudycz in #101

2. Added raw sql projections to support raw SQL handling

Generalised also projection definition.

Made postgreSQLProjection and inlineProjection obsolete, use postgreSQLProjection and postgreSQLInlineProjection instead.

Now you can use the new projections raw projection as:

import { sql } from '@event-driven-io/dumbo';
import { postgreSQLRawSQLProjection } from '@event-driven-io/emmett-postgresql';

const rawSQLProjection = postgreSQLRawSQLProjection<ProductItemAdded>({
  name: 'customProjection',
  canHandle: ['ProductItemAdded'],
  handle: (event, context) => 
    sql(
      'INSERT INTO product_items VALUES(%s, %s, %L)', 
      event.shoppingCartId, event.productId, event.quantity)
    )
});

There's also postgreSQLRawBatchSQLProjection that handles a batch of events accordingly. Both can be sync or async. You can access and query the database through the context handler param.

by @oskardudycz in #102

3. Added a basic example of projection handling to PostgreSQL samples.

You can check it here in the source code. by @oskardudycz in 100

Full Changelog: 0.14.1...0.15.0

0.14.1

05 Aug 09:17
Compare
Choose a tag to compare

📝 What's Changed

  • Exposed missing Pongo projection types and functions by @oskardudycz in #98

Full Changelog: 0.14.0...0.14.1