From f719306dd74a9f2a6af847356eed93f70154b790 Mon Sep 17 00:00:00 2001 From: Yuichi <913637919@qq.com> Date: Mon, 9 Dec 2024 02:25:01 +0000 Subject: [PATCH 1/7] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=9B=86=E6=88=90?= =?UTF-8?q?=E6=B5=8B=E8=AF=95=E6=97=A0=E6=B3=95=E5=9C=A8=E6=96=B0=E7=89=88?= =?UTF-8?q?=E6=9C=ACapiserver=E4=B8=8A=E8=BF=90=E8=A1=8C=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 114 +++++++++++++++++++++++++++++++----------- Cargo.toml | 3 +- src/cores/handlers.rs | 109 +++++++++++++++++++++++++++++++--------- tests/route_tests.rs | 0 4 files changed, 172 insertions(+), 54 deletions(-) create mode 100644 tests/route_tests.rs diff --git a/Cargo.lock b/Cargo.lock index f509f09..3cb9db6 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -65,7 +65,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -182,7 +182,7 @@ dependencies = [ "actix-router", "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -356,7 +356,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -367,7 +367,7 @@ checksum = "721cae7de5c34fbb2acd27e21e6d2cf7b886dce0c27388d46c4e6c47ea4318dd" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -668,7 +668,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn", + "syn 2.0.89", ] [[package]] @@ -679,7 +679,20 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core", "quote", - "syn", + "syn 2.0.89", +] + +[[package]] +name = "dashmap" +version = "5.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978747c1d849a7d2ee5e8adc0159961c48fb7e5db2f06af6723b80123bb53856" +dependencies = [ + "cfg-if", + "hashbrown 0.14.5", + "lock_api", + "once_cell", + "parking_lot_core", ] [[package]] @@ -717,7 +730,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn", + "syn 2.0.89", ] [[package]] @@ -746,7 +759,7 @@ dependencies = [ "dsl_auto_type", "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -766,7 +779,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" dependencies = [ - "syn", + "syn 2.0.89", ] [[package]] @@ -796,7 +809,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -816,7 +829,7 @@ dependencies = [ "heck", "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -961,6 +974,7 @@ dependencies = [ "schemars", "serde", "serde_json", + "serial_test", "tokio", ] @@ -1056,7 +1070,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -1135,6 +1149,12 @@ dependencies = [ "tracing", ] +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + [[package]] name = "hashbrown" version = "0.15.0" @@ -1392,7 +1412,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -1436,7 +1456,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.0", ] [[package]] @@ -2020,7 +2040,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -2209,7 +2229,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn", + "syn 2.0.89", ] [[package]] @@ -2261,7 +2281,7 @@ checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -2272,7 +2292,7 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -2304,7 +2324,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -2328,6 +2348,31 @@ dependencies = [ "serde", ] +[[package]] +name = "serial_test" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c789ec87f4687d022a2405cf46e0cd6284889f1839de292cadeb6c6019506f2" +dependencies = [ + "dashmap", + "futures", + "lazy_static", + "log", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b64f9e531ce97c88b4778aad0ceee079216071cffec6ac9b904277f8f92e7fe3" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "sha1" version = "0.10.6" @@ -2464,6 +2509,17 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + [[package]] name = "syn" version = "2.0.89" @@ -2492,7 +2548,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -2512,7 +2568,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -2597,7 +2653,7 @@ checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -2839,7 +2895,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn", + "syn 2.0.89", "wasm-bindgen-shared", ] @@ -2873,7 +2929,7 @@ checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3088,7 +3144,7 @@ checksum = "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", "synstructure", ] @@ -3110,7 +3166,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -3130,7 +3186,7 @@ checksum = "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", "synstructure", ] @@ -3151,7 +3207,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] @@ -3173,7 +3229,7 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn", + "syn 2.0.89", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 1e3061b..2c73961 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -30,4 +30,5 @@ schemars = "0.8.21" chrono = "0.4.38" once_cell = "1.20.2" lazy_static = "1.5.0" -async-stream = "0.3.6" \ No newline at end of file +async-stream = "0.3.6" +serial_test = "0.10.0" \ No newline at end of file diff --git a/src/cores/handlers.rs b/src/cores/handlers.rs index 4aca858..6cdb93f 100644 --- a/src/cores/handlers.rs +++ b/src/cores/handlers.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use std::fmt::Debug; use std::sync::{Arc, LazyLock, Mutex}; -use std::{time}; +use std::{time, vec::Vec}; use async_trait::async_trait; use actix_web::{HttpResponse, Result, web, Error}; use chrono::Utc; @@ -1123,18 +1123,39 @@ async fn get_all_data_from_kine( } // 检查所有表的数据是否一致 - let mut unique_results: HashMap = HashMap::new(); - for results in table_results.values() { - for result in results { - *unique_results.entry(result.clone()).or_insert(0) += 1; + let vec1 = table_results[tables[0]].clone(); + let vec2 = table_results[tables[1]].clone(); + let vec3 = table_results[tables[2]].clone(); + + let max_len = vec1.len().max(vec2.len()).max(vec3.len()); + let mut filtered_results = Vec::new(); + + // 筛选出出现次数大于等于 2 的数据 + for i in 0..max_len { + // 获取当前索引的值,越界的填入 None + let value1 = vec1.get(i); + let value2 = vec2.get(i); + let value3 = vec3.get(i); + + // 统计每个值的出现次数 + let mut counts = HashMap::new(); + if let Some(v) = value1 { + *counts.entry(v).or_insert(0) += 1; + } + if let Some(v) = value2 { + *counts.entry(v).or_insert(0) += 1; } + if let Some(v) = value3 { + *counts.entry(v).or_insert(0) += 1; + } + + // 找出出现次数大于等于 2 的值 + if let Some((value, &_)) = counts.iter().find(|(_, &count)| count >= 2) { + filtered_results.push((*value).clone()); // 将该值存入结果 + } + } - // 筛选出出现次数大于等于 2 的数据 - let filtered_results: Vec = unique_results - .into_iter() - .filter(|(_, count)| *count >= 2) // 过滤条件 - .map(|(data, _)| data) // 提取数据部分 - .collect(); + Ok(filtered_results) } @@ -2642,6 +2663,8 @@ mod tests { use std::sync::Arc; use feventbus::traits::controller::EventBus; use once_cell::sync::Lazy; + use crate::cores::handlers::EventManager; + use serial_test::serial; // 使用 Lazy 来初始化静态的共享资源 static DB_POOL: Lazy> = Lazy::new(|| { @@ -2652,22 +2675,29 @@ mod tests { static HANDLER: Lazy> = Lazy::new(|| Arc::new(DefaultHandler::new())); + #[actix_web::test] + #[serial] async fn test_api_with_namespace() { let nats_cli = Arc::new(NatsCli::new().await.unwrap()); + // 初始化事件管理器 + let event_manager = EventManager::new(); let app = test::init_service( App::new() .route("/api/{version}/namespaces/{namespace}/{plural}", web::post().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + move |path, data| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.create_api_with_namespace(path, data, &mut conn, nats_cli).await, + Ok(mut conn) => handler.create_api_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -2675,14 +2705,17 @@ mod tests { })) .route("/api/{version}/namespaces/{namespace}/{plural}/{name}", web::put().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + move |path, data| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.update_api_with_namespace(path, data, &mut conn, nats_cli).await, + Ok(mut conn) => handler.update_api_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -2690,14 +2723,17 @@ mod tests { })) .route("/api/{version}/namespaces/{namespace}/{plural}/{name}", web::delete().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + move |path| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.delete_api_with_namespace(path, &mut conn, nats_cli).await, + Ok(mut conn) => handler.delete_api_with_namespace(path, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -2705,6 +2741,7 @@ mod tests { })) .route("/api/{version}/namespaces/{namespace}/{plural}/{name}", web::get().to({ let nats_cli = Arc::clone(&nats_cli); + move |path, data| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); @@ -2997,22 +3034,26 @@ mod tests { #[actix_web::test] + #[serial] async fn test_api_without_namespace() { let nats_cli = Arc::new(NatsCli::new().await.unwrap()); + let event_manager = EventManager::new(); let app = test::init_service( App::new() .route("/api/{version}/{plural}", web::post().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); move |path, data| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.create_api_without_namespace(path, data, &mut conn, nats_cli).await, + Ok(mut conn) => handler.create_api_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -3020,15 +3061,17 @@ mod tests { })) .route("/api/{version}/{plural}/{name}", web::put().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); move |path, data| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.update_api_without_namespace(path, data, &mut conn, nats_cli).await, + Ok(mut conn) => handler.update_api_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -3036,15 +3079,17 @@ mod tests { })) .route("/api/{version}/{plural}/{name}", web::delete().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); move |path| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.delete_api_without_namespace(path, &mut conn, nats_cli).await, + Ok(mut conn) => handler.delete_api_without_namespace(path, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -3316,22 +3361,26 @@ mod tests { #[actix_web::test] + #[serial] async fn test_apis_with_namespace() { let nats_cli = Arc::new(NatsCli::new().await.unwrap()); + let event_manager = EventManager::new(); let app = test::init_service( App::new() .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}", web::post().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); move |path, data| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.create_apis_with_namespace(path, data, &mut conn, nats_cli).await, + Ok(mut conn) => handler.create_apis_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -3339,15 +3388,17 @@ mod tests { })) .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}", web::put().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); move |path, data| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.update_apis_with_namespace(path, data, &mut conn, nats_cli).await, + Ok(mut conn) => handler.update_apis_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -3355,15 +3406,17 @@ mod tests { })) .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}", web::delete().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); move |path| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.delete_apis_with_namespace(path, &mut conn, nats_cli).await, + Ok(mut conn) => handler.delete_apis_with_namespace(path, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -3634,22 +3687,26 @@ mod tests { #[actix_web::test] + #[serial] async fn test_crd_and_apis_without_namespace() { - let nats_cli = Arc::new(NatsCli::new().await.unwarp()); + let nats_cli = Arc::new(NatsCli::new().await.unwrap()); + let event_manager = EventManager::new(); let app = test::init_service( App::new() .route("/apis/{group}/{version}/{plural}", web::post().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); move |path, data| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.create_apis_without_namespace(path, data, &mut conn, nats_cli).await, + Ok(mut conn) => handler.create_apis_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -3657,15 +3714,17 @@ mod tests { })) .route("/apis/{group}/{version}/{plural}/{name}", web::put().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); move |path, data| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.update_apis_without_namespace(path, data, &mut conn, nats_cli).await, + Ok(mut conn) => handler.update_apis_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } @@ -3673,15 +3732,17 @@ mod tests { })) .route("/apis/{group}/{version}/{plural}/{name}", web::delete().to({ let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); move |path| { let handler = HANDLER.clone(); let db_pool = DB_POOL.clone(); let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); async move { match db_pool.get_connection() { - Ok(mut conn) => handler.delete_apis_without_namespace(path, &mut conn, nats_cli).await, + Ok(mut conn) => handler.delete_apis_without_namespace(path, &mut conn, nats_cli, event_manager).await, Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), } } diff --git a/tests/route_tests.rs b/tests/route_tests.rs new file mode 100644 index 0000000..e69de29 -- Gitee From 93b114a4abd874ee8d6a7a640b722e4f42c47ef2 Mon Sep 17 00:00:00 2001 From: Yuichi <913637919@qq.com> Date: Mon, 9 Dec 2024 02:31:43 +0000 Subject: [PATCH 2/7] =?UTF-8?q?=E9=9B=86=E6=88=90=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E7=A7=BB=E5=8A=A8=E5=88=B0=E5=8D=95=E7=8B=AC=E7=9A=84=E8=B7=AF?= =?UTF-8?q?=E5=BE=84=E4=B8=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/cores/handlers.rs | 1387 ---------------------------------------- tests/route_tests.rs | 1393 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 1393 insertions(+), 1387 deletions(-) diff --git a/src/cores/handlers.rs b/src/cores/handlers.rs index 6cdb93f..6c0ab5d 100644 --- a/src/cores/handlers.rs +++ b/src/cores/handlers.rs @@ -2653,1390 +2653,3 @@ impl Handler for DefaultHandler { DefaultHandler {} } } - -#[cfg(test)] -mod tests { - use super::*; - use actix_web::{test, web, App}; - use serde_json::json; - use crate::cores::db::DbPool; - use std::sync::Arc; - use feventbus::traits::controller::EventBus; - use once_cell::sync::Lazy; - use crate::cores::handlers::EventManager; - use serial_test::serial; - - // 使用 Lazy 来初始化静态的共享资源 - static DB_POOL: Lazy> = Lazy::new(|| { - let pool = DbPool::new_in_memory().expect("Failed to create in-memory database pool"); - Arc::new(pool) - }); - - static HANDLER: Lazy> = Lazy::new(|| Arc::new(DefaultHandler::new())); - - - - #[actix_web::test] - #[serial] - async fn test_api_with_namespace() { - let nats_cli = Arc::new(NatsCli::new().await.unwrap()); - // 初始化事件管理器 - let event_manager = EventManager::new(); - - let app = test::init_service( - App::new() - .route("/api/{version}/namespaces/{namespace}/{plural}", web::post().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.create_api_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/api/{version}/namespaces/{namespace}/{plural}/{name}", web::put().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.update_api_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/api/{version}/namespaces/{namespace}/{plural}/{name}", web::delete().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.delete_api_with_namespace(path, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/api/{version}/namespaces/{namespace}/{plural}/{name}", web::get().to({ - let nats_cli = Arc::clone(&nats_cli); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.getone_api_with_namespace(path, data, &mut conn, nats_cli).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/api/{version}/namespaces/{namespace}/{plural}", web::get().to({ - let nats_cli = Arc::clone(&nats_cli); - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.listall_api_with_namespace(path, data, &mut conn, nats_cli).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - ).await; - - - - - let create_req_data = json!({ - "apiVersion": "v1", - "kind": "Cargo", - "metadata": { - "name": "cargo-case-01", - "annotations": { - "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" - } - }, - "spec": { - "containers": [ - { - "name": "cargo-case-01", - "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:latest", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let create_req = test::TestRequest::post() - .uri("/api/v1/namespaces/global/cargos") - .set_json(&create_req_data) - .to_request(); - - let create_resp = test::call_service(&app, create_req).await; - - assert!(create_resp.status().is_success()); - - let create_resp_body = test::read_body_json::(create_resp).await; - assert_eq!(create_resp_body, create_req_data); - - - - - - - - let update_req_data = json!({ - "apiVersion": "v1", - "kind": "Cargo", - "metadata": { - "name": "cargo-case-01", - "annotations": { - "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" - } - }, - "spec": { - "containers": [ - { - "name": "cargo-case-01", - "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:123", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let update_req = test::TestRequest::put() - .uri("/api/v1/namespaces/global/cargos/cargo-case-01") - .set_json(&update_req_data) - .to_request(); - - let update_resp = test::call_service(&app, update_req).await; - - assert!(update_resp.status().is_success()); - - let update_resp_body = test::read_body_json::(update_resp).await; - assert_eq!(update_resp_body, update_req_data); - - - - - let get_req_data = json!({ - "apiVersion": "v1", - "kind": "Cargo", - "metadata": { - "name": "cargo-case-01", - "annotations": { - "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" - } - }, - "spec": { - "containers": [ - { - "name": "cargo-case-01", - "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:123", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let get_req = test::TestRequest::get() - .uri("/api/v1/namespaces/global/cargos/cargo-case-01") - .to_request(); - - let get_resp = test::call_service(&app, get_req).await; - - assert!(get_resp.status().is_success()); - - let get_resp_body = test::read_body_json::(get_resp).await; - assert_eq!(get_resp_body, get_req_data); - - - - - let create_req_data2 = json!({ - "apiVersion": "v1", - "kind": "Cargo", - "metadata": { - "name": "cargo-case-02", - "annotations": { - "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" - } - }, - "spec": { - "containers": [ - { - "name": "cargo-case-02", - "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:latest", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let create_req2 = test::TestRequest::post() - .uri("/api/v1/namespaces/global/cargos") - .set_json(&create_req_data2) - .to_request(); - - let create_resp2 = test::call_service(&app, create_req2).await; - - assert!(create_resp2.status().is_success()); - - let create_resp_body2 = test::read_body_json::(create_resp2).await; - assert_eq!(create_resp_body2, create_req_data2); - - - - let list_req_data = json!([{ - "apiVersion": "v1", - "kind": "Cargo", - "metadata": { - "name": "cargo-case-01", - "annotations": { - "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" - } - }, - "spec": { - "containers": [ - { - "name": "cargo-case-01", - "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:123", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Cargo", - "metadata": { - "name": "cargo-case-02", - "annotations": { - "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" - } - }, - "spec": { - "containers": [ - { - "name": "cargo-case-02", - "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:latest", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }]); - - let list_req = test::TestRequest::get() - .uri("/api/v1/namespaces/global/cargos") - .to_request(); - - let list_resp = test::call_service(&app, list_req).await; - - assert!(list_resp.status().is_success()); - - let list_resp_body = test::read_body_json::(list_resp).await; - assert_eq!(list_resp_body, list_req_data); - - - - let delete_req = test::TestRequest::delete() - .uri("/api/v1/namespaces/global/cargos/cargo-case-01") - .to_request(); - - let delete_resp = test::call_service(&app, delete_req).await; - - assert!(delete_resp.status().is_success()); - - let list_req_data2 = json!([ - { - "apiVersion": "v1", - "kind": "Cargo", - "metadata": { - "name": "cargo-case-02", - "annotations": { - "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" - } - }, - "spec": { - "containers": [ - { - "name": "cargo-case-02", - "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:latest", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }]); - - let list_req2 = test::TestRequest::get() - .uri("/api/v1/namespaces/global/cargos") - .to_request(); - - let list_resp2 = test::call_service(&app, list_req2).await; - - assert!(list_resp2.status().is_success()); - - let list_resp_body2 = test::read_body_json::(list_resp2).await; - assert_eq!(list_resp_body2, list_req_data2); - - } - - - - #[actix_web::test] - #[serial] - async fn test_api_without_namespace() { - let nats_cli = Arc::new(NatsCli::new().await.unwrap()); - let event_manager = EventManager::new(); - - let app = test::init_service( - App::new() - .route("/api/{version}/{plural}", web::post().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.create_api_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/api/{version}/{plural}/{name}", web::put().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.update_api_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/api/{version}/{plural}/{name}", web::delete().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.delete_api_without_namespace(path, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/api/{version}/{plural}/{name}", web::get().to({ - let nats_cli = Arc::clone(&nats_cli); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.getone_api_without_namespace(path, data, &mut conn, nats_cli).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/api/{version}/{plural}", web::get().to({ - let nats_cli = Arc::clone(&nats_cli); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.listall_api_without_namespace(path, data, &mut conn, nats_cli).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - ).await; - - - let create_req_data = json!({ - "apiVersion": "v1", - "kind": "Node", - "metadata": { - "name": "node-case-01", - }, - "spec": { - "containers": [ - { - "name": "node-case-01", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let create_req = test::TestRequest::post() - .uri("/api/v1/nodes") - .set_json(&create_req_data) - .to_request(); - - let create_resp = test::call_service(&app, create_req).await; - - assert!(create_resp.status().is_success()); - - let create_resp_body = test::read_body_json::(create_resp).await; - assert_eq!(create_resp_body, create_req_data); - - - - - - - - let update_req_data = json!({ - "apiVersion": "v1", - "kind": "Node", - "metadata": { - "name": "node-case-01", - }, - "spec": { - "containers": [ - { - "name": "node-case-01", - "command": [ - "sleep", - "1" - ] - } - ] - } - }); - - let update_req = test::TestRequest::put() - .uri("/api/v1/nodes/node-case-01") - .set_json(&update_req_data) - .to_request(); - - let update_resp = test::call_service(&app, update_req).await; - - assert!(update_resp.status().is_success()); - - let update_resp_body = test::read_body_json::(update_resp).await; - assert_eq!(update_resp_body, update_req_data); - - - - - let get_req_data = json!({ - "apiVersion": "v1", - "kind": "Node", - "metadata": { - "name": "node-case-01", - }, - "spec": { - "containers": [ - { - "name": "node-case-01", - "command": [ - "sleep", - "1" - ] - } - ] - } - }); - - let get_req = test::TestRequest::get() - .uri("/api/v1/nodes/node-case-01") - .to_request(); - - let get_resp = test::call_service(&app, get_req).await; - - assert!(get_resp.status().is_success()); - - let get_resp_body = test::read_body_json::(get_resp).await; - assert_eq!(get_resp_body, get_req_data); - - - - - let create_req_data2 = json!({ - "apiVersion": "v1", - "kind": "Node", - "metadata": { - "name": "node-case-02", - }, - "spec": { - "containers": [ - { - "name": "node-case-02", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let create_req2 = test::TestRequest::post() - .uri("/api/v1/nodes") - .set_json(&create_req_data2) - .to_request(); - - let create_resp2 = test::call_service(&app, create_req2).await; - - assert!(create_resp2.status().is_success()); - - let create_resp_body2 = test::read_body_json::(create_resp2).await; - assert_eq!(create_resp_body2, create_req_data2); - - - - let list_req_data = json!([{ - "apiVersion": "v1", - "kind": "Node", - "metadata": { - "name": "node-case-01", - }, - "spec": { - "containers": [ - { - "name": "node-case-01", - "command": [ - "sleep", - "1" - ] - } - ] - } - }, - { - "apiVersion": "v1", - "kind": "Node", - "metadata": { - "name": "node-case-02", - }, - "spec": { - "containers": [ - { - "name": "node-case-02", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }]); - - let list_req = test::TestRequest::get() - .uri("/api/v1/nodes") - .to_request(); - - let list_resp = test::call_service(&app, list_req).await; - - assert!(list_resp.status().is_success()); - - let list_resp_body = test::read_body_json::(list_resp).await; - assert_eq!(list_resp_body, list_req_data); - - - - let delete_req = test::TestRequest::delete() - .uri("/api/v1/nodes/node-case-01") - .to_request(); - - let delete_resp = test::call_service(&app, delete_req).await; - - assert!(delete_resp.status().is_success()); - - let list_req_data2 = json!([ - { - "apiVersion": "v1", - "kind": "Node", - "metadata": { - "name": "node-case-02", - }, - "spec": { - "containers": [ - { - "name": "node-case-02", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }]); - let list_req2 = test::TestRequest::get() - .uri("/api/v1/nodes") - .to_request(); - - let list_resp2 = test::call_service(&app, list_req2).await; - - assert!(list_resp2.status().is_success()); - - let list_resp_body2 = test::read_body_json::(list_resp2).await; - assert_eq!(list_resp_body2, list_req_data2); - - } - - - - - #[actix_web::test] - #[serial] - async fn test_apis_with_namespace() { - let nats_cli = Arc::new(NatsCli::new().await.unwrap()); - let event_manager = EventManager::new(); - - let app = test::init_service( - App::new() - .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}", web::post().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.create_apis_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}", web::put().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.update_apis_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}", web::delete().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.delete_apis_with_namespace(path, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}", web::get().to({ - let nats_cli = Arc::clone(&nats_cli); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.getone_apis_with_namespace(path, data, &mut conn, nats_cli).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}", web::get().to({ - let nats_cli = Arc::clone(&nats_cli); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.listall_apis_with_namespace(path, data, &mut conn, nats_cli).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - ).await; - - - - let create_req_data = json!({ - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "job-case-01", - }, - "spec": { - "containers": [ - { - "name": "job-case-01", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let create_req = test::TestRequest::post() - .uri("/apis/batch/v1/namespaces/global/jobs") - .set_json(&create_req_data) - .to_request(); - - let create_resp = test::call_service(&app, create_req).await; - - assert!(create_resp.status().is_success()); - - let create_resp_body = test::read_body_json::(create_resp).await; - assert_eq!(create_resp_body, create_req_data); - - - - - - - - let update_req_data = json!({ - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "job-case-01", - }, - "spec": { - "containers": [ - { - "name": "job-case-01", - "command": [ - "sleep", - "1" - ] - } - ] - } - }); - - let update_req = test::TestRequest::put() - .uri("/apis/batch/v1/namespaces/global/jobs/job-case-01") - .set_json(&update_req_data) - .to_request(); - - let update_resp = test::call_service(&app, update_req).await; - - assert!(update_resp.status().is_success()); - - let update_resp_body = test::read_body_json::(update_resp).await; - assert_eq!(update_resp_body, update_req_data); - - - - - let get_req_data = json!({ - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "job-case-01", - }, - "spec": { - "containers": [ - { - "name": "job-case-01", - "command": [ - "sleep", - "1" - ] - } - ] - } - }); - - let get_req = test::TestRequest::get() - .uri("/apis/batch/v1/namespaces/global/jobs/job-case-01") - .to_request(); - - let get_resp = test::call_service(&app, get_req).await; - - assert!(get_resp.status().is_success()); - - let get_resp_body = test::read_body_json::(get_resp).await; - assert_eq!(get_resp_body, get_req_data); - - - - - let create_req_data2 = json!({ - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "job-case-02", - }, - "spec": { - "containers": [ - { - "name": "job-case-02", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let create_req2 = test::TestRequest::post() - .uri("/apis/batch/v1/namespaces/global/jobs") - .set_json(&create_req_data2) - .to_request(); - - let create_resp2 = test::call_service(&app, create_req2).await; - - assert!(create_resp2.status().is_success()); - - let create_resp_body2 = test::read_body_json::(create_resp2).await; - assert_eq!(create_resp_body2, create_req_data2); - - - - let list_req_data = json!([{ - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "job-case-01", - }, - "spec": { - "containers": [ - { - "name": "job-case-01", - "command": [ - "sleep", - "1" - ] - } - ] - } - }, - { - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "job-case-02", - }, - "spec": { - "containers": [ - { - "name": "job-case-02", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }]); - - let list_req = test::TestRequest::get() - .uri("/apis/batch/v1/namespaces/global/jobs") - .to_request(); - - let list_resp = test::call_service(&app, list_req).await; - - assert!(list_resp.status().is_success()); - - let list_resp_body = test::read_body_json::(list_resp).await; - assert_eq!(list_resp_body, list_req_data); - - - - let delete_req = test::TestRequest::delete() - .uri("/apis/batch/v1/namespaces/global/jobs/job-case-01") - .to_request(); - - let delete_resp = test::call_service(&app, delete_req).await; - - assert!(delete_resp.status().is_success()); - - let list_req_data2 = json!([ - { - "apiVersion": "batch/v1", - "kind": "Job", - "metadata": { - "name": "job-case-02", - }, - "spec": { - "containers": [ - { - "name": "job-case-02", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }]); - let list_req2 = test::TestRequest::get() - .uri("/apis/batch/v1/namespaces/global/jobs") - .to_request(); - - let list_resp2 = test::call_service(&app, list_req2).await; - - assert!(list_resp2.status().is_success()); - - let list_resp_body2 = test::read_body_json::(list_resp2).await; - assert_eq!(list_resp_body2, list_req_data2); - - } - - - #[actix_web::test] - #[serial] - async fn test_crd_and_apis_without_namespace() { - let nats_cli = Arc::new(NatsCli::new().await.unwrap()); - let event_manager = EventManager::new(); - - let app = test::init_service( - App::new() - .route("/apis/{group}/{version}/{plural}", web::post().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.create_apis_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/apis/{group}/{version}/{plural}/{name}", web::put().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.update_apis_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/apis/{group}/{version}/{plural}/{name}", web::delete().to({ - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - move |path| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.delete_apis_without_namespace(path, &mut conn, nats_cli, event_manager).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/apis/{group}/{version}/{plural}/{name}", web::get().to({ - let nats_cli = Arc::clone(&nats_cli); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.getone_apis_without_namespace(path, data, &mut conn, nats_cli).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - .route("/apis/{group}/{version}/{plural}", web::get().to({ - let nats_cli = Arc::clone(&nats_cli); - - move |path, data| { - let handler = HANDLER.clone(); - let db_pool = DB_POOL.clone(); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => handler.listall_apis_without_namespace(path, data, &mut conn, nats_cli).await, - Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), - } - } - } - })) - ).await; - - - let crd_req_data = json!({ - "apiVersion": "batch/v1", - "kind": "CustomResourceDefinition", - "metadata": { - "name": "myjob.iscas.cn" - }, - "spec": { - "names": - { - "kind": "Myjob", - "listKind": "MyjobList", - "plural": "myjobs", - "singular": "myjob" - } - } - }); - - let crd_req = test::TestRequest::post() - .uri("/apis/batch/v1/crds") - .set_json(&crd_req_data) - .to_request(); - - let crd_resp = test::call_service(&app, crd_req).await; - - assert!(crd_resp.status().is_success()); - - let crd_resp_body = test::read_body_json::(crd_resp).await; - assert_eq!(crd_resp_body, crd_req_data); - - - let create_req_data = json!({ - "apiVersion": "batch/v1", - "kind": "Myjob", - "metadata": { - "name": "myjob-case-01", - }, - "spec": { - "containers": [ - { - "name": "myjob-case-01", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let create_req = test::TestRequest::post() - .uri("/apis/batch/v1/myjobs") - .set_json(&create_req_data) - .to_request(); - - let create_resp = test::call_service(&app, create_req).await; - - assert!(create_resp.status().is_success()); - - let create_resp_body = test::read_body_json::(create_resp).await; - assert_eq!(create_resp_body, create_req_data); - - - - - - - - let update_req_data = json!({ - "apiVersion": "batch/v1", - "kind": "Myjob", - "metadata": { - "name": "myjob-case-01", - }, - "spec": { - "containers": [ - { - "name": "myjob-case-01", - "command": [ - "sleep", - "1" - ] - } - ] - } - }); - - let update_req = test::TestRequest::put() - .uri("/apis/batch/v1/myjobs/myjob-case-01") - .set_json(&update_req_data) - .to_request(); - - let update_resp = test::call_service(&app, update_req).await; - - assert!(update_resp.status().is_success()); - - let update_resp_body = test::read_body_json::(update_resp).await; - assert_eq!(update_resp_body, update_req_data); - - - - - let get_req_data = json!({ - "apiVersion": "batch/v1", - "kind": "Myjob", - "metadata": { - "name": "myjob-case-01", - }, - "spec": { - "containers": [ - { - "name": "myjob-case-01", - "command": [ - "sleep", - "1" - ] - } - ] - } - }); - - let get_req = test::TestRequest::get() - .uri("/apis/batch/v1/myjobs/myjob-case-01") - .to_request(); - - let get_resp = test::call_service(&app, get_req).await; - - assert!(get_resp.status().is_success()); - - let get_resp_body = test::read_body_json::(get_resp).await; - assert_eq!(get_resp_body, get_req_data); - - - - - let create_req_data2 = json!({ - "apiVersion": "batch/v1", - "kind": "Myjob", - "metadata": { - "name": "myjob-case-02", - }, - "spec": { - "containers": [ - { - "name": "myjob-case-02", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }); - - let create_req2 = test::TestRequest::post() - .uri("/apis/batch/v1/myjobs") - .set_json(&create_req_data2) - .to_request(); - - let create_resp2 = test::call_service(&app, create_req2).await; - - assert!(create_resp2.status().is_success()); - - let create_resp_body2 = test::read_body_json::(create_resp2).await; - assert_eq!(create_resp_body2, create_req_data2); - - - - let list_req_data = json!([{ - "apiVersion": "batch/v1", - "kind": "Myjob", - "metadata": { - "name": "myjob-case-01", - }, - "spec": { - "containers": [ - { - "name": "myjob-case-01", - "command": [ - "sleep", - "1" - ] - } - ] - } - }, - { - "apiVersion": "batch/v1", - "kind": "Myjob", - "metadata": { - "name": "myjob-case-02", - }, - "spec": { - "containers": [ - { - "name": "myjob-case-02", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }]); - - let list_req = test::TestRequest::get() - .uri("/apis/batch/v1/myjobs") - .to_request(); - - let list_resp = test::call_service(&app, list_req).await; - - assert!(list_resp.status().is_success()); - - let list_resp_body = test::read_body_json::(list_resp).await; - assert_eq!(list_resp_body, list_req_data); - - - - let delete_req = test::TestRequest::delete() - .uri("/apis/batch/v1/myjobs/myjob-case-01") - .to_request(); - - let delete_resp = test::call_service(&app, delete_req).await; - - assert!(delete_resp.status().is_success()); - - let list_req_data2 = json!([ - { - "apiVersion": "batch/v1", - "kind": "Myjob", - "metadata": { - "name": "myjob-case-02", - }, - "spec": { - "containers": [ - { - "name": "myjob-case-02", - "command": [ - "sleep", - "3600" - ] - } - ] - } - }]); - let list_req2 = test::TestRequest::get() - .uri("/apis/batch/v1/myjobs") - .to_request(); - - let list_resp2 = test::call_service(&app, list_req2).await; - - assert!(list_resp2.status().is_success()); - - let list_resp_body2 = test::read_body_json::(list_resp2).await; - assert_eq!(list_resp_body2, list_req_data2); - - } -} diff --git a/tests/route_tests.rs b/tests/route_tests.rs index e69de29..824b463 100644 --- a/tests/route_tests.rs +++ b/tests/route_tests.rs @@ -0,0 +1,1393 @@ +use actix_web::error::ErrorInternalServerError; +use feventbus::impls::nats::nats::NatsCli; +use serde_json::Value; +use fleet_apiserver::cores::db::DbPool; +use fleet_apiserver::cores::handlers::{DefaultHandler, EventManager}; + +#[cfg(test)] +mod tests { + use super::*; + use actix_web::{test, web, App}; + use serde_json::json; + use crate::DbPool; + use std::sync::Arc; + use feventbus::traits::controller::EventBus; + use once_cell::sync::Lazy; + use crate::EventManager; + use serial_test::serial; + use fleet_apiserver::cores::handlers::Handler; + + // 使用 Lazy 来初始化静态的共享资源 + static DB_POOL: Lazy> = Lazy::new(|| { + let pool = DbPool::new_in_memory().expect("Failed to create in-memory database pool"); + Arc::new(pool) + }); + + static HANDLER: Lazy> = Lazy::new(|| Arc::new(DefaultHandler::new())); + + + + #[actix_web::test] + #[serial] + async fn test_api_with_namespace() { + let nats_cli = Arc::new(NatsCli::new().await.unwrap()); + // 初始化事件管理器 + let event_manager = EventManager::new(); + + let app = test::init_service( + App::new() + .route("/api/{version}/namespaces/{namespace}/{plural}", web::post().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.create_api_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/api/{version}/namespaces/{namespace}/{plural}/{name}", web::put().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.update_api_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/api/{version}/namespaces/{namespace}/{plural}/{name}", web::delete().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.delete_api_with_namespace(path, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/api/{version}/namespaces/{namespace}/{plural}/{name}", web::get().to({ + let nats_cli = Arc::clone(&nats_cli); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.getone_api_with_namespace(path, data, &mut conn, nats_cli).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/api/{version}/namespaces/{namespace}/{plural}", web::get().to({ + let nats_cli = Arc::clone(&nats_cli); + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.listall_api_with_namespace(path, data, &mut conn, nats_cli).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + ).await; + + + + + let create_req_data = json!({ + "apiVersion": "v1", + "kind": "Cargo", + "metadata": { + "name": "cargo-case-01", + "annotations": { + "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" + } + }, + "spec": { + "containers": [ + { + "name": "cargo-case-01", + "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:latest", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let create_req = test::TestRequest::post() + .uri("/api/v1/namespaces/global/cargos") + .set_json(&create_req_data) + .to_request(); + + let create_resp = test::call_service(&app, create_req).await; + + assert!(create_resp.status().is_success()); + + let create_resp_body = test::read_body_json::(create_resp).await; + assert_eq!(create_resp_body, create_req_data); + + + + + + + + let update_req_data = json!({ + "apiVersion": "v1", + "kind": "Cargo", + "metadata": { + "name": "cargo-case-01", + "annotations": { + "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" + } + }, + "spec": { + "containers": [ + { + "name": "cargo-case-01", + "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:123", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let update_req = test::TestRequest::put() + .uri("/api/v1/namespaces/global/cargos/cargo-case-01") + .set_json(&update_req_data) + .to_request(); + + let update_resp = test::call_service(&app, update_req).await; + + assert!(update_resp.status().is_success()); + + let update_resp_body = test::read_body_json::(update_resp).await; + assert_eq!(update_resp_body, update_req_data); + + + + + let get_req_data = json!({ + "apiVersion": "v1", + "kind": "Cargo", + "metadata": { + "name": "cargo-case-01", + "annotations": { + "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" + } + }, + "spec": { + "containers": [ + { + "name": "cargo-case-01", + "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:123", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let get_req = test::TestRequest::get() + .uri("/api/v1/namespaces/global/cargos/cargo-case-01") + .to_request(); + + let get_resp = test::call_service(&app, get_req).await; + + assert!(get_resp.status().is_success()); + + let get_resp_body = test::read_body_json::(get_resp).await; + assert_eq!(get_resp_body, get_req_data); + + + + + let create_req_data2 = json!({ + "apiVersion": "v1", + "kind": "Cargo", + "metadata": { + "name": "cargo-case-02", + "annotations": { + "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" + } + }, + "spec": { + "containers": [ + { + "name": "cargo-case-02", + "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:latest", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let create_req2 = test::TestRequest::post() + .uri("/api/v1/namespaces/global/cargos") + .set_json(&create_req_data2) + .to_request(); + + let create_resp2 = test::call_service(&app, create_req2).await; + + assert!(create_resp2.status().is_success()); + + let create_resp_body2 = test::read_body_json::(create_resp2).await; + assert_eq!(create_resp_body2, create_req_data2); + + + + let list_req_data = json!([{ + "apiVersion": "v1", + "kind": "Cargo", + "metadata": { + "name": "cargo-case-01", + "annotations": { + "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" + } + }, + "spec": { + "containers": [ + { + "name": "cargo-case-01", + "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:123", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }, + { + "apiVersion": "v1", + "kind": "Cargo", + "metadata": { + "name": "cargo-case-02", + "annotations": { + "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" + } + }, + "spec": { + "containers": [ + { + "name": "cargo-case-02", + "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:latest", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }]); + + let list_req = test::TestRequest::get() + .uri("/api/v1/namespaces/global/cargos") + .to_request(); + + let list_resp = test::call_service(&app, list_req).await; + + assert!(list_resp.status().is_success()); + + let list_resp_body = test::read_body_json::(list_resp).await; + assert_eq!(list_resp_body, list_req_data); + + + + let delete_req = test::TestRequest::delete() + .uri("/api/v1/namespaces/global/cargos/cargo-case-01") + .to_request(); + + let delete_resp = test::call_service(&app, delete_req).await; + + assert!(delete_resp.status().is_success()); + + let list_req_data2 = json!([ + { + "apiVersion": "v1", + "kind": "Cargo", + "metadata": { + "name": "cargo-case-02", + "annotations": { + "k8s.v1.cni.cncf.io/networks": "testns1/macvlan-conf-1" + } + }, + "spec": { + "containers": [ + { + "name": "cargo-case-02", + "image": "g-ubjg5602-docker.pkg.coding.net/iscas-system/containers/busybox:latest", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }]); + + let list_req2 = test::TestRequest::get() + .uri("/api/v1/namespaces/global/cargos") + .to_request(); + + let list_resp2 = test::call_service(&app, list_req2).await; + + assert!(list_resp2.status().is_success()); + + let list_resp_body2 = test::read_body_json::(list_resp2).await; + assert_eq!(list_resp_body2, list_req_data2); + + } + + + + #[actix_web::test] + #[serial] + async fn test_api_without_namespace() { + let nats_cli = Arc::new(NatsCli::new().await.unwrap()); + let event_manager = EventManager::new(); + + let app = test::init_service( + App::new() + .route("/api/{version}/{plural}", web::post().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.create_api_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/api/{version}/{plural}/{name}", web::put().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.update_api_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/api/{version}/{plural}/{name}", web::delete().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.delete_api_without_namespace(path, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/api/{version}/{plural}/{name}", web::get().to({ + let nats_cli = Arc::clone(&nats_cli); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.getone_api_without_namespace(path, data, &mut conn, nats_cli).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/api/{version}/{plural}", web::get().to({ + let nats_cli = Arc::clone(&nats_cli); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.listall_api_without_namespace(path, data, &mut conn, nats_cli).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + ).await; + + + let create_req_data = json!({ + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "name": "node-case-01", + }, + "spec": { + "containers": [ + { + "name": "node-case-01", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let create_req = test::TestRequest::post() + .uri("/api/v1/nodes") + .set_json(&create_req_data) + .to_request(); + + let create_resp = test::call_service(&app, create_req).await; + + assert!(create_resp.status().is_success()); + + let create_resp_body = test::read_body_json::(create_resp).await; + assert_eq!(create_resp_body, create_req_data); + + + + + + + + let update_req_data = json!({ + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "name": "node-case-01", + }, + "spec": { + "containers": [ + { + "name": "node-case-01", + "command": [ + "sleep", + "1" + ] + } + ] + } + }); + + let update_req = test::TestRequest::put() + .uri("/api/v1/nodes/node-case-01") + .set_json(&update_req_data) + .to_request(); + + let update_resp = test::call_service(&app, update_req).await; + + assert!(update_resp.status().is_success()); + + let update_resp_body = test::read_body_json::(update_resp).await; + assert_eq!(update_resp_body, update_req_data); + + + + + let get_req_data = json!({ + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "name": "node-case-01", + }, + "spec": { + "containers": [ + { + "name": "node-case-01", + "command": [ + "sleep", + "1" + ] + } + ] + } + }); + + let get_req = test::TestRequest::get() + .uri("/api/v1/nodes/node-case-01") + .to_request(); + + let get_resp = test::call_service(&app, get_req).await; + + assert!(get_resp.status().is_success()); + + let get_resp_body = test::read_body_json::(get_resp).await; + assert_eq!(get_resp_body, get_req_data); + + + + + let create_req_data2 = json!({ + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "name": "node-case-02", + }, + "spec": { + "containers": [ + { + "name": "node-case-02", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let create_req2 = test::TestRequest::post() + .uri("/api/v1/nodes") + .set_json(&create_req_data2) + .to_request(); + + let create_resp2 = test::call_service(&app, create_req2).await; + + assert!(create_resp2.status().is_success()); + + let create_resp_body2 = test::read_body_json::(create_resp2).await; + assert_eq!(create_resp_body2, create_req_data2); + + + + let list_req_data = json!([{ + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "name": "node-case-01", + }, + "spec": { + "containers": [ + { + "name": "node-case-01", + "command": [ + "sleep", + "1" + ] + } + ] + } + }, + { + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "name": "node-case-02", + }, + "spec": { + "containers": [ + { + "name": "node-case-02", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }]); + + let list_req = test::TestRequest::get() + .uri("/api/v1/nodes") + .to_request(); + + let list_resp = test::call_service(&app, list_req).await; + + assert!(list_resp.status().is_success()); + + let list_resp_body = test::read_body_json::(list_resp).await; + assert_eq!(list_resp_body, list_req_data); + + + + let delete_req = test::TestRequest::delete() + .uri("/api/v1/nodes/node-case-01") + .to_request(); + + let delete_resp = test::call_service(&app, delete_req).await; + + assert!(delete_resp.status().is_success()); + + let list_req_data2 = json!([ + { + "apiVersion": "v1", + "kind": "Node", + "metadata": { + "name": "node-case-02", + }, + "spec": { + "containers": [ + { + "name": "node-case-02", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }]); + let list_req2 = test::TestRequest::get() + .uri("/api/v1/nodes") + .to_request(); + + let list_resp2 = test::call_service(&app, list_req2).await; + + assert!(list_resp2.status().is_success()); + + let list_resp_body2 = test::read_body_json::(list_resp2).await; + assert_eq!(list_resp_body2, list_req_data2); + + } + + + + + #[actix_web::test] + #[serial] + async fn test_apis_with_namespace() { + let nats_cli = Arc::new(NatsCli::new().await.unwrap()); + let event_manager = EventManager::new(); + + let app = test::init_service( + App::new() + .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}", web::post().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.create_apis_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}", web::put().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.update_apis_with_namespace(path, data, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}", web::delete().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.delete_apis_with_namespace(path, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}", web::get().to({ + let nats_cli = Arc::clone(&nats_cli); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.getone_apis_with_namespace(path, data, &mut conn, nats_cli).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/apis/{group}/{version}/namespaces/{namespace}/{plural}", web::get().to({ + let nats_cli = Arc::clone(&nats_cli); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.listall_apis_with_namespace(path, data, &mut conn, nats_cli).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + ).await; + + + + let create_req_data = json!({ + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "name": "job-case-01", + }, + "spec": { + "containers": [ + { + "name": "job-case-01", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let create_req = test::TestRequest::post() + .uri("/apis/batch/v1/namespaces/global/jobs") + .set_json(&create_req_data) + .to_request(); + + let create_resp = test::call_service(&app, create_req).await; + + assert!(create_resp.status().is_success()); + + let create_resp_body = test::read_body_json::(create_resp).await; + assert_eq!(create_resp_body, create_req_data); + + + + + + + + let update_req_data = json!({ + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "name": "job-case-01", + }, + "spec": { + "containers": [ + { + "name": "job-case-01", + "command": [ + "sleep", + "1" + ] + } + ] + } + }); + + let update_req = test::TestRequest::put() + .uri("/apis/batch/v1/namespaces/global/jobs/job-case-01") + .set_json(&update_req_data) + .to_request(); + + let update_resp = test::call_service(&app, update_req).await; + + assert!(update_resp.status().is_success()); + + let update_resp_body = test::read_body_json::(update_resp).await; + assert_eq!(update_resp_body, update_req_data); + + + + + let get_req_data = json!({ + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "name": "job-case-01", + }, + "spec": { + "containers": [ + { + "name": "job-case-01", + "command": [ + "sleep", + "1" + ] + } + ] + } + }); + + let get_req = test::TestRequest::get() + .uri("/apis/batch/v1/namespaces/global/jobs/job-case-01") + .to_request(); + + let get_resp = test::call_service(&app, get_req).await; + + assert!(get_resp.status().is_success()); + + let get_resp_body = test::read_body_json::(get_resp).await; + assert_eq!(get_resp_body, get_req_data); + + + + + let create_req_data2 = json!({ + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "name": "job-case-02", + }, + "spec": { + "containers": [ + { + "name": "job-case-02", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let create_req2 = test::TestRequest::post() + .uri("/apis/batch/v1/namespaces/global/jobs") + .set_json(&create_req_data2) + .to_request(); + + let create_resp2 = test::call_service(&app, create_req2).await; + + assert!(create_resp2.status().is_success()); + + let create_resp_body2 = test::read_body_json::(create_resp2).await; + assert_eq!(create_resp_body2, create_req_data2); + + + + let list_req_data = json!([{ + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "name": "job-case-01", + }, + "spec": { + "containers": [ + { + "name": "job-case-01", + "command": [ + "sleep", + "1" + ] + } + ] + } + }, + { + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "name": "job-case-02", + }, + "spec": { + "containers": [ + { + "name": "job-case-02", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }]); + + let list_req = test::TestRequest::get() + .uri("/apis/batch/v1/namespaces/global/jobs") + .to_request(); + + let list_resp = test::call_service(&app, list_req).await; + + assert!(list_resp.status().is_success()); + + let list_resp_body = test::read_body_json::(list_resp).await; + assert_eq!(list_resp_body, list_req_data); + + + + let delete_req = test::TestRequest::delete() + .uri("/apis/batch/v1/namespaces/global/jobs/job-case-01") + .to_request(); + + let delete_resp = test::call_service(&app, delete_req).await; + + assert!(delete_resp.status().is_success()); + + let list_req_data2 = json!([ + { + "apiVersion": "batch/v1", + "kind": "Job", + "metadata": { + "name": "job-case-02", + }, + "spec": { + "containers": [ + { + "name": "job-case-02", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }]); + let list_req2 = test::TestRequest::get() + .uri("/apis/batch/v1/namespaces/global/jobs") + .to_request(); + + let list_resp2 = test::call_service(&app, list_req2).await; + + assert!(list_resp2.status().is_success()); + + let list_resp_body2 = test::read_body_json::(list_resp2).await; + assert_eq!(list_resp_body2, list_req_data2); + + } + + + #[actix_web::test] + #[serial] + async fn test_crd_and_apis_without_namespace() { + let nats_cli = Arc::new(NatsCli::new().await.unwrap()); + let event_manager = EventManager::new(); + + let app = test::init_service( + App::new() + .route("/apis/{group}/{version}/{plural}", web::post().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.create_apis_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/apis/{group}/{version}/{plural}/{name}", web::put().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.update_apis_without_namespace(path, data, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/apis/{group}/{version}/{plural}/{name}", web::delete().to({ + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + move |path| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.delete_apis_without_namespace(path, &mut conn, nats_cli, event_manager).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/apis/{group}/{version}/{plural}/{name}", web::get().to({ + let nats_cli = Arc::clone(&nats_cli); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.getone_apis_without_namespace(path, data, &mut conn, nats_cli).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + .route("/apis/{group}/{version}/{plural}", web::get().to({ + let nats_cli = Arc::clone(&nats_cli); + + move |path, data| { + let handler = HANDLER.clone(); + let db_pool = DB_POOL.clone(); + let nats_cli = Arc::clone(&nats_cli); + + async move { + match db_pool.get_connection() { + Ok(mut conn) => handler.listall_apis_without_namespace(path, data, &mut conn, nats_cli).await, + Err(_) => Err(ErrorInternalServerError("Failed to get database connection")), + } + } + } + })) + ).await; + + + let crd_req_data = json!({ + "apiVersion": "batch/v1", + "kind": "CustomResourceDefinition", + "metadata": { + "name": "myjob.iscas.cn" + }, + "spec": { + "names": + { + "kind": "Myjob", + "listKind": "MyjobList", + "plural": "myjobs", + "singular": "myjob" + } + } + }); + + let crd_req = test::TestRequest::post() + .uri("/apis/batch/v1/crds") + .set_json(&crd_req_data) + .to_request(); + + let crd_resp = test::call_service(&app, crd_req).await; + + assert!(crd_resp.status().is_success()); + + let crd_resp_body = test::read_body_json::(crd_resp).await; + assert_eq!(crd_resp_body, crd_req_data); + + + let create_req_data = json!({ + "apiVersion": "batch/v1", + "kind": "Myjob", + "metadata": { + "name": "myjob-case-01", + }, + "spec": { + "containers": [ + { + "name": "myjob-case-01", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let create_req = test::TestRequest::post() + .uri("/apis/batch/v1/myjobs") + .set_json(&create_req_data) + .to_request(); + + let create_resp = test::call_service(&app, create_req).await; + + assert!(create_resp.status().is_success()); + + let create_resp_body = test::read_body_json::(create_resp).await; + assert_eq!(create_resp_body, create_req_data); + + + + + + + + let update_req_data = json!({ + "apiVersion": "batch/v1", + "kind": "Myjob", + "metadata": { + "name": "myjob-case-01", + }, + "spec": { + "containers": [ + { + "name": "myjob-case-01", + "command": [ + "sleep", + "1" + ] + } + ] + } + }); + + let update_req = test::TestRequest::put() + .uri("/apis/batch/v1/myjobs/myjob-case-01") + .set_json(&update_req_data) + .to_request(); + + let update_resp = test::call_service(&app, update_req).await; + + assert!(update_resp.status().is_success()); + + let update_resp_body = test::read_body_json::(update_resp).await; + assert_eq!(update_resp_body, update_req_data); + + + + + let get_req_data = json!({ + "apiVersion": "batch/v1", + "kind": "Myjob", + "metadata": { + "name": "myjob-case-01", + }, + "spec": { + "containers": [ + { + "name": "myjob-case-01", + "command": [ + "sleep", + "1" + ] + } + ] + } + }); + + let get_req = test::TestRequest::get() + .uri("/apis/batch/v1/myjobs/myjob-case-01") + .to_request(); + + let get_resp = test::call_service(&app, get_req).await; + + assert!(get_resp.status().is_success()); + + let get_resp_body = test::read_body_json::(get_resp).await; + assert_eq!(get_resp_body, get_req_data); + + + + + let create_req_data2 = json!({ + "apiVersion": "batch/v1", + "kind": "Myjob", + "metadata": { + "name": "myjob-case-02", + }, + "spec": { + "containers": [ + { + "name": "myjob-case-02", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }); + + let create_req2 = test::TestRequest::post() + .uri("/apis/batch/v1/myjobs") + .set_json(&create_req_data2) + .to_request(); + + let create_resp2 = test::call_service(&app, create_req2).await; + + assert!(create_resp2.status().is_success()); + + let create_resp_body2 = test::read_body_json::(create_resp2).await; + assert_eq!(create_resp_body2, create_req_data2); + + + + let list_req_data = json!([{ + "apiVersion": "batch/v1", + "kind": "Myjob", + "metadata": { + "name": "myjob-case-01", + }, + "spec": { + "containers": [ + { + "name": "myjob-case-01", + "command": [ + "sleep", + "1" + ] + } + ] + } + }, + { + "apiVersion": "batch/v1", + "kind": "Myjob", + "metadata": { + "name": "myjob-case-02", + }, + "spec": { + "containers": [ + { + "name": "myjob-case-02", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }]); + + let list_req = test::TestRequest::get() + .uri("/apis/batch/v1/myjobs") + .to_request(); + + let list_resp = test::call_service(&app, list_req).await; + + assert!(list_resp.status().is_success()); + + let list_resp_body = test::read_body_json::(list_resp).await; + assert_eq!(list_resp_body, list_req_data); + + + + let delete_req = test::TestRequest::delete() + .uri("/apis/batch/v1/myjobs/myjob-case-01") + .to_request(); + + let delete_resp = test::call_service(&app, delete_req).await; + + assert!(delete_resp.status().is_success()); + + let list_req_data2 = json!([ + { + "apiVersion": "batch/v1", + "kind": "Myjob", + "metadata": { + "name": "myjob-case-02", + }, + "spec": { + "containers": [ + { + "name": "myjob-case-02", + "command": [ + "sleep", + "3600" + ] + } + ] + } + }]); + let list_req2 = test::TestRequest::get() + .uri("/apis/batch/v1/myjobs") + .to_request(); + + let list_resp2 = test::call_service(&app, list_req2).await; + + assert!(list_resp2.status().is_success()); + + let list_resp_body2 = test::read_body_json::(list_resp2).await; + assert_eq!(list_resp_body2, list_req_data2); + + } +} -- Gitee From 99cdf6e6ce9840381f51c7b738d8eb081695ecdf Mon Sep 17 00:00:00 2001 From: Yuichi <913637919@qq.com> Date: Wed, 11 Dec 2024 08:43:29 +0000 Subject: [PATCH 3/7] =?UTF-8?q?apiserver=20=E8=AE=A2=E9=98=85=20topic?= =?UTF-8?q?=E4=B8=BA=20GET=20=E7=9A=84=E6=B6=88=E6=81=AF=EF=BC=8C=E4=B8=BA?= =?UTF-8?q?scheduler=E7=BB=84=E4=BB=B6=E6=8F=90=E4=BE=9B=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Cargo.lock | 343 +++++++++++++++++++++++++++++++++++++++-- Cargo.toml | 3 +- src/cores/apiserver.rs | 7 +- src/lib.rs | 112 +++++++++++++- 4 files changed, 448 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3cb9db6..8ee8846 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -39,7 +39,7 @@ dependencies = [ "encoding_rs", "flate2", "futures-core", - "h2", + "h2 0.3.26", "http 0.2.12", "httparse", "httpdate", @@ -289,7 +289,7 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a" dependencies = [ - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -299,7 +299,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8" dependencies = [ "anstyle", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -323,7 +323,7 @@ dependencies = [ "nuid", "once_cell", "regex", - "rustls-pemfile", + "rustls-pemfile 0.3.0", "serde", "serde_json", "serde_nanos", @@ -331,7 +331,7 @@ dependencies = [ "subslice", "time", "tokio", - "tokio-rustls", + "tokio-rustls 0.23.4", "tokio-util", "url", "webpki-roots", @@ -370,6 +370,12 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + [[package]] name = "autocfg" version = "1.4.0" @@ -600,6 +606,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -912,6 +928,16 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +[[package]] +name = "errno" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + [[package]] name = "fancy-regex" version = "0.13.0" @@ -923,6 +949,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "fastrand" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4" + [[package]] name = "feventbus" version = "0.3.0" @@ -971,6 +1003,7 @@ dependencies = [ "lazy_static", "once_cell", "r2d2", + "reqwest", "schemars", "serde", "serde_json", @@ -995,6 +1028,21 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1149,6 +1197,25 @@ dependencies = [ "tracing", ] +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.1.0", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + [[package]] name = "hashbrown" version = "0.14.5" @@ -1245,6 +1312,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", + "h2 0.4.7", "http 1.1.0", "http-body", "httparse", @@ -1255,6 +1323,39 @@ dependencies = [ "want", ] +[[package]] +name = "hyper-rustls" +version = "0.27.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" +dependencies = [ + "futures-util", + "http 1.1.0", + "hyper", + "hyper-util", + "rustls 0.23.19", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.1", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + [[package]] name = "hyper-util" version = "0.1.9" @@ -1563,6 +1664,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "linux-raw-sys" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" + [[package]] name = "litemap" version = "0.7.3" @@ -1654,7 +1761,24 @@ dependencies = [ "libc", "log", "wasi", - "windows-sys", + "windows-sys 0.52.0", +] + +[[package]] +name = "native-tls" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8614eb2c83d59d1c8cc974dd3f920198647674a0a035e1af1fa58707e317466" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", ] [[package]] @@ -1788,6 +1912,50 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" +[[package]] +name = "openssl" +version = "0.10.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6174bc48f102d208783c2c84bf931bb75927a617866870de8a4ea85597f871f5" +dependencies = [ + "bitflags", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.89", +] + +[[package]] +name = "openssl-probe" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + [[package]] name = "ordered-float" version = "2.10.1" @@ -2099,26 +2267,34 @@ checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ "base64 0.22.1", "bytes", + "encoding_rs", "futures-channel", "futures-core", "futures-util", + "h2 0.4.7", "http 1.1.0", "http-body", "http-body-util", "hyper", + "hyper-rustls", + "hyper-tls", "hyper-util", "ipnet", "js-sys", "log", "mime", + "native-tls", "once_cell", "percent-encoding", "pin-project-lite", + "rustls-pemfile 2.2.0", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", + "system-configuration", "tokio", + "tokio-native-tls", "tower-service", "url", "wasm-bindgen", @@ -2154,7 +2330,7 @@ dependencies = [ "libc", "spin 0.9.8", "untrusted 0.9.0", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2172,6 +2348,19 @@ dependencies = [ "semver", ] +[[package]] +name = "rustix" +version = "0.38.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.52.0", +] + [[package]] name = "rustls" version = "0.20.9" @@ -2184,6 +2373,19 @@ dependencies = [ "webpki", ] +[[package]] +name = "rustls" +version = "0.23.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "934b404430bb06b3fae2cba809eb45a1ab1aecd64491213d7c3301b88393f8d1" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + [[package]] name = "rustls-pemfile" version = "0.3.0" @@ -2193,12 +2395,47 @@ dependencies = [ "base64 0.13.1", ] +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring 0.17.8", + "rustls-pki-types", + "untrusted 0.9.0", +] + [[package]] name = "ryu" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" +[[package]] +name = "schannel" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01227be5826fa0690321a2ba6c5cd57a19cf3f6a09e76973b58e61de6ab9d1c1" +dependencies = [ + "windows-sys 0.59.0", +] + [[package]] name = "scheduled-thread-pool" version = "0.2.7" @@ -2248,6 +2485,29 @@ dependencies = [ "untrusted 0.9.0", ] +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea4a292869320c0272d7bc55a5a6aafaff59b4f63404a003887b679a2e05b4b6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "semver" version = "1.0.23" @@ -2458,7 +2718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce305eb0b4296696835b71df73eb912e0f1ffd2556a501fcede6e0c50349191c" dependencies = [ "libc", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2551,6 +2811,40 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tempfile" +version = "3.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b" +dependencies = [ + "cfg-if", + "fastrand", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -2642,7 +2936,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", - "windows-sys", + "windows-sys 0.52.0", ] [[package]] @@ -2656,17 +2950,37 @@ dependencies = [ "syn 2.0.89", ] +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + [[package]] name = "tokio-rustls" version = "0.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c43ee83903113e03984cb9e5cebe6c04a5116269e900e3ddba8f068a62adda59" dependencies = [ - "rustls", + "rustls 0.20.9", "tokio", "webpki", ] +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls 0.23.19", + "tokio", +] + [[package]] name = "tokio-util" version = "0.7.12" @@ -3039,6 +3353,15 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + [[package]] name = "windows-targets" version = "0.52.6" diff --git a/Cargo.toml b/Cargo.toml index 2c73961..1f7f076 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,4 +31,5 @@ chrono = "0.4.38" once_cell = "1.20.2" lazy_static = "1.5.0" async-stream = "0.3.6" -serial_test = "0.10.0" \ No newline at end of file +serial_test = "0.10.0" +reqwest = "0.12.9" \ No newline at end of file diff --git a/src/cores/apiserver.rs b/src/cores/apiserver.rs index 1a0597a..ce1a21b 100644 --- a/src/cores/apiserver.rs +++ b/src/cores/apiserver.rs @@ -2,12 +2,11 @@ use actix_web::{HttpServer, App, web, HttpResponse}; use crate::cores::config::{Config, APIS_WITHOUT_NAMESPACE, APIS_WITH_NAMESPACE, API_WITHOUT_NAMESPACE, API_WITH_NAMESPACE}; use crate::cores::handlers::{EventManager, Handler}; - use std::sync::{Arc}; use feventbus::impls::nats::nats::NatsCli; -use feventbus::traits::controller::EventBus; use crate::cores::db::{DbPool}; + pub struct ApiServer { // Config是用户请求URL与路由处理器的映射关系封装 @@ -25,11 +24,9 @@ impl ApiServer // TODO 未来加上else // TODO 优化注册流程 pub async fn start - (self: Arc, addr: &str, handler: T, db_pool: Arc) -> Result<(), std::io::Error> { + (self: Arc, addr: &str, handler: T, db_pool: Arc, nats_cli: Arc) -> Result<(), std::io::Error> { let handler = Arc::new(handler); - // 初始化 NatsCli 实例 - let nats_cli = Arc::new(NatsCli::new().await.unwrap()); // 初始化事件管理器 let event_manager = EventManager::new(); diff --git a/src/lib.rs b/src/lib.rs index 2f3e95c..9811361 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,11 +1,34 @@ pub mod cores; pub mod schema; +use std::fmt::Debug; use cores::apiserver::ApiServer; use cores::config::DefaultConfig; use cores::handlers::DefaultHandler; use cores::db::DbPool; use std::sync::Arc; +use feventbus::err::Error; +use feventbus::impls::nats::nats::NatsCli; +use feventbus::message::Message; +use feventbus::traits::consumer::{Consumer, MessageHandler}; +use feventbus::traits::controller::EventBus; +use serde::{Deserialize, Serialize}; +use reqwest::Client; +#[derive(Serialize, Deserialize, Debug, Clone)] +struct ResourcesMessage { + group: Option, + version: String, + namespace: Option, + plural: String +} + +async fn subscribe_to_topic(topic: &str, nats_cli: Arc, handler: MessageHandler) +where + T: Serialize + for<'de> Deserialize<'de> + Debug + Clone + Send + Sync + 'static, +{ + nats_cli.subscribe(topic, handler).await; + println!("Subscribed to topic: {}", topic); +} /// 启动 Web 服务器 /// @@ -25,7 +48,94 @@ pub async fn start_server(database_url: &str, address: &str) -> Result<(), Box = Arc::new(move |msg: Message| { + let http_client = Arc::clone(&http_client); + Box::pin(async move { + match msg.body { + Some(body) => { + if let Ok(resource_message) = serde_json::from_value::(body.clone()) { + let route_path = match resource_message.group { + Some(group) => { + match resource_message.namespace { + Some(namespace) => { + format!( + "/apis/{}/{}/namespaces/{}/{}", + group, resource_message.version, namespace, resource_message.plural + ) + } + None => { + format!( + "/apis/{}/{}/{}", + group, resource_message.version, resource_message.plural + ) + } + } + } + None => { + match resource_message.namespace { + Some(namespace) => { + format!( + "/api/{}/namespaces/{}/{}", + resource_message.version, namespace, resource_message.plural + ) + } + None => { + format!("/api/{}/{}", resource_message.version, resource_message.plural) + } + } + } + }; + + let base_url = "http://localhost:8080"; + let full_url = format!("{}{}", base_url, route_path); + match http_client.get(&full_url).send().await { + Ok(response) => { + if response.status().is_success() { + let text = response.text().await.unwrap_or_default(); + println!("{}",text); + Ok(format!("---Sub get resources topic success: {}---", text)) + } else { + Err(Error::MessageHandling(format!( + "---Sub get resources topic failed: HTTP error {}---", + response.status() + ))) + } + } + Err(err) => Err(Error::MessageHandling(format!( + "---Sub get resources topic failed: Request error: {}---", + err + ))), + }.expect("TODO: panic message"); + Ok("---Sub get resources topic success---".to_string()) + } else { + Err(Error::MessageHandling( + "---Sub get resources topic failed: message body is null---".to_string(), + )) + } + } + None => { + Err(Error::MessageHandling( + "---Sub get resources topic failed: message body is null---".to_string(), + )) + } + } + }) + }); + + tokio::spawn(subscribe_to_topic( + "GET", + Arc::clone(&nats_cli), + sub_get_handler, + )); + + let _ = Arc::new(server).start(address, handler, db_pool, nats_cli).await?; + + Ok(()) } -- Gitee From 9f2ad307a9ff7dcd70a1e514b7a01c458600f76f Mon Sep 17 00:00:00 2001 From: Yuichi <913637919@qq.com> Date: Thu, 12 Dec 2024 16:58:46 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0pods=E8=B5=84=E6=BA=90=20?= =?UTF-8?q?eventbus=E8=AE=A2=E9=98=85topic=E4=B8=BAGET=E7=9A=84=E8=AF=9D?= =?UTF-8?q?=E9=A2=98=20=E4=BE=9B=E5=85=B6=E4=BB=96=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=E4=BD=BF=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database.sqlite | Bin 0 -> 61440 bytes src/cores/db.rs | 4 ++-- src/cores/handlers.rs | 2 +- src/lib.rs | 28 +++++++++++++++------------- 4 files changed, 18 insertions(+), 16 deletions(-) create mode 100644 database.sqlite diff --git a/database.sqlite b/database.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..501647cee188e8571a6facf03844690e57bfa4ff GIT binary patch literal 61440 zcmeI5O>^7E8OKS0?mU>@nflx9?_$57`qN}K#vy?f!W-nG?To&8Gdu$sxbx>i@^p}^ghN~u^a3Dx398zo^VNw_~H&_;0$ z;YqQw^7CTlzMNI&7o6yh+GYXKR#&Z`m8N#JrfKxLXB2CwwklLhpH|(<>n2le)~MOq znFr~0DSvQsxw5`ltb8UsDt#v0=gnUbcx&C?PU98?uPyVAG4qAHk;LY`aELOq=hhcG zT?W+Do^3ec+7OnSG!%>}>d}s6VQC zUrddFeVcgRn~EeJ%$_@RopE^kI1!B|QmOFEXAZqKvsHfD`SZd^#N-gyk>oz)3nzk) zwzoG*#Vz;D_RhOEItN^I_7^83iJeps!V$x3(nwV7I5BY^+>sJec{GKA)CFIU`A8 zTFlFGI>$qcl1v{#%r8s%<#cw6>(ecE6^gFqn+?SFIN&dUiWo1Bf!z09tyeQxYpyQ*DprAAEhqE8I_*x3Ew?Xp?BtJB+S+{W|J44|kR9b2XM zoJ=D3lz^K}j!-pC^?=%;wbkYT!V#p;j%W+wAg9wgDL64J=U!-Gf;4lyy|zYOa(<7O zjm9Z3YNun^?z;xm6w-emAq1r3-JHn;JFK?fdB}A00@8p2!H?xfB*=900`V<0jcTMcE?hSx8OY*j4orJ<~yn)=|OE zy2%{5&Z^dCmQ9tBY^Q(EOsvv3Wr`wwe(LOy*r%uH%5`9Q-uA&F-y!wVzOw6Jizq*j zwYqAZE0#9cp4!vxLDkn&`k$`U>C@z5lZrM});2nVvA6di`D`KSYlA*bcA8q}IgewV zeMcg-f9%rMliKF?YKaQ(t9p+glQd)-y}GR#9Xjlx*GQi6NvZO9eS6Ex`_MGnT%&bQ zV|t^)_Wb>_YPYzd%vKw!tu8soB8gQxyeGx#+3bXux!SA6wQikYxu@%8L)YpDRLT0@ zmSLAoX0gtI7_L{`he2h#hH1O1@qRSf7dPNvWp(skp@vZ?KBI%~3$RaXff@2h3o>@jXyKZ*2( z-546KjqQ$pP%#YqA(gTYEK=C@Q+u7FwPqSU8k&+Q5-O9&>{!fnBd8hshTdzlRkp7= zmXg43!`bimbBaPKtHBk2`=iQDUUbVPdB3~6n?|qG@1r)~^(tU?J>`th6m`Si5|_yB z*TlnG)2oTobB%GE&&TIbMLo+*SuNFM{pwnFTR*9c4@R%53yZ2tHWBw5;*@O}mhD$S zR()xhq~zRO)reIxb>_jj@w`}3*Hnv-6Qak%+&&)S(}8YKFD~*}I-W{$z%6FFK`N~A zIYW|jOCtS~9GG5tN}$qyh}uee(p#=vG$rs5Rp_F3kL=>yRH;t?%yz&px+#2s_Q`vv znAeGfnsk}%1IO6IlYFP4^5(exN($BPL@+Qw?1L_!W2h1mx8`TYKt;}&^6}E~URPDm z=pZyx+db!`Bkq=~2ffQ^Ruoz!pR99G-4^u_HLlD$4KB6Kr@*GMHrm% zi&YA%6jrHay3oQZ#ivQEQa@n9`+u%dB3)i&WJSu$be%f%KnSf;{X|)!2LQPLe=Ppz zQ2ZbC3mXW400@8p2!H?xfB*=900@8p2!Oy9CGcK^dqcxq&fmMmKj;b_n~U(f(2m^y zK)QDen(P0=|9?fh7R@??0Q~>l%LxC!^Ci0Q|HJ?9Jj~!c^KtH@1MvU5&q=}m{~G-N z1&NfRPrXIz`~M3PeGzvK{(r1e{YP)GN@114Dut)i-rQ4aj*CAnWpbjDIotnFiTQjc z1ONXSe_|pE1V8`;KmY_l00ck)1V8`;KmY`;90Bms7wh_2yQo zzW=|F6UBh{|I<=|L~IoJ|1a@RRa6ZEAOHd&00JNY0w4eaAOHd&00OU#zz_5Pe-EUM B6PN%1 literal 0 HcmV?d00001 diff --git a/src/cores/db.rs b/src/cores/db.rs index 6e9fd6a..56016fb 100644 --- a/src/cores/db.rs +++ b/src/cores/db.rs @@ -24,12 +24,12 @@ pub enum DbConnection { fn resource_templates() -> HashMap<&'static str, (bool, Value)> { let mut templates = HashMap::new(); templates.insert( - "cargos", + "pods", ( true, json!({ "apiVersion": "v1", - "kind": "Cargo", + "kind": "Pod", "metadata": { "name": "string", "annotations": "object" diff --git a/src/cores/handlers.rs b/src/cores/handlers.rs index 6c0ab5d..8798c30 100644 --- a/src/cores/handlers.rs +++ b/src/cores/handlers.rs @@ -23,7 +23,7 @@ use actix_web::web::Bytes; // 定义全局哈希表来获取model名 static GLOBAL_HASHMAP: LazyLock>> = LazyLock::new(|| { let mut map = HashMap::new(); - map.insert("cargos".to_string(), "CARGO".to_string()); + map.insert("pods".to_string(), "POD".to_string()); map.insert("nodes".to_string(), "NODE".to_string()); map.insert("jobs".to_string(), "JOB".to_string()); Mutex::new(map) diff --git a/src/lib.rs b/src/lib.rs index 9811361..559854f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -22,14 +22,18 @@ struct ResourcesMessage { plural: String } -async fn subscribe_to_topic(topic: &str, nats_cli: Arc, handler: MessageHandler) -where - T: Serialize + for<'de> Deserialize<'de> + Debug + Clone + Send + Sync + 'static, +async fn reply_to_topic( + topic: &str, + nats_cli: Arc, + handler: MessageHandler, +) -> Result<(), Box> { - nats_cli.subscribe(topic, handler).await; - println!("Subscribed to topic: {}", topic); + nats_cli + .reply(topic, handler) + .await + .map_err(|e| Box::new(e) as Box)?; + Ok(()) } - /// 启动 Web 服务器 /// /// # 参数 @@ -53,7 +57,7 @@ pub async fn start_server(database_url: &str, address: &str) -> Result<(), Box = Arc::new(move |msg: Message| { + let reply_get_handler: MessageHandler = Arc::new(move |msg: Message| { let http_client = Arc::clone(&http_client); Box::pin(async move { match msg.body { @@ -97,8 +101,7 @@ pub async fn start_server(database_url: &str, address: &str) -> Result<(), Box { if response.status().is_success() { let text = response.text().await.unwrap_or_default(); - println!("{}",text); - Ok(format!("---Sub get resources topic success: {}---", text)) + Ok(text) } else { Err(Error::MessageHandling(format!( "---Sub get resources topic failed: HTTP error {}---", @@ -110,8 +113,7 @@ pub async fn start_server(database_url: &str, address: &str) -> Result<(), Box Result<(), Box Date: Fri, 13 Dec 2024 09:06:14 +0000 Subject: [PATCH 5/7] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=B7=AF=E7=94=B1?= =?UTF-8?q?=E6=B3=A8=E5=86=8C=E9=80=BB=E8=BE=91=EF=BC=8C=E5=87=8F=E5=B0=91?= =?UTF-8?q?=E9=87=8D=E5=A4=8D=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database.sqlite | Bin 61440 -> 0 bytes src/cores/apiserver.rs | 747 ++++------------- src/cores/config.rs | 134 ++- src/cores/handlers.rs | 1805 +++++++++------------------------------- 4 files changed, 613 insertions(+), 2073 deletions(-) delete mode 100644 database.sqlite diff --git a/database.sqlite b/database.sqlite deleted file mode 100644 index 501647cee188e8571a6facf03844690e57bfa4ff..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61440 zcmeI5O>^7E8OKS0?mU>@nflx9?_$57`qN}K#vy?f!W-nG?To&8Gdu$sxbx>i@^p}^ghN~u^a3Dx398zo^VNw_~H&_;0$ z;YqQw^7CTlzMNI&7o6yh+GYXKR#&Z`m8N#JrfKxLXB2CwwklLhpH|(<>n2le)~MOq znFr~0DSvQsxw5`ltb8UsDt#v0=gnUbcx&C?PU98?uPyVAG4qAHk;LY`aELOq=hhcG zT?W+Do^3ec+7OnSG!%>}>d}s6VQC zUrddFeVcgRn~EeJ%$_@RopE^kI1!B|QmOFEXAZqKvsHfD`SZd^#N-gyk>oz)3nzk) zwzoG*#Vz;D_RhOEItN^I_7^83iJeps!V$x3(nwV7I5BY^+>sJec{GKA)CFIU`A8 zTFlFGI>$qcl1v{#%r8s%<#cw6>(ecE6^gFqn+?SFIN&dUiWo1Bf!z09tyeQxYpyQ*DprAAEhqE8I_*x3Ew?Xp?BtJB+S+{W|J44|kR9b2XM zoJ=D3lz^K}j!-pC^?=%;wbkYT!V#p;j%W+wAg9wgDL64J=U!-Gf;4lyy|zYOa(<7O zjm9Z3YNun^?z;xm6w-emAq1r3-JHn;JFK?fdB}A00@8p2!H?xfB*=900`V<0jcTMcE?hSx8OY*j4orJ<~yn)=|OE zy2%{5&Z^dCmQ9tBY^Q(EOsvv3Wr`wwe(LOy*r%uH%5`9Q-uA&F-y!wVzOw6Jizq*j zwYqAZE0#9cp4!vxLDkn&`k$`U>C@z5lZrM});2nVvA6di`D`KSYlA*bcA8q}IgewV zeMcg-f9%rMliKF?YKaQ(t9p+glQd)-y}GR#9Xjlx*GQi6NvZO9eS6Ex`_MGnT%&bQ zV|t^)_Wb>_YPYzd%vKw!tu8soB8gQxyeGx#+3bXux!SA6wQikYxu@%8L)YpDRLT0@ zmSLAoX0gtI7_L{`he2h#hH1O1@qRSf7dPNvWp(skp@vZ?KBI%~3$RaXff@2h3o>@jXyKZ*2( z-546KjqQ$pP%#YqA(gTYEK=C@Q+u7FwPqSU8k&+Q5-O9&>{!fnBd8hshTdzlRkp7= zmXg43!`bimbBaPKtHBk2`=iQDUUbVPdB3~6n?|qG@1r)~^(tU?J>`th6m`Si5|_yB z*TlnG)2oTobB%GE&&TIbMLo+*SuNFM{pwnFTR*9c4@R%53yZ2tHWBw5;*@O}mhD$S zR()xhq~zRO)reIxb>_jj@w`}3*Hnv-6Qak%+&&)S(}8YKFD~*}I-W{$z%6FFK`N~A zIYW|jOCtS~9GG5tN}$qyh}uee(p#=vG$rs5Rp_F3kL=>yRH;t?%yz&px+#2s_Q`vv znAeGfnsk}%1IO6IlYFP4^5(exN($BPL@+Qw?1L_!W2h1mx8`TYKt;}&^6}E~URPDm z=pZyx+db!`Bkq=~2ffQ^Ruoz!pR99G-4^u_HLlD$4KB6Kr@*GMHrm% zi&YA%6jrHay3oQZ#ivQEQa@n9`+u%dB3)i&WJSu$be%f%KnSf;{X|)!2LQPLe=Ppz zQ2ZbC3mXW400@8p2!H?xfB*=900@8p2!Oy9CGcK^dqcxq&fmMmKj;b_n~U(f(2m^y zK)QDen(P0=|9?fh7R@??0Q~>l%LxC!^Ci0Q|HJ?9Jj~!c^KtH@1MvU5&q=}m{~G-N z1&NfRPrXIz`~M3PeGzvK{(r1e{YP)GN@114Dut)i-rQ4aj*CAnWpbjDIotnFiTQjc z1ONXSe_|pE1V8`;KmY_l00ck)1V8`;KmY`;90Bms7wh_2yQo zzW=|F6UBh{|I<=|L~IoJ|1a@RRa6ZEAOHd&00JNY0w4eaAOHd&00OU#zz_5Pe-EUM B6PN%1 diff --git a/src/cores/apiserver.rs b/src/cores/apiserver.rs index ce1a21b..c4a9c7b 100644 --- a/src/cores/apiserver.rs +++ b/src/cores/apiserver.rs @@ -1,15 +1,43 @@ -use actix_web::{HttpServer, App, web, HttpResponse}; - -use crate::cores::config::{Config, APIS_WITHOUT_NAMESPACE, APIS_WITH_NAMESPACE, API_WITHOUT_NAMESPACE, API_WITH_NAMESPACE}; +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ + +use actix_web::{HttpServer, App, web, HttpResponse, HttpRequest}; +use crate::cores::config::{Config}; use crate::cores::handlers::{EventManager, Handler}; use std::sync::{Arc}; use feventbus::impls::nats::nats::NatsCli; use crate::cores::db::{DbPool}; +use std::collections::HashMap; + +fn parse_path(template: &str, path: &str) -> HashMap { + let template_parts: Vec<&str> = template.split('/').collect(); + let path_parts: Vec<&str> = path.split('/').collect(); + + if template_parts.len() != path_parts.len() { + return HashMap::new(); + } + + let mut params = HashMap::new(); + for (template_part, path_part) in template_parts.iter().zip(path_parts.iter()) { + if template_part.starts_with('{') && template_part.ends_with('}') { + let key = &template_part[1..template_part.len() - 1]; + params.insert(key.to_string(), path_part.to_string()); + } else if template_part != path_part { + return HashMap::new(); + } + } + + params +} + pub struct ApiServer { - // Config是用户请求URL与路由处理器的映射关系封装 config: Arc, } @@ -21,8 +49,6 @@ impl ApiServer } } - // TODO 未来加上else - // TODO 优化注册流程 pub async fn start (self: Arc, addr: &str, handler: T, db_pool: Arc, nats_cli: Arc) -> Result<(), std::io::Error> { @@ -40,696 +66,239 @@ impl ApiServer let nats_cli = Arc::clone(&nats_cli); let event_manager = event_manager.clone(); - // URL是手动注册的,不会存在APIS_WITHOUT_NAMESPACE, APIS_WITH_NAMESPACE, - // API_WITHOUT_NAMESPACE, API_WITH_NAMESPACE之外情况 - for (key, route) in config.create_routes() { - if route == API_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::post().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .create_api_without_namespace(path, data, &mut conn, nats_cli, event_manager) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == API_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::post().to( - move |path, data| { + for key in config.create_routes() { + let handler = Arc::clone(&handler); + let db_pool = Arc::clone(&db_pool); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + let route_key = key.clone(); + app = app.route( + key, + web::post().to( + move |req: HttpRequest, data| { let handler = Arc::clone(&handler); let db_pool = Arc::clone(&db_pool); let nats_cli = Arc::clone(&nats_cli); let event_manager = event_manager.clone(); + let template = route_key.clone(); // 路由模板 async move { match db_pool.get_connection() { - Ok(mut conn) => { + Ok(mut conn) => { + // 自定义解析路径 + let params = parse_path(&template, req.path()); handler .default() - .create_api_with_namespace(path, data, &mut conn, nats_cli, event_manager) + .create_resource(params, data, &mut conn, nats_cli, event_manager) .await - }, + } Err(_) => Err(actix_web::error::ErrorInternalServerError( "Failed to get database connection", )), } } - })); - } else if route == APIS_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::post().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .create_apis_without_namespace(path, data, &mut conn, nats_cli, event_manager) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == APIS_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::post().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .create_apis_with_namespace(path, data, &mut conn, nats_cli, event_manager) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } + }, + ), + ); } - // URL是手动注册的,不会存在APIS_WITHOUT_NAMESPACE, APIS_WITH_NAMESPACE, - // API_WITHOUT_NAMESPACE, API_WITH_NAMESPACE之外情况 - for (key, route) in config.delete_routes() { - if route == API_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::delete().to( - move |path| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .delete_api_without_namespace(path, &mut conn, nats_cli, event_manager) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == API_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::delete().to( - move |path| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .delete_api_with_namespace(path, &mut conn, nats_cli, event_manager) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == APIS_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::delete().to( - move |path| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .delete_apis_without_namespace(path, &mut conn, nats_cli, event_manager) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == APIS_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::delete().to( - move |path| { + for key in config.delete_routes() { + let handler = Arc::clone(&handler); + let db_pool = Arc::clone(&db_pool); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + let route_key = key.clone(); + + app = app.route( + key, + web::delete().to( + move |req: HttpRequest| { let handler = Arc::clone(&handler); let db_pool = Arc::clone(&db_pool); let nats_cli = Arc::clone(&nats_cli); let event_manager = event_manager.clone(); + let template = route_key.clone(); // 路由模板 async move { match db_pool.get_connection() { - Ok(mut conn) => { + Ok(mut conn) => { + // 自定义路径解析 + let params = parse_path(&template, req.path()); handler .default() - .delete_apis_with_namespace(path, &mut conn, nats_cli, event_manager) + .delete_resource(params, &mut conn, nats_cli, event_manager) .await - }, + } Err(_) => Err(actix_web::error::ErrorInternalServerError( "Failed to get database connection", )), } } - })); - } + }, + ), + ); } - // URL是手动注册的,不会存在APIS_WITHOUT_NAMESPACE, APIS_WITH_NAMESPACE, - // API_WITHOUT_NAMESPACE, API_WITH_NAMESPACE之外情况 - for (key, route) in config.update_routes() { - if route == API_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::put().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .update_api_without_namespace(path, data, &mut conn, nats_cli, event_manager) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == API_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::put().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .update_api_with_namespace(path, data, &mut conn, nats_cli,event_manager) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == APIS_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::put().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .update_apis_without_namespace(path, data, &mut conn, nats_cli, event_manager) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == APIS_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - let event_manager = event_manager.clone(); - - app = app.route(key, web::put().to( - move |path, data| { + for key in config.update_routes() { + let handler = Arc::clone(&handler); + let db_pool = Arc::clone(&db_pool); + let nats_cli = Arc::clone(&nats_cli); + let event_manager = event_manager.clone(); + let route_key = key.clone(); + + app = app.route( + key, + web::put().to( + move |req: HttpRequest, data| { let handler = Arc::clone(&handler); let db_pool = Arc::clone(&db_pool); let nats_cli = Arc::clone(&nats_cli); let event_manager = event_manager.clone(); + let template = route_key.clone(); // 路由模板 async move { match db_pool.get_connection() { - Ok(mut conn) => { + Ok(mut conn) => { + // 自定义解析路径 + let params = parse_path(&template, req.path()); handler .default() - .update_apis_with_namespace(path, data, &mut conn, nats_cli, event_manager) + .update_resource(params, data, &mut conn, nats_cli, event_manager) .await - }, + } Err(_) => Err(actix_web::error::ErrorInternalServerError( "Failed to get database connection", )), } } - })); - } + }, + ), + ); } - // URL是手动注册的,不会存在APIS_WITHOUT_NAMESPACE, APIS_WITH_NAMESPACE, - // API_WITHOUT_NAMESPACE, API_WITH_NAMESPACE之外情况 - for (key, route) in config.getone_routes() { - if route == API_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .getone_api_without_namespace(path, data, &mut conn, nats_cli) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == API_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); + for key in config.getone_routes() { + let handler = Arc::clone(&handler); + let db_pool = Arc::clone(&db_pool); + let nats_cli = Arc::clone(&nats_cli); + let route_key = key.clone(); - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .getone_api_with_namespace(path, data, &mut conn, nats_cli) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == APIS_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - app = app.route(key, web::get().to( - move |path, data| { + app = app.route( + key, + web::get().to( + move |req: HttpRequest, data| { let handler = Arc::clone(&handler); let db_pool = Arc::clone(&db_pool); let nats_cli = Arc::clone(&nats_cli); + let template = route_key.clone(); // 路由模板 async move { match db_pool.get_connection() { - Ok(mut conn) => { + Ok(mut conn) => { + // 自定义解析路径 + let params = parse_path(&template, req.path()); handler .default() - .getone_apis_without_namespace(path, data, &mut conn, nats_cli) + .getone_resource(params, data, &mut conn, nats_cli) .await - }, + } Err(_) => Err(actix_web::error::ErrorInternalServerError( "Failed to get database connection", )), } } - })); - } else if route == APIS_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .getone_apis_with_namespace(path, data, &mut conn, nats_cli) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } + }, + ), + ); } + for key in config.listall_routes() { + let handler = Arc::clone(&handler); + let db_pool = Arc::clone(&db_pool); + let nats_cli = Arc::clone(&nats_cli); + let route_key = key.clone(); - // URL是手动注册的,不会存在APIS_WITHOUT_NAMESPACE, APIS_WITH_NAMESPACE, - // API_WITHOUT_NAMESPACE, API_WITH_NAMESPACE之外情况 - for (key, route) in config.listall_routes() { - if route == API_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .listall_api_without_namespace(path, data, &mut conn, nats_cli) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == API_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .listall_api_with_namespace(path, data, &mut conn, nats_cli) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == APIS_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - async move { - match db_pool.get_connection() { - Ok(mut conn) => { - handler - .default() - .listall_apis_without_namespace(path, data, &mut conn, nats_cli) - .await - }, - Err(_) => Err(actix_web::error::ErrorInternalServerError( - "Failed to get database connection", - )), - } - } - })); - } else if route == APIS_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let db_pool = Arc::clone(&db_pool); - let nats_cli = Arc::clone(&nats_cli); - - app = app.route(key, web::get().to( - move |path, data| { + app = app.route( + key, + web::get().to( + move |req: HttpRequest, data| { let handler = Arc::clone(&handler); let db_pool = Arc::clone(&db_pool); let nats_cli = Arc::clone(&nats_cli); + let template = route_key.clone(); // 路由模板 async move { match db_pool.get_connection() { - Ok(mut conn) => { + Ok(mut conn) => { + // 自定义解析路径 + let params = parse_path(&template, req.path()); handler .default() - .listall_apis_with_namespace(path, data, &mut conn, nats_cli) + .listall_resource(params, data, &mut conn, nats_cli) .await - }, + } Err(_) => Err(actix_web::error::ErrorInternalServerError( "Failed to get database connection", )), } } - })); - } + }, + ), + ); } - for (key, route) in config.watchall_routes() { - if route == API_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - async move { - handler - .default() - .watchall_api_without_namespace(path, data, event_manager) - .await - } - })); - } else if route == API_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); + for key in config.watchall_routes() { + let handler = Arc::clone(&handler); + let event_manager = event_manager.clone(); + let route_key = key.clone(); - async move { - handler - .default() - .watchall_api_with_namespace(path, data, event_manager) - .await - } - })); - } else if route == APIS_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - app = app.route(key, web::get().to( - move |path, data| { + app = app.route( + key, + web::get().to( + move |req: HttpRequest, data| { let handler = Arc::clone(&handler); let event_manager = event_manager.clone(); + let template = route_key.clone(); // 路由模板 async move { + // 自定义解析路径 + let params = parse_path(&template, req.path()); handler .default() - .watchall_apis_without_namespace(path, data, event_manager) + .watchall_resource(params, data, event_manager) .await } - })); - } else if route == APIS_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - async move { - handler - .default() - .watchall_apis_with_namespace(path, data, event_manager) - .await - } - })); - } + }, + ), + ); } - for (key, route) in config.watchone_routes() { - if route == API_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); + for key in config.watchone_routes() { + let handler = Arc::clone(&handler); + let event_manager = event_manager.clone(); + let route_key = key.clone(); - async move { - handler - .default() - .watchone_api_without_namespace(path, data, event_manager) - .await - } - })); - } else if route == API_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - app = app.route(key, web::get().to( - move |path, data| { + app = app.route( + key, + web::get().to( + move |req: HttpRequest, data| { let handler = Arc::clone(&handler); let event_manager = event_manager.clone(); + let template = route_key.clone(); // 路由模板 async move { + // 自定义解析路径 + let params = parse_path(&template, req.path()); handler .default() - .watchone_api_with_namespace(path, data, event_manager) + .watchone_resource(params, data, event_manager) .await } - })); - } else if route == APIS_WITHOUT_NAMESPACE { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); + }, + ), + ); + } - async move { - handler - .default() - .watchone_apis_without_namespace(path, data, event_manager) - .await - } - })); - } else if route == APIS_WITH_NAMESPACE { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - app = app.route(key, web::get().to( - move |path, data| { - let handler = Arc::clone(&handler); - let event_manager = event_manager.clone(); - - async move { - handler - .default() - .watchone_apis_with_namespace(path, data, event_manager) - .await - } - })); - } - } // TODO 与Kubernetes的状态一致 app.default_service(web::to(move || async { diff --git a/src/cores/config.rs b/src/cores/config.rs index f741fca..50e3f36 100644 --- a/src/cores/config.rs +++ b/src/cores/config.rs @@ -1,11 +1,10 @@ /** * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences - * author: wuheng@iscas.ac.cn + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn * since: 0.1.0 * **/ -use std::collections::HashMap; // 执行kubectl api-resources,有以下输出,其中APIVERSION为v1的都在URL都是/api/开头,其它的都是/apis/开头,是否支持namespace // 由NAMESPACED取值确定。因此,定于API_WITH_NAMESPACE、API_WITHOUT_NAMESPACE、APIS_WITH_NAMESPACE、APIS_WITHOUT_NAMESPACE @@ -68,10 +67,6 @@ use std::collections::HashMap; // csistoragecapacities storage.k8s.io/v1 true CSIStorageCapacity // storageclasses sc storage.k8s.io/v1 false StorageClass // volumeattachments storage.k8s.io/v1 false VolumeAttachment -pub const API_WITH_NAMESPACE: &str = "api_with_namespace"; -pub const API_WITHOUT_NAMESPACE: &str = "api_without_namespace"; -pub const APIS_WITH_NAMESPACE: &str = "apis_with_namespace"; -pub const APIS_WITHOUT_NAMESPACE: &str = "apis_without_namespace"; // 定义URL配置的接口,用户可以自定义自己需要的URL规则 @@ -81,113 +76,113 @@ pub const APIS_WITHOUT_NAMESPACE: &str = "apis_without_namespace"; pub trait Config: Sync + Send { // 创建一个对象(如Pod、Job的具体实例)的URL和Web服务器路由的映射关系 - fn create_routes(&self) -> &HashMap; + fn create_routes(&self) -> &Vec; // 删除指定对象(如Pod、Job的具体实例)的URL列表和Web服务器路由的映射关系 - fn delete_routes(&self) -> &HashMap; + fn delete_routes(&self) -> &Vec; // 更新指定对象(如Pod、Job的具体实例)的URL列表和Web服务器路由的映射关系 - fn update_routes(&self) -> &HashMap; + fn update_routes(&self) -> &Vec; // 获取指定对象(如Pod、Job的具体实例)的URL列表和Web服务器路由的映射关系 - fn getone_routes(&self) -> &HashMap; + fn getone_routes(&self) -> &Vec; // 获取指定类型(如Pod、Job的所有实例)的URL列表和Web服务器路由的映射关系 - fn listall_routes(&self) -> &HashMap; + fn listall_routes(&self) -> &Vec; - fn watchall_routes(&self) -> &HashMap; + fn watchall_routes(&self) -> &Vec; - fn watchone_routes(&self) -> &HashMap; + fn watchone_routes(&self) -> &Vec; } // Config接口的默认实现,用户可以自定义自己需要的URL规则 // DefaultConfig实现了Kubernetes风格的URL #[derive(Clone)] pub struct DefaultConfig { - create_routes: HashMap, - delete_routes: HashMap, - update_routes: HashMap, - getone_routes: HashMap, - listall_routes: HashMap, - watchall_routes: HashMap, - watchone_routes: HashMap, + create_routes: Vec, + delete_routes: Vec, + update_routes: Vec, + getone_routes: Vec, + listall_routes: Vec, + watchall_routes: Vec, + watchone_routes: Vec, } impl DefaultConfig { pub fn new() -> Self { DefaultConfig { create_routes: { - let mut map: HashMap = HashMap::new(); + let mut vec: Vec = Vec::new(); // create a specific resource: POST // - with namespace: /api(s)/{group}/{version}/namespaces/{namespace}/{plural} // - without namespace/api(s)/{group}/{version}/namespaces/{plural} - map.insert("/api/{version}/namespaces/{namespace}/{plural}".to_string(), API_WITH_NAMESPACE.to_string()); - map.insert("/api/{version}/{plural}".to_string(), API_WITHOUT_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/namespaces/{namespace}/{plural}".to_string(), APIS_WITH_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/{plural}".to_string(), APIS_WITHOUT_NAMESPACE.to_string()); - map + vec.push("/api/{version}/namespaces/{namespace}/{plural}".to_string()); + vec.push("/api/{version}/{plural}".to_string()); + vec.push("/apis/{group}/{version}/namespaces/{namespace}/{plural}".to_string()); + vec.push("/apis/{group}/{version}/{plural}".to_string()); + vec }, delete_routes: { - let mut map: HashMap = HashMap::new(); + let mut vec: Vec = Vec::new(); // delete a specific resource: DELETE // - with namespace: /api(s)/{group}/{version}/namespaces/{namespace}/{plural}/{name} // - without namespace/api(s)/{group}/{version}/namespaces/{plural}/{name} - map.insert("/api/{version}/namespaces/{namespace}/{plural}/{name}".to_string(), API_WITH_NAMESPACE.to_string()); - map.insert("/api/{version}/{plural}/{name}".to_string(), API_WITHOUT_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}".to_string(), APIS_WITH_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/{plural}/{name}".to_string(), APIS_WITHOUT_NAMESPACE.to_string()); - map + vec.push("/api/{version}/namespaces/{namespace}/{plural}/{name}".to_string()); + vec.push("/api/{version}/{plural}/{name}".to_string()); + vec.push("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}".to_string()); + vec.push("/apis/{group}/{version}/{plural}/{name}".to_string()); + vec }, update_routes: { - let mut map: HashMap = HashMap::new(); + let mut vec: Vec = Vec::new(); // update a specific resource:PUT // - with namespace: /api(s)/{group}/{version}/namespaces/{namespace}/{plural}/{name} // - without namespace/api(s)/{group}/{version}/namespaces/{plural}/{name} - map.insert("/api/{version}/namespaces/{namespace}/{plural}/{name}".to_string(), API_WITH_NAMESPACE.to_string()); - map.insert("/api/{version}/{plural}/{name}".to_string(), API_WITHOUT_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}".to_string(), APIS_WITH_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/{plural}/{name}".to_string(), APIS_WITHOUT_NAMESPACE.to_string()); - map + vec.push("/api/{version}/namespaces/{namespace}/{plural}/{name}".to_string()); + vec.push("/api/{version}/{plural}/{name}".to_string()); + vec.push("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}".to_string()); + vec.push("/apis/{group}/{version}/{plural}/{name}".to_string()); + vec }, getone_routes: { - let mut map: HashMap = HashMap::new(); + let mut vec: Vec = Vec::new(); // get a specific resource: GET // - with namespace: /api(s)/{group}/{version}/namespaces/{namespace}/{plural}/{name} // - without namespace/api(s)/{group}/{version}/namespaces/{plural}/{name} - map.insert("/api/{version}/namespaces/{namespace}/{plural}/{name}".to_string(), API_WITH_NAMESPACE.to_string()); - map.insert("/api/{version}/{plural}/{name}".to_string(), API_WITHOUT_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}".to_string(), APIS_WITH_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/{plural}/{name}".to_string(), APIS_WITHOUT_NAMESPACE.to_string()); - map + vec.push("/api/{version}/namespaces/{namespace}/{plural}/{name}".to_string()); + vec.push("/api/{version}/{plural}/{name}".to_string()); + vec.push("/apis/{group}/{version}/namespaces/{namespace}/{plural}/{name}".to_string()); + vec.push("/apis/{group}/{version}/{plural}/{name}".to_string()); + vec }, listall_routes: { - let mut map: HashMap = HashMap::new(); + let mut vec: Vec = Vec::new(); // get some resources: GET // - with namespace: /api(s)/{group}/{version}/namespaces/{namespace}/{plural} // - without namespace/api(s)/{group}/{version}/namespaces/{plural} - map.insert("/api/{version}/namespaces/{namespace}/{plural}".to_string(), API_WITH_NAMESPACE.to_string()); - map.insert("/api/{version}/{plural}".to_string(), API_WITHOUT_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/namespaces/{namespace}/{plural}".to_string(), APIS_WITH_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/{plural}".to_string(), APIS_WITHOUT_NAMESPACE.to_string()); - map + vec.push("/api/{version}/namespaces/{namespace}/{plural}".to_string()); + vec.push("/api/{version}/{plural}".to_string()); + vec.push("/apis/{group}/{version}/namespaces/{namespace}/{plural}".to_string()); + vec.push("/apis/{group}/{version}/{plural}".to_string()); + vec }, watchall_routes: { - let mut map: HashMap = HashMap::new(); + let mut vec: Vec = Vec::new(); // watch some resources: GET // - with namespace: /api(s)/{group}/{version}/watch/namespaces/{namespace}/{plural} // - without namespace/api(s)/{group}/{version}/watch/namespaces/{plural} - map.insert("/api/{version}/watch/namespaces/{namespace}/{plural}".to_string(), API_WITH_NAMESPACE.to_string()); - map.insert("/api/{version}/watch/{plural}".to_string(), API_WITHOUT_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/watch/namespaces/{namespace}/{plural}".to_string(), APIS_WITH_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/watch/{plural}".to_string(), APIS_WITHOUT_NAMESPACE.to_string()); - map + vec.push("/api/{version}/watch/namespaces/{namespace}/{plural}".to_string()); + vec.push("/api/{version}/watch/{plural}".to_string()); + vec.push("/apis/{group}/{version}/watch/namespaces/{namespace}/{plural}".to_string()); + vec.push("/apis/{group}/{version}/watch/{plural}".to_string()); + vec }, watchone_routes: { - let mut map: HashMap = HashMap::new(); + let mut vec: Vec = Vec::new(); // watch one resource: GET // - with namespace: /api(s)/{group}/{version}/watch/namespaces/{namespace}/{plural}/{name} // - without namespace/api(s)/{group}/{version}/watch/namespaces/{plural}/{name} - map.insert("/api/{version}/watch/namespaces/{namespace}/{plural}/{name}".to_string(), API_WITH_NAMESPACE.to_string()); - map.insert("/api/{version}/watch/{plural}/{name}".to_string(), API_WITHOUT_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/watch/namespaces/{namespace}/{plural}/{name}".to_string(), APIS_WITH_NAMESPACE.to_string()); - map.insert("/apis/{group}/{version}/watch/{plural}/{name}".to_string(), APIS_WITHOUT_NAMESPACE.to_string()); - map + vec.push("/api/{version}/watch/namespaces/{namespace}/{plural}/{name}".to_string()); + vec.push("/api/{version}/watch/{plural}/{name}".to_string()); + vec.push("/apis/{group}/{version}/watch/namespaces/{namespace}/{plural}/{name}".to_string()); + vec.push("/apis/{group}/{version}/watch/{plural}/{name}".to_string()); + vec } } } @@ -196,26 +191,25 @@ impl DefaultConfig { // 返回所有的注册URL和路由关系集合 impl Config for DefaultConfig { - - fn create_routes(&self) -> &HashMap { + fn create_routes(&self) -> &Vec { &self.create_routes } - fn delete_routes(&self) -> &HashMap { + fn delete_routes(&self) -> &Vec { &self.delete_routes } - fn update_routes(&self) -> &HashMap { + fn update_routes(&self) -> &Vec { &self.update_routes } - fn getone_routes(&self) -> &HashMap { + fn getone_routes(&self) -> &Vec { &self.getone_routes } - fn listall_routes(&self) -> &HashMap { &self.listall_routes } + fn listall_routes(&self) -> &Vec { &self.listall_routes } - fn watchall_routes(&self) -> &HashMap { &self.watchall_routes } + fn watchall_routes(&self) -> &Vec { &self.watchall_routes } - fn watchone_routes(&self) -> &HashMap { &self.watchone_routes } + fn watchone_routes(&self) -> &Vec { &self.watchone_routes } } diff --git a/src/cores/handlers.rs b/src/cores/handlers.rs index 8798c30..01f467c 100644 --- a/src/cores/handlers.rs +++ b/src/cores/handlers.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::sync::{Arc, LazyLock, Mutex}; use std::{time, vec::Vec}; use async_trait::async_trait; -use actix_web::{HttpResponse, Result, web, Error}; +use actix_web::{HttpResponse, Result, web}; use chrono::Utc; use diesel::{QueryResult, RunQueryDsl, QueryDsl, ExpressionMethods, OptionalExtension, QueryableByName, PgConnection, sql_query, SqliteConnection, Connection}; use serde_json::{json}; @@ -11,7 +11,6 @@ use serde_json::Value; use crate::cores::db::DbConnection; use diesel::sql_types::{Text}; use actix_web::error::ErrorInternalServerError; -use actix_web::web::{Path, Query}; use feventbus::impls::nats::nats::NatsCli; use feventbus::message::Message; use feventbus::message; @@ -43,248 +42,60 @@ fn get_value(key: &str) -> Option { #[async_trait] pub trait Handler { - // /api/{version}/{plural} - async fn create_api_without_namespace( + async fn create_resource( &self, - info: web::Path<(String, String)>, + params: HashMap, data: web::Json, db_connection: &mut DbConnection, nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /api/{version}/namespaces/{namespace}/{plural} - async fn create_api_with_namespace( - &self, - info: web::Path<(String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /apis/{group}/{version}/{plural} - async fn create_apis_without_namespace( - &self, - info: web::Path<(String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /apis/{group}/{version}/namespaces/{namespace}/{plural} - async fn create_apis_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /api/{version}/{plural}/{name} - async fn delete_api_without_namespace( - &self, - info: web::Path<(String, String, String)>, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /api/{version}/namespaces/{namespace}/{plural}/{name} - async fn delete_api_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /apis/{group}/{version}/{plural}/{name} - async fn delete_apis_without_namespace( - &self, - info: web::Path<(String, String, String, String)>, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /apis/{group}/{version}/namespaces/{namespace}/{plural}/{name} - async fn delete_apis_with_namespace( - &self, - info: web::Path<(String, String, String, String, String)>, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /api/{version}/{plural}/{name} - async fn update_api_without_namespace( - &self, - info: web::Path<(String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /api/{version}/namespaces/{namespace}/{plural}/{name} - async fn update_api_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager + event_manager: EventManager, ) -> Result; - // /apis/{group}/{version}/{plural}/{name} - async fn update_apis_without_namespace( + async fn delete_resource( &self, - info: web::Path<(String, String, String, String)>, - data: web::Json, + params: HashMap, db_connection: &mut DbConnection, nats_cli: Arc, - event_manager: EventManager + event_manager: EventManager, ) -> Result; - // /apis/{group}/{version}/namespaces/{namespace}/{plural}/{name} - async fn update_apis_with_namespace( + async fn update_resource( &self, - info: web::Path<(String, String, String, String, String)>, + params: HashMap, data: web::Json, db_connection: &mut DbConnection, nats_cli: Arc, - event_manager: EventManager - ) -> Result; - - // /api/{version}/{plural}/{name} - async fn getone_api_without_namespace( - &self, - info: web::Path<(String, String, String)>, - data: web::Query, - db_connection: &mut DbConnection, - nats_cli: Arc, - ) -> Result; - - // /api/{version}/namespaces/{namespace}/{plural}/{name} - async fn getone_api_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - data: web::Query, - db_connection: &mut DbConnection, - nats_cli: Arc, - ) -> Result; - - // /apis/{group}/{version}/{plural}/{name} - async fn getone_apis_without_namespace( - &self, - info: web::Path<(String, String, String, String)>, - data: web::Query, - db_connection: &mut DbConnection, - nats_cli: Arc, - ) -> Result; - - // /apis/{group}/{version}/namespaces/{namespace}/{plural}/{name} - async fn getone_apis_with_namespace( - &self, - info: web::Path<(String, String, String, String, String)>, - data: web::Query, - db_connection: &mut DbConnection, - nats_cli: Arc, - ) -> Result; - - // /api/{version}/{plural} - async fn listall_api_without_namespace( - &self, - info: web::Path<(String, String)>, - data: web::Query, - db_connection: &mut DbConnection, - nats_cli: Arc, - ) -> Result; - - // /api/{version}/namespaces/{namespace}/{plural} - async fn listall_api_with_namespace( - &self, - info: web::Path<(String, String, String)>, - data: web::Query, - db_connection: &mut DbConnection, - nats_cli: Arc, + event_manager: EventManager, ) -> Result; - // /apis/{group}/{version}/{plural} - async fn listall_apis_without_namespace( + async fn getone_resource( &self, - info: web::Path<(String, String, String)>, - data: web::Query, + params: HashMap, + _data: web::Query, db_connection: &mut DbConnection, - nats_cli: Arc, + _nats_cli: Arc, ) -> Result; - // /apis/{group}/{version}/namespaces/{namespace}/{plural} - async fn listall_apis_with_namespace( + async fn listall_resource( &self, - info: web::Path<(String, String, String, String)>, - data: web::Query, + params: HashMap, + _data: web::Query, db_connection: &mut DbConnection, - nats_cli: Arc, - ) -> Result; - - async fn watchall_api_without_namespace( - &self, - info: web::Path<(String, String)>, - data: web::Query, - event_manager: EventManager - ) -> Result; - - async fn watchall_api_with_namespace( - &self, - info: web::Path<(String, String, String)>, - data: web::Query, - event_manager: EventManager - ) -> Result; - - async fn watchall_apis_without_namespace( - &self, - info: web::Path<(String, String, String)>, - data: web::Query, - event_manager: EventManager - ) -> Result; - - async fn watchall_apis_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - data: web::Query, - event_manager: EventManager - ) -> Result; - - async fn watchone_api_without_namespace( - &self, - info: web::Path<(String, String, String)>, - data: web::Query, - event_manager: EventManager - ) -> Result; - - async fn watchone_api_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - data: web::Query, - event_manager: EventManager + _nats_cli: Arc, ) -> Result; - async fn watchone_apis_without_namespace( + async fn watchall_resource( &self, - info: web::Path<(String, String, String, String)>, - data: web::Query, - event_manager: EventManager + params: HashMap, + _data: web::Query, + event_manager: EventManager, ) -> Result; - async fn watchone_apis_with_namespace( + async fn watchone_resource( &self, - info: web::Path<(String, String, String, String, String)>, - data: web::Query, - event_manager: EventManager + params: HashMap, + _data: web::Query, + event_manager: EventManager, ) -> Result; // 不满足以上请求路径的处理 @@ -1248,21 +1059,29 @@ impl EventManager { #[async_trait] impl Handler for DefaultHandler { - - // /api/{version}/{plural} - async fn create_api_without_namespace( + async fn create_resource( &self, - info: web::Path<(String, String)>, + params: HashMap, data: web::Json, db_connection: &mut DbConnection, nats_cli: Arc, event_manager: EventManager, ) -> Result { - let (version, plural) = info.into_inner(); let data = data.into_inner(); + // 获取 path 参数 + let group = params.get("group").map(String::as_str); + let version = params.get("version").unwrap(); // 必须存在 + let namespace = params.get("namespace").map(String::as_str); + let plural = params.get("plural").unwrap(); // 必须存在 + + let ver = if let Some(group) = group { + format!("{}/{}", group, version) + } else { + version.to_string() + }; - //判断是正常的插入函数还是 crd 资源的注册函数 if plural == "crds" { + // 处理 CRD 资源注册 let item_kind = data .get("spec") .and_then(|spec| spec.get("names")) @@ -1277,47 +1096,59 @@ impl Handler for DefaultHandler { .and_then(|kind| kind.as_str()) .unwrap_or("error"); - - // 调用check_metadata函数 - let metadata_exists = check_metadata(db_connection, item_kind, &version,false).await.map_err(ErrorInternalServerError)?; + let metadata_exists = check_metadata(db_connection, item_kind, &ver, namespace.is_some()) + .await + .map_err(ErrorInternalServerError)?; if metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 crd 资源已存在,无需重复注册" }))); + return Ok(HttpResponse::NotFound().json(json!({ "error": "该 CRD 资源已存在,无需重复注册" }))); } - insert_metadata(db_connection, item_kind, &version, false, &data) + insert_metadata(db_connection, item_kind, &ver, namespace.is_some(), &data) .await .map_err(ErrorInternalServerError)?; insert_key_value(item_kind, kind_upper); } else { - // 调用check_metadata函数 - let metadata_exists = check_metadata(db_connection, &plural, &version,false).await.map_err(ErrorInternalServerError)?; + // 检查 metadata 是否存在 + let metadata_exists = check_metadata(db_connection, plural, &ver, namespace.is_some()) + .await + .map_err(ErrorInternalServerError)?; if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请重新注册或检查 plural 版本以及是否需要 namespace" }))); + return Ok(HttpResponse::NotFound().json(json!({ + "error": "该 plural 不存在,请重新注册或检查 plural 版本以及是否需要 namespace" + }))); } - // 从 json_data 中提取 metadata.name + // 检查资源是否已存在 let item_name = data .get("metadata") .and_then(|metadata| metadata.get("name")) .and_then(|name| name.as_str()) .unwrap_or("error"); - let kine_exists = check_kine(db_connection, &plural, item_name, &version, None).await.map_err(ErrorInternalServerError)?; + let kine_exists = check_kine( + db_connection, + plural, + item_name, + &ver, + namespace, + ) + .await + .map_err(ErrorInternalServerError)?; if kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "该资源已存在,请勿重复创建" }))); + return Ok(HttpResponse::InternalServerError().json(json!({ + "error": "该资源已存在,请勿重复创建" + }))); } let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - + let kind = get_value(plural).unwrap(); model_map.insert("MODEL".to_string(), kind); - - // request消息 + // 创建请求消息 let request_message = Message::new( "CREATE".to_string(), message::NativeEventAction::Other, @@ -1328,818 +1159,221 @@ impl Handler for DefaultHandler { None, ); - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}", version, plural); - let watchone_resource_type = format!("{}/{}/{}", version, plural, item_name); - - event_manager.send_event(&resource_type, EventType::Create, data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Create, data.clone()).await; + send_request(request_message, nats_cli) + .await + .map_err(ErrorInternalServerError)?; - insert_kine(db_connection, &plural, item_name, &data, &version, None) + // 发送事件 + let resource_type = if let Some(namespace) = namespace { + format!("{}/{}/{}", ver, namespace, plural) + } else { + format!("{}/{}", ver, plural) + }; + let watchone_resource_type = if let Some(namespace) = namespace { + format!("{}/{}/{}/{}", ver, namespace, plural, item_name) + } else { + format!("{}/{}/{}", ver, plural, item_name) + }; + + event_manager + .send_event(&resource_type, EventType::Create, data.clone()) + .await; + event_manager + .send_event(&watchone_resource_type, EventType::Create, data.clone()) + .await; + + insert_kine( + db_connection, + plural, + item_name, + &data, + &ver, + namespace, + ) .await .map_err(ErrorInternalServerError)?; } - Ok(HttpResponse::Ok().json(data)) } - // /api/{version}/namespaces/{namespace}/{plural} - async fn create_api_with_namespace( + async fn delete_resource( &self, - info: web::Path<(String, String, String)>, - data: web::Json, + params: HashMap, db_connection: &mut DbConnection, nats_cli: Arc, event_manager: EventManager, ) -> Result { - let (version, namespace, plural) = info.into_inner(); - let data = data.into_inner(); - - //判断是正常的插入函数还是 crd 资源的注册函数 - if plural == "crds" { - let item_kind = data - .get("spec") - .and_then(|spec| spec.get("names")) - .and_then(|names| names.get("plural")) - .and_then(|plural| plural.as_str()) - .unwrap_or("error"); - - let kind_upper = data - .get("spec") - .and_then(|spec| spec.get("names")) - .and_then(|names| names.get("kind")) - .and_then(|kind| kind.as_str()) - .unwrap_or("error"); - - // 调用check_metadata函数 - let metadata_exists = check_metadata(db_connection, item_kind, &version, true).await.map_err(ErrorInternalServerError)?; - - if metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 crd 资源已存在,无需重复注册" }))); - } - - insert_metadata(db_connection, item_kind, &version, true, &data) - .await - .map_err(ErrorInternalServerError)?; - - insert_key_value(item_kind, kind_upper); + // 解析路径参数 + let group = params.get("group").map(String::as_str); + let version = params.get("version").unwrap(); // 必须存在 + let namespace = params.get("namespace").map(String::as_str); + let plural = params.get("plural").unwrap(); // 必须存在 + let name = params.get("name").unwrap(); // 必须存在 + + let ver = if let Some(group) = group { + format!("{}/{}", group, version) } else { - // 调用check_metadata函数 - let metadata_exists = check_metadata(db_connection, &plural, &version, true).await.map_err(ErrorInternalServerError)?; + version.to_string() + }; - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请重新注册或检查 plural 版本以及是否需要 namespace" }))); - } + // 检查 metadata + let metadata_exists = check_metadata(db_connection, plural, &ver, namespace.is_some()) + .await + .map_err(ErrorInternalServerError)?; - // 从 json_data 中提取 metadata.name - let item_name = data - .get("metadata") - .and_then(|metadata| metadata.get("name")) - .and_then(|name| name.as_str()) - .unwrap_or("error"); + if !metadata_exists { + return Ok(HttpResponse::NotFound().json(json!({ + "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" + }))); + } - let kine_exists = check_kine(db_connection, &plural, item_name, &version, Some(&namespace)).await.map_err(ErrorInternalServerError)?; + // 检查资源是否存在 + let kine_exists = check_kine( + db_connection, + plural, + name, + &ver, + namespace, + ) + .await + .map_err(ErrorInternalServerError)?; - if kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "该资源已存在,请勿重复创建" }))); - } + if !kine_exists { + return Ok(HttpResponse::InternalServerError().json(json!({ + "error": "指定数据不存在" + }))); + } - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); + let mut model_map = HashMap::new(); + let kind = get_value(plural).unwrap(); + model_map.insert("MODEL".to_string(), kind); - model_map.insert("MODEL".to_string(), kind); + let request_data = json!({ + "apiVersion": ver.clone(), + "Namespaces": namespace.unwrap_or(""), + "Plural": plural.clone(), + "Name": name.clone(), + }); - // request消息 - let request_message = Message::new( - "CREATE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: data.clone(), - }), - None, - ); + // 创建请求消息 + let request_message = Message::new( + "DELETE".to_string(), + message::NativeEventAction::Other, + Some(model_map), + Some(ApiServerMessage { + content: request_data.clone(), + }), + None, + ); - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; + send_request(request_message, nats_cli) + .await + .map_err(ErrorInternalServerError)?; - let resource_type = format!("{}/{}/{}", version, namespace, plural); - let watchone_resource_type = format!("{}/{}/{}/{}", version, namespace, plural, item_name); + // 发送事件 + let resource_type = if let Some(namespace) = namespace { + format!("{}/{}/{}", ver, namespace, plural) + } else { + format!("{}/{}", ver, plural) + }; + let watchone_resource_type = if let Some(namespace) = namespace { + format!("{}/{}/{}/{}", ver, namespace, plural, name) + } else { + format!("{}/{}/{}", ver, plural, name) + }; - event_manager.send_event(&resource_type, EventType::Create, data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Create, data.clone()).await; + event_manager + .send_event(&resource_type, EventType::Delete, request_data.clone()) + .await; + event_manager + .send_event(&watchone_resource_type, EventType::Delete, request_data) + .await; + + // 删除资源 + let deleted = delete_from_kine( + db_connection, + plural, + name, + &ver, + namespace, + ) + .await + .map_err(ErrorInternalServerError)?; - insert_kine(db_connection, &plural, item_name, &data, &version, Some(&namespace)) - .await - .map_err(ErrorInternalServerError)?; + if !deleted { + return Ok(HttpResponse::NotFound().json(json!({ + "error": "指定数据不存在" + }))); } - Ok(HttpResponse::Ok().json(data)) + Ok(HttpResponse::Ok().json(json!({ + "status": "资源删除成功" + }))) } - // /apis/{group}/{version}/{plural} - async fn create_apis_without_namespace( + async fn update_resource( &self, - info: web::Path<(String, String, String)>, + params: HashMap, data: web::Json, db_connection: &mut DbConnection, nats_cli: Arc, event_manager: EventManager, ) -> Result { - let (group, version, plural) = info.into_inner(); - let ver = group + "/" + &*version; + // 解析路径参数 + let group = params.get("group").map(String::as_str); + let version = params.get("version").unwrap(); // 必须存在 + let namespace = params.get("namespace").map(String::as_str); + let plural = params.get("plural").unwrap(); // 必须存在 + let name = params.get("name").unwrap(); // 必须存在 let data = data.into_inner(); - //判断是正常的插入函数还是 crd 资源的注册函数 - if plural == "crds" { - let item_kind = data - .get("spec") - .and_then(|spec| spec.get("names")) - .and_then(|names| names.get("plural")) - .and_then(|plural| plural.as_str()) - .unwrap_or("error"); - - let kind_upper = data - .get("spec") - .and_then(|spec| spec.get("names")) - .and_then(|names| names.get("kind")) - .and_then(|kind| kind.as_str()) - .unwrap_or("error"); - - // 调用check_metadata函数 - let metadata_exists = check_metadata(db_connection, item_kind, &ver, false).await.map_err(ErrorInternalServerError)?; - - if metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 crd 资源已存在,无需重复注册" }))); - } - - insert_metadata(db_connection, item_kind, &ver, false, &data) - .await - .map_err(ErrorInternalServerError)?; - - insert_key_value(item_kind, kind_upper); + let ver = if let Some(group) = group { + format!("{}/{}", group, version) } else { - // 调用check_metadata函数 - let metadata_exists = check_metadata(db_connection, &plural, &ver, false).await.map_err(ErrorInternalServerError)?; + version.to_string() + }; - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请重新注册或检查 plural 版本以及是否需要 namespace" }))); - } + // 检查 metadata 是否存在 + let metadata_exists = check_metadata(db_connection, plural, &ver, namespace.is_some()) + .await + .map_err(ErrorInternalServerError)?; - // 从 json_data 中提取 metadata.name - let item_name = data - .get("metadata") - .and_then(|metadata| metadata.get("name")) - .and_then(|name| name.as_str()) - .unwrap_or("error"); + if !metadata_exists { + return Ok(HttpResponse::NotFound().json(json!({ + "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" + }))); + } - let kine_exists = check_kine(db_connection, &plural, item_name, &ver, None).await.map_err(ErrorInternalServerError)?; + // 检查资源是否存在 + let kine_exists = check_kine( + db_connection, + plural, + name, + &ver, + namespace, + ) + .await + .map_err(ErrorInternalServerError)?; - if kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "该资源已存在,请勿重复创建" }))); - } + if !kine_exists { + return Ok(HttpResponse::InternalServerError().json(json!({ + "error": "指定数据不存在" + }))); + } - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); - - // request消息 - let request_message = Message::new( - "CREATE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: data.clone(), - }), - None, - ); - - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}", ver, plural); - let watchone_resource_type = format!("{}/{}/{}", ver, plural, item_name); - - event_manager.send_event(&resource_type, EventType::Create, data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Create, data.clone()).await; - - insert_kine(db_connection, &plural, item_name, &data, &ver, None) - .await - .map_err(ErrorInternalServerError)?; - } - - Ok(HttpResponse::Ok().json(data)) - } - - // /apis/{group}/{version}/namespaces/{namespace}/{plural} - async fn create_apis_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager, - ) -> Result { - let (group, version, namespace, plural) = info.into_inner(); - - let ver = group + "/" + &*version; - let data = data.into_inner(); - - //判断是正常的插入函数还是 crd 资源的注册函数 - if plural == "crds" { - let item_kind = data - .get("spec") - .and_then(|spec| spec.get("names")) - .and_then(|names| names.get("plural")) - .and_then(|plural| plural.as_str()) - .unwrap_or("error"); - - let kind_upper = data - .get("spec") - .and_then(|spec| spec.get("names")) - .and_then(|names| names.get("kind")) - .and_then(|kind| kind.as_str()) - .unwrap_or("error"); - - // 调用 check_metadata 函数 - let metadata_exists = check_metadata(db_connection, item_kind, &ver, true).await.map_err(ErrorInternalServerError)?; - - if metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 crd 资源已存在,无需重复注册" }))); - } - - insert_metadata(db_connection, item_kind, &ver, true, &data) - .await - .map_err(ErrorInternalServerError)?; - - insert_key_value(item_kind, kind_upper); - } else { - // 调用 check_metadata 函数 - let metadata_exists = check_metadata(db_connection, &plural, &ver, true).await.map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请重新注册或检查 plural 版本以及是否需要 namespace" }))); - } - - // 从 json_data 中提取 metadata.name - let item_name = data - .get("metadata") - .and_then(|metadata| metadata.get("name")) - .and_then(|name| name.as_str()) - .unwrap_or("error"); - - let kine_exists = check_kine(db_connection, &plural, item_name, &ver, Some(&namespace)).await.map_err(ErrorInternalServerError)?; - - if kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "该资源已存在,请勿重复创建" }))); - } - - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); - - // request消息 - let request_message = Message::new( - "CREATE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: data.clone(), - }), - None, - ); - - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}/{}", ver, namespace, plural); - let watchone_resource_type = format!("{}/{}/{}/{}", ver, namespace, plural, item_name); - - event_manager.send_event(&resource_type, EventType::Create, data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Create, data.clone()).await; - - insert_kine(db_connection, &plural, item_name, &data, &ver, Some(&namespace)) - .await - .map_err(ErrorInternalServerError)?; - } - - Ok(HttpResponse::Ok().json(data)) - } - - // /api/{version}/{plural}/{name} - async fn delete_api_without_namespace( - &self, - info: web::Path<(String, String, String)>, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager, - ) -> Result { - let (version, plural, name) = info.into_inner(); - - // 检查 metadata 中是否存在该 plural 且不需要 namespace - let metadata_exists = check_metadata(db_connection, &plural, &version, false) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let kine_exists = check_kine(db_connection, &plural, &name, &version, None).await.map_err(ErrorInternalServerError)?; - - if !kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "指定数据不存在" }))); - } - - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); - - let request_data = json!({ - "apiVersion": version.clone(), - "Namespaces": "", - "Plural": plural.clone(), - "Name": name.clone(), - }); - - // request消息 - let request_message = Message::new( - "DELETE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: request_data.clone(), - }), - None, - ); - - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}", version, plural); - let watchone_resource_type = format!("{}/{}/{}", version, plural, name); - - event_manager.send_event(&resource_type, EventType::Delete, request_data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Delete, request_data).await; - - // 从 plural 表中删除指定的 name - let deleted = delete_from_kine(db_connection, &plural, &name, &version, None) - .await - .map_err(ErrorInternalServerError)?; - - if !deleted { - return Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))); - } - - Ok(HttpResponse::Ok().json(json!({ "status": "delete_api_without_namespace success" }))) - } - - // /api/{version}/namespaces/{namespace}/{plural}/{name} - async fn delete_api_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager, - ) -> Result { - let (version, namespace, plural, name) = info.into_inner(); - // 检查 metadata 中是否存在该 plural 且需要 namespace - let metadata_exists = check_metadata(db_connection, &plural, &version, true) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let kine_exists = check_kine(db_connection, &plural, &name, &version, Some(&namespace)).await.map_err(ErrorInternalServerError)?; - - if !kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "指定数据不存在" }))); - } - - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); - - let request_data = json!({ - "apiVersion": version.clone(), - "Namespaces": namespace.clone(), - "Plural": plural.clone(), - "Name": name.clone(), - }); - - // request消息 - let request_message = Message::new( - "DELETE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: request_data.clone(), - }), - None, - ); - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}/{}", version, namespace, plural); - let watchone_resource_type = format!("{}/{}/{}/{}", version, namespace, plural, name); - - event_manager.send_event(&resource_type, EventType::Delete, request_data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Delete, request_data).await; - - // 从 plural 表中删除指定的数据 - let deleted = delete_from_kine(db_connection, &plural, &name, &version, Some(&namespace)) - .await - .map_err(ErrorInternalServerError)?; - - if !deleted { - return Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))); - } - - Ok(HttpResponse::Ok().json(json!({ "status": "delete_api_with_namespace success" }))) - } - - // /apis/{group}/{version}/{plural}/{name} - async fn delete_apis_without_namespace( - &self, - info: web::Path<(String, String, String, String)>, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager, - ) -> Result { - let (group, version, plural, name) = info.into_inner(); - let ver = format!("{}/{}", group, version); - - let metadata_exists = check_metadata(db_connection, &plural, &ver, false) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let kine_exists = check_kine(db_connection, &plural, &name, &ver, None).await.map_err(ErrorInternalServerError)?; - - if !kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "指定数据不存在" }))); - } - - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); - - let request_data = json!({ - "apiVersion": ver.clone(), - "Namespaces": "", - "Plural": plural.clone(), - "Name": name.clone(), - }); - - // request消息 - let request_message = Message::new( - "DELETE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: request_data.clone(), - }), - None, - ); - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}", ver, plural); - let watchone_resource_type = format!("{}/{}/{}", ver, plural, name); - - event_manager.send_event(&resource_type, EventType::Delete, request_data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Delete, request_data).await; - - let deleted = delete_from_kine(db_connection, &plural, &name, &ver, None) - .await - .map_err(ErrorInternalServerError)?; - - if !deleted { - return Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))); - } - - Ok(HttpResponse::Ok().json(json!({ "status": "delete_apis_without_namespace success" }))) - } - - // /apis/{group}/{version}/namespaces/{namespace}/{plural}/{name} - async fn delete_apis_with_namespace( - &self, - info: web::Path<(String, String, String, String, String)>, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager, - ) -> Result { - let (group, version, namespace, plural, name) = info.into_inner(); - let ver = format!("{}/{}", group, version); - - let metadata_exists = check_metadata(db_connection, &plural, &ver, true) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let kine_exists = check_kine(db_connection, &plural, &name, &ver, Some(&namespace)).await.map_err(ErrorInternalServerError)?; - - if !kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "指定数据不存在" }))); - } - - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); - - let request_data = json!({ - "apiVersion": ver.clone(), - "Namespaces": namespace.clone(), - "Plural": plural.clone(), - "Name": name.clone(), - }); - - // request消息 - let request_message = Message::new( - "DELETE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: request_data.clone(), - }), - None, - ); - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}/{}", ver, namespace, plural); - let watchone_resource_type = format!("{}/{}/{}/{}", ver, namespace, plural, name); - - event_manager.send_event(&resource_type, EventType::Delete, request_data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Delete, request_data).await; - - let deleted = delete_from_kine(db_connection, &plural, &name, &ver, Some(&namespace)) - .await - .map_err(ErrorInternalServerError)?; - - if !deleted { - return Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))); - } - - Ok(HttpResponse::Ok().json(json!({ "status": "delete_apis_with_namespace success" }))) - } - - // /api/{version}/{plural}/{name} - async fn update_api_without_namespace( - &self, - info: web::Path<(String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager, - ) -> Result { - let (version, plural, name) = info.into_inner(); - let data = data.into_inner(); - - let metadata_exists = check_metadata(db_connection, &plural, &version, false) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let kine_exists = check_kine(db_connection, &plural, &name, &version, None).await.map_err(ErrorInternalServerError)?; - - if !kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "指定数据不存在" }))); - } - - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); - - let request_data = json!({ - "apiVersion": version.clone(), - "Namespaces": "", - "Plural": plural.clone(), - "Name": name.clone(), - "Data": data.clone() - }); - - // request消息 - let request_message = Message::new( - "UPDATE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: request_data.clone(), - }), - None, - ); - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}", version, plural); - let watchone_resource_type = format!("{}/{}/{}", version, plural, name); - - event_manager.send_event(&resource_type, EventType::Update, request_data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Update, request_data).await; - - // 查询 plural 表中是否存在 name 匹配的数据 - let updated = update_data_in_kine(db_connection, &plural, &name, &version, None, &data) - .await - .map_err(ErrorInternalServerError)?; - - if !updated { - return Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))); - } - - Ok(HttpResponse::Ok().json(data)) - } - - // /api/{version}/namespaces/{namespace}/{plural}/{name} - async fn update_api_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager, - ) -> Result { - let (version, namespace, plural, name) = info.into_inner(); - let data = data.into_inner(); - - // 检查 metadata 中是否存在 plural 且要求 namespace - let metadata_exists = check_metadata(db_connection, &plural, &version, true) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let kine_exists = check_kine(db_connection, &plural, &name, &version, Some(&namespace)).await.map_err(ErrorInternalServerError)?; - - if !kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "指定数据不存在" }))); - } - - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); - - let request_data = json!({ - "apiVersion": version.clone(), - "Namespaces": namespace.clone(), - "Plural": plural.clone(), - "Name": name.clone(), - "Data": data.clone() - }); - - // request消息 - let request_message = Message::new( - "UPDATE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: request_data.clone(), - }), - None, - ); - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}/{}", version, namespace, plural); - let watchone_resource_type = format!("{}/{}/{}/{}", version, namespace, plural, name); - - event_manager.send_event(&resource_type, EventType::Update, request_data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Update, request_data).await; - - let updated = update_data_in_kine(db_connection, &plural, &name, &version, Some(&namespace), &data) - .await - .map_err(ErrorInternalServerError)?; - - if !updated { - return Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))); - } - - Ok(HttpResponse::Ok().json(data)) - } - - // /apis/{group}/{version}/{plural}/{name} - async fn update_apis_without_namespace( - &self, - info: web::Path<(String, String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager, - ) -> Result { - let (group, version, plural, name) = info.into_inner(); - let ver = format!("{}/{}", group, version); - let data = data.into_inner(); - - let metadata_exists = check_metadata(db_connection, &plural, &ver, false) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let kine_exists = check_kine(db_connection, &plural, &name, &ver, None).await.map_err(ErrorInternalServerError)?; - - if !kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "指定数据不存在" }))); - } - - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); - - let request_data = json!({ - "apiVersion": ver, - "Namespaces": "", - "Plural": plural.clone(), - "Name": name.clone(), - "Data": data.clone() - }); - - // request消息 - let request_message = Message::new( - "UPDATE".to_string(), - message::NativeEventAction::Other, - Some(model_map), - Some(ApiServerMessage { - content: request_data.clone(), - }), - None, - ); - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}", ver, plural); - let watchone_resource_type = format!("{}/{}/{}", ver, plural, name); - - event_manager.send_event(&resource_type, EventType::Update, request_data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Update, request_data).await; - - let updated = update_data_in_kine(db_connection, &plural, &name, &ver, None, &data) - .await - .map_err(ErrorInternalServerError)?; - - if !updated { - return Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))); - } - - Ok(HttpResponse::Ok().json(data)) - } - - // /apis/{group}/{version}/namespaces/{namespace}/{plural}/{name} - async fn update_apis_with_namespace( - &self, - info: web::Path<(String, String, String, String, String)>, - data: web::Json, - db_connection: &mut DbConnection, - nats_cli: Arc, - event_manager: EventManager, - ) -> Result { - let (group, version, namespace, plural, name) = info.into_inner(); - let ver = format!("{}/{}", group, version); - let data = data.into_inner(); - - let metadata_exists = check_metadata(db_connection, &plural, &ver, true) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let kine_exists = check_kine(db_connection, &plural, &name, &ver, Some(&namespace)).await.map_err(ErrorInternalServerError)?; - - if !kine_exists { - return Ok(HttpResponse::InternalServerError().json(json!({ "error": "指定数据不存在" }))); - } - - let mut model_map = HashMap::new(); - let kind = get_value(&*plural.clone()).unwrap(); - - model_map.insert("MODEL".to_string(), kind); + let mut model_map = HashMap::new(); + let kind = get_value(plural).unwrap(); + model_map.insert("MODEL".to_string(), kind); let request_data = json!({ - "apiVersion": ver, - "Namespaces": namespace.clone(), - "Plural": plural.clone(), - "Name": name.clone(), - "Data": data.clone() + "apiVersion": ver.clone(), + "Namespaces": namespace.unwrap_or(""), + "Plural": plural.clone(), + "Name": name.clone(), + "Data": data.clone() }); - // request消息 + // 创建请求消息 let request_message = Message::new( "UPDATE".to_string(), message::NativeEventAction::Other, @@ -2149,506 +1383,249 @@ impl Handler for DefaultHandler { }), None, ); - send_request(request_message, nats_cli).await.map_err(ErrorInternalServerError)?; - - let resource_type = format!("{}/{}/{}", ver, namespace, plural); - let watchone_resource_type = format!("{}/{}/{}/{}", ver, namespace, plural, name); - - event_manager.send_event(&resource_type, EventType::Update, request_data.clone()).await; - event_manager.send_event(&watchone_resource_type, EventType::Update, request_data).await; - - let updated = update_data_in_kine(db_connection, &plural, &name, &ver, Some(&namespace), &data) - .await - .map_err(ErrorInternalServerError)?; - - if !updated { - return Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))); - } - - Ok(HttpResponse::Ok().json(data)) - } - - // /api/{version}/{plural}/{name} - async fn getone_api_without_namespace( - &self, - info: web::Path<(String, String, String)>, - _data: web::Query, - db_connection: &mut DbConnection, - _nats_cli: Arc, - ) -> Result { - let (version, plural, name) = info.into_inner(); - let metadata_exists = check_metadata(db_connection, &plural, &version, false) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - if let Some(data) = get_data_from_kine(db_connection, &plural, &name, &version, None) - .await - .map_err(ErrorInternalServerError)? { - // 将字符串转换为 JSON 格式 - match serde_json::from_str::(&data) { - Ok(json_data) => return Ok(HttpResponse::Ok().json(json_data)), // 成功解析为 JSON,返回 JSON 响应 - Err(_) => return Ok(HttpResponse::InternalServerError().json(json!({ "error": "数据格式错误,无法解析为 JSON" }))), - } - } - Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))) - } - - // /api/{version}/namespaces/{namespace}/{plural}/{name} - async fn getone_api_with_namespace( - &self, - info: web::Path<(String, String, String, String)>, - _data: web::Query, - db_connection: &mut DbConnection, - _nats_cli: Arc, - ) -> Result { - let (version, namespace, plural, name) = info.into_inner(); - let metadata_exists = check_metadata(db_connection, &plural, &version, true) + send_request(request_message, nats_cli) .await .map_err(ErrorInternalServerError)?; - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - if let Some(data) = get_data_from_kine(db_connection, &plural, &name, &version, Some(&namespace)) - .await - .map_err(ErrorInternalServerError)? { - // 将字符串转换为 JSON 格式 - match serde_json::from_str::(&data) { - Ok(json_data) => return Ok(HttpResponse::Ok().json(json_data)), // 成功解析为 JSON,返回 JSON 响应 - Err(_) => return Ok(HttpResponse::InternalServerError().json(json!({ "error": "数据格式错误,无法解析为 JSON" }))), - } - } - Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))) - } - - // /apis/{group}/{version}/{plural}/{name} - async fn getone_apis_without_namespace( - &self, - info: web::Path<(String, String, String, String)>, - _data: web::Query, - db_connection: &mut DbConnection, - _nats_cli: Arc, - ) -> Result { - let (group, version, plural, name) = info.into_inner(); - let ver = format!("{}/{}", group, version); + // 发送事件 + let resource_type = if let Some(namespace) = namespace { + format!("{}/{}/{}", ver, namespace, plural) + } else { + format!("{}/{}", ver, plural) + }; + let watchone_resource_type = if let Some(namespace) = namespace { + format!("{}/{}/{}/{}", ver, namespace, plural, name) + } else { + format!("{}/{}/{}", ver, plural, name) + }; - let metadata_exists = check_metadata(db_connection, &plural, &ver, false) + event_manager + .send_event(&resource_type, EventType::Update, request_data.clone()) + .await; + event_manager + .send_event(&watchone_resource_type, EventType::Update, request_data) + .await; + + // 更新资源 + let updated = update_data_in_kine( + db_connection, + plural, + name, + &ver, + namespace, + &data, + ) .await .map_err(ErrorInternalServerError)?; - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); + if !updated { + return Ok(HttpResponse::NotFound().json(json!({ + "error": "指定数据不存在" + }))); } - if let Some(data) = get_data_from_kine(db_connection, &plural, &name, &ver, None) - .await - .map_err(ErrorInternalServerError)? { - // 将字符串转换为 JSON 格式 - match serde_json::from_str::(&data) { - Ok(json_data) => return Ok(HttpResponse::Ok().json(json_data)), // 成功解析为 JSON,返回 JSON 响应 - Err(_) => return Ok(HttpResponse::InternalServerError().json(json!({ "error": "数据格式错误,无法解析为 JSON" }))), - } - } - Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))) + Ok(HttpResponse::Ok().json(data)) } - // /apis/{group}/{version}/namespaces/{namespace}/{plural}/{name} - async fn getone_apis_with_namespace( + async fn getone_resource( &self, - info: web::Path<(String, String, String, String, String)>, + params: HashMap, _data: web::Query, db_connection: &mut DbConnection, _nats_cli: Arc, ) -> Result { - let (group, version, namespace, plural, name) = info.into_inner(); - let ver = format!("{}/{}", group, version); + // 解析路径参数 + let group = params.get("group").map(String::as_str); + let version = params.get("version").unwrap(); // 必须存在 + let namespace = params.get("namespace").map(String::as_str); + let plural = params.get("plural").unwrap(); // 必须存在 + let name = params.get("name").unwrap(); // 必须存在 + + let ver = if let Some(group) = group { + format!("{}/{}", group, version) + } else { + version.to_string() + }; - let metadata_exists = check_metadata(db_connection, &plural, &ver, true) + // 检查 metadata 是否存在 + let metadata_exists = check_metadata(db_connection, plural, &ver, namespace.is_some()) .await .map_err(ErrorInternalServerError)?; if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); + return Ok(HttpResponse::NotFound().json(json!({ + "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" + }))); } - if let Some(data) = get_data_from_kine(db_connection, &plural, &name, &ver, Some(&namespace)) + // 获取资源数据 + if let Some(data) = get_data_from_kine( + db_connection, + plural, + name, + &ver, + namespace, + ) .await .map_err(ErrorInternalServerError)? { - // 将字符串转换为 JSON 格式 - match serde_json::from_str::(&data) { - Ok(json_data) => return Ok(HttpResponse::Ok().json(json_data)), // 成功解析为 JSON,返回 JSON 响应 - Err(_) => return Ok(HttpResponse::InternalServerError().json(json!({ "error": "数据格式错误,无法解析为 JSON" }))), - } + // 将字符串解析为 JSON 格式 + match serde_json::from_str::(&data) { + Ok(json_data) => return Ok(HttpResponse::Ok().json(json_data)), + Err(_) => return Ok(HttpResponse::InternalServerError().json(json!({ + "error": "数据格式错误,无法解析为 JSON" + }))), } - Ok(HttpResponse::NotFound().json(json!({ "error": "指定数据不存在" }))) - } - - // /api/{version}/{plural} - async fn listall_api_without_namespace( - &self, - info: web::Path<(String, String)>, - _data: web::Query, - db_connection: &mut DbConnection, - _nats_cli: Arc, - ) -> Result { - let (version, plural) = info.into_inner(); - let metadata_exists = check_metadata(db_connection, &plural, &version, false) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let data_list = get_all_data_from_kine(db_connection, &plural, &version, None) - .await - .map_err(ErrorInternalServerError)?; - - // 将所有字符串解析为 JSON 格式并收集到一个数组中 - let json_array: Vec = data_list - .into_iter() - .filter_map(|data_str| serde_json::from_str(&data_str).ok()) // 解析成功的数据 - .collect(); - - Ok(HttpResponse::Ok().json(json_array)) - } - - // /api/{version}/namespaces/{namespace}/{plural} - async fn listall_api_with_namespace( - &self, - info: web::Path<(String, String, String)>, - _data: web::Query, - db_connection: &mut DbConnection, - _nats_cli: Arc, - ) -> Result { - let (version, namespace, plural) = info.into_inner(); - let metadata_exists = check_metadata(db_connection, &plural, &version, true) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); } - let data_list = get_all_data_from_kine(db_connection, &plural, &version, Some(&namespace)) - .await - .map_err(ErrorInternalServerError)?; - - // 将所有字符串解析为 JSON 格式并收集到一个数组中 - let json_array: Vec = data_list - .into_iter() - .filter_map(|data_str| serde_json::from_str(&data_str).ok()) // 解析成功的数据 - .collect(); - - Ok(HttpResponse::Ok().json(json_array)) + // 如果资源不存在 + Ok(HttpResponse::NotFound().json(json!({ + "error": "指定数据不存在" + }))) } - // /apis/{group}/{version}/{plural} - async fn listall_apis_without_namespace( + async fn listall_resource( &self, - info: web::Path<(String, String, String)>, + params: HashMap, _data: web::Query, db_connection: &mut DbConnection, _nats_cli: Arc, ) -> Result { - let (group, version, plural) = info.into_inner(); - let ver = format!("{}/{}", group, version); + // 解析路径参数 + let group = params.get("group").map(String::as_str); + let version = params.get("version").unwrap(); // 必须存在 + let namespace = params.get("namespace").map(String::as_str); + let plural = params.get("plural").unwrap(); // 必须存在 + + // 组合版本号 + let ver = if let Some(group) = group { + format!("{}/{}", group, version) + } else { + version.to_string() + }; - let metadata_exists = check_metadata(db_connection, &plural, &ver, false) + // 检查 metadata 是否存在 + let metadata_exists = check_metadata(db_connection, plural, &ver, namespace.is_some()) .await .map_err(ErrorInternalServerError)?; if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); + return Ok(HttpResponse::NotFound().json(json!({ + "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" + }))); } - let data_list = get_all_data_from_kine(db_connection, &plural, &ver, None) + // 获取资源列表 + let data_list = get_all_data_from_kine(db_connection, plural, &ver, namespace) .await .map_err(ErrorInternalServerError)?; - // 将所有字符串解析为 JSON 格式并收集到一个数组中 + // 将所有字符串解析为 JSON 格式并收集到数组中 let json_array: Vec = data_list .into_iter() .filter_map(|data_str| serde_json::from_str(&data_str).ok()) // 解析成功的数据 .collect(); + // 返回结果 Ok(HttpResponse::Ok().json(json_array)) } - // /apis/{group}/{version}/namespaces/{namespace}/{plural} - async fn listall_apis_with_namespace( + async fn watchall_resource( &self, - info: web::Path<(String, String, String, String)>, + params: HashMap, _data: web::Query, - db_connection: &mut DbConnection, - _nats_cli: Arc, - ) -> Result { - let (group, version, namespace, plural) = info.into_inner(); - let ver = format!("{}/{}", group, version); - - let metadata_exists = check_metadata(db_connection, &plural, &ver, true) - .await - .map_err(ErrorInternalServerError)?; - - if !metadata_exists { - return Ok(HttpResponse::NotFound().json(json!({ "error": "该 plural 不存在,请检查 plural 版本以及是否需要 namespace" }))); - } - - let data_list = get_all_data_from_kine(db_connection, &plural, &ver, Some(&namespace)) - .await - .map_err(ErrorInternalServerError)?; - - // 将所有字符串解析为 JSON 格式并收集到一个数组中 - let json_array: Vec = data_list - .into_iter() - .filter_map(|data_str| serde_json::from_str(&data_str).ok()) // 解析成功的数据 - .collect(); - - Ok(HttpResponse::Ok().json(json_array)) - } - - async fn watchall_api_without_namespace( - &self, - info: Path<(String, String)>, - _data: Query, - event_manager: EventManager + event_manager: EventManager, ) -> Result { - let (version, plural) = info.into_inner(); - let resource_type = format!("{}/{}", version, plural); - - let channel = event_manager.get_channel(&resource_type); - let mut rx = channel.subscribe(); - - let stream = async_stream::stream! { - loop { - match rx.recv().await { - Ok(event) => { - let json_event = serde_json::to_string(&event).unwrap(); - println!("{}", json_event); - yield Ok::(Bytes::from(json_event)); - } - Err(_) => { - yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); - } - } - } + // 解析路径参数 + let group = params.get("group").map(String::as_str); + let version = params.get("version").unwrap(); // 必须存在 + let namespace = params.get("namespace").map(String::as_str); + let plural = params.get("plural").unwrap(); // 必须存在 + + // 生成 resource_type + let ver = if let Some(group) = group { + format!("{}/{}", group, version) + } else { + version.to_string() }; - - Ok(HttpResponse::Ok() - .content_type("text/event-stream") - .streaming(stream)) - } - - async fn watchall_api_with_namespace( - &self, - info: Path<(String, String, String)>, - _data: Query, - event_manager: EventManager - ) -> Result { - let (version, namespace, plural) = info.into_inner(); - let resource_type = format!("{}/{}/{}", version, namespace, plural); - - let channel = event_manager.get_channel(&resource_type); - let mut rx = channel.subscribe(); - - let stream = async_stream::stream! { - loop { - match rx.recv().await { - Ok(event) => { - let json_event = serde_json::to_string(&event).unwrap(); - yield Ok::(Bytes::from(json_event)); - } - Err(_) => { - yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); - } - } - } + let resource_type = if let Some(namespace) = namespace { + format!("{}/{}/{}", ver, namespace, plural) + } else { + format!("{}/{}", ver, plural) }; - Ok(HttpResponse::Ok() - .content_type("text/event-stream") - .streaming(stream)) - } - - async fn watchall_apis_without_namespace( - &self, - info: Path<(String, String, String)>, - _data: Query, - event_manager: EventManager - ) -> Result { - let (group, version, plural) = info.into_inner(); - let ver = format!("{}/{}", group, version); - let resource_type = format!("{}/{}", ver, plural); - + // 监听事件 let channel = event_manager.get_channel(&resource_type); let mut rx = channel.subscribe(); let stream = async_stream::stream! { - loop { - match rx.recv().await { - Ok(event) => { - let json_event = serde_json::to_string(&event).unwrap(); - yield Ok::(Bytes::from(json_event)); - } - Err(_) => { - yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); - } + loop { + match rx.recv().await { + Ok(event) => { + let json_event = serde_json::to_string(&event).unwrap(); + yield Ok::(Bytes::from(json_event)); } - } - }; - - Ok(HttpResponse::Ok() - .content_type("text/event-stream") - .streaming(stream)) - } - - async fn watchall_apis_with_namespace( - &self, - info: Path<(String, String, String, String)>, - _data: Query, - event_manager: EventManager - ) -> Result { - let (group, version, namespace, plural) = info.into_inner(); - let ver = format!("{}/{}", group, version); - - let resource_type = format!("{}/{}/{}", ver, namespace, plural); - - let channel = event_manager.get_channel(&resource_type); - let mut rx = channel.subscribe(); - - let stream = async_stream::stream! { - loop { - match rx.recv().await { - Ok(event) => { - let json_event = serde_json::to_string(&event).unwrap(); - yield Ok::(Bytes::from(json_event)); - } - Err(_) => { - yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); - } + Err(_) => { + yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); } } - }; + } + }; + // 返回 SSE 流 Ok(HttpResponse::Ok() .content_type("text/event-stream") .streaming(stream)) } - async fn watchone_api_without_namespace(&self, info: Path<(String, String, String)>, _data: Query, event_manager: EventManager) -> Result { - let (version, plural, name) = info.into_inner(); - let resource_type = format!("{}/{}/{}", version, plural, name); - - let channel = event_manager.get_channel(&resource_type); - let mut rx = channel.subscribe(); - - let stream = async_stream::stream! { - loop { - match rx.recv().await { - Ok(event) => { - let json_event = serde_json::to_string(&event).unwrap(); - yield Ok::(Bytes::from(json_event)); - } - Err(_) => { - yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); - } - } - } + async fn watchone_resource( + &self, + params: HashMap, + _data: web::Query, + event_manager: EventManager, + ) -> Result { + // 解析路径参数 + let group = params.get("group").map(String::as_str); + let version = params.get("version").unwrap(); // 必须存在 + let namespace = params.get("namespace").map(String::as_str); + let plural = params.get("plural").unwrap(); // 必须存在 + let name = params.get("name").unwrap(); // 必须存在 + + // 生成 resource_type + let ver = if let Some(group) = group { + format!("{}/{}", group, version) + } else { + version.to_string() }; - - Ok(HttpResponse::Ok() - .content_type("text/event-stream") - .streaming(stream)) - } - - async fn watchone_api_with_namespace(&self, info: Path<(String, String, String, String)>, _data: Query, event_manager: EventManager) -> Result { - let (version, namespace, plural, name) = info.into_inner(); - let resource_type = format!("{}/{}/{}/{}", version, namespace, plural, name); - - let channel = event_manager.get_channel(&resource_type); - let mut rx = channel.subscribe(); - - let stream = async_stream::stream! { - loop { - match rx.recv().await { - Ok(event) => { - let json_event = serde_json::to_string(&event).unwrap(); - yield Ok::(Bytes::from(json_event)); - } - Err(_) => { - yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); - } - } - } + let resource_type = if let Some(namespace) = namespace { + format!("{}/{}/{}/{}", ver, namespace, plural, name) + } else { + format!("{}/{}/{}", ver, plural, name) }; - Ok(HttpResponse::Ok() - .content_type("text/event-stream") - .streaming(stream)) - } - - async fn watchone_apis_without_namespace(&self, info: Path<(String, String, String, String)>, _data: Query, event_manager: EventManager) -> Result { - let (group, version, plural, name) = info.into_inner(); - let ver = format!("{}/{}", group, version); - - let resource_type = format!("{}/{}/{}", ver, plural, name); - + // 订阅事件 let channel = event_manager.get_channel(&resource_type); let mut rx = channel.subscribe(); let stream = async_stream::stream! { - loop { - match rx.recv().await { - Ok(event) => { - let json_event = serde_json::to_string(&event).unwrap(); - yield Ok::(Bytes::from(json_event)); - } - Err(_) => { - yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); - } + loop { + match rx.recv().await { + Ok(event) => { + let json_event = serde_json::to_string(&event).unwrap(); + yield Ok::(Bytes::from(json_event)); } - } - }; - - Ok(HttpResponse::Ok() - .content_type("text/event-stream") - .streaming(stream)) - } - - async fn watchone_apis_with_namespace(&self, info: Path<(String, String, String, String, String)>, _data: Query, event_manager: EventManager) -> Result { - let (group, version, namespace, plural, name) = info.into_inner(); - let ver = format!("{}/{}", group, version); - - let resource_type = format!("{}/{}/{}/{}", ver, namespace, plural, name); - - let channel = event_manager.get_channel(&resource_type); - let mut rx = channel.subscribe(); - - let stream = async_stream::stream! { - loop { - match rx.recv().await { - Ok(event) => { - let json_event = serde_json::to_string(&event).unwrap(); - yield Ok::(Bytes::from(json_event)); - } - Err(_) => { - yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); - } + Err(_) => { + yield Err(actix_web::error::ErrorInternalServerError("Error occurred").into()); } } - }; + } + }; + // 返回 SSE 流 Ok(HttpResponse::Ok() .content_type("text/event-stream") .streaming(stream)) } - fn default(&self) -> DefaultHandler { DefaultHandler {} } -- Gitee From 1b2c119dc3932455bbff5294ccce1053acf605e1 Mon Sep 17 00:00:00 2001 From: Yuichi <913637919@qq.com> Date: Fri, 13 Dec 2024 09:11:13 +0000 Subject: [PATCH 6/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0copyright?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database.sqlite | Bin 0 -> 61440 bytes src/cores/apiserver.rs | 2 -- src/cores/checker.rs | 4 ++-- src/cores/db.rs | 7 +++++++ src/cores/handlers.rs | 7 +++++++ src/cores/mod.rs | 5 +++-- src/lib.rs | 7 +++++++ src/main.rs | 7 +++++++ tests/route_tests.rs | 7 +++++++ 9 files changed, 40 insertions(+), 6 deletions(-) create mode 100644 database.sqlite diff --git a/database.sqlite b/database.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..a9f56ec84f00630aca3f332896a584e2c82972a7 GIT binary patch literal 61440 zcmeI)&2QRf9KdlqB)p}Brdot{h~%M3DN@p4FmK6aG^JVEytI^bt)>b&;H7kc8933V zu7}Y658H9l{)YVzQ?I@4u;Y$9?NIHs$57G`;$)qaNpZdw1p9d&+t2fP9vkov=fVB; znq??oneB#dDM{(7B+JrgiXuspM?AygIhcH6W_++9Ugab6PO~2A#us-&;kVM1_q8%uYG?zZGdzH5I_I{1Wp&|E`)rMxjFgO$CkcR zH@>bljdI&))oT?!F|;tXQY_?41*Md~xn59)vXtvVMNA^CsyxgWS3b`duP0LJ*nBU! zsW%LVr+^U1Gh)#Bx*K$i|#3iNeOWop0*mV?bHov&`Odd5EQ~ z4lRbe3zJUNLnYNKj@0UHPpQp@I_{3#0F zhi-2iPIeqk=a@K z<=vr?J197QBBgtV=i<0GvNC(-@H~W@ICv!cBKf@BUG906%hj6EG3w<`<*Cuo%MI~1 ztXnm+*>TPp8!~k+_fA=SPS`iv9kI>B13MLuDM#OlH@8a4=Jxt}|2!a8Xzw-4qI_v> zqwt`V-?*m?N+`DqxAWWUC1qv1SS)Oo%7;aQu?J(`$Zp=r!nvMh)j7}pZtu50Pld!& z4*w>J2NMDaAbfBAKz57SzOonpEPs zrFeEJnGT9}$>BdG@nAv#0R#|0009ILKmY**5I_I{R|rgdVu9gH0(Si06Z%97e>8Pl zyf7hv00IagfB*srAb(Xo-XdI&!ghDj)(oL8XoR=d{RjlP(Vc3MWo zE@~N#R$bihWzV0si6g62%%-J_JA>L{4_}JfwRi1Z>Fu`uo!C>Yq3<5q)yS@&yb#j~ z;o%#Zq^6})@s#SEspirLcgOq@|8#eHb+u_$jn0VAzWQEfQjBH_KJY8frA9@J#f<#(;IWotR8EN-fWsy z|6T)8owNJ;SjSb&Mnmt7pbzLfht5f8T2@OrW@a*K+>ZZy!f&MToA4juzb@}#PRkKM z009ILKmY**5I_I{1Q57j0@o(Q^(y)3#VSu|LR_7Z-(8v+n-m461N}=I#Q!f??^0a^ z5I_I{1Q0*~0R#{@KLO(Znz(N~>l8&6nTf1=k>dX;P0hv~V&Ym#OKKU{#Q&4&bTXNB zXsgJ~#B;>|&+pX1mJvVz0R#|0009ILKmY**5I9Ex;{WG}%cc=P009ILKmY**5J14i z0>uBdOeUo|MUh2jI+MFl@&BBfaQrnxDwoJ;?uq}4|N5WPoZ|o4gqBl@|GW6yMbi*K z009ILKmY**5I_I{1l%m}!SR3Ts+&1z8v+O*fB*srAb9 z0R#|0009ILKmY**5OBAE{r~^@{_pNSrgaD)fB*srAb( diff --git a/src/cores/checker.rs b/src/cores/checker.rs index ab13881..e47d071 100644 --- a/src/cores/checker.rs +++ b/src/cores/checker.rs @@ -1,7 +1,7 @@ /** * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences - * author: wuheng@iscas.ac.cn - * since: 0.1.3 + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 * **/ diff --git a/src/cores/db.rs b/src/cores/db.rs index 56016fb..881f079 100644 --- a/src/cores/db.rs +++ b/src/cores/db.rs @@ -1,3 +1,10 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ + use std::collections::HashMap; use chrono::Utc; use diesel::pg::PgConnection; diff --git a/src/cores/handlers.rs b/src/cores/handlers.rs index 01f467c..ff06fe5 100644 --- a/src/cores/handlers.rs +++ b/src/cores/handlers.rs @@ -1,3 +1,10 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ + use std::collections::HashMap; use std::fmt::Debug; use std::sync::{Arc, LazyLock, Mutex}; diff --git a/src/cores/mod.rs b/src/cores/mod.rs index a34795b..223f690 100644 --- a/src/cores/mod.rs +++ b/src/cores/mod.rs @@ -1,8 +1,9 @@ /** * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences - * author: wuheng@iscas.ac.cn + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn * since: 0.1.0 - **/ + * +**/ pub mod apiserver; pub mod config; diff --git a/src/lib.rs b/src/lib.rs index 559854f..ae9c790 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,3 +1,10 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ + pub mod cores; pub mod schema; diff --git a/src/main.rs b/src/main.rs index d20e3b9..07fc105 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,10 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ + use std::env; use dotenv::dotenv; use fleet_apiserver::start_server; diff --git a/tests/route_tests.rs b/tests/route_tests.rs index 824b463..befa906 100644 --- a/tests/route_tests.rs +++ b/tests/route_tests.rs @@ -1,3 +1,10 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ + use actix_web::error::ErrorInternalServerError; use feventbus::impls::nats::nats::NatsCli; use serde_json::Value; -- Gitee From a6fecb8a22112aa1cb358822f818092defce6858 Mon Sep 17 00:00:00 2001 From: Yuichi <913637919@qq.com> Date: Sat, 14 Dec 2024 12:48:30 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=83=A8=E5=88=86?= =?UTF-8?q?=E4=BB=A3=E7=A0=81=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- database.sqlite | Bin 61440 -> 0 bytes src/cores/apiserver.rs | 2 +- src/cores/handlers.rs | 873 +--------------------------------------- src/cores/mod.rs | 1 - src/db/check_exist.rs | 193 +++++++++ src/{cores => db}/db.rs | 1 - src/db/delete.rs | 92 +++++ src/db/get.rs | 313 ++++++++++++++ src/db/insert.rs | 197 +++++++++ src/db/mod.rs | 12 + src/db/update.rs | 100 +++++ src/lib.rs | 3 +- 12 files changed, 916 insertions(+), 871 deletions(-) delete mode 100644 database.sqlite create mode 100644 src/db/check_exist.rs rename src/{cores => db}/db.rs (99%) create mode 100644 src/db/delete.rs create mode 100644 src/db/get.rs create mode 100644 src/db/insert.rs create mode 100644 src/db/mod.rs create mode 100644 src/db/update.rs diff --git a/database.sqlite b/database.sqlite deleted file mode 100644 index a9f56ec84f00630aca3f332896a584e2c82972a7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 61440 zcmeI)&2QRf9KdlqB)p}Brdot{h~%M3DN@p4FmK6aG^JVEytI^bt)>b&;H7kc8933V zu7}Y658H9l{)YVzQ?I@4u;Y$9?NIHs$57G`;$)qaNpZdw1p9d&+t2fP9vkov=fVB; znq??oneB#dDM{(7B+JrgiXuspM?AygIhcH6W_++9Ugab6PO~2A#us-&;kVM1_q8%uYG?zZGdzH5I_I{1Wp&|E`)rMxjFgO$CkcR zH@>bljdI&))oT?!F|;tXQY_?41*Md~xn59)vXtvVMNA^CsyxgWS3b`duP0LJ*nBU! zsW%LVr+^U1Gh)#Bx*K$i|#3iNeOWop0*mV?bHov&`Odd5EQ~ z4lRbe3zJUNLnYNKj@0UHPpQp@I_{3#0F zhi-2iPIeqk=a@K z<=vr?J197QBBgtV=i<0GvNC(-@H~W@ICv!cBKf@BUG906%hj6EG3w<`<*Cuo%MI~1 ztXnm+*>TPp8!~k+_fA=SPS`iv9kI>B13MLuDM#OlH@8a4=Jxt}|2!a8Xzw-4qI_v> zqwt`V-?*m?N+`DqxAWWUC1qv1SS)Oo%7;aQu?J(`$Zp=r!nvMh)j7}pZtu50Pld!& z4*w>J2NMDaAbfBAKz57SzOonpEPs zrFeEJnGT9}$>BdG@nAv#0R#|0009ILKmY**5I_I{R|rgdVu9gH0(Si06Z%97e>8Pl zyf7hv00IagfB*srAb(Xo-XdI&!ghDj)(oL8XoR=d{RjlP(Vc3MWo zE@~N#R$bihWzV0si6g62%%-J_JA>L{4_}JfwRi1Z>Fu`uo!C>Yq3<5q)yS@&yb#j~ z;o%#Zq^6})@s#SEspirLcgOq@|8#eHb+u_$jn0VAzWQEfQjBH_KJY8frA9@J#f<#(;IWotR8EN-fWsy z|6T)8owNJ;SjSb&Mnmt7pbzLfht5f8T2@OrW@a*K+>ZZy!f&MToA4juzb@}#PRkKM z009ILKmY**5I_I{1Q57j0@o(Q^(y)3#VSu|LR_7Z-(8v+n-m461N}=I#Q!f??^0a^ z5I_I{1Q0*~0R#{@KLO(Znz(N~>l8&6nTf1=k>dX;P0hv~V&Ym#OKKU{#Q&4&bTXNB zXsgJ~#B;>|&+pX1mJvVz0R#|0009ILKmY**5I9Ex;{WG}%cc=P009ILKmY**5J14i z0>uBdOeUo|MUh2jI+MFl@&BBfaQrnxDwoJ;?uq}4|N5WPoZ|o4gqBl@|GW6yMbi*K z009ILKmY**5I_I{1l%m}!SR3Ts+&1z8v+O*fB*srAb9 z0R#|0009ILKmY**5OBAE{r~^@{_pNSrgaD)fB*srAb HashMap { diff --git a/src/cores/handlers.rs b/src/cores/handlers.rs index ff06fe5..e46f02e 100644 --- a/src/cores/handlers.rs +++ b/src/cores/handlers.rs @@ -11,12 +11,9 @@ use std::sync::{Arc, LazyLock, Mutex}; use std::{time, vec::Vec}; use async_trait::async_trait; use actix_web::{HttpResponse, Result, web}; -use chrono::Utc; -use diesel::{QueryResult, RunQueryDsl, QueryDsl, ExpressionMethods, OptionalExtension, QueryableByName, PgConnection, sql_query, SqliteConnection, Connection}; use serde_json::{json}; use serde_json::Value; -use crate::cores::db::DbConnection; -use diesel::sql_types::{Text}; +use crate::db::db::DbConnection; use actix_web::error::ErrorInternalServerError; use feventbus::impls::nats::nats::NatsCli; use feventbus::message::Message; @@ -25,7 +22,11 @@ use feventbus::traits::producer::Producer; use serde::{Deserialize, Serialize}; use tokio::sync::broadcast; use actix_web::web::Bytes; - +use crate::db::check_exist::{check_metadata, check_kine}; +use crate::db::get::{get_all_data_from_kine, get_data_from_kine}; +use crate::db::insert::{insert_kine, insert_metadata}; +use crate::db::delete::delete_from_kine; +use crate::db::update::update_data_in_kine; // 定义全局哈希表来获取model名 static GLOBAL_HASHMAP: LazyLock>> = LazyLock::new(|| { let mut map = HashMap::new(); @@ -120,867 +121,6 @@ impl DefaultHandler { } } - -// 查询 metadata 表中的 plural 是否存在,并检查 namespace 要求是否满足 -async fn check_metadata( - conn: &mut DbConnection, - plural: &str, - version: &str, - requires_namespace: bool, -) -> QueryResult { - use diesel::dsl::count_star; - use crate::schema::metadata::dsl as metadata_dsl; - use crate::schema::metadata_replica1::dsl as replica1_dsl; - use crate::schema::metadata_replica2::dsl as replica2_dsl; - - let count; - let count_replica1; - let count_replica2; - match conn { - DbConnection::Pg(pg_conn) => { - count = metadata_dsl::metadata - .filter(metadata_dsl::name.eq(plural)) - .filter(metadata_dsl::apigroup.eq(version)) - .filter(metadata_dsl::namespace.eq(requires_namespace)) - .select(count_star()) - .first::(pg_conn)?; - - count_replica1 = replica1_dsl::metadata_replica1 - .filter(replica1_dsl::name.eq(plural)) - .filter(replica1_dsl::apigroup.eq(version)) - .filter(replica1_dsl::namespace.eq(requires_namespace)) - .select(count_star()) - .first::(pg_conn)?; - - count_replica2 = replica2_dsl::metadata_replica2 - .filter(replica2_dsl::name.eq(plural)) - .filter(replica2_dsl::apigroup.eq(version)) - .filter(replica2_dsl::namespace.eq(requires_namespace)) - .select(count_star()) - .first::(pg_conn)?; - }, - DbConnection::Sqlite(sqlite_conn) => { - count = metadata_dsl::metadata - .filter(metadata_dsl::name.eq(plural)) - .filter(metadata_dsl::apigroup.eq(version)) - .filter(metadata_dsl::namespace.eq(requires_namespace)) - .select(count_star()) - .first::(sqlite_conn)?; - - count_replica1 = replica1_dsl::metadata_replica1 - .filter(replica1_dsl::name.eq(plural)) - .filter(replica1_dsl::apigroup.eq(version)) - .filter(replica1_dsl::namespace.eq(requires_namespace)) - .select(count_star()) - .first::(sqlite_conn)?; - - count_replica2 = replica2_dsl::metadata_replica2 - .filter(replica2_dsl::name.eq(plural)) - .filter(replica2_dsl::apigroup.eq(version)) - .filter(replica2_dsl::namespace.eq(requires_namespace)) - .select(count_star()) - .first::(sqlite_conn)?; - } - } - let positive_count = [count, count_replica1, count_replica2].iter().filter(|&&x| x > 0).count(); - Ok(positive_count >= 2) -} - - - -// 查询 kine 表中指定的数据是否存在 -async fn check_kine( - conn: &mut DbConnection, - item_kind: &str, - item_name: &str, - item_version: &str, - item_namespace: Option<&str>, -) -> QueryResult { - use diesel::dsl::count_star; - use crate::schema::kine::dsl as kine_dsl; - use crate::schema::kine_replica1::dsl as replica1_dsl; - use crate::schema::kine_replica2::dsl as replica2_dsl; - - let count; - let count_replica1; - let count_replica2; - match conn { - DbConnection::Pg(pg_conn) => { - if let Some(_) = item_namespace { - count = kine_dsl::kine - .filter(kine_dsl::kind.eq(item_kind)) - .filter(kine_dsl::name.eq(item_name)) - .filter(kine_dsl::apigroup.eq(item_version)) - .filter(kine_dsl::namespace.eq(item_namespace)) - .select(count_star()) - .first::(pg_conn)?; - - count_replica1 = replica1_dsl::kine_replica1 - .filter(replica1_dsl::kind.eq(item_kind)) - .filter(replica1_dsl::name.eq(item_name)) - .filter(replica1_dsl::apigroup.eq(item_version)) - .filter(replica1_dsl::namespace.eq(item_namespace)) - .select(count_star()) - .first::(pg_conn)?; - - count_replica2 = replica2_dsl::kine_replica2 - .filter(replica2_dsl::kind.eq(item_kind)) - .filter(replica2_dsl::name.eq(item_name)) - .filter(replica2_dsl::apigroup.eq(item_version)) - .filter(replica2_dsl::namespace.eq(item_namespace)) - .select(count_star()) - .first::(pg_conn)?; - } else { - count = kine_dsl::kine - .filter(kine_dsl::kind.eq(item_kind)) - .filter(kine_dsl::name.eq(item_name)) - .filter(kine_dsl::apigroup.eq(item_version)) - .select(count_star()) - .first::(pg_conn)?; - - count_replica1 = replica1_dsl::kine_replica1 - .filter(replica1_dsl::kind.eq(item_kind)) - .filter(replica1_dsl::name.eq(item_name)) - .filter(replica1_dsl::apigroup.eq(item_version)) - .select(count_star()) - .first::(pg_conn)?; - - count_replica2 = replica2_dsl::kine_replica2 - .filter(replica2_dsl::kind.eq(item_kind)) - .filter(replica2_dsl::name.eq(item_name)) - .filter(replica2_dsl::apigroup.eq(item_version)) - .select(count_star()) - .first::(pg_conn)?; - } - }, - DbConnection::Sqlite(sqlite_conn) => { - if let Some(_) = item_namespace { - count = kine_dsl::kine - .filter(kine_dsl::kind.eq(item_kind)) - .filter(kine_dsl::name.eq(item_name)) - .filter(kine_dsl::apigroup.eq(item_version)) - .filter(kine_dsl::namespace.eq(item_namespace)) - .select(count_star()) - .first::(sqlite_conn)?; - - count_replica1 = replica1_dsl::kine_replica1 - .filter(replica1_dsl::kind.eq(item_kind)) - .filter(replica1_dsl::name.eq(item_name)) - .filter(replica1_dsl::apigroup.eq(item_version)) - .filter(replica1_dsl::namespace.eq(item_namespace)) - .select(count_star()) - .first::(sqlite_conn)?; - - count_replica2 = replica2_dsl::kine_replica2 - .filter(replica2_dsl::kind.eq(item_kind)) - .filter(replica2_dsl::name.eq(item_name)) - .filter(replica2_dsl::apigroup.eq(item_version)) - .filter(replica2_dsl::namespace.eq(item_namespace)) - .select(count_star()) - .first::(sqlite_conn)?; - } else { - count = kine_dsl::kine - .filter(kine_dsl::kind.eq(item_kind)) - .filter(kine_dsl::name.eq(item_name)) - .filter(kine_dsl::apigroup.eq(item_version)) - .select(count_star()) - .first::(sqlite_conn)?; - - count_replica1 = replica1_dsl::kine_replica1 - .filter(replica1_dsl::kind.eq(item_kind)) - .filter(replica1_dsl::name.eq(item_name)) - .filter(replica1_dsl::apigroup.eq(item_version)) - .select(count_star()) - .first::(sqlite_conn)?; - - count_replica2 = replica2_dsl::kine_replica2 - .filter(replica2_dsl::kind.eq(item_kind)) - .filter(replica2_dsl::name.eq(item_name)) - .filter(replica2_dsl::apigroup.eq(item_version)) - .select(count_star()) - .first::(sqlite_conn)?; - } - } - } - let positive_count = [count, count_replica1, count_replica2].iter().filter(|&&x| x > 0).count(); - Ok(positive_count >= 2) -} - -fn insert_metadata_in_transaction_pg( - transaction: &mut PgConnection, - plural: &str, - version: &str, - namespace_required: bool, - json_data: &Value, -) -> QueryResult<()> { - use diesel::sql_types::{Bool}; - - // 表名列表 - let table_array: [&str; 3] = ["metadata", "metadata_replica1", "metadata_replica2"]; - - for table_name in table_array { - // 使用参数绑定构建插入查询 - let insert_metadata_query = format!( - "INSERT INTO {} (name, namespace, apigroup, data, created_time, updated_time) - VALUES ($1, $2, $3, $4, $5, $6) - ON CONFLICT DO NOTHING;", - table_name - ); - - // 执行插入操作 - sql_query(insert_metadata_query) - .bind::(plural) // 名称 - .bind::(namespace_required) // 是否需要命名空间 - .bind::(version) // 版本 - .bind::(json_data.to_string()) // JSON 数据 - .bind::(Utc::now().naive_utc().to_string()) // 创建时间 - .bind::(Utc::now().naive_utc().to_string()) // 更新时间 - .execute(transaction)?; - } - - Ok(()) -} - - -fn insert_metadata_in_transaction_sqlite( - transaction: &mut SqliteConnection, - plural: &str, - version: &str, - namespace_required: bool, - json_data: &Value, -) -> QueryResult<()> { - use diesel::sql_types::{Bool}; - - // 表名列表 - let table_array: [&str; 3] = ["metadata", "metadata_replica1", "metadata_replica2"]; - - for table_name in table_array { - // 使用参数绑定构建插入查询 - let insert_metadata_query = format!( - "INSERT OR IGNORE INTO {} (name, namespace, apigroup, data, created_time, updated_time) - VALUES (?, ?, ?, ?, ?, ?);", - table_name - ); - - // 执行插入操作 - sql_query(insert_metadata_query) - .bind::(plural) // 名称 - .bind::(namespace_required) // 是否需要命名空间 - .bind::(version) // 版本 - .bind::(json_data.to_string()) // JSON 数据 - .bind::(Utc::now().naive_utc().to_string()) // 创建时间 - .bind::(Utc::now().naive_utc().to_string()) // 更新时间 - .execute(transaction)?; - } - - Ok(()) -} - - -async fn insert_metadata( - conn: &mut DbConnection, - plural: &str, - version: &str, - namespace_required: bool, - json_data: &Value -) -> QueryResult<()> { - match conn { - DbConnection::Pg(pg_conn) => { - pg_conn.transaction(|transaction| { - insert_metadata_in_transaction_pg(transaction, plural, version, namespace_required, json_data) - }) - } - DbConnection::Sqlite(sqlite_conn) => { - sqlite_conn.transaction(|transaction| { - insert_metadata_in_transaction_sqlite(transaction, plural, version, namespace_required, json_data) - }) - } - }.expect("unknow conn in insert_metadata"); - Ok(()) -} - -fn insert_kine_in_transaction_pg( - transaction: &mut PgConnection, - item_kind: &str, - item_name: &str, - json_data: &Value, - version: &str, - namespace: Option<&str>, -) -> QueryResult<()> { - - // 表列表 - let table_array: [&str; 3] = ["kine", "kine_replica1", "kine_replica2"]; - - for table_name in table_array { - // 使用参数绑定构建插入查询 - let insert_metadata_query = format!( - "INSERT INTO {} (kind, name, namespace, apigroup, data, created_time, updated_time) - VALUES ($1, $2, $3, $4, $5, $6, $7) - ON CONFLICT DO NOTHING;", - table_name - ); - - // 执行插入操作 - sql_query(insert_metadata_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace.unwrap_or("")) // 空字符串作为默认 namespace - .bind::(version) - .bind::(json_data.to_string()) // 将 JSON 数据转换为字符串 - .bind::(Utc::now().naive_utc().to_string()) // 创建时间 - .bind::(Utc::now().naive_utc().to_string()) // 更新时间 - .execute(transaction)?; - } - - Ok(()) -} - - -fn insert_kine_in_transaction_sqlite( - transaction: &mut SqliteConnection, - item_kind: &str, - item_name: &str, - json_data: &Value, - version: &str, - namespace: Option<&str>, -) -> QueryResult<()> { - - let table_array: [&str; 3] = ["kine", "kine_replica1", "kine_replica2"]; - - for table_name in table_array { - let insert_metadata_query = format!( - "INSERT OR IGNORE INTO {} (kind, name, namespace, apigroup, data, created_time, updated_time) - VALUES (?, ?, ?, ?, ?, ?, ?);", - table_name - ); - - sql_query(insert_metadata_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace.unwrap_or("")) // 使用 Nullable 处理空值 - .bind::(version) - .bind::(json_data.to_string()) - .bind::(Utc::now().naive_utc().to_string()) - .bind::(Utc::now().naive_utc().to_string()) - .execute(transaction)?; - } - Ok(()) -} - - -// 在 kine 表中插入新记录 -async fn insert_kine( - conn: &mut DbConnection, - item_kind: &str, - item_name: &str, - json_data: &Value, - version: &str, - namespace: Option<&str>, -) -> QueryResult<()> { - match conn { - DbConnection::Pg(pg_conn) => { - pg_conn.transaction(|transaction| { - insert_kine_in_transaction_pg(transaction, item_kind, item_name, json_data, version, namespace) - }) - } - DbConnection::Sqlite(sqlite_conn) => { - sqlite_conn.transaction(|transaction| { - insert_kine_in_transaction_sqlite(transaction, item_kind, item_name, json_data, version, namespace) - }) - } - }.expect("unknow conn in insert_kine"); - Ok(()) -} - -// 从 kine 表中删除特定 name 的记录 -async fn delete_from_kine( - conn: &mut DbConnection, - item_kind: &str, - item_name: &str, - item_version: &str, - item_namespace: Option<&str>, -) -> QueryResult { - use diesel::sql_types::Text; - - // 表名列表 - let tables = ["kine", "kine_replica1", "kine_replica2"]; - - // 遍历每个表,执行删除操作 - let mut total_rows_affected = 0; - - for &table in &tables { - let delete_query = if let Some(_) = item_namespace { - match conn { - DbConnection::Pg(_) => format!( - "DELETE FROM {} WHERE kind = $1 AND name = $2 AND namespace = $3 AND apigroup = $4", - table - ), - DbConnection::Sqlite(_) => format!( - "DELETE FROM {} WHERE kind = ? AND name = ? AND namespace = ? AND apigroup = ?", - table - ), - } - } else { - match conn { - DbConnection::Pg(_) => format!( - "DELETE FROM {} WHERE kind = $1 AND name = $2 AND apigroup = $3", - table - ), - DbConnection::Sqlite(_) => format!( - "DELETE FROM {} WHERE kind = ? AND name = ? AND apigroup = ?", - table - ), - } - }; - - // 执行删除 - let rows_affected = match conn { - DbConnection::Pg(pg_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(delete_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace) - .bind::(item_version) - .execute(pg_conn)? - } else { - diesel::sql_query(delete_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(item_version) - .execute(pg_conn)? - } - } - DbConnection::Sqlite(sqlite_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(delete_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace) - .bind::(item_version) - .execute(sqlite_conn)? - } else { - diesel::sql_query(delete_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(item_version) - .execute(sqlite_conn)? - } - } - }; - - total_rows_affected += rows_affected; - } - - // 如果至少有两个表进行了删除,则返回 true - Ok(total_rows_affected > 1) -} - - - -async fn update_data_in_kine( - conn: &mut DbConnection, - item_kind: &str, - item_name: &str, - item_version: &str, - item_namespace: Option<&str>, - json_data: &Value, -) -> QueryResult { - use diesel::sql_types::Text; - use chrono::Utc; - - // 需要更新的表列表 - let tables = ["kine", "kine_replica1", "kine_replica2"]; - let mut total_rows_affected = 0; - - for &table in &tables { - let update_query = if let Some(_) = item_namespace { - match conn { - DbConnection::Pg(_) => format!( - "UPDATE {} SET data = $1, updated_time = $2 WHERE kind = $3 AND name = $4 AND namespace = $5 AND apigroup = $6", - table - ), - DbConnection::Sqlite(_) => format!( - "UPDATE {} SET data = ?, updated_time = ? WHERE kind = ? AND name = ? AND namespace = ? AND apigroup = ?", - table - ), - } - } else { - match conn { - DbConnection::Pg(_) => format!( - "UPDATE {} SET data = $1, updated_time = $2 WHERE kind = $3 AND name = $4 AND apigroup = $5", - table - ), - DbConnection::Sqlite(_) => format!( - "UPDATE {} SET data = ?, updated_time = ? WHERE kind = ? AND name = ? AND apigroup = ?", - table - ), - } - }; - - // 执行更新操作 - let rows_affected = match conn { - DbConnection::Pg(pg_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(update_query) - .bind::(json_data.to_string()) - .bind::(Utc::now().naive_utc().to_string()) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace) - .bind::(item_version) - .execute(pg_conn)? - } else { - diesel::sql_query(update_query) - .bind::(json_data.to_string()) - .bind::(Utc::now().naive_utc().to_string()) - .bind::(item_kind) - .bind::(item_name) - .bind::(item_version) - .execute(pg_conn)? - } - } - DbConnection::Sqlite(sqlite_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(update_query) - .bind::(json_data.to_string()) - .bind::(Utc::now().naive_utc().to_string()) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace) - .bind::(item_version) - .execute(sqlite_conn)? - } else { - diesel::sql_query(update_query) - .bind::(json_data.to_string()) - .bind::(Utc::now().naive_utc().to_string()) - .bind::(item_kind) - .bind::(item_name) - .bind::(item_version) - .execute(sqlite_conn)? - } - } - }; - - total_rows_affected += rows_affected; - } - - // 如果至少有两个表更新成功,则返回 true - Ok(total_rows_affected > 1) -} - - -// 辅助查询函数,用于获取数据的 `data` 字段 -#[derive(QueryableByName)] -struct DataResult { - #[diesel(sql_type = Text)] - data: String, -} - - - -async fn get_data_from_kine( - conn: &mut DbConnection, - item_kind: &str, - item_name: &str, - item_version: &str, - item_namespace: Option<&str>, -) -> QueryResult> { - use diesel::sql_types::Text; - use std::collections::HashMap; - - // 表名列表 - let tables = ["kine", "kine_replica1", "kine_replica2"]; - - // 存储每个表的查询结果 - let mut results = HashMap::new(); - - for &table in &tables { - let select_query = if let Some(_) = item_namespace { - match conn { - DbConnection::Pg(_) => format!( - "SELECT data FROM {} WHERE kind = $1 AND name = $2 AND namespace = $3 AND apigroup = $4", - table - ), - DbConnection::Sqlite(_) => format!( - "SELECT data FROM {} WHERE kind = ? AND name = ? AND namespace = ? AND apigroup = ?", - table - ), - } - } else { - match conn { - DbConnection::Pg(_) => format!( - "SELECT data FROM {} WHERE kind = $1 AND name = $2 AND apigroup = $3", - table - ), - DbConnection::Sqlite(_) => format!( - "SELECT data FROM {} WHERE kind = ? AND name = ? AND apigroup = ?", - table - ), - } - }; - - let data_result = match conn { - DbConnection::Pg(pg_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(select_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace) - .bind::(item_version) - .get_result::(pg_conn) - .optional()? - .map(|res| res.data) - } else { - diesel::sql_query(select_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(item_version) - .get_result::(pg_conn) - .optional()? - .map(|res| res.data) - } - } - DbConnection::Sqlite(sqlite_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(select_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace) - .bind::(item_version) - .get_result::(sqlite_conn) - .optional()? - .map(|res| res.data) - } else { - diesel::sql_query(select_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(item_version) - .get_result::(sqlite_conn) - .optional()? - .map(|res| res.data) - } - } - }; - - if let Some(data) = data_result { - *results.entry(data).or_insert(0) += 1; - } - } - - // 按少数服从多数规则返回数据 - if results.len() == 1 { - // 如果三个表的结果一致,直接返回任意结果 - Ok(results.into_iter().next().map(|(data, _)| data)) - } else if results.values().all(|&count| count == 1) { - // 如果所有表结果不同,直接回退到 kine 表的数据 - get_data_from_kine_primary(conn, item_kind, item_name, item_version, item_namespace) - } else if let Some((data, _)) = results.into_iter().max_by_key(|&(_, count)| count) { - // 如果有多数一致的数据,返回该数据 - Ok(Some(data)) - } else { - // 默认回退到 kine 表的数据 - get_data_from_kine_primary(conn, item_kind, item_name, item_version, item_namespace) - } -} - -/// 获取主表 `kine` 的数据 -fn get_data_from_kine_primary( - conn: &mut DbConnection, - item_kind: &str, - item_name: &str, - item_version: &str, - item_namespace: Option<&str>, -) -> QueryResult> { - let fallback_query = if let Some(_) = item_namespace { - match conn { - DbConnection::Pg(_) => "SELECT data FROM kine WHERE kind = $1 AND name = $2 AND namespace = $3 AND apigroup = $4".to_string(), - DbConnection::Sqlite(_) => "SELECT data FROM kine WHERE kind = ? AND name = ? AND namespace = ? AND apigroup = ?".to_string(), - } - } else { - match conn { - DbConnection::Pg(_) => "SELECT data FROM kine WHERE kind = $1 AND name = $2 AND apigroup = $3".to_string(), - DbConnection::Sqlite(_) => "SELECT data FROM kine WHERE kind = ? AND name = ? AND apigroup = ?".to_string(), - } - }; - - match conn { - DbConnection::Pg(pg_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(fallback_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace) - .bind::(item_version) - .get_result::(pg_conn) - .optional() - .map(|res| res.map(|data_result| data_result.data)) - } else { - diesel::sql_query(fallback_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(item_version) - .get_result::(pg_conn) - .optional() - .map(|res| res.map(|data_result| data_result.data)) - } - } - DbConnection::Sqlite(sqlite_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(fallback_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(namespace) - .bind::(item_version) - .get_result::(sqlite_conn) - .optional() - .map(|res| res.map(|data_result| data_result.data)) - } else { - diesel::sql_query(fallback_query) - .bind::(item_kind) - .bind::(item_name) - .bind::(item_version) - .get_result::(sqlite_conn) - .optional() - .map(|res| res.map(|data_result| data_result.data)) - } - } - } -} - - - - -// 辅助函数:从指定表中获取所有符合条件的数据的 `data` 字段 -async fn get_all_data_from_kine( - conn: &mut DbConnection, - item_kind: &str, - item_version: &str, - item_namespace: Option<&str>, -) -> QueryResult> { - use diesel::sql_types::Text; - use std::collections::HashMap; - - // 定义需要查询的表 - let tables = ["kine", "kine_replica1", "kine_replica2"]; - - // 存储每个表的查询结果 - let mut table_results: HashMap<&str, Vec> = HashMap::new(); - - // 遍历每个表进行查询 - for &table in &tables { - let select_query = if let Some(_) = item_namespace { - match conn { - DbConnection::Pg(_) => format!( - "SELECT data FROM {} WHERE kind = $1 AND namespace = $2 AND apigroup = $3", - table - ), - DbConnection::Sqlite(_) => format!( - "SELECT data FROM {} WHERE kind = ? AND namespace = ? AND apigroup = ?", - table - ), - } - } else { - match conn { - DbConnection::Pg(_) => format!( - "SELECT data FROM {} WHERE kind = $1 AND apigroup = $2", - table - ), - DbConnection::Sqlite(_) => format!( - "SELECT data FROM {} WHERE kind = ? AND apigroup = ?", - table - ), - } - }; - - // 执行查询 - let results: Vec = match conn { - DbConnection::Pg(pg_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(select_query) - .bind::(item_kind) - .bind::(namespace) - .bind::(item_version) - .load::(pg_conn)? - .into_iter() - .map(|res| res.data) - .collect() - } else { - diesel::sql_query(select_query) - .bind::(item_kind) - .bind::(item_version) - .load::(pg_conn)? - .into_iter() - .map(|res| res.data) - .collect() - } - } - DbConnection::Sqlite(sqlite_conn) => { - if let Some(namespace) = item_namespace { - diesel::sql_query(select_query) - .bind::(item_kind) - .bind::(namespace) - .bind::(item_version) - .load::(sqlite_conn)? - .into_iter() - .map(|res| res.data) - .collect() - } else { - diesel::sql_query(select_query) - .bind::(item_kind) - .bind::(item_version) - .load::(sqlite_conn)? - .into_iter() - .map(|res| res.data) - .collect() - } - } - }; - - table_results.insert(table, results); - } - - // 检查所有表的数据是否一致 - let vec1 = table_results[tables[0]].clone(); - let vec2 = table_results[tables[1]].clone(); - let vec3 = table_results[tables[2]].clone(); - - let max_len = vec1.len().max(vec2.len()).max(vec3.len()); - let mut filtered_results = Vec::new(); - - // 筛选出出现次数大于等于 2 的数据 - for i in 0..max_len { - // 获取当前索引的值,越界的填入 None - let value1 = vec1.get(i); - let value2 = vec2.get(i); - let value3 = vec3.get(i); - - // 统计每个值的出现次数 - let mut counts = HashMap::new(); - if let Some(v) = value1 { - *counts.entry(v).or_insert(0) += 1; - } - if let Some(v) = value2 { - *counts.entry(v).or_insert(0) += 1; - } - if let Some(v) = value3 { - *counts.entry(v).or_insert(0) += 1; - } - - // 找出出现次数大于等于 2 的值 - if let Some((value, &_)) = counts.iter().find(|(_, &count)| count >= 2) { - filtered_results.push((*value).clone()); // 将该值存入结果 - } - - } - - - Ok(filtered_results) -} - - - - // 发送请求并等待响应 async fn send_request( message: Message, @@ -1011,7 +151,6 @@ pub struct ApiServerMessage { pub content: Value, } - // watch的事件类型 #[derive(Clone, Debug, Serialize)] pub enum EventType { diff --git a/src/cores/mod.rs b/src/cores/mod.rs index 223f690..2a299bc 100644 --- a/src/cores/mod.rs +++ b/src/cores/mod.rs @@ -9,4 +9,3 @@ pub mod apiserver; pub mod config; pub mod handlers; pub mod checker; -pub mod db; \ No newline at end of file diff --git a/src/db/check_exist.rs b/src/db/check_exist.rs new file mode 100644 index 0000000..c04cf76 --- /dev/null +++ b/src/db/check_exist.rs @@ -0,0 +1,193 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ +use diesel::{QueryDsl, QueryResult, RunQueryDsl, ExpressionMethods}; +use crate::db::db::DbConnection; + +// 查询 metadata 表中的 plural 是否存在,并检查 namespace 要求是否满足 +pub async fn check_metadata( + conn: &mut DbConnection, + plural: &str, + version: &str, + requires_namespace: bool, +) -> QueryResult { + use diesel::dsl::count_star; + use crate::schema::metadata::dsl as metadata_dsl; + use crate::schema::metadata_replica1::dsl as replica1_dsl; + use crate::schema::metadata_replica2::dsl as replica2_dsl; + + let count; + let count_replica1; + let count_replica2; + match conn { + DbConnection::Pg(pg_conn) => { + count = metadata_dsl::metadata + .filter(metadata_dsl::name.eq(plural)) + .filter(metadata_dsl::apigroup.eq(version)) + .filter(metadata_dsl::namespace.eq(requires_namespace)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica1 = replica1_dsl::metadata_replica1 + .filter(replica1_dsl::name.eq(plural)) + .filter(replica1_dsl::apigroup.eq(version)) + .filter(replica1_dsl::namespace.eq(requires_namespace)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica2 = replica2_dsl::metadata_replica2 + .filter(replica2_dsl::name.eq(plural)) + .filter(replica2_dsl::apigroup.eq(version)) + .filter(replica2_dsl::namespace.eq(requires_namespace)) + .select(count_star()) + .first::(pg_conn)?; + }, + DbConnection::Sqlite(sqlite_conn) => { + count = metadata_dsl::metadata + .filter(metadata_dsl::name.eq(plural)) + .filter(metadata_dsl::apigroup.eq(version)) + .filter(metadata_dsl::namespace.eq(requires_namespace)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica1 = replica1_dsl::metadata_replica1 + .filter(replica1_dsl::name.eq(plural)) + .filter(replica1_dsl::apigroup.eq(version)) + .filter(replica1_dsl::namespace.eq(requires_namespace)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica2 = replica2_dsl::metadata_replica2 + .filter(replica2_dsl::name.eq(plural)) + .filter(replica2_dsl::apigroup.eq(version)) + .filter(replica2_dsl::namespace.eq(requires_namespace)) + .select(count_star()) + .first::(sqlite_conn)?; + } + } + let positive_count = [count, count_replica1, count_replica2].iter().filter(|&&x| x > 0).count(); + Ok(positive_count >= 2) +} + + + +// 查询 kine 表中指定的数据是否存在 +pub async fn check_kine( + conn: &mut DbConnection, + item_kind: &str, + item_name: &str, + item_version: &str, + item_namespace: Option<&str>, +) -> QueryResult { + use diesel::dsl::count_star; + use crate::schema::kine::dsl as kine_dsl; + use crate::schema::kine_replica1::dsl as replica1_dsl; + use crate::schema::kine_replica2::dsl as replica2_dsl; + + let count; + let count_replica1; + let count_replica2; + match conn { + DbConnection::Pg(pg_conn) => { + if let Some(_) = item_namespace { + count = kine_dsl::kine + .filter(kine_dsl::kind.eq(item_kind)) + .filter(kine_dsl::name.eq(item_name)) + .filter(kine_dsl::apigroup.eq(item_version)) + .filter(kine_dsl::namespace.eq(item_namespace)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica1 = replica1_dsl::kine_replica1 + .filter(replica1_dsl::kind.eq(item_kind)) + .filter(replica1_dsl::name.eq(item_name)) + .filter(replica1_dsl::apigroup.eq(item_version)) + .filter(replica1_dsl::namespace.eq(item_namespace)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica2 = replica2_dsl::kine_replica2 + .filter(replica2_dsl::kind.eq(item_kind)) + .filter(replica2_dsl::name.eq(item_name)) + .filter(replica2_dsl::apigroup.eq(item_version)) + .filter(replica2_dsl::namespace.eq(item_namespace)) + .select(count_star()) + .first::(pg_conn)?; + } else { + count = kine_dsl::kine + .filter(kine_dsl::kind.eq(item_kind)) + .filter(kine_dsl::name.eq(item_name)) + .filter(kine_dsl::apigroup.eq(item_version)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica1 = replica1_dsl::kine_replica1 + .filter(replica1_dsl::kind.eq(item_kind)) + .filter(replica1_dsl::name.eq(item_name)) + .filter(replica1_dsl::apigroup.eq(item_version)) + .select(count_star()) + .first::(pg_conn)?; + + count_replica2 = replica2_dsl::kine_replica2 + .filter(replica2_dsl::kind.eq(item_kind)) + .filter(replica2_dsl::name.eq(item_name)) + .filter(replica2_dsl::apigroup.eq(item_version)) + .select(count_star()) + .first::(pg_conn)?; + } + }, + DbConnection::Sqlite(sqlite_conn) => { + if let Some(_) = item_namespace { + count = kine_dsl::kine + .filter(kine_dsl::kind.eq(item_kind)) + .filter(kine_dsl::name.eq(item_name)) + .filter(kine_dsl::apigroup.eq(item_version)) + .filter(kine_dsl::namespace.eq(item_namespace)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica1 = replica1_dsl::kine_replica1 + .filter(replica1_dsl::kind.eq(item_kind)) + .filter(replica1_dsl::name.eq(item_name)) + .filter(replica1_dsl::apigroup.eq(item_version)) + .filter(replica1_dsl::namespace.eq(item_namespace)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica2 = replica2_dsl::kine_replica2 + .filter(replica2_dsl::kind.eq(item_kind)) + .filter(replica2_dsl::name.eq(item_name)) + .filter(replica2_dsl::apigroup.eq(item_version)) + .filter(replica2_dsl::namespace.eq(item_namespace)) + .select(count_star()) + .first::(sqlite_conn)?; + } else { + count = kine_dsl::kine + .filter(kine_dsl::kind.eq(item_kind)) + .filter(kine_dsl::name.eq(item_name)) + .filter(kine_dsl::apigroup.eq(item_version)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica1 = replica1_dsl::kine_replica1 + .filter(replica1_dsl::kind.eq(item_kind)) + .filter(replica1_dsl::name.eq(item_name)) + .filter(replica1_dsl::apigroup.eq(item_version)) + .select(count_star()) + .first::(sqlite_conn)?; + + count_replica2 = replica2_dsl::kine_replica2 + .filter(replica2_dsl::kind.eq(item_kind)) + .filter(replica2_dsl::name.eq(item_name)) + .filter(replica2_dsl::apigroup.eq(item_version)) + .select(count_star()) + .first::(sqlite_conn)?; + } + } + } + let positive_count = [count, count_replica1, count_replica2].iter().filter(|&&x| x > 0).count(); + Ok(positive_count >= 2) +} \ No newline at end of file diff --git a/src/cores/db.rs b/src/db/db.rs similarity index 99% rename from src/cores/db.rs rename to src/db/db.rs index 881f079..fb8aa9d 100644 --- a/src/cores/db.rs +++ b/src/db/db.rs @@ -14,7 +14,6 @@ use diesel::r2d2::{ConnectionManager, Pool, PooledConnection}; use diesel_migrations::{embed_migrations, EmbeddedMigrations, MigrationHarness}; use serde_json::{json, Value}; use diesel::connection::Connection; - pub const MIGRATIONS: EmbeddedMigrations = embed_migrations!(); pub enum DbPool { diff --git a/src/db/delete.rs b/src/db/delete.rs new file mode 100644 index 0000000..bc646b7 --- /dev/null +++ b/src/db/delete.rs @@ -0,0 +1,92 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ +use diesel::{QueryResult, RunQueryDsl}; +use crate::db::db::DbConnection; + +// 从 kine 表中删除特定 name 的记录 +pub async fn delete_from_kine( + conn: &mut DbConnection, + item_kind: &str, + item_name: &str, + item_version: &str, + item_namespace: Option<&str>, +) -> QueryResult { + use diesel::sql_types::Text; + + // 表名列表 + let tables = ["kine", "kine_replica1", "kine_replica2"]; + + // 遍历每个表,执行删除操作 + let mut total_rows_affected = 0; + + for &table in &tables { + let delete_query = if let Some(_) = item_namespace { + match conn { + DbConnection::Pg(_) => format!( + "DELETE FROM {} WHERE kind = $1 AND name = $2 AND namespace = $3 AND apigroup = $4", + table + ), + DbConnection::Sqlite(_) => format!( + "DELETE FROM {} WHERE kind = ? AND name = ? AND namespace = ? AND apigroup = ?", + table + ), + } + } else { + match conn { + DbConnection::Pg(_) => format!( + "DELETE FROM {} WHERE kind = $1 AND name = $2 AND apigroup = $3", + table + ), + DbConnection::Sqlite(_) => format!( + "DELETE FROM {} WHERE kind = ? AND name = ? AND apigroup = ?", + table + ), + } + }; + + // 执行删除 + let rows_affected = match conn { + DbConnection::Pg(pg_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(delete_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace) + .bind::(item_version) + .execute(pg_conn)? + } else { + diesel::sql_query(delete_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(item_version) + .execute(pg_conn)? + } + } + DbConnection::Sqlite(sqlite_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(delete_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace) + .bind::(item_version) + .execute(sqlite_conn)? + } else { + diesel::sql_query(delete_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(item_version) + .execute(sqlite_conn)? + } + } + }; + + total_rows_affected += rows_affected; + } + + // 如果至少有两个表进行了删除,则返回 true + Ok(total_rows_affected > 1) +} \ No newline at end of file diff --git a/src/db/get.rs b/src/db/get.rs new file mode 100644 index 0000000..b07433a --- /dev/null +++ b/src/db/get.rs @@ -0,0 +1,313 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ +use diesel::{OptionalExtension, QueryResult, QueryableByName, RunQueryDsl}; +use diesel::sql_types::Text; +use crate::db::db::DbConnection; + +// 辅助查询函数,用于获取数据的 `data` 字段 +#[derive(QueryableByName)] +struct DataResult { + #[diesel(sql_type = Text)] + data: String, +} +pub async fn get_data_from_kine( + conn: &mut DbConnection, + item_kind: &str, + item_name: &str, + item_version: &str, + item_namespace: Option<&str>, +) -> QueryResult> { + use diesel::sql_types::Text; + use std::collections::HashMap; + + // 表名列表 + let tables = ["kine", "kine_replica1", "kine_replica2"]; + + // 存储每个表的查询结果 + let mut results = HashMap::new(); + + for &table in &tables { + let select_query = if let Some(_) = item_namespace { + match conn { + DbConnection::Pg(_) => format!( + "SELECT data FROM {} WHERE kind = $1 AND name = $2 AND namespace = $3 AND apigroup = $4", + table + ), + DbConnection::Sqlite(_) => format!( + "SELECT data FROM {} WHERE kind = ? AND name = ? AND namespace = ? AND apigroup = ?", + table + ), + } + } else { + match conn { + DbConnection::Pg(_) => format!( + "SELECT data FROM {} WHERE kind = $1 AND name = $2 AND apigroup = $3", + table + ), + DbConnection::Sqlite(_) => format!( + "SELECT data FROM {} WHERE kind = ? AND name = ? AND apigroup = ?", + table + ), + } + }; + + let data_result = match conn { + DbConnection::Pg(pg_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(select_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace) + .bind::(item_version) + .get_result::(pg_conn) + .optional()? + .map(|res| res.data) + } else { + diesel::sql_query(select_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(item_version) + .get_result::(pg_conn) + .optional()? + .map(|res| res.data) + } + } + DbConnection::Sqlite(sqlite_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(select_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace) + .bind::(item_version) + .get_result::(sqlite_conn) + .optional()? + .map(|res| res.data) + } else { + diesel::sql_query(select_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(item_version) + .get_result::(sqlite_conn) + .optional()? + .map(|res| res.data) + } + } + }; + + if let Some(data) = data_result { + *results.entry(data).or_insert(0) += 1; + } + } + + // 按少数服从多数规则返回数据 + if results.len() == 1 { + // 如果三个表的结果一致,直接返回任意结果 + Ok(results.into_iter().next().map(|(data, _)| data)) + } else if results.values().all(|&count| count == 1) { + // 如果所有表结果不同,直接回退到 kine 表的数据 + get_data_from_kine_primary(conn, item_kind, item_name, item_version, item_namespace) + } else if let Some((data, _)) = results.into_iter().max_by_key(|&(_, count)| count) { + // 如果有多数一致的数据,返回该数据 + Ok(Some(data)) + } else { + // 默认回退到 kine 表的数据 + get_data_from_kine_primary(conn, item_kind, item_name, item_version, item_namespace) + } +} + +/// 获取主表 `kine` 的数据 +fn get_data_from_kine_primary( + conn: &mut DbConnection, + item_kind: &str, + item_name: &str, + item_version: &str, + item_namespace: Option<&str>, +) -> QueryResult> { + let fallback_query = if let Some(_) = item_namespace { + match conn { + DbConnection::Pg(_) => "SELECT data FROM kine WHERE kind = $1 AND name = $2 AND namespace = $3 AND apigroup = $4".to_string(), + DbConnection::Sqlite(_) => "SELECT data FROM kine WHERE kind = ? AND name = ? AND namespace = ? AND apigroup = ?".to_string(), + } + } else { + match conn { + DbConnection::Pg(_) => "SELECT data FROM kine WHERE kind = $1 AND name = $2 AND apigroup = $3".to_string(), + DbConnection::Sqlite(_) => "SELECT data FROM kine WHERE kind = ? AND name = ? AND apigroup = ?".to_string(), + } + }; + + match conn { + DbConnection::Pg(pg_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(fallback_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace) + .bind::(item_version) + .get_result::(pg_conn) + .optional() + .map(|res| res.map(|data_result| data_result.data)) + } else { + diesel::sql_query(fallback_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(item_version) + .get_result::(pg_conn) + .optional() + .map(|res| res.map(|data_result| data_result.data)) + } + } + DbConnection::Sqlite(sqlite_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(fallback_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace) + .bind::(item_version) + .get_result::(sqlite_conn) + .optional() + .map(|res| res.map(|data_result| data_result.data)) + } else { + diesel::sql_query(fallback_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(item_version) + .get_result::(sqlite_conn) + .optional() + .map(|res| res.map(|data_result| data_result.data)) + } + } + } +} + + + + +// 辅助函数:从指定表中获取所有符合条件的数据的 `data` 字段 +pub async fn get_all_data_from_kine( + conn: &mut DbConnection, + item_kind: &str, + item_version: &str, + item_namespace: Option<&str>, +) -> QueryResult> { + use diesel::sql_types::Text; + use std::collections::HashMap; + + // 定义需要查询的表 + let tables = ["kine", "kine_replica1", "kine_replica2"]; + + // 存储每个表的查询结果 + let mut table_results: HashMap<&str, Vec> = HashMap::new(); + + // 遍历每个表进行查询 + for &table in &tables { + let select_query = if let Some(_) = item_namespace { + match conn { + DbConnection::Pg(_) => format!( + "SELECT data FROM {} WHERE kind = $1 AND namespace = $2 AND apigroup = $3", + table + ), + DbConnection::Sqlite(_) => format!( + "SELECT data FROM {} WHERE kind = ? AND namespace = ? AND apigroup = ?", + table + ), + } + } else { + match conn { + DbConnection::Pg(_) => format!( + "SELECT data FROM {} WHERE kind = $1 AND apigroup = $2", + table + ), + DbConnection::Sqlite(_) => format!( + "SELECT data FROM {} WHERE kind = ? AND apigroup = ?", + table + ), + } + }; + + // 执行查询 + let results: Vec = match conn { + DbConnection::Pg(pg_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(select_query) + .bind::(item_kind) + .bind::(namespace) + .bind::(item_version) + .load::(pg_conn)? + .into_iter() + .map(|res| res.data) + .collect() + } else { + diesel::sql_query(select_query) + .bind::(item_kind) + .bind::(item_version) + .load::(pg_conn)? + .into_iter() + .map(|res| res.data) + .collect() + } + } + DbConnection::Sqlite(sqlite_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(select_query) + .bind::(item_kind) + .bind::(namespace) + .bind::(item_version) + .load::(sqlite_conn)? + .into_iter() + .map(|res| res.data) + .collect() + } else { + diesel::sql_query(select_query) + .bind::(item_kind) + .bind::(item_version) + .load::(sqlite_conn)? + .into_iter() + .map(|res| res.data) + .collect() + } + } + }; + + table_results.insert(table, results); + } + + // 检查所有表的数据是否一致 + let vec1 = table_results[tables[0]].clone(); + let vec2 = table_results[tables[1]].clone(); + let vec3 = table_results[tables[2]].clone(); + + let max_len = vec1.len().max(vec2.len()).max(vec3.len()); + let mut filtered_results = Vec::new(); + + // 筛选出出现次数大于等于 2 的数据 + for i in 0..max_len { + // 获取当前索引的值,越界的填入 None + let value1 = vec1.get(i); + let value2 = vec2.get(i); + let value3 = vec3.get(i); + + // 统计每个值的出现次数 + let mut counts = HashMap::new(); + if let Some(v) = value1 { + *counts.entry(v).or_insert(0) += 1; + } + if let Some(v) = value2 { + *counts.entry(v).or_insert(0) += 1; + } + if let Some(v) = value3 { + *counts.entry(v).or_insert(0) += 1; + } + + // 找出出现次数大于等于 2 的值 + if let Some((value, &_)) = counts.iter().find(|(_, &count)| count >= 2) { + filtered_results.push((*value).clone()); // 将该值存入结果 + } + + } + + Ok(filtered_results) +} diff --git a/src/db/insert.rs b/src/db/insert.rs new file mode 100644 index 0000000..c0b9e3d --- /dev/null +++ b/src/db/insert.rs @@ -0,0 +1,197 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ +use chrono::Utc; +use diesel::{sql_query, Connection, PgConnection, QueryResult, RunQueryDsl, SqliteConnection}; +use diesel::sql_types::Text; +use serde_json::Value; +use crate::db::db::DbConnection; + +fn insert_metadata_in_transaction_pg( + transaction: &mut PgConnection, + plural: &str, + version: &str, + namespace_required: bool, + json_data: &Value, +) -> QueryResult<()> { + use diesel::sql_types::{Bool}; + + // 表名列表 + let table_array: [&str; 3] = ["metadata", "metadata_replica1", "metadata_replica2"]; + + for table_name in table_array { + // 使用参数绑定构建插入查询 + let insert_metadata_query = format!( + "INSERT INTO {} (name, namespace, apigroup, data, created_time, updated_time) + VALUES ($1, $2, $3, $4, $5, $6) + ON CONFLICT DO NOTHING;", + table_name + ); + + // 执行插入操作 + sql_query(insert_metadata_query) + .bind::(plural) // 名称 + .bind::(namespace_required) // 是否需要命名空间 + .bind::(version) // 版本 + .bind::(json_data.to_string()) // JSON 数据 + .bind::(Utc::now().naive_utc().to_string()) // 创建时间 + .bind::(Utc::now().naive_utc().to_string()) // 更新时间 + .execute(transaction)?; + } + + Ok(()) +} + + +fn insert_metadata_in_transaction_sqlite( + transaction: &mut SqliteConnection, + plural: &str, + version: &str, + namespace_required: bool, + json_data: &Value, +) -> QueryResult<()> { + use diesel::sql_types::{Bool}; + + // 表名列表 + let table_array: [&str; 3] = ["metadata", "metadata_replica1", "metadata_replica2"]; + + for table_name in table_array { + // 使用参数绑定构建插入查询 + let insert_metadata_query = format!( + "INSERT OR IGNORE INTO {} (name, namespace, apigroup, data, created_time, updated_time) + VALUES (?, ?, ?, ?, ?, ?);", + table_name + ); + + // 执行插入操作 + sql_query(insert_metadata_query) + .bind::(plural) // 名称 + .bind::(namespace_required) // 是否需要命名空间 + .bind::(version) // 版本 + .bind::(json_data.to_string()) // JSON 数据 + .bind::(Utc::now().naive_utc().to_string()) // 创建时间 + .bind::(Utc::now().naive_utc().to_string()) // 更新时间 + .execute(transaction)?; + } + + Ok(()) +} + + +pub async fn insert_metadata( + conn: &mut DbConnection, + plural: &str, + version: &str, + namespace_required: bool, + json_data: &Value +) -> QueryResult<()> { + match conn { + DbConnection::Pg(pg_conn) => { + pg_conn.transaction(|transaction| { + insert_metadata_in_transaction_pg(transaction, plural, version, namespace_required, json_data) + }) + } + DbConnection::Sqlite(sqlite_conn) => { + sqlite_conn.transaction(|transaction| { + insert_metadata_in_transaction_sqlite(transaction, plural, version, namespace_required, json_data) + }) + } + }.expect("unknow conn in insert_metadata"); + Ok(()) +} + +fn insert_kine_in_transaction_pg( + transaction: &mut PgConnection, + item_kind: &str, + item_name: &str, + json_data: &Value, + version: &str, + namespace: Option<&str>, +) -> QueryResult<()> { + + // 表列表 + let table_array: [&str; 3] = ["kine", "kine_replica1", "kine_replica2"]; + + for table_name in table_array { + // 使用参数绑定构建插入查询 + let insert_metadata_query = format!( + "INSERT INTO {} (kind, name, namespace, apigroup, data, created_time, updated_time) + VALUES ($1, $2, $3, $4, $5, $6, $7) + ON CONFLICT DO NOTHING;", + table_name + ); + + // 执行插入操作 + sql_query(insert_metadata_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace.unwrap_or("")) // 空字符串作为默认 namespace + .bind::(version) + .bind::(json_data.to_string()) // 将 JSON 数据转换为字符串 + .bind::(Utc::now().naive_utc().to_string()) // 创建时间 + .bind::(Utc::now().naive_utc().to_string()) // 更新时间 + .execute(transaction)?; + } + + Ok(()) +} + + +fn insert_kine_in_transaction_sqlite( + transaction: &mut SqliteConnection, + item_kind: &str, + item_name: &str, + json_data: &Value, + version: &str, + namespace: Option<&str>, +) -> QueryResult<()> { + + let table_array: [&str; 3] = ["kine", "kine_replica1", "kine_replica2"]; + + for table_name in table_array { + let insert_metadata_query = format!( + "INSERT OR IGNORE INTO {} (kind, name, namespace, apigroup, data, created_time, updated_time) + VALUES (?, ?, ?, ?, ?, ?, ?);", + table_name + ); + + sql_query(insert_metadata_query) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace.unwrap_or("")) // 使用 Nullable 处理空值 + .bind::(version) + .bind::(json_data.to_string()) + .bind::(Utc::now().naive_utc().to_string()) + .bind::(Utc::now().naive_utc().to_string()) + .execute(transaction)?; + } + Ok(()) +} + + +// 在 kine 表中插入新记录 +pub async fn insert_kine( + conn: &mut DbConnection, + item_kind: &str, + item_name: &str, + json_data: &Value, + version: &str, + namespace: Option<&str>, +) -> QueryResult<()> { + match conn { + DbConnection::Pg(pg_conn) => { + pg_conn.transaction(|transaction| { + insert_kine_in_transaction_pg(transaction, item_kind, item_name, json_data, version, namespace) + }) + } + DbConnection::Sqlite(sqlite_conn) => { + sqlite_conn.transaction(|transaction| { + insert_kine_in_transaction_sqlite(transaction, item_kind, item_name, json_data, version, namespace) + }) + } + }.expect("unknow conn in insert_kine"); + Ok(()) +} \ No newline at end of file diff --git a/src/db/mod.rs b/src/db/mod.rs new file mode 100644 index 0000000..129c1fd --- /dev/null +++ b/src/db/mod.rs @@ -0,0 +1,12 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ +pub mod db; +pub mod check_exist; +pub mod insert; +pub mod update; +pub mod get; +pub mod delete; \ No newline at end of file diff --git a/src/db/update.rs b/src/db/update.rs new file mode 100644 index 0000000..d957223 --- /dev/null +++ b/src/db/update.rs @@ -0,0 +1,100 @@ +/** + * Copyright (2024, ) Institute of Software, Chinese Academy of Sciences + * author: chenhongyu23@otcaix.iscas.ac.cn, wuheng@iscas.ac.cn + * since: 0.1.0 + * +**/ +use diesel::{QueryResult, RunQueryDsl}; +use serde_json::Value; +use crate::db::db::DbConnection; + +pub async fn update_data_in_kine( + conn: &mut DbConnection, + item_kind: &str, + item_name: &str, + item_version: &str, + item_namespace: Option<&str>, + json_data: &Value, +) -> QueryResult { + use diesel::sql_types::Text; + use chrono::Utc; + + // 需要更新的表列表 + let tables = ["kine", "kine_replica1", "kine_replica2"]; + let mut total_rows_affected = 0; + + for &table in &tables { + let update_query = if let Some(_) = item_namespace { + match conn { + DbConnection::Pg(_) => format!( + "UPDATE {} SET data = $1, updated_time = $2 WHERE kind = $3 AND name = $4 AND namespace = $5 AND apigroup = $6", + table + ), + DbConnection::Sqlite(_) => format!( + "UPDATE {} SET data = ?, updated_time = ? WHERE kind = ? AND name = ? AND namespace = ? AND apigroup = ?", + table + ), + } + } else { + match conn { + DbConnection::Pg(_) => format!( + "UPDATE {} SET data = $1, updated_time = $2 WHERE kind = $3 AND name = $4 AND apigroup = $5", + table + ), + DbConnection::Sqlite(_) => format!( + "UPDATE {} SET data = ?, updated_time = ? WHERE kind = ? AND name = ? AND apigroup = ?", + table + ), + } + }; + + // 执行更新操作 + let rows_affected = match conn { + DbConnection::Pg(pg_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(update_query) + .bind::(json_data.to_string()) + .bind::(Utc::now().naive_utc().to_string()) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace) + .bind::(item_version) + .execute(pg_conn)? + } else { + diesel::sql_query(update_query) + .bind::(json_data.to_string()) + .bind::(Utc::now().naive_utc().to_string()) + .bind::(item_kind) + .bind::(item_name) + .bind::(item_version) + .execute(pg_conn)? + } + } + DbConnection::Sqlite(sqlite_conn) => { + if let Some(namespace) = item_namespace { + diesel::sql_query(update_query) + .bind::(json_data.to_string()) + .bind::(Utc::now().naive_utc().to_string()) + .bind::(item_kind) + .bind::(item_name) + .bind::(namespace) + .bind::(item_version) + .execute(sqlite_conn)? + } else { + diesel::sql_query(update_query) + .bind::(json_data.to_string()) + .bind::(Utc::now().naive_utc().to_string()) + .bind::(item_kind) + .bind::(item_name) + .bind::(item_version) + .execute(sqlite_conn)? + } + } + }; + + total_rows_affected += rows_affected; + } + + // 如果至少有两个表更新成功,则返回 true + Ok(total_rows_affected > 1) +} diff --git a/src/lib.rs b/src/lib.rs index ae9c790..826987f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +7,13 @@ pub mod cores; pub mod schema; +pub mod db; use std::fmt::Debug; use cores::apiserver::ApiServer; use cores::config::DefaultConfig; use cores::handlers::DefaultHandler; -use cores::db::DbPool; +use db::db::DbPool; use std::sync::Arc; use feventbus::err::Error; use feventbus::impls::nats::nats::NatsCli; -- Gitee