Skip to content

Commit 0589955

Browse files
committed
Added optional parameter to change_status to get a specific change
1 parent f57414b commit 0589955

3 files changed

Lines changed: 493 additions & 58 deletions

File tree

vcs-worker/src/operations/change/change_status_op.rs

Lines changed: 129 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,17 @@ use tracing::{error, info};
66
use crate::database::{DatabaseRef, ObjectsTreeError};
77
use crate::object_diff::build_object_diff_from_change;
88
use crate::providers::index::IndexProvider;
9+
use crate::providers::workspace::WorkspaceProvider;
910
use crate::types::ChangeStatus;
1011
use crate::types::User;
1112
use moor_var::{E_INVARG, v_error};
1213

1314
/// Request structure for change status operations
1415
#[derive(Debug, Clone, Serialize, Deserialize)]
1516
pub struct ChangeStatusRequest {
16-
// No fields needed - lists status of current change
17+
/// Optional change ID to query. If not provided, queries the current top local change.
18+
#[serde(default)]
19+
pub change_id: Option<String>,
1720
}
1821

1922
/// Response structure for change status
@@ -39,56 +42,92 @@ impl ChangeStatusOperation {
3942
/// Process the change status request
4043
fn process_change_status(
4144
&self,
42-
_request: ChangeStatusRequest,
45+
request: ChangeStatusRequest,
4346
) -> Result<moor_var::Var, ObjectsTreeError> {
44-
// Get the top change from the index
45-
let top_change_id = self
46-
.database
47-
.index()
48-
.get_top_change()
49-
.map_err(|e| ObjectsTreeError::SerializationError(e.to_string()))?;
50-
51-
if let Some(change_id) = top_change_id {
52-
// Get the actual change object
53-
let current_change = self
47+
// If a specific change_id was provided, fetch that change
48+
let current_change = if let Some(ref change_id) = request.change_id {
49+
info!("Fetching status for specific change: {}", change_id);
50+
51+
// Resolve short or full hash to full hash
52+
let resolved_change_id = self.database.resolve_change_id(change_id)?;
53+
info!("Resolved '{}' to full change ID: {}", change_id, resolved_change_id);
54+
55+
// Try to find the change in index first
56+
let change_from_index = self
57+
.database
58+
.index()
59+
.get_change(&resolved_change_id)
60+
.map_err(|e| ObjectsTreeError::SerializationError(e.to_string()))?;
61+
62+
if let Some(change) = change_from_index {
63+
change
64+
} else {
65+
// Try workspace if not in index
66+
let change_from_workspace = self
67+
.database
68+
.workspace()
69+
.get_workspace_change(&resolved_change_id)
70+
.map_err(|e| ObjectsTreeError::SerializationError(e.to_string()))?;
71+
72+
change_from_workspace.ok_or_else(|| {
73+
ObjectsTreeError::SerializationError(format!(
74+
"Change '{}' not found in index or workspace",
75+
resolved_change_id
76+
))
77+
})?
78+
}
79+
} else {
80+
// Default behavior: get the top change from the index
81+
let top_change_id = self
5482
.database
5583
.index()
56-
.get_change(&change_id)
57-
.map_err(|e| ObjectsTreeError::SerializationError(e.to_string()))?
58-
.ok_or_else(|| {
59-
ObjectsTreeError::SerializationError(format!("Change '{change_id}' not found"))
60-
})?;
61-
62-
// Check if the top change is local status
63-
if current_change.status != ChangeStatus::Local {
64-
info!(
65-
"Top change '{}' is not local status (status: {:?}), returning error",
66-
current_change.name, current_change.status
67-
);
84+
.get_top_change()
85+
.map_err(|e| ObjectsTreeError::SerializationError(e.to_string()))?;
86+
87+
if let Some(change_id) = top_change_id {
88+
// Get the actual change object
89+
let change = self
90+
.database
91+
.index()
92+
.get_change(&change_id)
93+
.map_err(|e| ObjectsTreeError::SerializationError(e.to_string()))?
94+
.ok_or_else(|| {
95+
ObjectsTreeError::SerializationError(format!("Change '{change_id}' not found"))
96+
})?;
97+
98+
// Check if the top change is local status
99+
if change.status != ChangeStatus::Local {
100+
info!(
101+
"Top change '{}' is not local status (status: {:?}), returning error",
102+
change.name, change.status
103+
);
104+
return Ok(v_error(
105+
E_INVARG.msg("No local change on top of index - nothing to do"),
106+
));
107+
}
108+
109+
change
110+
} else {
111+
info!("No top change found, returning error");
68112
return Ok(v_error(
69-
E_INVARG.msg("No local change on top of index - nothing to do"),
113+
E_INVARG.msg("No change on top of index - nothing to do"),
70114
));
71115
}
116+
};
72117

73-
info!("Getting status for local change: {}", current_change.id);
118+
info!("Getting status for change: {} ({})", current_change.name, current_change.id);
74119

75-
// Build the ObjectDiffModel by comparing local change against the compiled state below it
76-
let diff_model = build_object_diff_from_change(&self.database, &current_change)?;
120+
// Build the ObjectDiffModel by comparing change against the compiled state
121+
let diff_model = build_object_diff_from_change(&self.database, &current_change)?;
77122

78-
// Convert to MOO Var and return
79-
let status_map = diff_model.to_moo_var();
123+
// Convert to MOO Var and return
124+
let status_map = diff_model.to_moo_var();
80125

81-
info!(
82-
"Successfully retrieved change status for '{}'",
83-
current_change.name
84-
);
85-
Ok(status_map)
86-
} else {
87-
info!("No top change found, returning error");
88-
Ok(v_error(
89-
E_INVARG.msg("No change on top of index - nothing to do"),
90-
))
91-
}
126+
info!(
127+
"Successfully retrieved change status for '{}'",
128+
current_change.name
129+
);
130+
Ok(status_map)
92131
}
93132
}
94133

@@ -98,38 +137,62 @@ impl Operation for ChangeStatusOperation {
98137
}
99138

100139
fn description(&self) -> &'static str {
101-
"Lists all objects that have been modified in the current change (added, modified, deleted, renamed)"
140+
"Lists all objects that have been modified in a change (added, modified, deleted, renamed). Can query current change or a specific change by ID."
102141
}
103142

