-
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #142 from castore-dev/create-http-event-storage-ad…
…apter feature: add HTTP event storage adapter
- Loading branch information
Showing
34 changed files
with
1,702 additions
and
56 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { EventDetail } from '@castore/core'; | ||
|
||
import { pokemonsEventStore } from '~/libs/eventStores/pokemons'; | ||
import { applyConsoleMiddleware } from '~/libs/middlewares/console'; | ||
|
||
import { Input, inputSchema } from './schema'; | ||
|
||
export const getPokemonEvents = async ( | ||
event: Input, | ||
): Promise<{ events: EventDetail[] }> => { | ||
const { | ||
queryStringParameters: { aggregateId }, | ||
} = event; | ||
|
||
return pokemonsEventStore.getEvents(aggregateId); | ||
}; | ||
|
||
export const main = applyConsoleMiddleware(getPokemonEvents, { inputSchema }); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import type { AWS } from '@serverless/typescript'; | ||
|
||
export const getPokemonEvents: Exclude<AWS['functions'], undefined>[string] = { | ||
handler: 'functions/getPokemonEvents/handler.main', | ||
events: [ | ||
{ | ||
httpApi: { | ||
method: 'GET', | ||
path: '/events', | ||
}, | ||
}, | ||
], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,15 +1,15 @@ | ||
import { catchPokemon } from './catchPokemon'; | ||
import { getPokemonEvents } from './getPokemonEvents'; | ||
import { levelUpPokemon } from './levelUpPokemon'; | ||
import { logPokemonEvents } from './logPokemonEvents'; | ||
import { logPokemonIds } from './logPokemonIds'; | ||
import { listPokemonAggregateIds } from './listPokemonAggregateIds/'; | ||
import { startPokemonGame } from './startPokemonGame'; | ||
import { wildPokemonAppear } from './wildPokemonAppear'; | ||
|
||
export const functions = { | ||
catchPokemon, | ||
getPokemonEvents, | ||
levelUpPokemon, | ||
logPokemonEvents, | ||
logPokemonIds, | ||
listPokemonAggregateIds, | ||
startPokemonGame, | ||
wildPokemonAppear, | ||
}; |
13 changes: 13 additions & 0 deletions
13
demo/implementation/functions/listPokemonAggregateIds/handler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import { ListAggregateIdsOutput } from '@castore/core'; | ||
|
||
import { pokemonsEventStore } from '~/libs/eventStores/pokemons'; | ||
import { applyConsoleMiddleware } from '~/libs/middlewares/console'; | ||
|
||
export const listPokemonAggregateIds = | ||
async (): Promise<ListAggregateIdsOutput> => { | ||
const { aggregateIds } = await pokemonsEventStore.listAggregateIds(); | ||
|
||
return { aggregateIds }; | ||
}; | ||
|
||
export const main = applyConsoleMiddleware(listPokemonAggregateIds); |
16 changes: 16 additions & 0 deletions
16
demo/implementation/functions/listPokemonAggregateIds/index.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import type { AWS } from '@serverless/typescript'; | ||
|
||
export const listPokemonAggregateIds: Exclude< | ||
AWS['functions'], | ||
undefined | ||
>[string] = { | ||
handler: 'functions/listPokemonAggregateIds/handler.main', | ||
events: [ | ||
{ | ||
httpApi: { | ||
method: 'GET', | ||
path: '/aggregateIds', | ||
}, | ||
}, | ||
], | ||
}; |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
import { pokemonsEventStore as $pokemonsEventStore } from '@castore/demo-blueprint'; | ||
import { LegacyDynamoDBEventStorageAdapter } from '@castore/event-storage-adapter-dynamodb'; | ||
import { DynamoDBSingleTableEventStorageAdapter } from '@castore/event-storage-adapter-dynamodb'; | ||
|
||
import { dynamoDBClient } from './client'; | ||
|
||
export const pokemonsEventStore = $pokemonsEventStore; | ||
|
||
pokemonsEventStore.eventStorageAdapter = new LegacyDynamoDBEventStorageAdapter({ | ||
tableName: process.env.POKEMON_EVENTS_TABLE_NAME as string, | ||
dynamoDBClient, | ||
}); | ||
pokemonsEventStore.eventStorageAdapter = | ||
new DynamoDBSingleTableEventStorageAdapter({ | ||
tableName: process.env.POKEMON_EVENTS_TABLE_NAME as string, | ||
dynamoDBClient, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,12 @@ | ||
import { trainersEventStore as $trainersEventStore } from '@castore/demo-blueprint'; | ||
import { LegacyDynamoDBEventStorageAdapter } from '@castore/event-storage-adapter-dynamodb'; | ||
import { DynamoDBSingleTableEventStorageAdapter } from '@castore/event-storage-adapter-dynamodb'; | ||
|
||
import { dynamoDBClient } from './client'; | ||
|
||
export const trainersEventStore = $trainersEventStore; | ||
|
||
trainersEventStore.eventStorageAdapter = new LegacyDynamoDBEventStorageAdapter({ | ||
tableName: process.env.TRAINER_EVENTS_TABLE_NAME as string, | ||
dynamoDBClient, | ||
}); | ||
trainersEventStore.eventStorageAdapter = | ||
new DynamoDBSingleTableEventStorageAdapter({ | ||
tableName: process.env.TRAINER_EVENTS_TABLE_NAME as string, | ||
dynamoDBClient, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
const baseConfig = require('../../.lintstagedrc'); | ||
module.exports = baseConfig; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
# HTTP Event Storage Adapter | ||
|
||
DRY Castore [`EventStorageAdapter`](https://github.com/castore-dev/castore/#--eventstorageadapter) implementation using a HTTP API. | ||
|
||
This class is mainly useful when you already have the logic for events implemented and you want to expose your methods for a front-end to use them, eg. | ||
|
||
## 📥 Installation | ||
|
||
```bash | ||
# npm | ||
npm install @castore/event-storage-adapter-http | ||
|
||
# yarn | ||
yarn add @castore/event-storage-adapter-http | ||
``` | ||
|
||
This package has `@castore/core` as peer dependency, so you will have to install it as well: | ||
|
||
```bash | ||
# npm | ||
npm install @castore/core | ||
|
||
# yarn | ||
yarn add @castore/core | ||
``` | ||
|
||
### 👩💻 Usage | ||
|
||
```ts | ||
import { HttpEventStorageAdapter } from '@castore/event-storage-adapter-http'; | ||
import { swagger } from './swagger.json'; // your swagger file | ||
|
||
|
||
const pokemonHttpEventStorageAdapter = new HttpEventStorageAdapter({ swagger }); | ||
|
||
const pokemonEventStore = new EventStore({ | ||
... | ||
eventStorageAdapter: pokemonHttpEventStorageAdapter, | ||
}); | ||
``` | ||
|
||
### 🤔 How it works | ||
|
||
You need to expose 2 API endpoints that will be used by the adapter. They need to return the data correctly formatted: | ||
|
||
- getEvents: `(aggregateId: string) => { events: EventDetail[] }` | ||
- listAggregateIds: `() => ListAggregateIdsOutput` | ||
|
||
See [here](https://castore-dev.github.io/castore/docs/event-sourcing/events/) for more details about the EventDetails type. | ||
For the `ListAggregateIdsOutput` type: | ||
|
||
```typescript | ||
type ListAggregateIdsOutput = { | ||
aggregateIds: { | ||
aggregateId: string; | ||
initialEventTimestamp: string; | ||
}[]; | ||
nextPageToken?: string; | ||
}; | ||
``` | ||
|
||
Once your API is deployed, you can export is as an OpenAPI specification (swagger) and pass it to the adapter. | ||
[Here](https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-export.html) is how export an API gateway as a swagger. | ||
|
||
This adapter uses the swagger passed in input to generate requests to you API endpoints. | ||
For each method, it looks for the tags `operationId` in the swagger to generate the request. | ||
|
||
The swagger should be typed like this, with at least the paths for the `getEvents` and `listAggregateIds` methods: | ||
|
||
```typescript | ||
type Swagger = { | ||
openapi: string; // the OpenAPI version you are using. Ex: 3.0.1 | ||
info: { | ||
title: string; // the title of your API | ||
version: string; // timestamps | ||
}; | ||
servers: { | ||
url: string; // the base url of your API | ||
variables: { | ||
basePath: { | ||
default: string; // the default value can be '' | ||
}; | ||
}; | ||
}[]; | ||
paths: { | ||
[path: string]: { | ||
[verb: string]: { | ||
operationId: string; // the operation id for the castore method (getEvents | listAggregateIds) | ||
responses: { | ||
[statusCode: string]: { | ||
description: string; | ||
content?: { | ||
[type: string]: { | ||
schema: { | ||
$ref: string; | ||
}; | ||
}; | ||
}; | ||
}; | ||
default: { | ||
description: string; | ||
}; | ||
}; | ||
parameters?: { | ||
name: string; | ||
in: string; // 'path' | 'query' | 'header' | 'cookie' | ||
description: string; | ||
required: boolean; | ||
format: string; | ||
}[]; | ||
}; | ||
}; | ||
}; | ||
}; | ||
``` | ||
|
||
### 📝 Examples | ||
|
||
Example of swagger: | ||
|
||
```json | ||
{ | ||
"openapi": "3.0.1", | ||
"info": { | ||
"title": "event-store-api", | ||
"version": "2023-10-27 14:58:17UTC" | ||
}, | ||
"servers": [ | ||
{ | ||
"url": "https://yourApiGatewayId.execute-api.eu-west-1.amazonaws.com/{basePath}", | ||
"variables": { | ||
"basePath": { | ||
"default": "" | ||
} | ||
} | ||
} | ||
], | ||
"paths": { | ||
"/aggregateIds": { | ||
"get": { | ||
"responses": { | ||
"default": { | ||
"description": "Default response for GET /aggregateIds" | ||
} | ||
}, | ||
"operationId": "listAggregateIds" | ||
} | ||
}, | ||
"/events?aggregateId={aggregateId}": { | ||
"get": { | ||
"responses": { | ||
"default": { | ||
"description": "Default response for GET /events" | ||
} | ||
}, | ||
"x-castore-operationId": "getEvents", | ||
// you can alternatively use the operationId field | ||
// "operationId": "getEvents", | ||
"parameters": [ | ||
{ | ||
"name": "aggregateId", | ||
"in": "path", | ||
"description": "aggregateId of the event-trace we want to retrieve", | ||
"required": true, | ||
"format": "int64" | ||
} | ||
] | ||
} | ||
} | ||
} | ||
} | ||
``` | ||
|
||
Note that if you don't specify the `x-castore-operationId` or the `operationId` field, then the adapter will not be able to find the method to call. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
const commonBabelConfig = require('../../commonConfiguration/babel.config'); | ||
|
||
module.exports = commonBabelConfig(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
/** @type {import('dependency-cruiser').IConfiguration} */ | ||
const baseConfig = require('../../dependency-cruiser'); | ||
module.exports = baseConfig; |
Oops, something went wrong.