diff --git a/README.md b/README.md index 60650a7..5431b25 100644 --- a/README.md +++ b/README.md @@ -417,6 +417,14 @@ for await (const eventType of client.readEventTypes()) { controller.abort(); ``` +### 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") +``` + ### 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); + }); +});