Skip to content

Releases: event-driven-io/emmett

0.12.1

11 Jul 13:49
Compare
Choose a tag to compare

📝 What's Changed

  • Fixed mapping of optimistic concurrency for PostgreSQL event store by @oskardudycz in #88

Full Changelog: 0.12.0...0.12.1

0.12.0

11 Jul 13:06
Compare
Choose a tag to compare

🚀 What's New

  • Boom! 💣 🔥 🐘 Added a first experimental implementation of PostgreSQL inline projections. by @oskardudycz in #90

You can do it e.g with:

type ShoppingCartShortInfo = {
  productItemsCount: number;
  totalAmount: number;
};

const shoppingCartShortInfoCollectionName = 'shoppingCartShortInfo';

const evolve = (
  document: ShoppingCartShortInfo | null,
  { type, data: event }: ProductItemAdded | DiscountApplied,
): ShoppingCartShortInfo => {
  document = document ?? { productItemsCount: 0, totalAmount: 0 };

  switch (type) {
    case 'ProductItemAdded':
      return {
        totalAmount:
          document.totalAmount +
          event.productItem.price * event.productItem.quantity,
        productItemsCount:
          document.productItemsCount + event.productItem.quantity,
      };
    case 'DiscountApplied':
      return {
        ...document,
        totalAmount: (document.totalAmount * (100 - event.percent)) / 100,
      };
  }
};

const shoppingCartShortInfoProjection = pongoSingleProjection(
  shoppingCartShortInfoCollectionName,
  evolve,
  'ProductItemAdded',
  'DiscountApplied',
);

Then register it as:

import { getPool, getPostgreSQLEventStore } from '@event-driven-io/emmett-postgresql';

const connectionString =
  "postgresql://dbuser:[email protected]:3211/mydb";

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

And read models will be updated in the same transaction as appended events.

const shoppingCartId = `shopping_cart-${uuid()}`;
const productItem: PricedProductItem = {
  productId: '123',
  quantity: 10,
  price: 3,
};

await eventStore.appendToStream<ShoppingCartEvent>(shoppingCartId, [
  { type: 'ProductItemAdded', data: { productItem } },
]);

const shoppingCartShortInfo = pongo
  .db()
  .collection<ShoppingCartShortInfo>(shoppingCartShortInfoCollectionName);

const document = await shoppingCartShortInfo.findOne({ _id: shoppingCartId });

📝 What's Changed

  • Added sample showing PostgreSQL event store by @oskardudycz in #87
  • Used code from Dumbo instead of duplicated one between Emmett and Pongo by @oskardudycz in #89

Full Changelog: 0.12.1...0.13.0

0.11.2

24 Jun 15:35
Compare
Choose a tag to compare

📝 What's Changed

  • Fixed in-memory message bus declaration to correctly expose command and event processor methods. Added missing explicit definition that message bus is also Event and Command processor. by @oskardudycz in #83

Full Changelog: 0.11.1...0.11.2

0.11.1

24 Jun 15:33
Compare
Choose a tag to compare

📝 What's Changed

  • Added missing exports for MessageBus. It appeared that I forgot to export the MessageBus related code in the main index file, and then they were trimmed by tree shaking during publishing. Now they're correctly exported. by @oskardudycz in #82

Full Changelog: 0.11.0...0.11.1

0.11.0

13 Jun 07:35
Compare
Choose a tag to compare

🚀 What's New

Full Changelog: 0.10.0...0.11.0

0.10.0

12 Jun 14:38
Compare
Choose a tag to compare

🚀 What's New

  • Added example showing how to run Emmett application in Docker container. It shows the full setup with running WebApi, building Docker image with bundling etc. by @oskardudycz in #67

  • BREAKING: Aligned initial state naming for state aggregation. It wasn't consistent, sometimes it was called initialState, sometimes getInitialState. Now all is aligned to initialState by @oskardudycz in #80

  • Extended thenThrows in DeciderSpecification to handle type checks. Now it allows multiple options like:

    • just checking if the error was thrown,
    • if it is of the specified type,
    • if matches condition,
    • if it is of the specified type and matches the condition.

    by @oskardudycz in #72

  • Added returning events from the command handler. Now, besides the new state, you'll also get new events in the command handler result. Note: The returned events are raw domain events without the metadata assigned by the event store during appending. by @oskardudycz in #73

  • Updated CreatedHttpResponse not to require both createdId and URL but allow providing either of them. Sometimes there's no clear record id to return, so forcing the user to provide createdId is redundant. Just redirect is fine. by @oskardudycz in #71

  • Exposed Typed TestRequest for API integration and E2E tests Now, it's possible to provide a named setup without referencing supertest in the end project. For instance

