feat(nacos): 增强 NacosAdmin 接口支持命名空间列表和配置发布表单
- 添加 list_namespaces 方法,支持从 Nacos 获取命名空间列表 - 重构 publish_config 方法,使用 build_publish_forms 函数构建 V3 和 V1 的发布表单 - 增加 namespace_list_error 函数,改进命名空间列表错误处理 - 添加单元测试,验证表单构建和错误处理逻辑
This commit is contained in:
parent
c15fb85561
commit
c0bd9313d7
|
|
@ -341,6 +341,7 @@ impl NacosAdmin for NacosOpenApiAdmin {
|
|||
async fn test_connection(&self) -> Result<NacosConnectionInfo, String> {
|
||||
let raw = self.get_server_state().await?;
|
||||
let _ = self.access_token().await?;
|
||||
let _ = self.list_namespaces().await?;
|
||||
Ok(NacosConnectionInfo {
|
||||
server_addr: self.cfg.server_addr.clone(),
|
||||
display_server_addr: self.cfg.display_server_addr.clone(),
|
||||
|
|
@ -358,7 +359,10 @@ impl NacosAdmin for NacosOpenApiAdmin {
|
|||
async fn list_namespaces(&self) -> Result<Vec<NacosNamespaceInfo>, String> {
|
||||
let value = match self.get_json("/v3/console/core/namespace/list", Vec::new()).await {
|
||||
Ok(value) => value,
|
||||
Err(_) => self.get_json("/v1/console/namespaces", Vec::new()).await?,
|
||||
Err(v3_err) => self
|
||||
.get_json("/v1/console/namespaces", Vec::new())
|
||||
.await
|
||||
.map_err(|v1_err| namespace_list_error(&v3_err, &v1_err))?,
|
||||
};
|
||||
Ok(parse_namespaces(value))
|
||||
}
|
||||
|
|
@ -524,35 +528,15 @@ impl NacosAdmin for NacosOpenApiAdmin {
|
|||
|
||||
async fn publish_config(&self, req: NacosConfigUpsert) -> Result<(), String> {
|
||||
let namespace = self.namespace(req.namespace.as_deref());
|
||||
let mut v3_query = vec![
|
||||
("dataId".to_string(), req.data_id.clone()),
|
||||
("groupName".to_string(), req.group.clone()),
|
||||
("content".to_string(), req.content.clone()),
|
||||
("namespaceId".to_string(), namespace.clone()),
|
||||
];
|
||||
push_optional(&mut v3_query, "type", req.config_type.clone());
|
||||
push_optional(&mut v3_query, "appName", req.app_name.clone());
|
||||
push_optional(&mut v3_query, "desc", req.desc.clone());
|
||||
push_optional(&mut v3_query, "tags", req.tags.clone());
|
||||
|
||||
let mut v1_form = vec![
|
||||
("dataId".to_string(), req.data_id),
|
||||
("group".to_string(), req.group),
|
||||
("content".to_string(), req.content),
|
||||
("tenant".to_string(), namespace),
|
||||
];
|
||||
push_optional(&mut v1_form, "type", req.config_type);
|
||||
push_optional(&mut v1_form, "appName", req.app_name);
|
||||
push_optional(&mut v1_form, "desc", req.desc);
|
||||
push_optional(&mut v1_form, "config_tags", req.tags);
|
||||
let (v3_form, v1_form) = build_publish_forms(req, namespace);
|
||||
|
||||
let mut errors = Vec::new();
|
||||
for (path, query) in [
|
||||
("/v3/console/cs/config", v3_query.clone()),
|
||||
("/v3/console/cs/config/publish", v3_query.clone()),
|
||||
("/v3/console/cs/config/update", v3_query),
|
||||
for (path, form) in [
|
||||
("/v3/console/cs/config", v3_form.clone()),
|
||||
("/v3/console/cs/config/publish", v3_form.clone()),
|
||||
("/v3/console/cs/config/update", v3_form),
|
||||
] {
|
||||
match self.request(reqwest::Method::POST, path, query, None, None).await {
|
||||
match self.request(reqwest::Method::POST, path, Vec::new(), Some(form), None).await {
|
||||
Ok(resp) => match error_for_status(resp, path).await {
|
||||
Ok(_) => return Ok(()),
|
||||
Err(err) => errors.push(err),
|
||||
|
|
@ -988,6 +972,38 @@ fn push_optional(params: &mut Vec<(String, String)>, key: &str, value: Option<St
|
|||
}
|
||||
}
|
||||
|
||||
fn build_publish_forms(req: NacosConfigUpsert, namespace: String) -> (Vec<(String, String)>, Vec<(String, String)>) {
|
||||
let mut v3_form = vec![
|
||||
("dataId".to_string(), req.data_id.clone()),
|
||||
("groupName".to_string(), req.group.clone()),
|
||||
("content".to_string(), req.content.clone()),
|
||||
("namespaceId".to_string(), namespace.clone()),
|
||||
];
|
||||
push_optional(&mut v3_form, "type", req.config_type.clone());
|
||||
push_optional(&mut v3_form, "appName", req.app_name.clone());
|
||||
push_optional(&mut v3_form, "desc", req.desc.clone());
|
||||
push_optional(&mut v3_form, "configTags", req.tags.clone());
|
||||
push_optional(&mut v3_form, "config_tags", req.tags.clone());
|
||||
|
||||
let mut v1_form = vec![
|
||||
("dataId".to_string(), req.data_id),
|
||||
("group".to_string(), req.group),
|
||||
("content".to_string(), req.content),
|
||||
("tenant".to_string(), namespace),
|
||||
];
|
||||
push_optional(&mut v1_form, "type", req.config_type);
|
||||
push_optional(&mut v1_form, "appName", req.app_name);
|
||||
push_optional(&mut v1_form, "desc", req.desc);
|
||||
push_optional(&mut v1_form, "config_tags", req.tags);
|
||||
|
||||
(v3_form, v1_form)
|
||||
}
|
||||
|
||||
fn namespace_list_error(v3_err: &str, v1_err: &str) -> String {
|
||||
let message = format!("Failed to list Nacos namespaces with v3 and v1 APIs. v3: {v3_err}; v1: {v1_err}");
|
||||
classified_error(classify_nacos_error(&message), &message)
|
||||
}
|
||||
|
||||
async fn response_json_or_text(resp: reqwest::Response) -> Result<Value, String> {
|
||||
let bytes = resp.bytes().await.map_err(|e| format!("Failed to read Nacos response: {e}"))?;
|
||||
if bytes.is_empty() {
|
||||
|
|
@ -1023,11 +1039,7 @@ fn classify_nacos_error(message: &str) -> &'static str {
|
|||
{
|
||||
return "authFailed";
|
||||
}
|
||||
if lower.contains("no static resource")
|
||||
|| lower.contains("context path")
|
||||
|| lower.contains("path\":\"/")
|
||||
|| lower.contains("path=/")
|
||||
{
|
||||
if lower.contains("no static resource") || lower.contains("context path") {
|
||||
return "contextPathMismatch";
|
||||
}
|
||||
if lower.contains("history")
|
||||
|
|
@ -1476,6 +1488,44 @@ mod tests {
|
|||
assert_eq!(parsed.content.as_deref(), Some("hello"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn builds_v3_publish_form_fields() {
|
||||
let (v3_form, v1_form) = build_publish_forms(
|
||||
NacosConfigUpsert {
|
||||
namespace: Some("ops".to_string()),
|
||||
data_id: "app.yaml".to_string(),
|
||||
group: "DEFAULT_GROUP".to_string(),
|
||||
content: "server:\n port: 8080".to_string(),
|
||||
config_type: Some("yaml".to_string()),
|
||||
app_name: Some("portal".to_string()),
|
||||
desc: Some("main config".to_string()),
|
||||
tags: Some("prod,gray".to_string()),
|
||||
},
|
||||
"ops".to_string(),
|
||||
);
|
||||
|
||||
assert!(v3_form.contains(&("dataId".to_string(), "app.yaml".to_string())));
|
||||
assert!(v3_form.contains(&("groupName".to_string(), "DEFAULT_GROUP".to_string())));
|
||||
assert!(v3_form.contains(&("namespaceId".to_string(), "ops".to_string())));
|
||||
assert!(v3_form.contains(&("content".to_string(), "server:\n port: 8080".to_string())));
|
||||
assert!(v3_form.contains(&("type".to_string(), "yaml".to_string())));
|
||||
assert!(v3_form.contains(&("configTags".to_string(), "prod,gray".to_string())));
|
||||
assert!(v3_form.contains(&("config_tags".to_string(), "prod,gray".to_string())));
|
||||
assert!(v1_form.contains(&("group".to_string(), "DEFAULT_GROUP".to_string())));
|
||||
assert!(v1_form.contains(&("tenant".to_string(), "ops".to_string())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn namespace_list_error_keeps_v3_and_v1_details() {
|
||||
let err = namespace_list_error(
|
||||
"NACOS_ERROR[authFailed]: Nacos admin /v3/console/core/namespace/list returned 403 Forbidden",
|
||||
"NACOS_ERROR[apiVersionMismatch]: Nacos admin /v1/console/namespaces returned 410 Gone",
|
||||
);
|
||||
assert!(err.starts_with("NACOS_ERROR[authFailed]:"));
|
||||
assert!(err.contains("/v3/console/core/namespace/list returned 403 Forbidden"));
|
||||
assert!(err.contains("/v1/console/namespaces returned 410 Gone"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_v1_show_all_config_detail_metadata() {
|
||||
let parsed = parse_config_detail(
|
||||
|
|
@ -1696,6 +1746,12 @@ mod tests {
|
|||
fn classifies_common_nacos_errors() {
|
||||
assert_eq!(classify_nacos_error("401 Unauthorized invalid access token"), "authFailed");
|
||||
assert_eq!(classify_nacos_error("No static resource nacos/v3/console/server/state"), "contextPathMismatch");
|
||||
assert_eq!(
|
||||
classify_nacos_error(
|
||||
r#"410 Gone {"message":"Current API will be deprecated","path":"/v1/console/namespaces"}"#
|
||||
),
|
||||
"apiVersionMismatch"
|
||||
);
|
||||
assert_eq!(classify_nacos_error("404 Not Found"), "apiVersionMismatch");
|
||||
assert_eq!(classify_nacos_error("connection refused"), "connectionFailed");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue