mirror of
https://github.com/fabro-sh/fabro.git
synced 2026-10-07 03:00:29 +00:00
feat(tracker): add Tracker trait with Linear and GitHub Projects V2 impls
- New arc-tracker crate defines the Tracker trait and shared Issue/BlockerRef types - arc-linear: replace Issue/BlockerRef with arc-tracker re-exports, add LinearTracker struct implementing Tracker, remove free functions - arc-github: add GitHubTracker implementing Tracker via Projects V2 GraphQL API, with lazy project node ID resolution (org → user fallback) and create_installation_access_token_for_projects helper - 47 tests in arc-github (15 new), 24 tests in arc-linear (updated to use LinearTracker) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
10f7316f28
commit
111b730eef
6 changed files with 1308 additions and 0 deletions
11
Cargo.lock
generated
11
Cargo.lock
generated
|
|
@ -303,6 +303,8 @@ dependencies = [
|
|||
name = "arc-github"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"arc-tracker",
|
||||
"async-trait",
|
||||
"base64",
|
||||
"chrono",
|
||||
"jsonwebtoken",
|
||||
|
|
@ -318,6 +320,8 @@ dependencies = [
|
|||
name = "arc-linear"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"arc-tracker",
|
||||
"async-trait",
|
||||
"mockito",
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
|
|
@ -439,6 +443,13 @@ dependencies = [
|
|||
"tracing",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arc-tracker"
|
||||
version = "0.2.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arc-types"
|
||||
version = "0.2.0"
|
||||
|
|
|
|||
|
|
@ -9,12 +9,15 @@ description = "GitHub App authentication and API helpers for Arc"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
arc-tracker = { path = "../arc-tracker" }
|
||||
async-trait.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
reqwest.workspace = true
|
||||
jsonwebtoken.workspace = true
|
||||
chrono.workspace = true
|
||||
tracing.workspace = true
|
||||
tokio = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
mockito = "1"
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -9,6 +9,8 @@ description = "Linear issue tracker API helpers for Arc"
|
|||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
arc-tracker = { path = "../arc-tracker" }
|
||||
async-trait.workspace = true
|
||||
serde.workspace = true
|
||||
serde_json.workspace = true
|
||||
reqwest.workspace = true
|
||||
|
|
|
|||
12
lib/crates/arc-tracker/Cargo.toml
Normal file
12
lib/crates/arc-tracker/Cargo.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
[package]
|
||||
name = "arc-tracker"
|
||||
edition.workspace = true
|
||||
version.workspace = true
|
||||
license.workspace = true
|
||||
description = "Tracker trait and types for issue tracking integrations"
|
||||
|
||||
[lib]
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
async-trait.workspace = true
|
||||
57
lib/crates/arc-tracker/src/lib.rs
Normal file
57
lib/crates/arc-tracker/src/lib.rs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
use async_trait::async_trait;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct BlockerRef {
|
||||
pub id: String,
|
||||
pub identifier: String,
|
||||
pub state: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Issue {
|
||||
/// Provider-native issue node ID.
|
||||
pub id: String,
|
||||
/// Provider-native project-item ID for status updates.
|
||||
/// None for providers where the issue ID is sufficient (e.g. Linear).
|
||||
/// For GitHub Projects, this is the ProjectV2Item node ID.
|
||||
/// Each Tracker impl is scoped to a single project, so this is unambiguous
|
||||
/// even when an issue belongs to multiple project boards.
|
||||
pub project_item_id: Option<String>,
|
||||
/// Human-readable identifier (e.g. "ABC-123" or "#42").
|
||||
pub identifier: String,
|
||||
pub title: String,
|
||||
pub description: Option<String>,
|
||||
pub priority: Option<i32>,
|
||||
pub state: String,
|
||||
pub branch_name: Option<String>,
|
||||
pub url: String,
|
||||
pub assignee_id: Option<String>,
|
||||
pub labels: Vec<String>,
|
||||
pub blocked_by: Vec<BlockerRef>,
|
||||
pub created_at: Option<String>,
|
||||
pub updated_at: Option<String>,
|
||||
}
|
||||
|
||||
/// Unified interface for project management / issue tracking systems.
|
||||
///
|
||||
/// Implementations are constructed with provider-specific config and used as
|
||||
/// `Arc<dyn Tracker>` or `Box<dyn Tracker>`.
|
||||
#[async_trait]
|
||||
pub trait Tracker: Send + Sync {
|
||||
/// Return the authenticated user's ID in the provider's system.
|
||||
async fn fetch_viewer_id(&self) -> Result<String, String>;
|
||||
|
||||
/// Add a comment to an issue. Each impl extracts the appropriate ID.
|
||||
async fn create_comment(&self, issue: &Issue, body: &str) -> Result<(), String>;
|
||||
|
||||
/// Transition an issue to a new state by name.
|
||||
/// Each impl extracts the appropriate ID (issue ID or project item ID).
|
||||
async fn update_issue_state(&self, issue: &Issue, state_name: &str) -> Result<(), String>;
|
||||
|
||||
/// Fetch issues matching any of the given state names.
|
||||
/// Project identity is in the impl's config, not here.
|
||||
async fn fetch_candidate_issues(&self, state_names: &[&str]) -> Result<Vec<Issue>, String>;
|
||||
|
||||
/// Fetch specific issues by their provider-native IDs (`Issue::id` values).
|
||||
async fn fetch_issues_by_ids(&self, ids: &[&str]) -> Result<Vec<Issue>, String>;
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue