feat(api): reshape raw events into TimelineItem
GET /v1/events now returns the presentation form rather than raw
upstream payloads. The frontend stays dumb: it renders title /
subtitle / body segments and picks an icon from a small kebab-case
enum. Title and subtitle are arrays of {text} | {text, url} segments
so the UI can interleave plain copy with anchors without parsing.
New entities (in moments-entities):
TimelineItem — id, source, action, occurred_at, icon, title, subtitle, body
TitleSegment — Text | Link
TimelineBody — Markdown | Commits | Links
CommitSummary — sha, short_sha, message, url, author
TimelineIcon — kebab-case enum; UI falls back to Generic on unknowns
Reshape lives in moments-core::presentation, dispatched by source.
github.rs covers the event types observed on grenade's feed:
PushEvent, PullRequest{,Review,ReviewComment}Event, Issues{,Comment}Event,
Create/Delete/Fork/Watch/Release/CommitComment/PublicEvent.
Anything else falls back to a generic "<action> on <repo>" line.
Other sources (gitea, hg, bugzilla) currently use a stub fallback;
they get their own reshape modules in steps 5 and 6.
4 unit tests cover the load-bearing cases (push commit list, merged
PR icon swap, issue-comment markdown body, unknown-event fallback).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,9 +9,9 @@ use axum::{
|
||||
};
|
||||
use chrono::{DateTime, Utc};
|
||||
use clap::Parser;
|
||||
use moments_core::EventReader;
|
||||
use moments_core::{EventReader, reshape};
|
||||
use moments_data::PgStore;
|
||||
use moments_entities::{Event, EventQuery, Source, SourceSummary};
|
||||
use moments_entities::{EventQuery, Source, SourceSummary, TimelineItem};
|
||||
use serde::Deserialize;
|
||||
use tower_http::{cors::CorsLayer, trace::TraceLayer};
|
||||
use tracing::info;
|
||||
@@ -83,7 +83,7 @@ struct EventsQueryParams {
|
||||
async fn list_events(
|
||||
State(state): State<AppState>,
|
||||
Query(params): Query<EventsQueryParams>,
|
||||
) -> Result<Json<Vec<Event>>, ApiError> {
|
||||
) -> Result<Json<Vec<TimelineItem>>, ApiError> {
|
||||
let sources = params
|
||||
.source
|
||||
.as_deref()
|
||||
@@ -100,7 +100,8 @@ async fn list_events(
|
||||
};
|
||||
|
||||
let events = state.store.list_events(&query).await.map_err(internal)?;
|
||||
Ok(Json(events))
|
||||
let items: Vec<TimelineItem> = events.iter().map(reshape).collect();
|
||||
Ok(Json(items))
|
||||
}
|
||||
|
||||
async fn list_sources(
|
||||
|
||||
Reference in New Issue
Block a user