cmd.rs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. use crate::{
  2. db::{self, Update},
  3. request::{
  4. self,
  5. url::{RequestUrl, RequestUrlOwned, Segment, UrlError},
  6. EntryRequestBody, HttpRequestParameters, HttpResponse, RequestBody, RequestHeader,
  7. RequestHeaderInsert, RequestHeaderUpdate, RequestPathParam, RequestPathUpdate,
  8. },
  9. state::AppState,
  10. var::{expand_vars, parse_vars},
  11. workspace::{
  12. Workspace, WorkspaceEntry, WorkspaceEntryBase, WorkspaceEntryCreate, WorkspaceEntryUpdate,
  13. WorkspaceEntryUpdateBase, WorkspaceEnvVariable, WorkspaceEnvironment,
  14. },
  15. };
  16. use tauri_plugin_log::log;
  17. #[tauri::command]
  18. pub async fn list_workspaces(state: tauri::State<'_, AppState>) -> Result<Vec<Workspace>, String> {
  19. match db::list_workspaces(state.db.clone()).await {
  20. Ok(ws) => Ok(ws),
  21. Err(e) => Err(e.to_string()),
  22. }
  23. }
  24. #[tauri::command]
  25. pub async fn create_workspace(
  26. state: tauri::State<'_, AppState>,
  27. name: String,
  28. ) -> Result<Workspace, String> {
  29. match db::create_workspace(state.db.clone(), name).await {
  30. Ok(ws) => Ok(ws),
  31. Err(e) => Err(e.to_string()),
  32. }
  33. }
  34. #[tauri::command]
  35. pub async fn list_workspace_entries(
  36. state: tauri::State<'_, AppState>,
  37. id: i64,
  38. ) -> Result<Vec<WorkspaceEntry>, String> {
  39. match db::list_workspace_entries(state.db.clone(), id).await {
  40. Ok(ws) => Ok(ws),
  41. Err(e) => Err(e.to_string()),
  42. }
  43. }
  44. #[tauri::command]
  45. pub async fn create_workspace_entry(
  46. state: tauri::State<'_, AppState>,
  47. data: WorkspaceEntryCreate,
  48. ) -> Result<WorkspaceEntryBase, String> {
  49. match db::create_workspace_entry(state.db.clone(), data).await {
  50. Ok(ws) => Ok(ws),
  51. Err(e) => Err(e.to_string()),
  52. }
  53. }
  54. #[tauri::command]
  55. pub async fn update_workspace_entry(
  56. state: tauri::State<'_, AppState>,
  57. entry_id: i64,
  58. data: WorkspaceEntryUpdate,
  59. ) -> Result<(), String> {
  60. match db::update_workspace_entry(state.db.clone(), entry_id, data).await {
  61. Ok(()) => Ok(()),
  62. Err(e) => Err(e.to_string()),
  63. }
  64. }
  65. #[tauri::command]
  66. pub async fn insert_request_body(
  67. state: tauri::State<'_, AppState>,
  68. entry_id: i64,
  69. body: RequestBody,
  70. ) -> Result<EntryRequestBody, String> {
  71. match db::insert_request_body(state.db.clone(), entry_id, body).await {
  72. Ok(b) => Ok(b),
  73. Err(e) => return Err(e.to_string()),
  74. }
  75. }
  76. #[tauri::command]
  77. pub async fn update_request_body(
  78. state: tauri::State<'_, AppState>,
  79. id: i64,
  80. body: Update<RequestBody>,
  81. ) -> Result<(), String> {
  82. if let Err(e) = db::update_request_body(state.db.clone(), id, body).await {
  83. return Err(e.to_string());
  84. }
  85. Ok(())
  86. }
  87. #[tauri::command]
  88. pub async fn parse_url(url: String) -> Result<RequestUrlOwned, UrlError> {
  89. match RequestUrl::parse(&url) {
  90. Ok(url_parsed) => Ok(url_parsed.into()),
  91. Err(e) => {
  92. log::debug!("{e:?}");
  93. Err(UrlError::Parse(e))
  94. }
  95. }
  96. }
  97. #[tauri::command]
  98. pub async fn expand_url(
  99. state: tauri::State<'_, AppState>,
  100. entry_id: i64,
  101. env_id: Option<i64>,
  102. mut url: String,
  103. ) -> Result<String, UrlError> {
  104. if let Some(env_id) = env_id {
  105. let vars = match parse_vars(&url) {
  106. Ok(vars) => vars.iter().map(|v| v.name).collect::<Vec<_>>(),
  107. Err(e) => return Err(UrlError::Var(e.to_string())),
  108. };
  109. if !vars.is_empty() {
  110. let vars = match db::get_env_variables(state.db.clone(), env_id, &vars).await {
  111. Ok(v) => v,
  112. Err(e) => return Err(UrlError::Db(e.to_string())),
  113. };
  114. url = expand_vars(&url, &vars);
  115. }
  116. }
  117. match RequestUrl::parse(&url) {
  118. Ok(mut url) => {
  119. let params = match db::list_request_path_params(state.db.clone(), entry_id).await {
  120. Ok(p) => p,
  121. Err(e) => return Err(UrlError::Db(e.to_string())),
  122. };
  123. url.populate_path(
  124. params
  125. .iter()
  126. .map(|p| (p.name.as_str(), p.value.as_str()))
  127. .collect(),
  128. );
  129. let url = url.to_string();
  130. let vars = match parse_vars(&url) {
  131. Ok(vars) => vars.iter().map(|v| v.name).collect::<Vec<_>>(),
  132. Err(e) => return Err(UrlError::Var(e.to_string())),
  133. };
  134. if vars.is_empty() {
  135. return Ok(url);
  136. }
  137. let Some(env_id) = env_id else {
  138. return Ok(url);
  139. };
  140. let vars = match db::get_env_variables(state.db.clone(), env_id, &vars).await {
  141. Ok(v) => v,
  142. Err(e) => return Err(UrlError::Db(e.to_string())),
  143. };
  144. Ok(expand_vars(&url.to_string(), &vars))
  145. }
  146. Err(e) => {
  147. log::debug!("{e:?}");
  148. Err(UrlError::Parse(e))
  149. }
  150. }
  151. }
  152. /// Updates a URL.
  153. ///
  154. /// * `entry_id`: The request entry ID whose URL will be updated.
  155. /// * `env_id`: The environment to use for expanding variables.
  156. /// * `url`: The URL string which will be populated from the env and parsed with [RequestUrl].
  157. /// * `path_params`: List of dynamic path params to persist for the URL.
  158. #[tauri::command]
  159. pub async fn update_url(
  160. state: tauri::State<'_, AppState>,
  161. entry_id: i64,
  162. use_path_params: bool,
  163. url: String,
  164. // Dynamic path params
  165. path_params: Vec<RequestPathParam>,
  166. ) -> Result<(RequestUrlOwned, Vec<RequestPathParam>), UrlError> {
  167. match RequestUrl::parse(&url) {
  168. Ok(mut url_parsed) => {
  169. let mut update: Vec<RequestPathUpdate> = vec![];
  170. let mut subs = vec![];
  171. for seg in url_parsed.path.iter_mut() {
  172. if let Segment::Dynamic(seg, position) = seg {
  173. let Some(path_param) = path_params
  174. .iter()
  175. .find(|pp| pp.position as usize == *position || &pp.name == seg)
  176. else {
  177. update.push(RequestPathUpdate {
  178. position: *position,
  179. name: seg.to_string(),
  180. value: None,
  181. });
  182. continue;
  183. };
  184. if use_path_params {
  185. update.push(RequestPathUpdate {
  186. position: *position,
  187. name: path_param.name.clone(),
  188. value: Some(path_param.value.clone()),
  189. });
  190. subs.push(Segment::Dynamic(&path_param.name, *position));
  191. } else {
  192. update.push(RequestPathUpdate {
  193. position: *position,
  194. name: seg.to_string(),
  195. value: Some(path_param.value.clone()),
  196. })
  197. }
  198. }
  199. }
  200. dbg!(&subs);
  201. for sub in subs {
  202. url_parsed.swap_path_segment(sub);
  203. }
  204. dbg!(&update, &url_parsed);
  205. db::update_workspace_entry(
  206. state.db.clone(),
  207. entry_id,
  208. WorkspaceEntryUpdate::Request {
  209. path_params: Some(update),
  210. url: Some(url_parsed.to_string()),
  211. base: WorkspaceEntryUpdateBase::default(),
  212. method: None,
  213. },
  214. )
  215. .await
  216. .map_err(|e| UrlError::Db(e.to_string()))?;
  217. let params = match db::list_request_path_params(state.db.clone(), entry_id).await {
  218. Ok(p) => p,
  219. Err(e) => return Err(UrlError::Db(e.to_string())),
  220. };
  221. dbg!(&url_parsed, &params);
  222. Ok((url_parsed.into(), params))
  223. }
  224. Err(e) => {
  225. log::debug!("{e:?}");
  226. Err(UrlError::Parse(e))
  227. }
  228. }
  229. }
  230. #[tauri::command]
  231. pub async fn send_request(
  232. state: tauri::State<'_, AppState>,
  233. req_id: i64,
  234. env_id: Option<i64>,
  235. ) -> Result<HttpResponse, String> {
  236. let mut req = match db::get_workspace_request(state.db.clone(), req_id).await {
  237. Ok(req) => req,
  238. Err(e) => return Err(e.to_string()),
  239. };
  240. req.url = if let Some(env_id) = env_id {
  241. let vars = match parse_vars(&req.url) {
  242. Ok(vars) => vars.iter().map(|v| v.name).collect::<Vec<_>>(),
  243. Err(e) => return Err(e.to_string()),
  244. };
  245. let vars = match db::get_env_variables(state.db.clone(), env_id, &vars).await {
  246. Ok(v) => v,
  247. Err(e) => return Err(e.to_string()),
  248. };
  249. expand_vars(&req.url, &vars)
  250. } else {
  251. req.url
  252. };
  253. let req = match RequestUrl::parse(&req.url.to_string()) {
  254. Ok(mut url) => {
  255. let params = match db::list_request_path_params(state.db.clone(), req_id).await {
  256. Ok(p) => p,
  257. Err(e) => return Err(e.to_string()),
  258. };
  259. url.populate_path(
  260. params
  261. .iter()
  262. .map(|p| (p.name.as_str(), p.value.as_str()))
  263. .collect(),
  264. );
  265. req.url = url.to_string();
  266. let vars = match parse_vars(&req.url) {
  267. Ok(vars) => vars.iter().map(|v| v.name).collect::<Vec<_>>(),
  268. Err(e) => return Err(e.to_string()),
  269. };
  270. req.url = if let Some(env_id) = env_id {
  271. let vars = match db::get_env_variables(state.db.clone(), env_id, &vars).await {
  272. Ok(v) => v,
  273. Err(e) => return Err(e.to_string()),
  274. };
  275. expand_vars(&url.to_string(), &vars)
  276. } else {
  277. req.url
  278. };
  279. HttpRequestParameters::try_from(req)?
  280. }
  281. Err(e) => {
  282. log::debug!("{e:?}");
  283. return Err(String::from("error parsing URL"));
  284. }
  285. };
  286. let response = match request::send(state.client.clone(), req).await {
  287. Ok(res) => res,
  288. Err(e) => return Err(e.to_string()),
  289. };
  290. Ok(response)
  291. }
  292. #[tauri::command]
  293. pub async fn list_environments(
  294. state: tauri::State<'_, AppState>,
  295. workspace_id: i64,
  296. ) -> Result<Vec<WorkspaceEnvironment>, String> {
  297. match db::list_environments(state.db.clone(), workspace_id).await {
  298. Ok(ws) => Ok(ws),
  299. Err(e) => Err(e.to_string()),
  300. }
  301. }
  302. #[tauri::command]
  303. pub async fn create_env(
  304. state: tauri::State<'_, AppState>,
  305. workspace_id: i64,
  306. name: String,
  307. ) -> Result<WorkspaceEnvironment, String> {
  308. match db::create_environment(state.db.clone(), workspace_id, name).await {
  309. Ok(ws) => Ok(ws),
  310. Err(e) => Err(e.to_string()),
  311. }
  312. }
  313. #[tauri::command]
  314. pub async fn update_env(
  315. state: tauri::State<'_, AppState>,
  316. id: i64,
  317. name: String,
  318. ) -> Result<(), String> {
  319. if let Err(e) = db::update_environment(state.db.clone(), id, name).await {
  320. return Err(e.to_string());
  321. }
  322. Ok(())
  323. }
  324. #[tauri::command]
  325. pub async fn insert_env_var(
  326. state: tauri::State<'_, AppState>,
  327. workspace_id: i64,
  328. env_id: i64,
  329. name: String,
  330. value: String,
  331. secret: bool,
  332. ) -> Result<WorkspaceEnvVariable, String> {
  333. match db::insert_env_var(state.db.clone(), workspace_id, env_id, name, value, secret).await {
  334. Ok(var) => Ok(var),
  335. Err(e) => Err(e.to_string()),
  336. }
  337. }
  338. #[tauri::command]
  339. pub async fn update_env_var(
  340. state: tauri::State<'_, AppState>,
  341. id: i64,
  342. name: Option<String>,
  343. value: Option<String>,
  344. secret: Option<bool>,
  345. ) -> Result<(), String> {
  346. if let Err(e) = db::update_env_var(state.db.clone(), id, name, value, secret).await {
  347. return Err(e.to_string());
  348. }
  349. Ok(())
  350. }
  351. #[tauri::command]
  352. pub async fn delete_env_var(state: tauri::State<'_, AppState>, id: i64) -> Result<(), String> {
  353. if let Err(e) = db::delete_env_var(state.db.clone(), id).await {
  354. return Err(e.to_string());
  355. }
  356. Ok(())
  357. }
  358. #[tauri::command]
  359. pub async fn insert_header(
  360. state: tauri::State<'_, AppState>,
  361. entry_id: i64,
  362. insert: RequestHeaderInsert,
  363. ) -> Result<RequestHeader, String> {
  364. match db::insert_headers(state.db.clone(), entry_id, vec![insert]).await {
  365. Ok(header) => Ok(header),
  366. Err(e) => return Err(e.to_string()),
  367. }
  368. }
  369. #[tauri::command]
  370. pub async fn update_header(
  371. state: tauri::State<'_, AppState>,
  372. update: RequestHeaderUpdate,
  373. ) -> Result<(), String> {
  374. if let Err(e) = db::update_header(state.db.clone(), update).await {
  375. return Err(e.to_string());
  376. }
  377. Ok(())
  378. }
  379. #[tauri::command]
  380. pub async fn delete_header(
  381. state: tauri::State<'_, AppState>,
  382. header_id: i64,
  383. ) -> Result<(), String> {
  384. if let Err(e) = db::delete_header(state.db.clone(), header_id).await {
  385. return Err(e.to_string());
  386. }
  387. Ok(())
  388. }