Skip to content

Commit

Permalink
Make Operation generic, and handle patch operations
Browse files Browse the repository at this point in the history
So far we only supported issue operations on the `activity_by_id`
command.

With this commit we separate the commands into a `activity_by_issue` and
`activity_by_patch` command that calls a generic `activity_by_id` trait
implementation.

`Operation` now takes as a generic either `cob/issue/Action.ts` or
`cob/patch/Action.ts`.

Also `Operation` now stores the array of all `Action` that happened on
said operation. I'm not entirely sure if this will help us in the
future, but it represents more closely the state of the relation between
an `Operation` and the cob `Actions`.
  • Loading branch information
sebastinez authored and rudolfs committed Jan 10, 2025
1 parent 63d1c87 commit 432db1c
Show file tree
Hide file tree
Showing 16 changed files with 218 additions and 134 deletions.
12 changes: 0 additions & 12 deletions crates/radicle-tauri/src/commands/cob.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,10 @@ use std::path::PathBuf;

use anyhow::{Context, Result};

use radicle::cob;
use radicle::git;
use radicle::identity;
use radicle_types as types;
use radicle_types::error::Error;
use radicle_types::traits::cobs::Cobs;
use radicle_types::traits::thread::Thread;
use tauri_plugin_clipboard_manager::ClipboardExt;
use tauri_plugin_dialog::DialogExt;
Expand Down Expand Up @@ -84,13 +82,3 @@ pub async fn save_embed_to_disk(

ctx.save_embed_to_disk(rid, oid, path)
}

#[tauri::command]
pub fn activity_by_id(
ctx: tauri::State<AppState>,
rid: identity::RepoId,
type_name: cob::TypeName,
id: git::Oid,
) -> Result<Vec<types::cobs::issue::Operation>, Error> {
ctx.activity_by_id(rid, type_name, id)
}
11 changes: 11 additions & 0 deletions crates/radicle-tauri/src/commands/cob/issue.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
use radicle::git;
use radicle::identity;

use radicle::issue::{Action, TYPENAME};
use radicle_types as types;
use radicle_types::error::Error;
use radicle_types::traits::cobs::Cobs;
use radicle_types::traits::issue::Issues;
use radicle_types::traits::issue::IssuesMut;

Expand Down Expand Up @@ -55,3 +57,12 @@ pub(crate) fn comment_threads_by_issue_id(
) -> Result<Option<Vec<types::cobs::thread::Thread>>, Error> {
ctx.comment_threads_by_issue_id(rid, id)
}

#[tauri::command]
pub fn activity_by_issue(
ctx: tauri::State<AppState>,
rid: identity::RepoId,
id: git::Oid,
) -> Result<Vec<types::cobs::Operation<Action>>, Error> {
ctx.activity_by_id(rid, &TYPENAME, id)
}
11 changes: 11 additions & 0 deletions crates/radicle-tauri/src/commands/cob/patch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ use radicle::git;
use radicle::identity;
use radicle::patch;

use radicle::patch::{Action, TYPENAME};
use radicle_types as types;
use radicle_types::error::Error;
use radicle_types::traits::cobs::Cobs;
use radicle_types::traits::patch::Patches;
use radicle_types::traits::patch::PatchesMut;

Expand Down Expand Up @@ -109,3 +111,12 @@ pub fn edit_patch(
) -> Result<types::cobs::patch::Patch, Error> {
ctx.edit_patch(rid, cob_id, action, opts)
}

#[tauri::command]
pub fn activity_by_patch(
ctx: tauri::State<AppState>,
rid: identity::RepoId,
id: git::Oid,
) -> Result<Vec<types::cobs::Operation<Action>>, Error> {
ctx.activity_by_id(rid, &TYPENAME, id)
}
3 changes: 2 additions & 1 deletion crates/radicle-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,16 @@ pub fn run() {
diff::get_diff,
cob::get_embed,
cob::save_embed_to_disk,
cob::activity_by_id,
cob::save_embed_by_path,
cob::save_embed_by_clipboard,
cob::save_embed_by_bytes,
cob::issue::activity_by_issue,
cob::issue::list_issues,
cob::issue::issue_by_id,
cob::issue::comment_threads_by_issue_id,
cob::issue::create_issue,
cob::issue::edit_issue,
cob::patch::activity_by_patch,
cob::patch::list_patches,
cob::patch::patch_by_id,
cob::patch::edit_patch,
Expand Down
13 changes: 13 additions & 0 deletions crates/radicle-types/bindings/cob/Operation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { Author } from "./Author";

/**
* Everything that can be done in the system is represented by an `Op`.
* Operations are applied to an accumulator to yield a final state.
*/
export type Operation<A> = {
id: string;
actions: Array<A>;
author: Author;
timestamp: number;
};
27 changes: 0 additions & 27 deletions crates/radicle-types/bindings/cob/issue/Operation.ts

This file was deleted.

16 changes: 16 additions & 0 deletions crates/radicle-types/src/cobs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use serde::{Deserialize, Serialize};
use ts_rs::TS;

use radicle::cob;
use radicle::identity;
use radicle::node::{Alias, AliasStore};

Expand Down Expand Up @@ -31,6 +32,21 @@ impl Author {
}
}

