feat: add operations mutation api for alpine ui
This commit is contained in:
+164
-11
@@ -1,6 +1,6 @@
|
||||
use axum::{
|
||||
Router,
|
||||
routing::{get, post},
|
||||
routing::{delete, get, post},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -16,10 +16,10 @@ use crate::{
|
||||
auth_profiles::{create_auth_profile, get_auth_profile, list_auth_profiles},
|
||||
observability::{get_agent_usage, get_log, get_operation_usage, get_usage, list_logs},
|
||||
operations::{
|
||||
create_operation, create_version, export_operation, generate_draft, get_operation,
|
||||
get_operation_version, list_grpc_services, list_operations, publish_operation,
|
||||
run_test, upload_descriptor_set, upload_input_json, upload_output_json,
|
||||
upload_proto_descriptor,
|
||||
archive_operation, create_operation, create_version, delete_operation,
|
||||
export_operation, generate_draft, get_operation, get_operation_version,
|
||||
list_grpc_services, list_operations, publish_operation, run_test, update_operation,
|
||||
upload_descriptor_set, upload_input_json, upload_output_json, upload_proto_descriptor,
|
||||
},
|
||||
workspaces::{create_workspace, get_workspace, list_workspaces, update_workspace},
|
||||
},
|
||||
@@ -30,7 +30,12 @@ pub fn build_app(state: AppState) -> Router {
|
||||
let workspace_router = Router::new()
|
||||
.route("/operations", get(list_operations).post(create_operation))
|
||||
.route("/operations/import", post(import_operation))
|
||||
.route("/operations/{operation_id}", get(get_operation))
|
||||
.route(
|
||||
"/operations/{operation_id}",
|
||||
get(get_operation)
|
||||
.patch(update_operation)
|
||||
.delete(delete_operation),
|
||||
)
|
||||
.route("/operations/{operation_id}/versions", post(create_version))
|
||||
.route(
|
||||
"/operations/{operation_id}/versions/{version}",
|
||||
@@ -40,6 +45,10 @@ pub fn build_app(state: AppState) -> Router {
|
||||
"/operations/{operation_id}/publish",
|
||||
post(publish_operation),
|
||||
)
|
||||
.route(
|
||||
"/operations/{operation_id}/archive",
|
||||
post(archive_operation),
|
||||
)
|
||||
.route("/operations/{operation_id}/test-runs", post(run_test))
|
||||
.route(
|
||||
"/operations/{operation_id}/samples/input-json",
|
||||
@@ -84,10 +93,7 @@ pub fn build_app(state: AppState) -> Router {
|
||||
"/invitations",
|
||||
get(list_invitations).post(create_invitation),
|
||||
)
|
||||
.route(
|
||||
"/invitations/{invitation_id}",
|
||||
axum::routing::delete(delete_invitation),
|
||||
)
|
||||
.route("/invitations/{invitation_id}", delete(delete_invitation))
|
||||
.route(
|
||||
"/platform-api-keys",
|
||||
get(list_platform_api_keys).post(create_platform_api_key),
|
||||
@@ -98,7 +104,7 @@ pub fn build_app(state: AppState) -> Router {
|
||||
)
|
||||
.route(
|
||||
"/platform-api-keys/{key_id}",
|
||||
axum::routing::delete(delete_platform_api_key),
|
||||
delete(delete_platform_api_key),
|
||||
)
|
||||
.route("/logs", get(list_logs))
|
||||
.route("/logs/{log_id}", get(get_log))
|
||||
@@ -213,6 +219,150 @@ mod tests {
|
||||
assert_eq!(test_run["response_preview"]["id"], "lead_123");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn updates_archives_and_deletes_operation() {
|
||||
let registry = test_registry().await;
|
||||
let storage_root = test_storage_root("operation_mutations");
|
||||
let upstream_base_url = spawn_upstream_server().await;
|
||||
let base_url = spawn_admin_api(build_test_app(registry, storage_root)).await;
|
||||
let client = reqwest::Client::new();
|
||||
|
||||
let created = client
|
||||
.post(format!("{base_url}/operations"))
|
||||
.json(&test_operation_payload(
|
||||
&upstream_base_url,
|
||||
"crm_mutable_operation",
|
||||
))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<Value>()
|
||||
.await
|
||||
.unwrap();
|
||||
let operation_id = created["operation_id"].as_str().unwrap().to_owned();
|
||||
|
||||
let listed = client
|
||||
.get(format!("{base_url}/operations"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<Value>()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(listed["total"], 1);
|
||||
assert_eq!(listed["items"][0]["category"], "sales");
|
||||
|
||||
let updated = client
|
||||
.patch(format!("{base_url}/operations/{operation_id}"))
|
||||
.json(&json!({
|
||||
"display_name": "Create Lead Updated",
|
||||
"category": "marketing",
|
||||
"target": {
|
||||
"kind": "rest",
|
||||
"base_url": upstream_base_url,
|
||||
"method": "POST",
|
||||
"path_template": "/crm/leads",
|
||||
"static_headers": {}
|
||||
},
|
||||
"input_schema": {
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"nullable": false,
|
||||
"fields": {
|
||||
"email": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"nullable": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"output_schema": {
|
||||
"type": "object",
|
||||
"required": true,
|
||||
"nullable": false,
|
||||
"fields": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"required": true,
|
||||
"nullable": false
|
||||
}
|
||||
}
|
||||
},
|
||||
"input_mapping": {
|
||||
"rules": [
|
||||
{
|
||||
"source": "$.mcp.email",
|
||||
"target": "$.request.body.email",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"output_mapping": {
|
||||
"rules": [
|
||||
{
|
||||
"source": "$.response.body.id",
|
||||
"target": "$.output.id",
|
||||
"required": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"execution_config": {
|
||||
"timeout_ms": 1000,
|
||||
"headers": {}
|
||||
},
|
||||
"tool_description": {
|
||||
"title": "Create Lead Updated",
|
||||
"description": "Creates a CRM lead",
|
||||
"tags": ["crm"]
|
||||
}
|
||||
}))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<Value>()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(updated["status"], "draft");
|
||||
|
||||
let detail = client
|
||||
.get(format!("{base_url}/operations/{operation_id}"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<Value>()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(detail["display_name"], "Create Lead Updated");
|
||||
assert_eq!(detail["category"], "marketing");
|
||||
|
||||
let archived = client
|
||||
.post(format!("{base_url}/operations/{operation_id}/archive"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<Value>()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(archived["status"], "archived");
|
||||
|
||||
let deleted = client
|
||||
.delete(format!("{base_url}/operations/{operation_id}"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap()
|
||||
.json::<Value>()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(deleted["operation_id"], operation_id);
|
||||
|
||||
let missing = client
|
||||
.get(format!("{base_url}/operations/{operation_id}"))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(missing.status(), reqwest::StatusCode::NOT_FOUND);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn creates_binds_and_publishes_agent() {
|
||||
let registry = test_registry().await;
|
||||
@@ -984,6 +1134,7 @@ mod tests {
|
||||
OperationPayload {
|
||||
name: name.to_owned(),
|
||||
display_name: "Create Lead".to_owned(),
|
||||
category: "sales".to_owned(),
|
||||
protocol: Protocol::Rest,
|
||||
target: Target::Rest(RestTarget {
|
||||
base_url: base_url.to_owned(),
|
||||
@@ -1035,6 +1186,7 @@ mod tests {
|
||||
OperationPayload {
|
||||
name: name.to_owned(),
|
||||
display_name: "Create Lead GraphQL".to_owned(),
|
||||
category: "sales".to_owned(),
|
||||
protocol: Protocol::Graphql,
|
||||
target: Target::Graphql(GraphqlTarget {
|
||||
endpoint: endpoint.to_owned(),
|
||||
@@ -1089,6 +1241,7 @@ mod tests {
|
||||
OperationPayload {
|
||||
name: name.to_owned(),
|
||||
display_name: "Unary Echo gRPC".to_owned(),
|
||||
category: "support".to_owned(),
|
||||
protocol: Protocol::Grpc,
|
||||
target: Target::Grpc(GrpcTarget {
|
||||
server_addr: server_addr.to_owned(),
|
||||
|
||||
Reference in New Issue
Block a user