104143
fn response_content_type(&self) -> &'static str {
105144
"text/x-moo"
106145
}
107146

108147
fn philosophy(&self) -> &'static str {
109-
"Provides a summary of all pending changes in your current local changelist. This is your primary \
110-
tool for reviewing what you've done before submitting - it shows which objects have been added, \
111-
modified, deleted, or renamed. The operation returns an ObjectDiffModel that categorizes all your \
112-
changes, making it easy to verify your work is correct before submission. Use this regularly during \
113-
development to track your progress and ensure you haven't accidentally modified objects you didn't \
114-
intend to change."
148+
"Provides a summary of all changes in a changelist. By default, it shows your current local changelist, \
149+
which is your primary tool for reviewing what you've done before submitting. You can also query any \
150+
specific change by ID to review historical changes, review submissions, or idle changes. The operation \
151+
shows which objects have been added, modified, deleted, or renamed. The operation returns an ObjectDiffModel \
152+
that categorizes all changes, making it easy to verify work is correct. Use this regularly during development \
153+
to track your progress and ensure you haven't accidentally modified objects you didn't intend to change."
115154
}
116155

117156
fn parameters(&self) -> Vec<OperationParameter> {
118-
vec![]
157+
vec![
158+
OperationParameter {
159+
name: "change_id".to_string(),
160+
description: "Optional change ID (full 64-char hash or short 12-char prefix) to query. If not provided, queries the current top local change.".to_string(),
161+
required: false,
162+
},
163+
]
119164
}
120165

