feat: add soap adapter foundation

This commit is contained in:
a.tolmachev
2026-04-07 00:00:19 +03:00
parent 31fbdfdc02
commit a6388e4353
24 changed files with 1346 additions and 25 deletions
+151
View File
@@ -6,6 +6,7 @@ use base64::{
engine::general_purpose::{STANDARD, URL_SAFE_NO_PAD},
};
use crank_adapter_grpc::test_support as grpc_test_support;
use crank_adapter_soap::{SoapServiceSummary, inspect_wsdl};
use crank_core::{
Agent, AgentId, AgentOperationBinding, AgentStatus, AgentVersion, AggregationMode,
AsyncJobHandle, AuthConfig, AuthKind, AuthProfile, AuthProfileId, ConfigExport, ExecutionMode,
@@ -563,6 +564,8 @@ pub struct GrpcMethodSummary {
pub output_schema: Schema,
}
pub type SoapServiceCatalog = Vec<SoapServiceSummary>;
fn default_operation_category() -> String {
"general".to_owned()
}
@@ -2599,6 +2602,153 @@ impl AdminService {
.collect::<Result<Vec<_>, _>>()
}
#[instrument(skip(self, payload), fields(operation_id = %operation_id.as_str(), source_name = source_name.unwrap_or("service.wsdl")))]
pub async fn upload_wsdl_file(
&self,
workspace_id: &WorkspaceId,
operation_id: &OperationId,
source_name: Option<&str>,
payload: &[u8],
) -> Result<DescriptorUploadResponse, ApiError> {
let summary = self.get_operation(workspace_id, operation_id).await?;
if summary.protocol != Protocol::Soap {
return Err(ApiError::validation(
"wsdl upload is only allowed for soap operations",
));
}
let services =
inspect_wsdl(payload).map_err(|error| ApiError::validation(error.to_string()))?;
let descriptor_id = crank_core::DescriptorId::new(new_prefixed_id("desc"));
let storage_ref = self
.storage
.write_descriptor(
operation_id,
summary.current_draft_version,
crank_registry::DescriptorKind::WsdlUpload,
&descriptor_id,
source_name,
payload,
)
.await?;
self.registry
.save_descriptor_metadata(SaveDescriptorMetadataRequest {
descriptor: &crank_registry::DescriptorMetadata {
id: descriptor_id.clone(),
operation_id: Some(operation_id.clone()),
version: Some(summary.current_draft_version),
descriptor_kind: crank_registry::DescriptorKind::WsdlUpload,
storage_ref,
source_name: source_name.map(ToOwned::to_owned),
package_index: Some(
serde_json::to_value(&services)
.map_err(|error| ApiError::internal(error.to_string()))?,
),
created_at: now_string()?,
},
})
.await?;
info!(
operation_id = %operation_id.as_str(),
descriptor_id = %descriptor_id.as_str(),
version = summary.current_draft_version,
"wsdl uploaded"
);
Ok(DescriptorUploadResponse {
descriptor_id: descriptor_id.as_str().to_owned(),
version: summary.current_draft_version,
})
}
#[instrument(skip(self, payload), fields(operation_id = %operation_id.as_str(), source_name = source_name.unwrap_or("schema.xsd")))]
pub async fn upload_xsd_file(
&self,
workspace_id: &WorkspaceId,
operation_id: &OperationId,
source_name: Option<&str>,
payload: &[u8],
) -> Result<DescriptorUploadResponse, ApiError> {
let summary = self.get_operation(workspace_id, operation_id).await?;
if summary.protocol != Protocol::Soap {
return Err(ApiError::validation(
"xsd upload is only allowed for soap operations",
));
}
let descriptor_id = crank_core::DescriptorId::new(new_prefixed_id("desc"));
let storage_ref = self
.storage
.write_descriptor(
operation_id,
summary.current_draft_version,
crank_registry::DescriptorKind::XsdUpload,
&descriptor_id,
source_name,
payload,
)
.await?;
self.registry
.save_descriptor_metadata(SaveDescriptorMetadataRequest {
descriptor: &crank_registry::DescriptorMetadata {
id: descriptor_id.clone(),
operation_id: Some(operation_id.clone()),
version: Some(summary.current_draft_version),
descriptor_kind: crank_registry::DescriptorKind::XsdUpload,
storage_ref,
source_name: source_name.map(ToOwned::to_owned),
package_index: None,
created_at: now_string()?,
},
})
.await?;
info!(
operation_id = %operation_id.as_str(),
descriptor_id = %descriptor_id.as_str(),
version = summary.current_draft_version,
"xsd uploaded"
);
Ok(DescriptorUploadResponse {
descriptor_id: descriptor_id.as_str().to_owned(),
version: summary.current_draft_version,
})
}
#[instrument(skip(self), fields(operation_id = %operation_id.as_str(), version))]
pub async fn list_soap_services(
&self,
workspace_id: &WorkspaceId,
operation_id: &OperationId,
version: Option<u32>,
) -> Result<SoapServiceCatalog, ApiError> {
let summary = self.get_operation(workspace_id, operation_id).await?;
if summary.protocol != Protocol::Soap {
return Err(ApiError::validation(
"soap services are only available for soap operations",
));
}
let version = version.unwrap_or(summary.current_draft_version);
let descriptor = self
.registry
.list_descriptor_metadata(operation_id, version)
.await?
.into_iter()
.rev()
.find(|descriptor| {
descriptor.descriptor_kind == crank_registry::DescriptorKind::WsdlUpload
})
.ok_or_else(|| ApiError::not_found("wsdl was not uploaded"))?;
let package_index = descriptor
.package_index
.ok_or_else(|| ApiError::not_found("wsdl inspection metadata is not available"))?;
serde_json::from_value(package_index).map_err(|error| ApiError::internal(error.to_string()))
}
#[instrument(skip(self))]
pub async fn get_auth_profile(
&self,
@@ -4017,6 +4167,7 @@ fn runtime_error_code(error: &RuntimeError) -> &'static str {
RuntimeError::GraphqlAdapter(_) => "graphql_error",
RuntimeError::GrpcAdapter(_) => "grpc_error",
RuntimeError::RestAdapter(_) => "rest_error",
RuntimeError::SoapAdapter(_) => "soap_error",
RuntimeError::WebsocketAdapter(_) => "websocket_error",
RuntimeError::UnsupportedProtocol { .. } => "unsupported_protocol",
RuntimeError::MissingStreamingConfig { .. } => "streaming_config_error",