aboutsummaryrefslogtreecommitdiffstats
path: root/crates/configuration
diff options
context:
space:
mode:
authorrtkay123 <dev@kanjala.com>2025-08-21 17:59:13 +0200
committerGitHub <noreply@github.com>2025-08-21 17:59:13 +0200
commit06421ed5455285eb5d5eb90ea689fa73ad0f3010 (patch)
treee7a56bb6ae95d9f6f84664c94fc2efb3758f6539 /crates/configuration
parent441196248603a7c1aed66e4c0a4342aeb06dca8f (diff)
downloadwarden-06421ed5455285eb5d5eb90ea689fa73ad0f3010.tar.bz2
warden-06421ed5455285eb5d5eb90ea689fa73ad0f3010.zip
tests: cov (#13)
Diffstat (limited to 'crates/configuration')
-rw-r--r--crates/configuration/Cargo.toml3
-rw-r--r--crates/configuration/src/server/http_svc.rs44
-rw-r--r--crates/configuration/src/server/http_svc/routes.rs14
-rw-r--r--crates/configuration/src/server/http_svc/routes/routing/get_active.rs52
-rw-r--r--crates/configuration/src/server/http_svc/routes/routing/post_routing.rs99
-rw-r--r--crates/configuration/src/server/http_svc/routes/rule.rs182
-rw-r--r--crates/configuration/src/server/http_svc/routes/rule/create.rs90
-rw-r--r--crates/configuration/src/server/http_svc/routes/rule/delete.rs102
-rw-r--r--crates/configuration/src/server/http_svc/routes/rule/get.rs113
-rw-r--r--crates/configuration/src/server/http_svc/routes/rule/update.rs133
-rw-r--r--crates/configuration/src/server/http_svc/routes/typology.rs224
-rw-r--r--crates/configuration/src/server/http_svc/routes/typology/create_typology.rs103
-rw-r--r--crates/configuration/src/server/http_svc/routes/typology/delete_typology.rs116
-rw-r--r--crates/configuration/src/server/http_svc/routes/typology/get_typology.rs126
-rw-r--r--crates/configuration/src/server/http_svc/routes/typology/post_typology.rs175
-rw-r--r--crates/configuration/src/state/rule/mutate_rule.rs4
-rw-r--r--crates/configuration/src/state/typology/mutate_typology.rs4
17 files changed, 1575 insertions, 9 deletions
diff --git a/crates/configuration/Cargo.toml b/crates/configuration/Cargo.toml
index b290f08..a77a215 100644
--- a/crates/configuration/Cargo.toml
+++ b/crates/configuration/Cargo.toml
@@ -54,6 +54,9 @@ redoc = ["dep:utoipa-redoc", "utoipa-redoc/axum"]
rapidoc = ["dep:utoipa-rapidoc", "utoipa-rapidoc/axum"]
scalar = ["dep:utoipa-scalar", "utoipa-scalar/axum"]
+[dev-dependencies]
+tower = { workspace = true, features = ["util"] }
+
[dependencies.warden-stack]
workspace = true
features = ["api", "cache", "nats-jetstream", "postgres", "opentelemetry-tonic", "tracing-loki"]
diff --git a/crates/configuration/src/server/http_svc.rs b/crates/configuration/src/server/http_svc.rs
index 423d67d..2997e13 100644
--- a/crates/configuration/src/server/http_svc.rs
+++ b/crates/configuration/src/server/http_svc.rs
@@ -63,3 +63,47 @@ pub fn build_router(state: AppHandle) -> Router {
warden_middleware::apply(router)
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn health_check(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+ let app = build_router(state);
+
+ let response = app
+ .oneshot(Request::builder().uri("/").body(Body::empty()).unwrap())
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::OK);
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes.rs b/crates/configuration/src/server/http_svc/routes.rs
index 92f3184..64fc4c3 100644
--- a/crates/configuration/src/server/http_svc/routes.rs
+++ b/crates/configuration/src/server/http_svc/routes.rs
@@ -31,3 +31,17 @@ pub fn router(store: AppHandle) -> OpenApiRouter {
))
.with_state(store)
}
+
+#[cfg(test)]
+pub(crate) fn test_config() -> warden_stack::Configuration {
+ use warden_stack::Configuration;
+
+ let config_path = "warden-config.toml";
+
+ let config = config::Config::builder()
+ .add_source(config::File::new(config_path, config::FileFormat::Toml))
+ .build()
+ .unwrap();
+
+ config.try_deserialize::<Configuration>().unwrap()
+}
diff --git a/crates/configuration/src/server/http_svc/routes/routing/get_active.rs b/crates/configuration/src/server/http_svc/routes/routing/get_active.rs
index 4875a80..6974c6d 100644
--- a/crates/configuration/src/server/http_svc/routes/routing/get_active.rs
+++ b/crates/configuration/src/server/http_svc/routes/routing/get_active.rs
@@ -37,3 +37,55 @@ pub async fn active_routing(
.into_inner();
Ok(axum::Json(config.configuration).into_response())
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn get_empty(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+ let app = build_router(state);
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("GET")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/routing")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::OK);
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/routing/post_routing.rs b/crates/configuration/src/server/http_svc/routes/routing/post_routing.rs
index 3578b65..ce9ba37 100644
--- a/crates/configuration/src/server/http_svc/routes/routing/post_routing.rs
+++ b/crates/configuration/src/server/http_svc/routes/routing/post_routing.rs
@@ -37,3 +37,102 @@ pub async fn post_routing(
Ok((axum::http::StatusCode::CREATED, axum::Json(response)))
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn create_routing(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+ let app = build_router(state);
+
+ let routing = serde_json::json!({
+ "active": true,
+ "name": "Public Network Map",
+ "version": "1.0.0",
+ "messages": [
+ {
+ "id": "004",
+ "version": "1.0.0",
+ "tx_tp": "pacs.002.001.12",
+ "typologies": [
+ {
+ "id": "999",
+ "version": "1.0.0",
+ "rules": [
+ {
+ "id": "901",
+ "version": "1.0.0"
+ }
+ ]
+ }
+ ]
+ }
+ ]
+ });
+
+ let body = serde_json::to_vec(&routing).unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/routing")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::CREATED);
+
+ // should have an active one
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("GET")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/routing")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::OK);
+
+ // let bytes = axum::body::to_bytes(response.into_body(), usize::MAX).await.unwrap();
+ // let routing_info: RoutingConfiguration = serde_json::from_slice(&bytes).unwrap();
+ //
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/rule.rs b/crates/configuration/src/server/http_svc/routes/rule.rs
index f4b0d33..597693f 100644
--- a/crates/configuration/src/server/http_svc/routes/rule.rs
+++ b/crates/configuration/src/server/http_svc/routes/rule.rs
@@ -2,3 +2,185 @@ pub mod create;
pub mod delete;
pub mod get;
pub mod update;
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::{self, Body},
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_core::configuration::rule::RuleConfiguration;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn all_operations(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let rule = serde_json::json!({
+ "id": "901",
+ "version": "1.0.0",
+ "description": "Number of outgoing transactions - debtor",
+ "configuration": {
+ "parameters": {
+ "max_query_range": 86400000
+ },
+ "exit_conditions": [
+ {
+ "sub_rule_ref": ".x00",
+ "reason": "Incoming transaction is unsuccessful"
+ }
+ ],
+ "bands": [
+ {
+ "sub_rule_ref": ".01",
+ "upper_limit": 2,
+ "reason": "The debtor has performed one transaction to date"
+ },
+ {
+ "sub_rule_ref": ".02",
+ "lower_limit": 2,
+ "upper_limit": 3,
+ "reason": "The debtor has performed two transactions to date"
+ },
+ {
+ "sub_rule_ref": ".03",
+ "lower_limit": 3,
+ "reason": "The debtor has performed three or more transactions to date"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&rule).unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::CREATED);
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("GET")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule?id=901&version=1.0.0")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let body = body::to_bytes(response.into_body(), usize::MAX)
+ .await
+ .unwrap();
+
+ let config: RuleConfiguration = serde_json::from_slice(&body).unwrap();
+
+ assert_eq!(&config.id, "901");
+ assert_eq!(&config.version, "1.0.0");
+
+ let rule = serde_json::json!({
+ "id": "902",
+ "version": "1.0.0",
+ "description": "Number of outgoing transactions - debtor",
+ "configuration": {
+ "parameters": {
+ "max_query_range": 86400000
+ },
+ "exit_conditions": [
+ {
+ "sub_rule_ref": ".x00",
+ "reason": "Incoming transaction is unsuccessful"
+ }
+ ],
+ "bands": []
+ }
+ });
+
+ let body = serde_json::to_vec(&rule).unwrap();
+
+ app.clone()
+ .oneshot(
+ Request::builder()
+ .method("PUT")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule?id=901&version=1.0.0")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("GET")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule?id=902&version=1.0.0")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let body = body::to_bytes(response.into_body(), usize::MAX)
+ .await
+ .unwrap();
+
+ let config: RuleConfiguration = serde_json::from_slice(&body).unwrap();
+
+ assert_eq!(&config.id, "902");
+ assert!(&config.configuration.unwrap().bands.is_empty());
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("DELETE")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule?id=902&version=1.0.0")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::OK);
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/rule/create.rs b/crates/configuration/src/server/http_svc/routes/rule/create.rs
index 809c00b..f7aba5b 100644
--- a/crates/configuration/src/server/http_svc/routes/rule/create.rs
+++ b/crates/configuration/src/server/http_svc/routes/rule/create.rs
@@ -36,3 +36,93 @@ pub async fn create_rule(
.into_inner();
Ok((axum::http::StatusCode::CREATED, axum::Json(response)))
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn post_rule(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let rule = serde_json::json!({
+ "id": "901",
+ "version": "1.0.0",
+ "description": "Number of outgoing transactions - debtor",
+ "configuration": {
+ "parameters": {
+ "max_query_range": 86400000
+ },
+ "exit_conditions": [
+ {
+ "sub_rule_ref": ".x00",
+ "reason": "Incoming transaction is unsuccessful"
+ }
+ ],
+ "bands": [
+ {
+ "sub_rule_ref": ".01",
+ "upper_limit": 2,
+ "reason": "The debtor has performed one transaction to date"
+ },
+ {
+ "sub_rule_ref": ".02",
+ "lower_limit": 2,
+ "upper_limit": 3,
+ "reason": "The debtor has performed two transactions to date"
+ },
+ {
+ "sub_rule_ref": ".03",
+ "lower_limit": 3,
+ "reason": "The debtor has performed three or more transactions to date"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&rule).unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::CREATED);
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/rule/delete.rs b/crates/configuration/src/server/http_svc/routes/rule/delete.rs
index 2352fba..0182e47 100644
--- a/crates/configuration/src/server/http_svc/routes/rule/delete.rs
+++ b/crates/configuration/src/server/http_svc/routes/rule/delete.rs
@@ -40,3 +40,105 @@ pub async fn delete_rule_config(
Ok(axum::Json(body))
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn delete_rule(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let rule = serde_json::json!({
+ "id": "901",
+ "version": "1.0.0",
+ "description": "Number of outgoing transactions - debtor",
+ "configuration": {
+ "parameters": {
+ "max_query_range": 86400000
+ },
+ "exit_conditions": [
+ {
+ "sub_rule_ref": ".x00",
+ "reason": "Incoming transaction is unsuccessful"
+ }
+ ],
+ "bands": [
+ {
+ "sub_rule_ref": ".01",
+ "upper_limit": 2,
+ "reason": "The debtor has performed one transaction to date"
+ },
+ {
+ "sub_rule_ref": ".02",
+ "lower_limit": 2,
+ "upper_limit": 3,
+ "reason": "The debtor has performed two transactions to date"
+ },
+ {
+ "sub_rule_ref": ".03",
+ "lower_limit": 3,
+ "reason": "The debtor has performed three or more transactions to date"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&rule).unwrap();
+
+ app.clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("DELETE")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule?id=901&version=1.0.0")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::OK);
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/rule/get.rs b/crates/configuration/src/server/http_svc/routes/rule/get.rs
index 935eefb..eccab62 100644
--- a/crates/configuration/src/server/http_svc/routes/rule/get.rs
+++ b/crates/configuration/src/server/http_svc/routes/rule/get.rs
@@ -40,3 +40,116 @@ pub async fn get_rule(
Ok(axum::Json(response))
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::{self, Body},
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_core::configuration::rule::RuleConfiguration;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn get(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let rule = serde_json::json!({
+ "id": "901",
+ "version": "1.0.0",
+ "description": "Number of outgoing transactions - debtor",
+ "configuration": {
+ "parameters": {
+ "max_query_range": 86400000
+ },
+ "exit_conditions": [
+ {
+ "sub_rule_ref": ".x00",
+ "reason": "Incoming transaction is unsuccessful"
+ }
+ ],
+ "bands": [
+ {
+ "sub_rule_ref": ".01",
+ "upper_limit": 2,
+ "reason": "The debtor has performed one transaction to date"
+ },
+ {
+ "sub_rule_ref": ".02",
+ "lower_limit": 2,
+ "upper_limit": 3,
+ "reason": "The debtor has performed two transactions to date"
+ },
+ {
+ "sub_rule_ref": ".03",
+ "lower_limit": 3,
+ "reason": "The debtor has performed three or more transactions to date"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&rule).unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::CREATED);
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("GET")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule?id=901&version=1.0.0")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let body = body::to_bytes(response.into_body(), usize::MAX)
+ .await
+ .unwrap();
+
+ let config: RuleConfiguration = serde_json::from_slice(&body).unwrap();
+
+ assert_eq!(&config.id, "901");
+ assert_eq!(&config.version, "1.0.0");
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/rule/update.rs b/crates/configuration/src/server/http_svc/routes/rule/update.rs
index 7bf3fe0..2f61bcb 100644
--- a/crates/configuration/src/server/http_svc/routes/rule/update.rs
+++ b/crates/configuration/src/server/http_svc/routes/rule/update.rs
@@ -1,7 +1,8 @@
-use axum::extract::State;
+use axum::extract::{Query, State};
use tonic::IntoRequest;
use warden_core::configuration::rule::{
- RuleConfiguration, UpdateRuleRequest, mutate_rule_configuration_server::MutateRuleConfiguration,
+ RuleConfiguration, RuleConfigurationRequest, UpdateRuleRequest,
+ mutate_rule_configuration_server::MutateRuleConfiguration,
};
use crate::{
@@ -15,6 +16,7 @@ use crate::{
path = "/{version}/rule",
params(
("version" = Version, Path, description = "API version, e.g., v1, v2, v3"),
+ RuleConfigurationRequest
),
responses((
status = OK,
@@ -22,18 +24,21 @@ use crate::{
)),
operation_id = "update rule configuration", // https://github.com/juhaku/utoipa/issues/1170
tag = TAG_RULES,
- )
+)
]
#[axum::debug_handler]
#[tracing::instrument(skip(state))]
pub async fn update_rule_config(
version: Version,
+ Query(params): Query<RuleConfigurationRequest>,
State(state): State<AppHandle>,
axum::Json(body): axum::Json<RuleConfiguration>,
) -> Result<axum::Json<RuleConfiguration>, AppError> {
let config = state
.update_rule_configuration(
UpdateRuleRequest {
+ id: params.id,
+ version: params.version,
configuration: Some(body),
}
.into_request(),
@@ -43,3 +48,125 @@ pub async fn update_rule_config(
Ok(axum::Json(config))
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn update(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let rule = serde_json::json!({
+ "id": "901",
+ "version": "1.0.0",
+ "description": "Number of outgoing transactions - debtor",
+ "configuration": {
+ "parameters": {
+ "max_query_range": 86400000
+ },
+ "exit_conditions": [
+ {
+ "sub_rule_ref": ".x00",
+ "reason": "Incoming transaction is unsuccessful"
+ }
+ ],
+ "bands": [
+ {
+ "sub_rule_ref": ".01",
+ "upper_limit": 2,
+ "reason": "The debtor has performed one transaction to date"
+ },
+ {
+ "sub_rule_ref": ".02",
+ "lower_limit": 2,
+ "upper_limit": 3,
+ "reason": "The debtor has performed two transactions to date"
+ },
+ {
+ "sub_rule_ref": ".03",
+ "lower_limit": 3,
+ "reason": "The debtor has performed three or more transactions to date"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&rule).unwrap();
+
+ app.clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let rule = serde_json::json!({
+ "id": "902",
+ "version": "1.0.0",
+ "description": "Number of outgoing transactions - debtor",
+ "configuration": {
+ "parameters": {
+ "max_query_range": 86400000
+ },
+ "exit_conditions": [
+ {
+ "sub_rule_ref": ".x00",
+ "reason": "Incoming transaction is unsuccessful"
+ }
+ ],
+ "bands": []
+ }
+ });
+
+ let body = serde_json::to_vec(&rule).unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("PUT")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/rule?id=901&version=1.0.0")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::OK);
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/typology.rs b/crates/configuration/src/server/http_svc/routes/typology.rs
index 85a593b..c2fa270 100644
--- a/crates/configuration/src/server/http_svc/routes/typology.rs
+++ b/crates/configuration/src/server/http_svc/routes/typology.rs
@@ -2,3 +2,227 @@ pub mod create_typology;
pub mod delete_typology;
pub mod get_typology;
pub mod post_typology;
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::{self, Body},
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_core::configuration::typology::TypologyConfiguration;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn all_operations(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let typology = serde_json::json!({
+ "description": "Test description",
+ "typology_name": "Rule-901-Typology-999",
+ "id": "999",
+ "version": "1.0.0",
+ "workflow": {
+ "alert_threshold": 200,
+ "interdiction_threshold": 400
+ },
+ "rules": [
+ {
+ "id": "901",
+ "version": "1.0.0",
+ "wghts": [
+ {
+ "ref": ".err",
+ "wght": 0
+ },
+ {
+ "ref": ".x00",
+ "wght": 100
+ },
+ {
+ "ref": ".01",
+ "wght": 100
+ },
+ {
+ "ref": ".02",
+ "wght": 200
+ },
+ {
+ "ref": ".03",
+ "wght": 400
+ }
+ ]
+ }
+ ],
+ "expression": {
+ "operator": "ADD",
+ "terms": [
+ {
+ "id": "901",
+ "version": "1.0.0"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&typology).unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::CREATED);
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("GET")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology?id=999&version=1.0.0")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let body = body::to_bytes(response.into_body(), usize::MAX)
+ .await
+ .unwrap();
+
+ let config: TypologyConfiguration = serde_json::from_slice(&body).unwrap();
+
+ assert_eq!(&config.id, "999");
+ assert_eq!(&config.version, "1.0.0");
+
+ let typology = serde_json::json!({
+ "description": "Test description",
+ "typology_name": "Rule-901-Typology-999",
+ "id": "901",
+ "version": "1.0.0",
+ "workflow": {
+ "alert_threshold": 200,
+ "interdiction_threshold": 400
+ },
+ "rules": [
+ {
+ "id": "901",
+ "version": "1.0.0",
+ "wghts": [
+ {
+ "ref": ".err",
+ "wght": 0
+ },
+ {
+ "ref": ".x00",
+ "wght": 100
+ },
+ {
+ "ref": ".01",
+ "wght": 100
+ },
+ {
+ "ref": ".02",
+ "wght": 200
+ },
+ {
+ "ref": ".03",
+ "wght": 400
+ }
+ ]
+ }
+ ],
+ "expression": {
+ "operator": "ADD",
+ "terms": [
+ {
+ "id": "901",
+ "version": "1.0.0"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&typology).unwrap();
+
+ app.clone()
+ .oneshot(
+ Request::builder()
+ .method("PUT")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology?id=999&version=1.0.0")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("GET")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology?id=901&version=1.0.0")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let body = body::to_bytes(response.into_body(), usize::MAX)
+ .await
+ .unwrap();
+
+ let config: TypologyConfiguration = serde_json::from_slice(&body).unwrap();
+
+ assert_eq!(&config.id, "901");
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("DELETE")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology?id=901&version=1.0.0")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::OK);
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/typology/create_typology.rs b/crates/configuration/src/server/http_svc/routes/typology/create_typology.rs
index 9f4985a..8aeeeec 100644
--- a/crates/configuration/src/server/http_svc/routes/typology/create_typology.rs
+++ b/crates/configuration/src/server/http_svc/routes/typology/create_typology.rs
@@ -36,3 +36,106 @@ pub async fn create_typology(
.into_inner();
Ok((axum::http::StatusCode::CREATED, axum::Json(response)))
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn post_typology(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let typology = serde_json::json!({
+ "description": "Test description",
+ "typology_name": "Rule-901-Typology-999",
+ "id": "999",
+ "version": "1.0.0",
+ "workflow": {
+ "alert_threshold": 200,
+ "interdiction_threshold": 400
+ },
+ "rules": [
+ {
+ "id": "901",
+ "version": "1.0.0",
+ "wghts": [
+ {
+ "ref": ".err",
+ "wght": 0
+ },
+ {
+ "ref": ".x00",
+ "wght": 100
+ },
+ {
+ "ref": ".01",
+ "wght": 100
+ },
+ {
+ "ref": ".02",
+ "wght": 200
+ },
+ {
+ "ref": ".03",
+ "wght": 400
+ }
+ ]
+ }
+ ],
+ "expression": {
+ "operator": "ADD",
+ "terms": [
+ {
+ "id": "901",
+ "version": "1.0.0"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&typology).unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::CREATED);
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/typology/delete_typology.rs b/crates/configuration/src/server/http_svc/routes/typology/delete_typology.rs
index 0e85e29..276aa67 100644
--- a/crates/configuration/src/server/http_svc/routes/typology/delete_typology.rs
+++ b/crates/configuration/src/server/http_svc/routes/typology/delete_typology.rs
@@ -39,3 +39,119 @@ pub async fn delete_typology(
Ok(axum::Json(config))
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn delete_typology(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let typology = serde_json::json!({
+ "description": "Test description",
+ "typology_name": "Rule-901-Typology-999",
+ "id": "999",
+ "version": "1.0.0",
+ "workflow": {
+ "alert_threshold": 200,
+ "interdiction_threshold": 400
+ },
+ "rules": [
+ {
+ "id": "901",
+ "version": "1.0.0",
+ "wghts": [
+ {
+ "ref": ".err",
+ "wght": 0
+ },
+ {
+ "ref": ".x00",
+ "wght": 100
+ },
+ {
+ "ref": ".01",
+ "wght": 100
+ },
+ {
+ "ref": ".02",
+ "wght": 200
+ },
+ {
+ "ref": ".03",
+ "wght": 400
+ }
+ ]
+ }
+ ],
+ "expression": {
+ "operator": "ADD",
+ "terms": [
+ {
+ "id": "901",
+ "version": "1.0.0"
+ }
+ ]
+ }
+
+ });
+
+ let body = serde_json::to_vec(&typology).unwrap();
+
+ app.clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("DELETE")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology?id=999&version=1.0.0")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::OK);
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/typology/get_typology.rs b/crates/configuration/src/server/http_svc/routes/typology/get_typology.rs
index 4962593..40d3faf 100644
--- a/crates/configuration/src/server/http_svc/routes/typology/get_typology.rs
+++ b/crates/configuration/src/server/http_svc/routes/typology/get_typology.rs
@@ -38,3 +38,129 @@ pub async fn get_typology(
Ok(axum::Json(config.configuration))
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::{self, Body},
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_core::configuration::typology::TypologyConfiguration;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn get(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let typology = serde_json::json!({
+ "description": "Test description",
+ "typology_name": "Rule-901-Typology-999",
+ "id": "999",
+ "version": "1.0.0",
+ "workflow": {
+ "alert_threshold": 200,
+ "interdiction_threshold": 400
+ },
+ "rules": [
+ {
+ "id": "901",
+ "version": "1.0.0",
+ "wghts": [
+ {
+ "ref": ".err",
+ "wght": 0
+ },
+ {
+ "ref": ".x00",
+ "wght": 100
+ },
+ {
+ "ref": ".01",
+ "wght": 100
+ },
+ {
+ "ref": ".02",
+ "wght": 200
+ },
+ {
+ "ref": ".03",
+ "wght": 400
+ }
+ ]
+ }
+ ],
+ "expression": {
+ "operator": "ADD",
+ "terms": [
+ {
+ "id": "901",
+ "version": "1.0.0"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&typology).unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::CREATED);
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("GET")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology?id=999&version=1.0.0")
+ .body(Body::empty())
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let body = body::to_bytes(response.into_body(), usize::MAX)
+ .await
+ .unwrap();
+
+ let config: TypologyConfiguration = serde_json::from_slice(&body).unwrap();
+
+ assert_eq!(&config.id, "999");
+ assert_eq!(&config.version, "1.0.0");
+ }
+}
diff --git a/crates/configuration/src/server/http_svc/routes/typology/post_typology.rs b/crates/configuration/src/server/http_svc/routes/typology/post_typology.rs
index 2864372..0c432ab 100644
--- a/crates/configuration/src/server/http_svc/routes/typology/post_typology.rs
+++ b/crates/configuration/src/server/http_svc/routes/typology/post_typology.rs
@@ -1,6 +1,7 @@
-use axum::extract::State;
+use axum::extract::{Query, State};
use warden_core::configuration::typology::{
- TypologyConfiguration, UpdateTypologyConfigRequest, mutate_typologies_server::MutateTypologies,
+ TypologyConfiguration, TypologyConfigurationRequest, UpdateTypologyConfigRequest,
+ mutate_typologies_server::MutateTypologies,
};
use crate::{
@@ -14,6 +15,7 @@ use crate::{
path = "/{version}/typology",
params(
("version" = Version, Path, description = "API version, e.g., v1, v2, v3"),
+ TypologyConfigurationRequest,
),
responses((
status = OK,
@@ -26,14 +28,183 @@ use crate::{
#[axum::debug_handler]
#[tracing::instrument(skip(state))]
pub async fn update(
+ version: Version,
+ Query(params): Query<TypologyConfigurationRequest>,
State(state): State<AppHandle>,
axum::Json(body): axum::Json<TypologyConfiguration>,
) -> Result<axum::Json<TypologyConfiguration>, AppError> {
let response = state
.update_typology_configuration(tonic::Request::new(UpdateTypologyConfigRequest {
+ id: params.id,
+ version: params.version,
configuration: Some(body),
}))
.await?
.into_inner();
Ok(axum::Json(response))
}
+
+#[cfg(test)]
+mod tests {
+ use axum::{
+ body::Body,
+ http::{Request, StatusCode},
+ };
+ use sqlx::PgPool;
+ use tower::ServiceExt;
+ use warden_stack::cache::RedisManager;
+
+ use crate::{
+ server::http_svc::{build_router, routes::test_config},
+ state::{AppState, Services},
+ };
+
+ #[sqlx::test]
+ async fn update(pool: PgPool) {
+ let config = test_config();
+
+ let cache = RedisManager::new(&config.cache).await.unwrap();
+ let client = async_nats::connect(&config.nats.hosts[0]).await.unwrap();
+ let jetstream = async_nats::jetstream::new(client);
+
+ let state = AppState::create(
+ Services {
+ postgres: pool,
+ cache,
+ jetstream,
+ },
+ &test_config(),
+ )
+ .await
+ .unwrap();
+
+ let app = build_router(state);
+
+ let typology = serde_json::json!({
+ "description": "Test description",
+ "typology_name": "Rule-901-Typology-999",
+ "id": "999",
+ "version": "1.0.0",
+ "workflow": {
+ "alert_threshold": 200,
+ "interdiction_threshold": 400
+ },
+ "rules": [
+ {
+ "id": "901",
+ "version": "1.0.0",
+ "wghts": [
+ {
+ "ref": ".err",
+ "wght": 0
+ },
+ {
+ "ref": ".x00",
+ "wght": 100
+ },
+ {
+ "ref": ".01",
+ "wght": 100
+ },
+ {
+ "ref": ".02",
+ "wght": 200
+ },
+ {
+ "ref": ".03",
+ "wght": 400
+ }
+ ]
+ }
+ ],
+ "expression": {
+ "operator": "ADD",
+ "terms": [
+ {
+ "id": "901",
+ "version": "1.0.0"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&typology).unwrap();
+
+ app.clone()
+ .oneshot(
+ Request::builder()
+ .method("POST")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ let typology = serde_json::json!({
+ "description": "Test description",
+ "typology_name": "Rule-901-Typology-999",
+ "id": "901",
+ "version": "1.0.0",
+ "workflow": {
+ "alert_threshold": 200,
+ "interdiction_threshold": 400
+ },
+ "rules": [
+ {
+ "id": "901",
+ "version": "1.0.0",
+ "wghts": [
+ {
+ "ref": ".err",
+ "wght": 0
+ },
+ {
+ "ref": ".x00",
+ "wght": 100
+ },
+ {
+ "ref": ".01",
+ "wght": 100
+ },
+ {
+ "ref": ".02",
+ "wght": 200
+ },
+ {
+ "ref": ".03",
+ "wght": 400
+ }
+ ]
+ }
+ ],
+ "expression": {
+ "operator": "ADD",
+ "terms": [
+ {
+ "id": "901",
+ "version": "1.0.0"
+ }
+ ]
+ }
+ });
+
+ let body = serde_json::to_vec(&typology).unwrap();
+
+ let response = app
+ .clone()
+ .oneshot(
+ Request::builder()
+ .method("PUT")
+ .header("Content-Type", "application/json")
+ .uri("/api/v0/typology?id=999&version=1.0.0")
+ .body(Body::from(body))
+ .unwrap(),
+ )
+ .await
+ .unwrap();
+
+ assert_eq!(response.status(), StatusCode::OK);
+ }
+}
diff --git a/crates/configuration/src/state/rule/mutate_rule.rs b/crates/configuration/src/state/rule/mutate_rule.rs
index 9c4f393..10898b6 100644
--- a/crates/configuration/src/state/rule/mutate_rule.rs
+++ b/crates/configuration/src/state/rule/mutate_rule.rs
@@ -73,8 +73,8 @@ impl MutateRuleConfiguration for AppHandle {
where id = $2 and version = $3
"#,
sqlx::types::Json(&config) as _,
- config.id,
- config.id,
+ request.id,
+ request.version,
)
.execute(&self.services.postgres)
.instrument(span)
diff --git a/crates/configuration/src/state/typology/mutate_typology.rs b/crates/configuration/src/state/typology/mutate_typology.rs
index f2ab2cc..2673297 100644
--- a/crates/configuration/src/state/typology/mutate_typology.rs
+++ b/crates/configuration/src/state/typology/mutate_typology.rs
@@ -73,8 +73,8 @@ impl MutateTypologies for AppHandle {
where id = $2 and version = $3
"#,
sqlx::types::Json(&config) as _,
- config.id,
- config.version,
+ request.id,
+ request.version,
)
.execute(&self.services.postgres)
.instrument(span)