121166
fn examples(&self) -> Vec<OperationExample> {
122-
vec![OperationExample {
123-
description: "Get status of current change".to_string(),
124-
moocode: r#"diff = worker_request("vcs", {"change/status"});
167+
vec![
168+
OperationExample {
169+
description: "Get status of current change".to_string(),
170+
moocode: r#"diff = worker_request("vcs", {"change/status"});
125171
// Returns an ObjectDiffModel map like:
126172
// [#<added_objects => {object_name => objdef}, ...>, #<modified_objects => {...}>, ...]
127173
player:tell("Added: ", length(diff["added_objects"]), " objects");
128174
player:tell("Modified: ", length(diff["modified_objects"]), " objects");
129175
player:tell("Deleted: ", length(diff["deleted_objects"]), " objects");"#
130-
.to_string(),
131-
http_curl: Some(r#"curl -X GET http://localhost:8081/api/change/status"#.to_string()),
132-
}]
176+
.to_string(),
177+
http_curl: Some(r#"curl -X GET http://localhost:8081/api/change/status"#.to_string()),
178+
},
179+
OperationExample {
180+
description: "Get status of a specific change by ID (full or short hash)".to_string(),
181+
moocode: r#"// Use full 64-character Blake3 hash
182+
change_id = "abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890";
183+
diff = worker_request("vcs", {"change/status", change_id});
184+
185+
// Or use short 12-character hash prefix
186+
short_id = "abcdef123456";
187+
diff = worker_request("vcs", {"change/status", short_id});
188+
189+
// Returns the ObjectDiffModel for the specific change
190+
player:tell("Added: ", length(diff["added_objects"]), " objects");
191+
player:tell("Modified: ", length(diff["modified_objects"]), " objects");"#
192+
.to_string(),
193+
http_curl: Some(r#"curl -X GET 'http://localhost:8081/api/change/status?change_id=abcdef123456'"#.to_string()),
194+
},
195+
]
133196
}
134197

135198
fn routes(&self) -> Vec<OperationRoute> {
@@ -165,10 +228,18 @@ player:tell("Deleted: ", length(diff["deleted_objects"]), " objects");"#
165228
]
166229
}
167230

168-
fn execute(&self, _args: Vec<String>, _user: &User) -> moor_var::Var {
169-
info!("Change status operation executed");
231+
fn execute(&self, args: Vec<String>, _user: &User) -> moor_var::Var {
232+
info!("Change status operation executed with {} args", args.len());
233+
234+
let request = ChangeStatusRequest {
235+
change_id: args.get(0).map(|s| s.to_string()),
236+
};
170237

171-
let request = ChangeStatusRequest {};
238+
if let Some(ref cid) = request.change_id {
239+
info!("Requesting status for specific change: {}", cid);
240+
} else {
241+
info!("Requesting status for current top local change");
242+
}
172243

173244
match self.process_change_status(request) {
174245
Ok(result_var) => {

vcs-worker/tests/common/client.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,10 +166,16 @@ impl VcsTestClient {
166166
}
167167

168168
/// Get change status
169+
/// Get the status of the current change (or a specific change by ID)
169170
pub async fn change_status(&self) -> Result<Value, Box<dyn std::error::Error>> {
170171
self.rpc_call("change/status", vec![]).await
171172
}
172173

174+
/// Get the status of a specific change by ID
175+
pub async fn change_status_by_id(&self, change_id: &str) -> Result<Value, Box<dyn std::error::Error>> {
176+
self.rpc_call("change/status", vec![Value::String(change_id.to_string())]).await
177+
}
178+
173179
/// Submit/commit a change
174180
pub async fn change_submit(&self) -> Result<Value, Box<dyn std::error::Error>> {
175181
self.rpc_call("change/submit", vec![]).await

0 commit comments

Comments
 (0)