feat: expose operation target summaries for alpine ui

This commit is contained in:
a.tolmachev
2026-03-30 01:15:53 +03:00
parent f45ace378a
commit 896078d99e
8 changed files with 112 additions and 33 deletions
+90 -30
View File
@@ -1,7 +1,8 @@
use crank_core::{
AgentId, AgentOperationBinding, AgentStatus, AgentVersion, AuthProfile, InvitationId,
InvitationToken, InvocationLog, InvocationLogId, OperationId, OperationStatus, PlatformApiKey,
PlatformApiKeyId, PlatformApiKeyStatus, UsageRollup, User, UserId, Workspace, WorkspaceId,
AgentId, AgentOperationBinding, AgentStatus, AgentVersion, AuthProfile, GraphqlOperationType,
HttpMethod, InvitationId, InvitationToken, InvocationLog, InvocationLogId, OperationId,
OperationStatus, PlatformApiKey, PlatformApiKeyId, PlatformApiKeyStatus, Target, UsageRollup,
User, UserId, Workspace, WorkspaceId,
};
use serde::{Serialize, de::DeserializeOwned};
use serde_json::Value;
@@ -1490,21 +1491,25 @@ impl PostgresRegistry {
) -> Result<Vec<OperationSummary>, RegistryError> {
let rows = sqlx::query(
"select
id,
workspace_id,
name,
display_name,
category,
protocol,
status,
current_draft_version,
latest_published_version,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at,
to_char(published_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as published_at
operations.id,
operations.workspace_id,
operations.name,
operations.display_name,
operations.category,
operations.protocol,
ov.target_json,
operations.status,
operations.current_draft_version,
operations.latest_published_version,
to_char(operations.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(operations.updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at,
to_char(operations.published_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as published_at
from operations
where workspace_id = $1
order by name asc",
join operation_versions ov
on ov.operation_id = operations.id
and ov.version = operations.current_draft_version
where operations.workspace_id = $1
order by operations.name asc",
)
.bind(workspace_id.as_str())
.fetch_all(&self.pool)
@@ -1571,20 +1576,24 @@ impl PostgresRegistry {
) -> Result<Option<OperationSummary>, RegistryError> {
let row = sqlx::query(
"select
id,
workspace_id,
name,
display_name,
category,
protocol,
status,
current_draft_version,
latest_published_version,
to_char(created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at,
to_char(published_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as published_at
operations.id,
operations.workspace_id,
operations.name,
operations.display_name,
operations.category,
operations.protocol,
ov.target_json,
operations.status,
operations.current_draft_version,
operations.latest_published_version,
to_char(operations.created_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as created_at,
to_char(operations.updated_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as updated_at,
to_char(operations.published_at at time zone 'UTC', 'YYYY-MM-DD\"T\"HH24:MI:SS\"Z\"') as published_at
from operations
where workspace_id = $1 and id = $2",
join operation_versions ov
on ov.operation_id = operations.id
and ov.version = operations.current_draft_version
where operations.workspace_id = $1 and operations.id = $2",
)
.bind(workspace_id.as_str())
.bind(operation_id.as_str())
@@ -2447,6 +2456,9 @@ fn map_agent_summary(row: &PgRow) -> Result<AgentSummary, RegistryError> {
}
fn map_operation_summary(row: &PgRow) -> Result<OperationSummary, RegistryError> {
let target: Target = deserialize_json_value(row.try_get::<Json<Value>, _>("target_json")?.0)?;
let (target_url, target_action) = target_summary(&target);
Ok(OperationSummary {
id: OperationId::new(row.try_get::<String, _>("id")?),
workspace_id: WorkspaceId::new(row.try_get::<String, _>("workspace_id")?),
@@ -2454,6 +2466,8 @@ fn map_operation_summary(row: &PgRow) -> Result<OperationSummary, RegistryError>
display_name: row.try_get("display_name")?,
category: row.try_get("category")?,
protocol: deserialize_enum_text(&row.try_get::<String, _>("protocol")?, "protocol")?,
target_url,
target_action,
status: deserialize_enum_text(&row.try_get::<String, _>("status")?, "status")?,
current_draft_version: from_db_version(
row.try_get("current_draft_version")?,
@@ -2478,6 +2492,52 @@ fn map_operation_usage_summary(row: &PgRow) -> Result<OperationUsageSummary, Reg
})
}
fn target_summary(target: &Target) -> (String, String) {
match target {
Target::Rest(rest) => (
join_target_url(&rest.base_url, &rest.path_template),
match rest.method {
HttpMethod::Get => "GET",
HttpMethod::Post => "POST",
HttpMethod::Put => "PUT",
HttpMethod::Patch => "PATCH",
HttpMethod::Delete => "DELETE",
}
.to_owned(),
),
Target::Graphql(graphql) => (
graphql.endpoint.clone(),
match graphql.operation_type {
GraphqlOperationType::Query => "QUERY",
GraphqlOperationType::Mutation => "MUTATION",
}
.to_owned(),
),
Target::Grpc(grpc) => (grpc.server_addr.clone(), grpc.method.clone()),
}
}
fn join_target_url(base: &str, path: &str) -> String {
if path.is_empty() {
return base.to_owned();
}
if path.starts_with("http://") || path.starts_with("https://") || path.starts_with("grpc://") {
return path.to_owned();
}
let trimmed_base = base.trim_end_matches('/');
let trimmed_path = path.trim_start_matches('/');
if trimmed_base.is_empty() {
format!("/{trimmed_path}")
} else if trimmed_path.is_empty() {
trimmed_base.to_owned()
} else {
format!("{trimmed_base}/{trimmed_path}")
}
}
fn map_operation_agent_ref(row: &PgRow) -> Result<OperationAgentRef, RegistryError> {
Ok(OperationAgentRef {
operation_id: OperationId::new(row.try_get::<String, _>("operation_id")?),