Polish community UI copy and cleanup
Deploy / deploy (push) Successful in 1m36s
CI / Rust Checks (push) Successful in 4m50s
CI / UI Checks (push) Successful in 5s
CI / Deployment Manifests (push) Successful in 2s
CI / Frontend E2E (push) Failing after 5m5s

This commit is contained in:
github-ops
2026-06-19 21:15:02 +00:00
parent 66dd0deee5
commit d072d142ca
57 changed files with 710 additions and 6773 deletions
+53 -278
View File
@@ -6,12 +6,8 @@ use time::OffsetDateTime;
use crate::{
edition::OperationSecurityLevel,
ids::{AuthProfileId, DescriptorId, OperationId, SampleId},
protocol::{ExportMode, GraphqlOperationType, HttpMethod, Protocol},
soap::{
SoapBindingStyle, SoapFaultContract, SoapHeaderConfig, SoapOperationMetadata, SoapVersion,
},
streaming::StreamingConfig,
ids::{AuthProfileId, OperationId, SampleId},
protocol::{ExportMode, HttpMethod, Protocol},
};
fn default_operation_category() -> String {
@@ -40,68 +36,10 @@ pub struct RestTarget {
pub static_headers: BTreeMap<String, String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct GraphqlTarget {
pub endpoint: String,
pub operation_type: GraphqlOperationType,
pub operation_name: String,
pub query_template: String,
pub response_path: String,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct GrpcTarget {
pub server_addr: String,
pub package: String,
pub service: String,
pub method: String,
#[serde(default)]
pub read_only: bool,
pub descriptor_ref: DescriptorId,
pub descriptor_set_b64: String,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct WebsocketTarget {
pub url: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub subprotocols: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub subscribe_message_template: Option<Value>,
#[serde(skip_serializing_if = "Option::is_none")]
pub unsubscribe_message_template: Option<Value>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub static_headers: BTreeMap<String, String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SoapTarget {
pub wsdl_ref: SampleId,
pub service_name: String,
pub port_name: String,
pub operation_name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub endpoint_override: Option<String>,
pub soap_version: SoapVersion,
#[serde(skip_serializing_if = "Option::is_none")]
pub soap_action: Option<String>,
pub binding_style: SoapBindingStyle,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub headers: Vec<SoapHeaderConfig>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fault_contract: Option<SoapFaultContract>,
#[serde(default)]
pub metadata: SoapOperationMetadata,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case")]
pub enum Target {
Rest(RestTarget),
Graphql(GraphqlTarget),
Grpc(GrpcTarget),
Websocket(WebsocketTarget),
Soap(SoapTarget),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
@@ -114,41 +52,6 @@ pub struct ResponseCachePolicy {
pub ttl_ms: u64,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct GrpcProtocolOptions {
pub use_tls: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct WebsocketProtocolOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub heartbeat_interval_ms: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reconnect_max_attempts: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub reconnect_backoff_ms: Option<u64>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct SoapProtocolOptions {
#[serde(default)]
pub use_tls: bool,
#[serde(default)]
pub validate_certificate: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub ws_security_profile: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Default)]
pub struct ProtocolOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub grpc: Option<GrpcProtocolOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub websocket: Option<WebsocketProtocolOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub soap: Option<SoapProtocolOptions>,
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct ExecutionConfig {
pub timeout_ms: u64,
@@ -160,10 +63,6 @@ pub struct ExecutionConfig {
pub auth_profile_ref: Option<AuthProfileId>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub headers: BTreeMap<String, String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub protocol_options: Option<ProtocolOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub streaming: Option<StreamingConfig>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
@@ -187,10 +86,6 @@ pub struct Samples {
pub input_json_sample_ref: Option<SampleId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub output_json_sample_ref: Option<SampleId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub proto_file_ref: Option<SampleId>,
#[serde(skip_serializing_if = "Option::is_none")]
pub descriptor_ref: Option<DescriptorId>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
@@ -301,17 +196,12 @@ mod tests {
use crate::{
auth::{AuthConfig, AuthProfile, BearerAuthConfig},
edition::OperationSecurityLevel,
ids::{AuthProfileId, OperationId, SampleId},
ids::{AuthProfileId, OperationId},
operation::{
ConfigExport, ExecutionConfig, GraphqlTarget, Operation, OperationStatus,
ProtocolOptions, RestTarget, Samples, SoapProtocolOptions, SoapTarget, Target,
ToolDescription, ToolExample, WebsocketProtocolOptions, WebsocketTarget,
},
protocol::{AuthKind, ExportMode, GraphqlOperationType, HttpMethod, Protocol},
soap::{
SoapBindingStyle, SoapFaultContract, SoapHeaderConfig, SoapOperationMetadata,
SoapVersion,
ConfigExport, ExecutionConfig, Operation, OperationStatus, RestTarget, Samples, Target,
ToolDescription, ToolExample,
},
protocol::{AuthKind, ExportMode, HttpMethod, Protocol},
};
fn timestamp(value: &str) -> OffsetDateTime {
@@ -333,132 +223,9 @@ mod tests {
assert_eq!(value["method"], "POST");
}
#[test]
fn graphql_target_serializes_response_path() {
let target = Target::Graphql(GraphqlTarget {
endpoint: "https://api.example.com/graphql".to_owned(),
operation_type: GraphqlOperationType::Mutation,
operation_name: "CreateLead".to_owned(),
query_template: "mutation {}".to_owned(),
response_path: "$.response.body.data.createLead".to_owned(),
});
let value = serde_json::to_value(target).unwrap();
assert_eq!(value["kind"], "graphql");
assert_eq!(value["operation_type"], "mutation");
}
#[test]
fn websocket_target_serializes_templates() {
let target = Target::Websocket(WebsocketTarget {
url: "wss://events.example.com/stream".to_owned(),
subprotocols: vec!["graphql-transport-ws".to_owned()],
subscribe_message_template: Some(json!({ "type": "subscribe" })),
unsubscribe_message_template: Some(json!({ "type": "unsubscribe" })),
static_headers: BTreeMap::from([("x-env".to_owned(), "test".to_owned())]),
});
let value = serde_json::to_value(target).unwrap();
assert_eq!(value["kind"], "websocket");
assert_eq!(value["subprotocols"][0], "graphql-transport-ws");
assert_eq!(value["subscribe_message_template"]["type"], "subscribe");
}
#[test]
fn soap_target_serializes_binding_metadata() {
let target = Target::Soap(SoapTarget {
wsdl_ref: SampleId::new("sample_wsdl_01"),
service_name: "LeadService".to_owned(),
port_name: "LeadPort".to_owned(),
operation_name: "CreateLead".to_owned(),
endpoint_override: Some("https://soap.example.com/lead".to_owned()),
soap_version: SoapVersion::Soap12,
soap_action: Some("urn:createLead".to_owned()),
binding_style: SoapBindingStyle::DocumentLiteral,
headers: vec![SoapHeaderConfig {
name: "CorrelationId".to_owned(),
namespace_uri: Some("urn:crm".to_owned()),
required: true,
value_path: Some("$.mcp.correlation_id".to_owned()),
}],
fault_contract: Some(SoapFaultContract {
code_path: Some("$.Envelope.Body.Fault.Code.Value".to_owned()),
message_path: Some("$.Envelope.Body.Fault.Reason.Text".to_owned()),
detail_path: Some("$.Envelope.Body.Fault.Detail".to_owned()),
}),
metadata: SoapOperationMetadata {
input_part_names: vec!["LeadRequest".to_owned()],
output_part_names: vec!["LeadResponse".to_owned()],
namespaces: vec!["urn:crm".to_owned()],
},
});
let value = serde_json::to_value(target).unwrap();
assert_eq!(value["kind"], "soap");
assert_eq!(value["soap_version"], "soap_12");
assert_eq!(value["binding_style"], "document_literal");
assert_eq!(value["metadata"]["input_part_names"][0], "LeadRequest");
}
#[test]
fn operation_exposes_local_domain_helpers() {
let operation = Operation {
id: OperationId::new("op_01"),
name: "crm_create_lead".to_owned(),
display_name: "Create Lead".to_owned(),
category: "sales".to_owned(),
protocol: Protocol::Rest,
security_level: OperationSecurityLevel::Standard,
status: OperationStatus::Draft,
version: 1,
target: Target::Rest(RestTarget {
base_url: "https://api.example.com".to_owned(),
method: HttpMethod::Post,
path_template: "/v1/leads".to_owned(),
static_headers: BTreeMap::new(),
}),
input_schema: json!({"type":"object"}),
output_schema: json!({"type":"object"}),
input_mapping: json!({"rules":[]}),
output_mapping: json!({"rules":[]}),
execution_config: ExecutionConfig {
timeout_ms: 10_000,
retry_policy: None,
response_cache: None,
auth_profile_ref: Some(AuthProfileId::new("auth_01")),
headers: BTreeMap::new(),
protocol_options: Some(ProtocolOptions {
grpc: None,
websocket: Some(WebsocketProtocolOptions {
heartbeat_interval_ms: Some(5_000),
reconnect_max_attempts: Some(3),
reconnect_backoff_ms: Some(250),
}),
soap: Some(SoapProtocolOptions {
use_tls: true,
validate_certificate: true,
ws_security_profile: Some("username_token".to_owned()),
}),
}),
streaming: None,
},
tool_description: ToolDescription {
title: "Create CRM lead".to_owned(),
description: "Creates a new lead.".to_owned(),
tags: Vec::new(),
examples: Vec::new(),
},
samples: Some(Samples::default()),
generated_draft: None,
config_export: None,
wizard_state: None,
created_at: timestamp("2026-03-25T08:00:00Z"),
updated_at: timestamp("2026-03-25T08:00:00Z"),
published_at: None,
};
let operation = test_operation(OperationStatus::Draft);
assert_eq!(operation.tool_name(), "crm_create_lead");
assert!(operation.is_draft());
@@ -497,51 +264,14 @@ mod tests {
#[test]
fn operation_roundtrips_through_yaml() {
let operation = Operation {
id: OperationId::new("op_01"),
name: "crm_create_lead".to_owned(),
display_name: "Create Lead".to_owned(),
category: "sales".to_owned(),
protocol: Protocol::Rest,
security_level: OperationSecurityLevel::Standard,
status: OperationStatus::Published,
version: 3,
target: Target::Rest(RestTarget {
base_url: "https://api.example.com".to_owned(),
method: HttpMethod::Post,
path_template: "/v1/leads".to_owned(),
static_headers: BTreeMap::from([("X-App-Source".to_owned(), "crank".to_owned())]),
}),
input_schema: json!({"type":"object","fields":{"email":{"type":"string","required":true}}}),
output_schema: json!({"type":"object","fields":{"id":{"type":"string","required":true}}}),
input_mapping: json!({"rules":[{"source":"$.mcp.email","target":"$.request.body.email"}]}),
output_mapping: json!({"rules":[{"source":"$.response.body.id","target":"$.output.id"}]}),
execution_config: ExecutionConfig {
timeout_ms: 10_000,
retry_policy: None,
response_cache: None,
auth_profile_ref: Some(AuthProfileId::new("auth_01")),
headers: BTreeMap::new(),
protocol_options: Some(ProtocolOptions::default()),
streaming: None,
},
tool_description: ToolDescription {
title: "Create CRM lead".to_owned(),
description: "Creates a new lead.".to_owned(),
tags: vec!["crm".to_owned()],
examples: vec![ToolExample {
input: json!({"email":"user@example.com"}),
}],
},
samples: Some(Samples::default()),
generated_draft: None,
config_export: Some(ConfigExport {
format_version: "1".to_owned(),
export_mode: ExportMode::Portable,
}),
wizard_state: None,
created_at: timestamp("2026-03-25T08:00:00Z"),
updated_at: timestamp("2026-03-25T08:10:00Z"),
published_at: Some(timestamp("2026-03-25T08:15:00Z")),
..test_operation(OperationStatus::Draft)
};
let yaml = serde_yaml::to_string(&operation).unwrap();
@@ -597,4 +327,49 @@ updated_at: 2026-03-25T08:10:00Z
assert_eq!(restored.security_level, OperationSecurityLevel::Standard);
assert_eq!(restored.published_at, None);
}
fn test_operation(status: OperationStatus) -> Operation<serde_json::Value, serde_json::Value> {
Operation {
id: OperationId::new("op_01"),
name: "crm_create_lead".to_owned(),
display_name: "Create Lead".to_owned(),
category: "sales".to_owned(),
protocol: Protocol::Rest,
security_level: OperationSecurityLevel::Standard,
status,
version: 1,
target: Target::Rest(RestTarget {
base_url: "https://api.example.com".to_owned(),
method: HttpMethod::Post,
path_template: "/v1/leads".to_owned(),
static_headers: BTreeMap::new(),
}),
input_schema: json!({"type":"object","fields":{"email":{"type":"string","required":true}}}),
output_schema: json!({"type":"object","fields":{"id":{"type":"string","required":true}}}),
input_mapping: json!({"rules":[{"source":"$.mcp.email","target":"$.request.body.email"}]}),
output_mapping: json!({"rules":[{"source":"$.response.body.id","target":"$.output.id"}]}),
execution_config: ExecutionConfig {
timeout_ms: 10_000,
retry_policy: None,
response_cache: None,
auth_profile_ref: Some(AuthProfileId::new("auth_01")),
headers: BTreeMap::new(),
},
tool_description: ToolDescription {
title: "Create CRM lead".to_owned(),
description: "Creates a new lead.".to_owned(),
tags: vec!["crm".to_owned()],
examples: vec![ToolExample {
input: json!({"email":"user@example.com"}),
}],
},
samples: Some(Samples::default()),
generated_draft: None,
config_export: None,
wizard_state: None,
created_at: timestamp("2026-03-25T08:00:00Z"),
updated_at: timestamp("2026-03-25T08:10:00Z"),
published_at: None,
}
}
}