refactor(issues): refresh crate across features and metrics
zhenyi committed
Modified lib/issues/features/issue/usecase/create.rs lib/issues/features/issue/usecase/create.rs +0 −3 View lib/issues/features/issue/usecase/create.rs
@@ -37,7 +37,6 @@
3737 let db = svc.db.writer();
3838 let txn = db.begin().await?;
3939
40 // 1) Resolve / create default issues board for this channel (atomic within txn)
4140 let collection: issues_entity::Model = match issues_entity::Entity::find()
4241 .filter(issues_entity::COLUMN.channel_id.eq(channel_id))
4342 .order_by_asc(issues_entity::COLUMN.created_at)
@@ -62,7 +61,6 @@
6261 }
6362 };
6463
65 // 2) SELECT … FOR UPDATE on this collection's issue rows to compute next id
6664 let max_id: Option<i32> = issue_entity::Entity::find()
6765 .filter(issue_entity::COLUMN.collection_id.eq(collection.id))
6866 .lock_exclusive()
@@ -74,7 +72,6 @@
7472 .flatten();
7573 let next_id = max_id.map(|n| n + 1).unwrap_or(1);
7674
77 // 3) Insert new issue
7875 let now = OffsetDateTime::now_utc();
7976 let issue_id = Uuid::new_v4();
8077 let active: issue_entity::ActiveModel = issue_entity::ActiveModel::builder()
Modified lib/issues/features/issue/usecase/helpers.rs lib/issues/features/issue/usecase/helpers.rs +1 −1 View lib/issues/features/issue/usecase/helpers.rs
@@ -27,7 +27,7 @@
2727 .ok_or_else(|| {
2828 IssueError::NotFound(format!("issues board for channel {channel_id}"))
2929 })?;
30 let _ = outer_load; // kept to document cross-module reuse possibility
30 let _ = outer_load;
3131 Ok(row)
3232}
3333
Modified lib/issues/features/issue_watch/usecase/upsert.rs lib/issues/features/issue_watch/usecase/upsert.rs +0 −1 View lib/issues/features/issue_watch/usecase/upsert.rs
@@ -1,2 +1 @@
1// alias re-export so handler/impls can call `upsert_watch` directly
21pub use super::helpers::upsert_watch;
No newline at end of file
Modified lib/issues/handler/middleware/metrics.rs lib/issues/handler/middleware/metrics.rs +0 −6 View lib/issues/handler/middleware/metrics.rs
@@ -2,12 +2,6 @@
22use axum::middleware::Next;
33use axum::response::Response;
44
5/// HTTP-level metrics layer for the `issues` crate.
6///
7/// Emits `issues_http_requests_total{route,method,status_class}` (counter) and
8/// `issues_http_request_duration_seconds{route,method,status_class}` (histogram)
9/// on every request that traverses the router. Uses `MatchedPath` so the
10/// `route` label stays low-cardinality (no channel-id path params).
115pub async fn metrics_layer(req: Request, next: Next) -> Response {
126 crate::metrics::ensure_described();
137
Modified lib/issues/error.rs lib/issues/error.rs +1 −2 View lib/issues/error.rs
@@ -77,8 +77,7 @@
7777 )
7878 }
7979
80 /// Bounded label for metrics. Keep stable and low-cardinality.
81 pub fn error_kind(&self) -> &'static str {
80 pub fn error_kind(&self) -> &'static str {
8281 match self {
8382 IssueError::BadRequest(_) => "bad_request",
8483 IssueError::Unauthorized(_) => "unauthorized",
Modified lib/issues/metrics.rs lib/issues/metrics.rs +0 −33 View lib/issues/metrics.rs
@@ -1,18 +1,10 @@
1//! Tracing layer for the `issues` crate.
2//!
3//! Emits metrics through the `metrics` facade. No exporter is installed here;
4//! the host binary is responsible for binding a recorder. All labels are
5//! bounded to keep cardinality low (no `channel_id`/`issue_id`/`user_id`).
6
71use std::time::Instant;
82
93use metrics::{counter, describe_counter, describe_histogram, histogram};
104
115use crate::error::IssueError;
126
13// ── Feature constants (one per usecase) ───────────────────────────────────
147
15// issue
168pub const FEATURE_ISSUES_CREATE: &str = "issues_create";
179pub const FEATURE_ISSUES_GET: &str = "issues_get";
1810pub const FEATURE_ISSUES_UPDATE: &str = "issues_update";
@@ -20,72 +12,57 @@
2012pub const FEATURE_ISSUES_LIST: &str = "issues_list";
2113pub const FEATURE_ISSUES_CLOSE: &str = "issues_close";
2214
23// issues (collection board)
2415pub const FEATURE_ISSUE_BOARDS_CREATE: &str = "issue_boards_create";
2516pub const FEATURE_ISSUE_BOARDS_GET: &str = "issue_boards_get";
2617pub const FEATURE_ISSUE_BOARDS_UPDATE: &str = "issue_boards_update";
2718pub const FEATURE_ISSUE_BOARDS_DELETE: &str = "issue_boards_delete";
2819pub const FEATURE_ISSUE_BOARDS_LIST: &str = "issue_boards_list";
2920
30// comment
3121pub const FEATURE_COMMENTS_CREATE: &str = "comments_create";
3222pub const FEATURE_COMMENTS_GET: &str = "comments_get";
3323pub const FEATURE_COMMENTS_UPDATE: &str = "comments_update";
3424pub const FEATURE_COMMENTS_DELETE: &str = "comments_delete";
3525pub const FEATURE_COMMENTS_LIST: &str = "comments_list";
3626
37// issue_attachment
3827pub const FEATURE_ISSUE_ATTACHMENTS_CREATE: &str = "issue_attachments_create";
3928pub const FEATURE_ISSUE_ATTACHMENTS_DELETE: &str = "issue_attachments_delete";
4029pub const FEATURE_ISSUE_ATTACHMENTS_LIST: &str = "issue_attachments_list";
4130
42// issue_content_history
4331pub const FEATURE_ISSUE_CONTENT_HISTORY_LIST: &str = "issue_content_history_list";
4432
45// issue_dependency
4633pub const FEATURE_ISSUE_DEPENDENCIES_CREATE: &str = "issue_dependencies_create";
4734pub const FEATURE_ISSUE_DEPENDENCIES_DELETE: &str = "issue_dependencies_delete";
4835pub const FEATURE_ISSUE_DEPENDENCIES_LIST: &str = "issue_dependencies_list";
4936
50// issue_event
5137pub const FEATURE_ISSUE_EVENTS_LIST: &str = "issue_events_list";
5238
53// issue_member
5439pub const FEATURE_ISSUE_MEMBERS_CREATE: &str = "issue_members_create";
5540pub const FEATURE_ISSUE_MEMBERS_DELETE: &str = "issue_members_delete";
5641pub const FEATURE_ISSUE_MEMBERS_LIST: &str = "issue_members_list";
5742
58// issue_pin
5943pub const FEATURE_ISSUE_PINS_CREATE: &str = "issue_pins_create";
6044pub const FEATURE_ISSUE_PINS_DELETE: &str = "issue_pins_delete";
6145pub const FEATURE_ISSUE_PINS_LIST: &str = "issue_pins_list";
6246
63// issue_reaction
6447pub const FEATURE_ISSUE_REACTIONS_CREATE: &str = "issue_reactions_create";
6548pub const FEATURE_ISSUE_REACTIONS_DELETE: &str = "issue_reactions_delete";
6649pub const FEATURE_ISSUE_REACTIONS_LIST: &str = "issue_reactions_list";
6750
68// issue_repo_rel
6951pub const FEATURE_ISSUE_REPO_REL_CREATE: &str = "issue_repo_rel_create";
7052pub const FEATURE_ISSUE_REPO_REL_DELETE: &str = "issue_repo_rel_delete";
7153pub const FEATURE_ISSUE_REPO_REL_LIST: &str = "issue_repo_rel_list";
7254
73// issue_stopwatch
7455pub const FEATURE_ISSUE_STOPWATCH_START: &str = "issue_stopwatch_start";
7556pub const FEATURE_ISSUE_STOPWATCH_DELETE: &str = "issue_stopwatch_delete";
7657
77// issue_tracked_time
7858pub const FEATURE_ISSUE_TRACKED_TIME_LIST: &str = "issue_tracked_time_list";
7959
80// issue_user
8160pub const FEATURE_ISSUE_USERS_LIST: &str = "issue_users_list";
8261pub const FEATURE_ISSUE_USERS_UPDATE: &str = "issue_users_update";
8362
84// issue_watch
8563pub const FEATURE_ISSUE_WATCHES_UPSERT: &str = "issue_watches_upsert";
8664pub const FEATURE_ISSUE_WATCHES_DELETE: &str = "issue_watches_delete";
8765
88// label
8966pub const FEATURE_LABELS_CREATE: &str = "labels_create";
9067pub const FEATURE_LABELS_GET: &str = "labels_get";
9168pub const FEATURE_LABELS_UPDATE: &str = "labels_update";
@@ -95,18 +72,15 @@
9572pub const FEATURE_LABELS_UNBIND: &str = "labels_unbind";
9673pub const FEATURE_LABELS_LIST_FOR_ISSUE: &str = "labels_list_for_issue";
9774
98// milestone
9975pub const FEATURE_MILESTONES_CREATE: &str = "milestones_create";
10076pub const FEATURE_MILESTONES_GET: &str = "milestones_get";
10177pub const FEATURE_MILESTONES_UPDATE: &str = "milestones_update";
10278pub const FEATURE_MILESTONES_DELETE: &str = "milestones_delete";
10379pub const FEATURE_MILESTONES_LIST: &str = "milestones_list";
10480
105// ── Outcome labels (bounded enums) ────────────────────────────────────────
10681
10782pub const OUTCOME_SUCCESS: &str = "success";
10883
109// ── Metric description registration (Once) ────────────────────────────────
11084
11185static DESCRIBE_ONCE: std::sync::Once = std::sync::Once::new();
11286
@@ -143,9 +117,7 @@
143117 );
144118}
145119
146// ── Timer (RAII) ──────────────────────────────────────────────────────────
147120
148/// Records the usecase execution duration on drop.
149121pub struct Timer {
150122 pub feature: &'static str,
151123 pub start: Instant,
@@ -172,10 +144,7 @@
172144 }
173145}
174146
175// ── Generic outcome counter ───────────────────────────────────────────────
176147
177/// Bump invocation counter (always) and, on `Err`, an error counter labeled
178/// by `IssueError::error_kind()`. Drop semantics of `_timer` capture latency.
179148pub fn record_outcome<T>(feature: &'static str, result: &Result<T, IssueError>) {
180149 match result {
181150 Ok(_) => {
@@ -204,7 +173,6 @@
204173 }
205174}
206175
207// ── Issue create helper ───────────────────────────────────────────────────
208176
209177#[derive(Debug, Clone, Copy)]
210178pub enum IssueCreateOutcome {
@@ -238,7 +206,6 @@
238206 .increment(1);
239207}
240208
241// ── Issue reaction helper ─────────────────────────────────────────────────
242209
243210#[derive(Debug, Clone, Copy)]
244211pub enum IssueReactionOutcome {