const openedShoppingCartWithProduct: TestRequest = (request) =>
      request
        .post(`/clients/${clientId}/shopping-carts/current/product-items`)
        .send(productItem);

by @oskardudycz in #77

  • Added helpers for parsing and validating dates in yyyy-mm-dd format You can now use formatDateToUtcYYYYMMDD, isValidYYYYMMDD, parseDateFromUtcYYYYMMDD. It can be useful for validation or parsing incoming web API data in a standardised way. by @oskardudycz in #78
  • Added assertions to not need to use built-in Node to make it compatible with browsers by @oskardudycz in #79
  • Added implementation of restreaming and shim for WebStreams API. This is the first step toward subscription handling by @oskardudycz in #70, #68

📝 What's Changed

  • Requiring only partial problem details in expectError helper for API and Integration tests by @oskardudycz in #75

Full Changelog: 0.9.0...0.10.0

0.9.0

17 May 09:15
Compare
Choose a tag to compare

🚀 What's New

  • Allow Custom appendToStream options when using CommandHandler Now you can extend the command handler to take your own event store implementation that supersets the default event store definition by @alex-laycalvert in #63

📝 What's Changed

  • Removed node packages usage in core Emmett package. It appeared in #64 that, by accident, the in-memory event store was using randomUUID from node:crypto, which was causing compatibility issues on the web. Added also the ESLint rule to prevent such things to happen in the future. by @oskardudycz in #66
  • Fixed configuration for a single file debugging by @oskardudycz in #65

New Contributors

Full Changelog: 0.8.0...0.9.0

0.8.0

28 Apr 12:42
Compare
Choose a tag to compare

🚀 What's New

  • Added default handling of EventStoreDB test container ARM64 image. That should make the Mac M1 setup easier. Also changed EventStoreDB test container options to be in the nested object and false by default. That should make them more straightforward. by @oskardudycz in #62

Full Changelog: 0.7.1...0.8.0

0.7.1

28 Apr 10:01
Compare
Choose a tag to compare

📝 What's Changed

  • Fixed CommonJs import issues by making event store features tests internal not to expose node packages in bundled Emmett code. It appeared that tsup and esbuild used internally as code bundlers are stripping node: prefix for non-node targets, causing compatibility issues for CommonJs modules. Made internal code that was exposing it in code bundle. by @oskardudycz in #61, #59
  • Fixed the linter issue with no-floating-promise rule in tests. It seems that marking describes and it with a void keyword fixes the issue and still allows floating promises to be caught in the test code. by @oskardudycz in #61, #16

Full Changelog: 0.7.0...0.7.1

0.7.0

12 Apr 16:02
Compare
Choose a tag to compare

🚀 What's New

  • Added in-memory Message Bus implementation. It's a small step towards handling read models and subscriptions in Emmett. It allows publishing events, sending commands, and handling them in memory. Read more in the article How to build an in-memory Message Bus in TypeScript by @oskardudycz in #58

    Basic usage:

    import { getInMemoryMessageBus } from '@event-driven-io/emmett';
    
    const messageBus = getInMemoryMessageBus();

    registering handlers:

    messageBus.subscribe(
      handleGuestStay,
      'GuestCheckedIn',
      'ChargeRecorded',
      'GuestCheckedOut',
      'GuestCheckoutFailed',
    );
    

    And publishing new event:

    const event:GuestCheckedOut = {
      type: 'GuestCheckedOut',
     data: {
        guestStayAccountId: 'r9293';
        checkedOutAt: new Date();
      }
    >;
    await messageBus.publish(event);

Full Changelog: 0.6.0...0.7.0