From 6864ad2dbde1edf0ffe5f4432c14057165dfa948 Mon Sep 17 00:00:00 2001 From: Matthias Harzer Date: Fri, 25 Jul 2025 15:44:05 +0200 Subject: [PATCH 1/2] feat: add support for the `/read-event-type` endpoint --- README.md | 8 +++++ src/Client.ts | 31 ++++++++++++++++ src/readEventType.test.ts | 76 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 115 insertions(+) create mode 100644 src/readEventType.test.ts diff --git a/README.md b/README.md index 60650a7..c703c2b 100644 --- a/README.md +++ b/README.md @@ -389,6 +389,7 @@ for await (const subject of client.readSubjects( controller.abort(); ``` + ### Listing Event Types To list all event types, call the `readEventTypes` function. The function returns an asynchronous iterator, which you can use e.g. inside a `for await` loop: @@ -417,6 +418,13 @@ for await (const eventType of client.readEventTypes()) { controller.abort(); ``` +### Listing A Specific Event Type +To list a specific event type, call the `readEventTypes` function with the event type as an argument. The function returns an detailed event type, which includes the schema: + +```typescript +eventType = await client.readEventType("io.eventsourcingdb.library.book-acquired") +``` + ### Using Testcontainers Import the `Container` class, create an instance, call the `start` function to run a test container, get a client, run your test code, and finally call the `stop` function to stop the test container: diff --git a/src/Client.ts b/src/Client.ts index b29abfd..05184d4 100644 --- a/src/Client.ts +++ b/src/Client.ts @@ -424,6 +424,37 @@ class Client { })(); } + public async readEventType(eventType: string): Promise { + const url = this.#getUrl('/api/v1/read-event-type'); + const response = await fetch(url, { + method: 'post', + headers: { + authorization: `Bearer ${this.#apiToken}`, + 'content-type': 'application/json', + }, + body: JSON.stringify({ + eventType, + }), + }); + + if (response.status !== 200) { + throw new Error( + `Failed to read event type, got HTTP status code '${response.status}', expected '200'.`, + ); + } + const responseBody = await response.json(); + if ( + !hasShapeOf(responseBody, { + eventType: 'string', + isPhantom: true, + }) + ) { + throw new Error('Failed to parse response.'); + } + + return responseBody; + } + public readEventTypes(signal?: AbortSignal): AsyncGenerator { const url = this.#getUrl('/api/v1/read-event-types'); const apiToken = this.#apiToken; diff --git a/src/readEventType.test.ts b/src/readEventType.test.ts new file mode 100644 index 0000000..a38c359 --- /dev/null +++ b/src/readEventType.test.ts @@ -0,0 +1,76 @@ +import assert from 'node:assert/strict'; +import { afterEach, beforeEach, suite, test } from 'node:test'; +import { Container } from './Container.js'; +import type { EventCandidate } from './EventCandidate.js'; +import { getImageVersionFromDockerfile } from './getImageVersionFromDockerfile.js'; + +suite('readEventType', { timeout: 20_000 }, () => { + let container: Container; + + beforeEach(async () => { + const imageVersion = getImageVersionFromDockerfile(); + container = new Container().withImageTag(imageVersion); + await container.start(); + }); + + afterEach(async () => { + await container.stop(); + }); + + test('fails if the event type does not exist.', async (): Promise => { + const client = container.getClient(); + + await assert.rejects( + async () => { + await client.readEventType('non.existent.eventType'); + }, + { + name: 'Error', + message: "Failed to read event type, got HTTP status code '404', expected '200'.", + }, + ); + }); + + test('fails if the evenet type is malformed.', async (): Promise => { + const client = container.getClient(); + + await assert.rejects( + async () => { + await client.readEventType('malformed.eventType.'); + }, + { + name: 'Error', + message: "Failed to read event type, got HTTP status code '400', expected '200'.", + }, + ); + }); + + test('read an existing event type.', async (): Promise => { + const client = container.getClient(); + + const firstEvent: EventCandidate = { + source: 'https://www.eventsourcingdb.io', + subject: '/test', + type: 'io.eventsourcingdb.test.foo', + data: { + value: 23, + }, + }; + + const secondEvent: EventCandidate = { + source: 'https://www.eventsourcingdb.io', + subject: '/test', + type: 'io.eventsourcingdb.test.bar', + data: { + value: 42, + }, + }; + + await client.writeEvents([firstEvent, secondEvent]); + + const readEventType = await client.readEventType('io.eventsourcingdb.test.foo'); + assert.equal(readEventType.eventType, 'io.eventsourcingdb.test.foo'); + assert.equal(readEventType.isPhantom, false); + assert.equal(readEventType.schema, undefined); + }); +}); From 08cdd9ba089a7d41cd98fc8ad055678744fcc2ec Mon Sep 17 00:00:00 2001 From: Golo Roden Date: Tue, 29 Jul 2025 22:34:30 +0200 Subject: [PATCH 2/2] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index c703c2b..5431b25 100644 --- a/README.md +++ b/README.md @@ -389,7 +389,6 @@ for await (const subject of client.readSubjects( controller.abort(); ``` - ### Listing Event Types To list all event types, call the `readEventTypes` function. The function returns an asynchronous iterator, which you can use e.g. inside a `for await` loop: @@ -418,8 +417,9 @@ for await (const eventType of client.readEventTypes()) { controller.abort(); ``` -### Listing A Specific Event Type -To list a specific event type, call the `readEventTypes` function with the event type as an argument. The function returns an detailed event type, which includes the schema: +### Listing a Specific Event Type + +To list a specific event type, call the `readEventType` function with the event type as an argument. The function returns the detailed event type, which includes the schema: ```typescript eventType = await client.readEventType("io.eventsourcingdb.library.book-acquired")