/// Everything that can be done in the system is represented by an `Op`.
/// Operations are applied to an accumulator to yield a final state.
#[derive(Debug, Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
#[ts(export_to = "cob/")]
pub struct Operation<A> {
#[ts(as = "String")]
pub id: cob::EntryId,
pub actions: Vec<A>,
pub author: Author,
#[ts(type = "number")]
pub timestamp: cob::Timestamp,
}

#[derive(Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
Expand Down
15 changes: 0 additions & 15 deletions crates/radicle-types/src/cobs/issue.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use std::collections::BTreeSet;

use radicle::git;
use radicle::node::AliasStore;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
Expand Down Expand Up @@ -55,20 +54,6 @@ impl Issue {
}
}

#[derive(Serialize, TS)]
#[serde(rename_all = "camelCase")]
#[ts(export)]
#[ts(export_to = "cob/issue/")]
pub struct Operation {
#[ts(as = "String")]
pub entry_id: git::Oid,
#[serde(flatten)]
pub action: Action,
#[ts(type = "number")]
pub timestamp: cob::Timestamp,
pub author: cobs::Author,
}

#[derive(Default, Serialize, Deserialize, TS)]
#[serde(rename_all = "camelCase", tag = "status")]
#[ts(export)]
Expand Down
4 changes: 4 additions & 0 deletions crates/radicle-types/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ pub enum Error {
#[error(transparent)]
Profile(#[from] radicle::profile::Error),

/// CobStore error.
#[error(transparent)]
CobStore(#[from] radicle::cob::store::Error),

/// Anyhow error.
#[error(transparent)]
Anyhow(#[from] anyhow::Error),
Expand Down
43 changes: 23 additions & 20 deletions crates/radicle-types/src/traits/cobs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,39 +2,42 @@ use radicle::cob::object::Storage;
use radicle::storage::refs::draft;
use radicle::storage::{self, ReadStorage};
use radicle::{cob, git, identity};
use serde::de::DeserializeOwned;

use crate::error::Error;
use crate::traits::Profile;

pub trait Cobs: Profile {
fn activity_by_id(
#[allow(clippy::unnecessary_filter_map)]
fn activity_by_id<A: DeserializeOwned>(
&self,
rid: identity::RepoId,
type_name: cob::TypeName,
type_name: &cob::TypeName,
id: git::Oid,
) -> Result<Vec<crate::cobs::issue::Operation>, Error> {
) -> Result<Vec<crate::cobs::Operation<A>>, Error> {
let profile = self.profile();
let aliases = profile.aliases();
let repo = profile.storage.repository(rid)?;
let ops = cob::store::ops(&id.into(), &type_name, &repo).unwrap();
let mut actions: Vec<crate::cobs::issue::Operation> = Vec::new();

for op in ops.into_iter() {
actions.extend(op.actions.iter().filter_map(
|action: &Vec<u8>| -> Option<crate::cobs::issue::Operation> {
let action: crate::cobs::issue::Action = serde_json::from_slice(action).ok()?;
let iter = cob::store::ops(&id.into(), type_name, &repo)?;
let ops = iter
.into_iter()
.filter_map(|op| {
let actions = op
.actions
.iter()
.filter_map(|a| serde_json::from_slice(a).ok())
.collect::<Vec<_>>();

Some(crate::cobs::issue::Operation {
entry_id: op.id,
action,
author: crate::cobs::Author::new(&op.author.into(), &aliases),
timestamp: op.timestamp,
})
},
))
}
Some(crate::cobs::Operation {
id: op.id,
actions,
author: crate::cobs::Author::new(&op.author.into(), &aliases),
timestamp: op.timestamp,
})
})
.collect::<Vec<_>>();

Ok::<_, Error>(actions)
Ok::<_, Error>(ops)
}

fn publish_draft(
Expand Down
32 changes: 24 additions & 8 deletions crates/test-http-api/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ use axum::routing::post;
use axum::Router;
use hyper::header::CONTENT_TYPE;
use hyper::Method;
use radicle::cob::TypeName;
use serde::{Deserialize, Serialize};
use tower_http::cors::{self, CorsLayer};

use radicle::{git, identity};
use radicle_types as types;
use radicle_types::cobs::issue::{Action, NewIssue};
use radicle_types::cobs::issue;
use radicle_types::cobs::issue::NewIssue;
use radicle_types::cobs::patch;
use radicle_types::cobs::CobOptions;
use radicle_types::error::Error;
use radicle_types::traits::auth::Auth;
Expand Down Expand Up @@ -57,7 +58,14 @@ pub fn router(ctx: Context) -> Router {
.route("/list_repos", post(repo_root_handler))
.route("/repo_by_id", post(repo_handler))
.route("/diff_stats", post(diff_stats_handler))
.route("/activity_by_id", post(activity_handler))
.route(
"/activity_by_issue",
post(activity_issue_handler::<issue::Action>),
)
.route(
"/activity_by_patch",
post(activity_patch_handler::<patch::Action>),
)
.route("/get_diff", post(diff_handler))
.route("/list_issues", post(issues_handler))
.route("/create_issue", post(create_issue_handler))
Expand Down Expand Up @@ -206,7 +214,7 @@ async fn create_issue_comment_handler(
struct EditIssuesBody {
pub rid: identity::RepoId,
pub cob_id: git::Oid,
pub action: Action,
pub action: issue::Action,
pub opts: CobOptions,
}

Expand All @@ -228,15 +236,23 @@ async fn edit_issue_handler(
#[serde(rename_all = "camelCase")]
struct ActivityBody {
pub rid: identity::RepoId,
pub type_name: TypeName,
pub id: git::Oid,
}

async fn activity_handler(
async fn activity_issue_handler<A: serde::Serialize + serde::de::DeserializeOwned>(
State(ctx): State<Context>,
Json(ActivityBody { rid, id }): Json<ActivityBody>,
) -> impl IntoResponse {
let activity = ctx.activity_by_id::<A>(rid, &radicle::cob::issue::TYPENAME, id)?;

Ok::<_, Error>(Json(activity))
}

async fn activity_patch_handler<A: serde::Serialize + serde::de::DeserializeOwned>(
State(ctx): State<Context>,
Json(ActivityBody { rid, type_name, id }): Json<ActivityBody>,
Json(ActivityBody { rid, id }): Json<ActivityBody>,
) -> impl IntoResponse {
let activity = ctx.activity_by_id(rid, type_name, id)?;
let activity = ctx.activity_by_id::<A>(rid, &radicle::cob::patch::TYPENAME, id)?;

Ok::<_, Error>(Json(activity))
}
Expand Down
14 changes: 8 additions & 6 deletions src/components/IssueTimelineLifecycleAction.svelte
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<script lang="ts">
import type { Operation } from "@bindings/cob/issue/Operation";
import type { Operation } from "@bindings/cob/Operation";
import type { Action } from "@bindings/cob/issue/Action";
import { authorForNodeId, formatTimestamp } from "@app/lib/utils";
Expand All @@ -8,19 +9,20 @@
import NodeId from "@app/components/NodeId.svelte";
interface Props {
operation: Extract<Operation, { type: "lifecycle" }>;
action: Extract<Action, { type: "lifecycle" }>;
op: Operation<Action>;
}
const { operation }: Props = $props();
const { op, action }: Props = $props();
</script>

<Border variant="float" stylePadding="1rem">
<div class="txt-small">
<div class="global-flex txt-small">
<NodeId {...authorForNodeId(operation.author)} />
<NodeId {...authorForNodeId(op.author)} />
changed status to
<IssueStateBadge state={operation.state} />
{formatTimestamp(operation.timestamp)}
<IssueStateBadge state={action.state} />
{formatTimestamp(op.timestamp)}
</div>
</div>
</Border>
28 changes: 28 additions & 0 deletions src/components/PatchTimelineLifecycleAction.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<script lang="ts">
import type { Operation } from "@bindings/cob/Operation";
import type { Action } from "@bindings/cob/patch/Action";
import { authorForNodeId, formatTimestamp } from "@app/lib/utils";
import Border from "@app/components/Border.svelte";
import NodeId from "@app/components/NodeId.svelte";
import PatchStateBadge from "./PatchStateBadge.svelte";
interface Props {
action: Extract<Action, { type: "lifecycle" }>;
op: Operation<Action>;
}
const { op, action }: Props = $props();
</script>

<Border variant="float" stylePadding="1rem">
<div class="txt-small">
<div class="global-flex txt-small">
<NodeId {...authorForNodeId(op.author)} />
changed status to
<PatchStateBadge state={action.state} />
{formatTimestamp(op.timestamp)}
</div>
</div>
</Border>
Loading

0 comments on commit 432db1c

Please sign in to comment.