mlua_swarm_server/
enhance_settings.rs1use axum::{
22 extract::{Path, State},
23 http::StatusCode,
24 routing::get,
25 Json, Router,
26};
27use mlua_swarm::blueprint::store::{blueprint_version, BlueprintStore, CommitMetadata};
28use mlua_swarm::blueprint::Blueprint;
29use mlua_swarm::enhance::{EnhanceSetting, EnhanceSettingInput};
30use mlua_swarm::store::enhance_setting::{
31 EnhanceSettingId, EnhanceSettingStore, EnhanceSettingStoreError,
32};
33use std::sync::Arc;
34
35#[derive(Clone)]
37pub struct EnhanceSettingsState {
38 pub setting_store: Arc<dyn EnhanceSettingStore>,
40 pub bp_store: Arc<dyn BlueprintStore>,
42}
43
44pub fn build_enhance_settings_router(
47 setting_store: Arc<dyn EnhanceSettingStore>,
48 bp_store: Arc<dyn BlueprintStore>,
49) -> Router {
50 let state = EnhanceSettingsState {
51 setting_store,
52 bp_store,
53 };
54 Router::new()
55 .route(
56 "/v1/enhance-settings",
57 get(list_settings).post(post_setting),
58 )
59 .route(
60 "/v1/enhance-settings/:id",
61 get(get_setting).put(put_setting).delete(delete_setting),
62 )
63 .with_state(state)
64}
65
66fn now_ms() -> i64 {
67 std::time::SystemTime::now()
68 .duration_since(std::time::UNIX_EPOCH)
69 .map(|d| d.as_millis() as i64)
70 .unwrap_or(0)
71}
72
73async fn commit_blueprint(
77 bp_store: &Arc<dyn BlueprintStore>,
78 blueprint: &Blueprint,
79 rationale: String,
80) -> Result<(), (StatusCode, String)> {
81 let bp_id = blueprint.id.clone();
82 let v = blueprint_version(blueprint).map_err(|e| {
83 (
84 StatusCode::INTERNAL_SERVER_ERROR,
85 format!("bp version: {e}"),
86 )
87 })?;
88
89 if let Ok(traced) = bp_store.read_head(&bp_id).await {
91 if traced.trace.version == v {
92 return Ok(());
93 }
94 }
95
96 let mut meta = CommitMetadata::seed(bp_id.clone(), v, now_ms());
97 meta.rationale = rationale;
98 bp_store
99 .write_new(&bp_id, blueprint, &[], meta)
100 .await
101 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("bp commit: {e}")))?;
102 Ok(())
103}
104
105async fn list_settings(
106 State(state): State<EnhanceSettingsState>,
107) -> Result<Json<Vec<String>>, (StatusCode, String)> {
108 let ids = state
109 .setting_store
110 .list()
111 .await
112 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
113 Ok(Json(ids.into_iter().map(|id| id.0).collect()))
114}
115
116async fn post_setting(
117 State(state): State<EnhanceSettingsState>,
118 Json(input): Json<EnhanceSettingInput>,
119) -> Result<(StatusCode, Json<EnhanceSetting>), (StatusCode, String)> {
120 let (blueprint, setting) = input.into_ref();
121 let rationale = format!(
122 "enhance-setting POST id={} blueprint_id={}",
123 setting.id, setting.blueprint_id
124 );
125 commit_blueprint(&state.bp_store, &blueprint, rationale).await?;
126 state
127 .setting_store
128 .put(&EnhanceSettingId::new(setting.id.clone()), setting.clone())
129 .await
130 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
131 Ok((StatusCode::CREATED, Json(setting)))
132}
133
134async fn get_setting(
135 State(state): State<EnhanceSettingsState>,
136 Path(id): Path<String>,
137) -> Result<Json<EnhanceSetting>, (StatusCode, String)> {
138 let setting = state
139 .setting_store
140 .get(&EnhanceSettingId::new(id))
141 .await
142 .map_err(|e| match e {
143 EnhanceSettingStoreError::NotFound(id) => (
144 StatusCode::NOT_FOUND,
145 format!("enhance setting not found: {id}"),
146 ),
147 EnhanceSettingStoreError::Other(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
148 })?;
149 Ok(Json(setting))
150}
151
152async fn put_setting(
153 State(state): State<EnhanceSettingsState>,
154 Path(id): Path<String>,
155 Json(input): Json<EnhanceSettingInput>,
156) -> Result<Json<EnhanceSetting>, (StatusCode, String)> {
157 if input.id != id {
158 return Err((
159 StatusCode::BAD_REQUEST,
160 format!("path id {id:?} != body id {:?}", input.id),
161 ));
162 }
163 let (blueprint, setting) = input.into_ref();
164 let rationale = format!(
165 "enhance-setting PUT id={} blueprint_id={}",
166 setting.id, setting.blueprint_id
167 );
168 commit_blueprint(&state.bp_store, &blueprint, rationale).await?;
169 state
170 .setting_store
171 .put(&EnhanceSettingId::new(id), setting.clone())
172 .await
173 .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()))?;
174 Ok(Json(setting))
175}
176
177async fn delete_setting(
178 State(state): State<EnhanceSettingsState>,
179 Path(id): Path<String>,
180) -> Result<StatusCode, (StatusCode, String)> {
181 state
182 .setting_store
183 .delete(&EnhanceSettingId::new(id))
184 .await
185 .map_err(|e| match e {
186 EnhanceSettingStoreError::NotFound(id) => (
187 StatusCode::NOT_FOUND,
188 format!("enhance setting not found: {id}"),
189 ),
190 EnhanceSettingStoreError::Other(msg) => (StatusCode::INTERNAL_SERVER_ERROR, msg),
191 })?;
192 Ok(StatusCode::NO_CONTENT)
193}