diff options
| author | Owen Jacobson <owen@grimoire.ca> | 2024-10-08 22:43:22 -0400 |
|---|---|---|
| committer | Owen Jacobson <owen@grimoire.ca> | 2024-10-09 11:45:31 -0400 |
| commit | 9e171096a72d3e63626df7b09970476aba28eb06 (patch) | |
| tree | 7272c065fdb85148dc5c90d937fc7d13fc716ccf | |
| parent | 653e2de752a97e377fc9963ba60d9408e7089528 (diff) | |
Use a two-tier hierarchy for events.
This will make it much easier to slot in new event types (login events!).
| -rw-r--r-- | docs/api.md | 10 | ||||
| -rw-r--r-- | hi-ui/src/apiServer.js | 40 | ||||
| -rw-r--r-- | hi-ui/src/store/messages.js | 4 | ||||
| -rw-r--r-- | src/channel/event.rs | 29 | ||||
| -rw-r--r-- | src/channel/history.rs | 16 | ||||
| -rw-r--r-- | src/channel/routes/test/on_create.rs | 6 | ||||
| -rw-r--r-- | src/channel/routes/test/on_send.rs | 10 | ||||
| -rw-r--r-- | src/channel/snapshot.rs | 8 | ||||
| -rw-r--r-- | src/event/mod.rs | 54 | ||||
| -rw-r--r-- | src/event/sequence.rs | 4 | ||||
| -rw-r--r-- | src/message/event.rs | 22 | ||||
| -rw-r--r-- | src/message/history.rs | 14 | ||||
| -rw-r--r-- | src/message/snapshot.rs | 10 | ||||
| -rw-r--r-- | src/test/fixtures/event.rs | 8 | ||||
| -rw-r--r-- | src/test/fixtures/filter.rs | 6 |
15 files changed, 106 insertions, 135 deletions
diff --git a/docs/api.md b/docs/api.md index 13e71c7..73bfb38 100644 --- a/docs/api.md +++ b/docs/api.md @@ -200,7 +200,8 @@ The returned event stream is a sequence of events: ```json id: 1233 data: { -data: "type": "created", +data: "type": "channel", +data: "event": "created", data: "at": "2024-09-27T23:18:10.208147Z", data: "id": "C9876cyyz", data: "name": "example channel 2" @@ -209,6 +210,7 @@ data: } id: 1234 data: { data: "type": "message", +data: "event": "sent", data: "at": "2024-09-27T23:19:10.208147Z", data: "channel": { data: "id": "C9876cyyz", @@ -224,19 +226,21 @@ data: } id: 1235 data: { +data: "type": "message", +data: "event": "deleted", data: "at": "2024-09-28T02:44:27.077355Z", data: "channel": { data: "id": "C9876cyyz", data: "name": "example channel 2" data: }, -data: "type": "message_deleted", data: "id": "M1312acab" data: } id: 1236 data: { +data: "type": "channel", +data: "event": "deleted", data: "at": "2024-09-28T03:40:25.384318Z", -data: "type": "deleted", data: "id": "C9876cyyz" data: } diff --git a/hi-ui/src/apiServer.js b/hi-ui/src/apiServer.js index ec94e82..648edf5 100644 --- a/hi-ui/src/apiServer.js +++ b/hi-ui/src/apiServer.js @@ -55,22 +55,36 @@ export function subscribeToEvents(resume_point) { const data = JSON.parse(evt.data); switch (data.type) { - case 'created': - channelsList.update((value) => value.addChannel(data.id, data.name)) + case 'channel': + onChannelEvent(data); break; case 'message': - messages.update((value) => value.addMessage(data)); - break; - case 'message_deleted': - messages.update((value) => value.deleteMessage(data.channel.id, data.id)); - break; - case 'deleted': - activeChannel.update((value) => value.deleteChannel(data.id)); - channelsList.update((value) => value.deleteChannel(data.id)); - messages.update((value) => value.deleteChannel(data.id)); - break; - default: + onMessageEvent(data); break; } } } + +function onChannelEvent(data) { + switch (data.event) { + case 'created': + channelsList.update((value) => value.addChannel(data.id, data.name)) + break; + case 'deleted': + activeChannel.update((value) => value.deleteChannel(data.id)); + channelsList.update((value) => value.deleteChannel(data.id)); + messages.update((value) => value.deleteChannel(data.id)); + break; + } +} + +function onMessageEvent(data) { + switch (data.event) { + case 'sent': + messages.update((value) => value.addMessage(data)); + break; + case 'deleted': + messages.update((value) => value.deleteMessage(data.channel.id, data.id)); + break; + } +} diff --git a/hi-ui/src/store/messages.js b/hi-ui/src/store/messages.js index 560b9e1..cefd725 100644 --- a/hi-ui/src/store/messages.js +++ b/hi-ui/src/store/messages.js @@ -24,8 +24,8 @@ export class Messages { deleteMessage(channel, message) { - let messages = this.messages(channel).filter((msg) => msg.message.id != message); - this.channels[channel] = messages; + this.updateChannel(channel, (messages) => messages.filter((msg) => msg.id != message)); + return this; } deleteChannel(id) { diff --git a/src/channel/event.rs b/src/channel/event.rs index 9f2e263..f3dca3e 100644 --- a/src/channel/event.rs +++ b/src/channel/event.rs @@ -5,33 +5,30 @@ use crate::{ }; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] -pub struct Event { - #[serde(flatten)] - pub instant: Instant, - #[serde(flatten)] - pub kind: Kind, +#[serde(tag = "event", rename_all = "snake_case")] +pub enum Event { + Created(Created), + Deleted(Deleted), } impl Sequenced for Event { fn instant(&self) -> Instant { - self.instant + match self { + Self::Created(event) => event.instant, + Self::Deleted(event) => event.instant, + } } } #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum Kind { - Created(Created), - Deleted(Deleted), -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] pub struct Created { #[serde(flatten)] + pub instant: Instant, + #[serde(flatten)] pub channel: Channel, } -impl From<Created> for Kind { +impl From<Created> for Event { fn from(event: Created) -> Self { Self::Created(event) } @@ -39,10 +36,12 @@ impl From<Created> for Kind { #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] pub struct Deleted { + #[serde(flatten)] + pub instant: Instant, pub id: channel::Id, } -impl From<Deleted> for Kind { +impl From<Deleted> for Event { fn from(event: Deleted) -> Self { Self::Deleted(event) } diff --git a/src/channel/history.rs b/src/channel/history.rs index 0499927..78b3437 100644 --- a/src/channel/history.rs +++ b/src/channel/history.rs @@ -40,22 +40,20 @@ impl History { } fn created(&self) -> Event { - Event { + Created { instant: self.created, - kind: Created { - channel: self.channel.clone(), - } - .into(), + channel: self.channel.clone(), } + .into() } fn deleted(&self) -> Option<Event> { - self.deleted.map(|instant| Event { - instant, - kind: Deleted { + self.deleted.map(|instant| { + Deleted { + instant, id: self.channel.id.clone(), } - .into(), + .into() }) } } diff --git a/src/channel/routes/test/on_create.rs b/src/channel/routes/test/on_create.rs index ed49017..8c3c62b 100644 --- a/src/channel/routes/test/on_create.rs +++ b/src/channel/routes/test/on_create.rs @@ -2,7 +2,7 @@ use axum::extract::{Json, State}; use futures::stream::StreamExt as _; use crate::{ - channel::{app, routes}, + channel::{self, app, routes}, event, test::fixtures::{self, future::Immediately as _}, }; @@ -53,8 +53,8 @@ async fn new_channel() { .expect("creation event published"); assert!(matches!( - event.kind, - event::Kind::ChannelCreated(event) + event, + event::Event::Channel(channel::Event::Created(event)) if event.channel == response_channel )); } diff --git a/src/channel/routes/test/on_send.rs b/src/channel/routes/test/on_send.rs index 3297093..d2acc48 100644 --- a/src/channel/routes/test/on_send.rs +++ b/src/channel/routes/test/on_send.rs @@ -4,8 +4,8 @@ use futures::stream::StreamExt; use crate::{ channel, channel::routes, - event, - message::app::SendError, + event::{self, Sequenced}, + message::{self, app::SendError}, test::fixtures::{self, future::Immediately as _}, }; @@ -53,10 +53,10 @@ async fn messages_in_order() { let events = events.collect::<Vec<_>>().immediately().await; for ((sent_at, message), event) in requests.into_iter().zip(events) { - assert_eq!(*sent_at, event.instant.at); + assert_eq!(*sent_at, event.at()); assert!(matches!( - event.kind, - event::Kind::MessageSent(event) + event, + event::Event::Message(message::Event::Sent(event)) if event.message.sender == sender && event.message.body == message )); diff --git a/src/channel/snapshot.rs b/src/channel/snapshot.rs index afef2fb..d4d1d27 100644 --- a/src/channel/snapshot.rs +++ b/src/channel/snapshot.rs @@ -1,5 +1,5 @@ use super::{ - event::{Created, Event, Kind}, + event::{Created, Event}, Id, }; @@ -11,9 +11,9 @@ pub struct Channel { impl Channel { fn apply(state: Option<Self>, event: Event) -> Option<Self> { - match (state, event.kind) { - (None, Kind::Created(event)) => Some(event.into()), - (Some(channel), Kind::Deleted(event)) if channel.id == event.id => None, + match (state, event) { + (None, Event::Created(event)) => Some(event.into()), + (Some(channel), Event::Deleted(event)) if channel.id == event.id => None, (state, event) => panic!("invalid channel event {event:#?} for state {state:#?}"), } } diff --git a/src/event/mod.rs b/src/event/mod.rs index e748d66..698e55a 100644 --- a/src/event/mod.rs +++ b/src/event/mod.rs @@ -15,63 +15,29 @@ pub use self::{ pub type ResumePoint = Option<Sequence>; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] -pub struct Event { - #[serde(flatten)] - pub instant: Instant, - #[serde(flatten)] - pub kind: Kind, +#[serde(tag = "type", rename_all = "snake_case")] +pub enum Event { + Channel(channel::Event), + Message(message::Event), } impl Sequenced for Event { fn instant(&self) -> Instant { - self.instant + match self { + Self::Channel(event) => event.instant(), + Self::Message(event) => event.instant(), + } } } impl From<channel::Event> for Event { fn from(event: channel::Event) -> Self { - Self { - instant: event.instant, - kind: event.kind.into(), - } + Self::Channel(event) } } impl From<message::Event> for Event { fn from(event: message::Event) -> Self { - Self { - instant: event.instant(), - kind: event.kind.into(), - } - } -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum Kind { - #[serde(rename = "created")] - ChannelCreated(channel::event::Created), - #[serde(rename = "message")] - MessageSent(message::event::Sent), - MessageDeleted(message::event::Deleted), - #[serde(rename = "deleted")] - ChannelDeleted(channel::event::Deleted), -} - -impl From<channel::event::Kind> for Kind { - fn from(kind: channel::event::Kind) -> Self { - match kind { - channel::event::Kind::Created(created) => Self::ChannelCreated(created), - channel::event::Kind::Deleted(deleted) => Self::ChannelDeleted(deleted), - } - } -} - -impl From<message::event::Kind> for Kind { - fn from(kind: message::event::Kind) -> Self { - match kind { - message::event::Kind::Sent(created) => Self::MessageSent(created), - message::event::Kind::Deleted(deleted) => Self::MessageDeleted(deleted), - } + Self::Message(event) } } diff --git a/src/event/sequence.rs b/src/event/sequence.rs index ceb5bcb..bf6d5b8 100644 --- a/src/event/sequence.rs +++ b/src/event/sequence.rs @@ -72,6 +72,10 @@ impl Sequence { pub trait Sequenced { fn instant(&self) -> Instant; + fn at(&self) -> DateTime { + self.instant().at + } + fn sequence(&self) -> Sequence { self.instant().into() } diff --git a/src/message/event.rs b/src/message/event.rs index 712ecc1..9f49a32 100644 --- a/src/message/event.rs +++ b/src/message/event.rs @@ -5,25 +5,13 @@ use crate::{ }; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] -pub struct Event { - #[serde(flatten)] - pub kind: Kind, -} - -impl Sequenced for Event { - fn instant(&self) -> Instant { - self.kind.instant() - } -} - -#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] -#[serde(tag = "type", rename_all = "snake_case")] -pub enum Kind { +#[serde(tag = "event", rename_all = "snake_case")] +pub enum Event { Sent(Sent), Deleted(Deleted), } -impl Sequenced for Kind { +impl Sequenced for Event { fn instant(&self) -> Instant { match self { Self::Sent(sent) => sent.instant(), @@ -44,7 +32,7 @@ impl Sequenced for Sent { } } -impl From<Sent> for Kind { +impl From<Sent> for Event { fn from(event: Sent) -> Self { Self::Sent(event) } @@ -64,7 +52,7 @@ impl Sequenced for Deleted { } } -impl From<Deleted> for Kind { +impl From<Deleted> for Event { fn from(event: Deleted) -> Self { Self::Deleted(event) } diff --git a/src/message/history.rs b/src/message/history.rs index 3c3f77a..b5886af 100644 --- a/src/message/history.rs +++ b/src/message/history.rs @@ -35,22 +35,20 @@ impl History { // Events interface impl History { fn sent(&self) -> Event { - Event { - kind: Sent { - message: self.message.clone(), - } - .into(), + Sent { + message: self.message.clone(), } + .into() } fn deleted(&self) -> Option<Event> { - self.deleted.map(|instant| Event { - kind: Deleted { + self.deleted.map(|instant| { + Deleted { instant, channel: self.message.channel.clone(), id: self.message.id.clone(), } - .into(), + .into() }) } diff --git a/src/message/snapshot.rs b/src/message/snapshot.rs index a06fbc4..a8cf734 100644 --- a/src/message/snapshot.rs +++ b/src/message/snapshot.rs @@ -1,12 +1,12 @@ use super::{ - event::{Event, Kind, Sent}, + event::{Event, Sent}, Id, }; use crate::{channel::Channel, event::Instant, login::Login}; #[derive(Clone, Debug, Eq, PartialEq, serde::Serialize)] pub struct Message { - #[serde(skip)] + #[serde(flatten)] pub sent: Instant, pub channel: Channel, pub sender: Login, @@ -16,9 +16,9 @@ pub struct Message { impl Message { fn apply(state: Option<Self>, event: Event) -> Option<Self> { - match (state, event.kind) { - (None, Kind::Sent(event)) => Some(event.into()), - (Some(message), Kind::Deleted(event)) if message.id == event.id => None, + match (state, event) { + (None, Event::Sent(event)) => Some(event.into()), + (Some(message), Event::Deleted(event)) if message.id == event.id => None, (state, event) => panic!("invalid message event {event:#?} for state {state:#?}"), } } diff --git a/src/test/fixtures/event.rs b/src/test/fixtures/event.rs index 09f0490..7fe2bf3 100644 --- a/src/test/fixtures/event.rs +++ b/src/test/fixtures/event.rs @@ -1,11 +1,11 @@ use crate::{ - event::{Event, Kind}, - message::Message, + event::Event, + message::{Event::Sent, Message}, }; pub fn message_sent(event: &Event, message: &Message) -> bool { matches!( - &event.kind, - Kind::MessageSent(event) if message == &event.into() + &event, + Event::Message(Sent(event)) if message == &event.into() ) } diff --git a/src/test/fixtures/filter.rs b/src/test/fixtures/filter.rs index 6e62aea..84d27b0 100644 --- a/src/test/fixtures/filter.rs +++ b/src/test/fixtures/filter.rs @@ -1,11 +1,11 @@ use futures::future; -use crate::event::{Event, Kind}; +use crate::{channel::Event::Created, event::Event, message::Event::Sent}; pub fn messages() -> impl FnMut(&Event) -> future::Ready<bool> { - |event| future::ready(matches!(event.kind, Kind::MessageSent(_))) + |event| future::ready(matches!(event, Event::Message(Sent(_)))) } pub fn created() -> impl FnMut(&Event) -> future::Ready<bool> { - |event| future::ready(matches!(event.kind, Kind::ChannelCreated(_))) + |event| future::ready(matches!(event, Event::Channel(Created(_)))) } |
