migrated remaining components to rust.
This commit is contained in:
63
src-tauri/src/requests/commands.rs
Normal file
63
src-tauri/src/requests/commands.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use tauri::State;
|
||||
|
||||
use crate::db::Database;
|
||||
|
||||
use super::service::RequestService;
|
||||
use super::types::{CreateRequestInput, Request, UpdateRequestInput};
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_request(db: State<Database>, id: String) -> Result<Request, String> {
|
||||
let service = RequestService::new(db.inner().clone());
|
||||
service.get(&id).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_requests_by_collection(
|
||||
db: State<Database>,
|
||||
collection_id: String,
|
||||
) -> Result<Vec<Request>, String> {
|
||||
let service = RequestService::new(db.inner().clone());
|
||||
service.get_by_collection(&collection_id).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_standalone_requests_by_workspace(
|
||||
db: State<Database>,
|
||||
workspace_id: String,
|
||||
) -> Result<Vec<Request>, String> {
|
||||
let service = RequestService::new(db.inner().clone());
|
||||
service.get_standalone_by_workspace(&workspace_id).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn get_all_requests_by_workspace(
|
||||
db: State<Database>,
|
||||
workspace_id: String,
|
||||
) -> Result<Vec<Request>, String> {
|
||||
let service = RequestService::new(db.inner().clone());
|
||||
service.get_all_by_workspace(&workspace_id).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn create_request(
|
||||
db: State<Database>,
|
||||
input: CreateRequestInput,
|
||||
) -> Result<Request, String> {
|
||||
let service = RequestService::new(db.inner().clone());
|
||||
service.create(input).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn update_request(
|
||||
db: State<Database>,
|
||||
input: UpdateRequestInput,
|
||||
) -> Result<Request, String> {
|
||||
let service = RequestService::new(db.inner().clone());
|
||||
service.update(input).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub fn delete_request(db: State<Database>, id: String) -> Result<(), String> {
|
||||
let service = RequestService::new(db.inner().clone());
|
||||
service.delete(&id).map_err(|e| e.to_string())
|
||||
}
|
||||
11
src-tauri/src/requests/mod.rs
Normal file
11
src-tauri/src/requests/mod.rs
Normal file
@@ -0,0 +1,11 @@
|
||||
mod commands;
|
||||
mod service;
|
||||
mod types;
|
||||
|
||||
pub use commands::*;
|
||||
#[allow(unused_imports)]
|
||||
pub use types::{
|
||||
BodyType, CreateRequestInput, FormDataItem, HttpMethod, Request, RequestHeader,
|
||||
RequestParam, UpdateRequestInput,
|
||||
};
|
||||
pub(crate) use service::RequestService;
|
||||
298
src-tauri/src/requests/service.rs
Normal file
298
src-tauri/src/requests/service.rs
Normal file
@@ -0,0 +1,298 @@
|
||||
use chrono::Utc;
|
||||
use redb::ReadableTable;
|
||||
|
||||
use crate::db::{
|
||||
Database, DbError, DbResult, REQUESTS, REQUESTS_BY_COLLECTION, REQUESTS_BY_WORKSPACE,
|
||||
};
|
||||
|
||||
use super::types::{CreateRequestInput, Request, UpdateRequestInput};
|
||||
|
||||
pub struct RequestService {
|
||||
db: Database,
|
||||
}
|
||||
|
||||
impl RequestService {
|
||||
pub fn new(db: Database) -> Self {
|
||||
Self { db }
|
||||
}
|
||||
|
||||
pub fn get(&self, id: &str) -> DbResult<Request> {
|
||||
let read_txn = self.db.begin_read()?;
|
||||
let table = read_txn.open_table(REQUESTS)?;
|
||||
|
||||
let value = table
|
||||
.get(id)?
|
||||
.ok_or_else(|| DbError::NotFound(format!("Request not found: {}", id)))?;
|
||||
|
||||
let request: Request = serde_json::from_str(value.value())
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
pub fn get_by_collection(&self, collection_id: &str) -> DbResult<Vec<Request>> {
|
||||
let read_txn = self.db.begin_read()?;
|
||||
let idx_table = read_txn.open_table(REQUESTS_BY_COLLECTION)?;
|
||||
|
||||
let request_ids: Vec<String> = match idx_table.get(collection_id)? {
|
||||
Some(value) => serde_json::from_str(value.value())
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?,
|
||||
None => return Ok(Vec::new()),
|
||||
};
|
||||
|
||||
drop(idx_table);
|
||||
drop(read_txn);
|
||||
|
||||
let mut requests = Vec::new();
|
||||
for id in request_ids {
|
||||
if let Ok(request) = self.get(&id) {
|
||||
requests.push(request);
|
||||
}
|
||||
}
|
||||
|
||||
requests.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
Ok(requests)
|
||||
}
|
||||
|
||||
pub fn get_standalone_by_workspace(&self, workspace_id: &str) -> DbResult<Vec<Request>> {
|
||||
let read_txn = self.db.begin_read()?;
|
||||
let idx_table = read_txn.open_table(REQUESTS_BY_WORKSPACE)?;
|
||||
|
||||
let request_ids: Vec<String> = match idx_table.get(workspace_id)? {
|
||||
Some(value) => serde_json::from_str(value.value())
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?,
|
||||
None => return Ok(Vec::new()),
|
||||
};
|
||||
|
||||
drop(idx_table);
|
||||
drop(read_txn);
|
||||
|
||||
let mut requests = Vec::new();
|
||||
for id in request_ids {
|
||||
if let Ok(request) = self.get(&id) {
|
||||
if request.collection_id.is_none() {
|
||||
requests.push(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
requests.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
Ok(requests)
|
||||
}
|
||||
|
||||
pub fn get_all_by_workspace(&self, workspace_id: &str) -> DbResult<Vec<Request>> {
|
||||
let read_txn = self.db.begin_read()?;
|
||||
let idx_table = read_txn.open_table(REQUESTS_BY_WORKSPACE)?;
|
||||
|
||||
let request_ids: Vec<String> = match idx_table.get(workspace_id)? {
|
||||
Some(value) => serde_json::from_str(value.value())
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?,
|
||||
None => return Ok(Vec::new()),
|
||||
};
|
||||
|
||||
drop(idx_table);
|
||||
drop(read_txn);
|
||||
|
||||
let mut requests = Vec::new();
|
||||
for id in request_ids {
|
||||
if let Ok(request) = self.get(&id) {
|
||||
requests.push(request);
|
||||
}
|
||||
}
|
||||
|
||||
requests.sort_by(|a, b| a.name.cmp(&b.name));
|
||||
Ok(requests)
|
||||
}
|
||||
|
||||
pub fn create(&self, input: CreateRequestInput) -> DbResult<Request> {
|
||||
let mut request = Request::new(input.name, input.method, input.workspace_id.clone());
|
||||
request.url = input.url;
|
||||
request.headers = input.headers;
|
||||
request.params = input.params;
|
||||
request.body_type = input.body_type;
|
||||
request.body = input.body;
|
||||
request.form_data = input.form_data;
|
||||
request.collection_id = input.collection_id.clone();
|
||||
|
||||
let json = serde_json::to_string(&request)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
|
||||
let write_txn = self.db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(REQUESTS)?;
|
||||
table.insert(request.id.as_str(), json.as_str())?;
|
||||
}
|
||||
|
||||
// Update workspace index
|
||||
{
|
||||
let mut idx_table = write_txn.open_table(REQUESTS_BY_WORKSPACE)?;
|
||||
let ids_json = match idx_table.get(input.workspace_id.as_str())? {
|
||||
Some(value) => value.value().to_string(),
|
||||
None => "[]".to_string(),
|
||||
};
|
||||
|
||||
let mut ids: Vec<String> = serde_json::from_str(&ids_json)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
|
||||
ids.push(request.id.clone());
|
||||
|
||||
let new_json = serde_json::to_string(&ids)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
|
||||
idx_table.insert(input.workspace_id.as_str(), new_json.as_str())?;
|
||||
}
|
||||
|
||||
// Update collection index if applicable
|
||||
if let Some(ref collection_id) = input.collection_id {
|
||||
let mut idx_table = write_txn.open_table(REQUESTS_BY_COLLECTION)?;
|
||||
let ids_json = match idx_table.get(collection_id.as_str())? {
|
||||
Some(value) => value.value().to_string(),
|
||||
None => "[]".to_string(),
|
||||
};
|
||||
|
||||
let mut ids: Vec<String> = serde_json::from_str(&ids_json)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
|
||||
ids.push(request.id.clone());
|
||||
|
||||
let new_json = serde_json::to_string(&ids)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
|
||||
idx_table.insert(collection_id.as_str(), new_json.as_str())?;
|
||||
}
|
||||
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
pub fn update(&self, input: UpdateRequestInput) -> DbResult<Request> {
|
||||
let mut request = self.get(&input.id)?;
|
||||
let old_collection_id = request.collection_id.clone();
|
||||
|
||||
if let Some(name) = input.name {
|
||||
request.name = name;
|
||||
}
|
||||
if let Some(method) = input.method {
|
||||
request.method = method;
|
||||
}
|
||||
if let Some(url) = input.url {
|
||||
request.url = url;
|
||||
}
|
||||
if let Some(headers) = input.headers {
|
||||
request.headers = headers;
|
||||
}
|
||||
if let Some(params) = input.params {
|
||||
request.params = params;
|
||||
}
|
||||
if let Some(body_type) = input.body_type {
|
||||
request.body_type = body_type;
|
||||
}
|
||||
if let Some(body) = input.body {
|
||||
request.body = body;
|
||||
}
|
||||
if let Some(form_data) = input.form_data {
|
||||
request.form_data = form_data;
|
||||
}
|
||||
if let Some(collection_id) = input.collection_id {
|
||||
request.collection_id = collection_id;
|
||||
}
|
||||
request.updated_at = Utc::now();
|
||||
|
||||
let json = serde_json::to_string(&request)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
|
||||
let write_txn = self.db.begin_write()?;
|
||||
|
||||
{
|
||||
let mut table = write_txn.open_table(REQUESTS)?;
|
||||
table.insert(request.id.as_str(), json.as_str())?;
|
||||
}
|
||||
|
||||
// Update collection index if collection changed
|
||||
if old_collection_id != request.collection_id {
|
||||
// Remove from old collection index
|
||||
if let Some(ref old_id) = old_collection_id {
|
||||
let mut idx_table = write_txn.open_table(REQUESTS_BY_COLLECTION)?;
|
||||
let ids_json = idx_table
|
||||
.get(old_id.as_str())?
|
||||
.map(|v| v.value().to_string())
|
||||
.unwrap_or_else(|| "[]".to_string());
|
||||
let mut ids: Vec<String> = serde_json::from_str(&ids_json)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
ids.retain(|i| i != &request.id);
|
||||
let new_json = serde_json::to_string(&ids)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
idx_table.insert(old_id.as_str(), new_json.as_str())?;
|
||||
}
|
||||
|
||||
// Add to new collection index
|
||||
if let Some(ref new_id) = request.collection_id {
|
||||
let mut idx_table = write_txn.open_table(REQUESTS_BY_COLLECTION)?;
|
||||
let ids_json = idx_table
|
||||
.get(new_id.as_str())?
|
||||
.map(|v| v.value().to_string())
|
||||
.unwrap_or_else(|| "[]".to_string());
|
||||
let mut ids: Vec<String> = serde_json::from_str(&ids_json)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
if !ids.contains(&request.id) {
|
||||
ids.push(request.id.clone());
|
||||
}
|
||||
let new_json = serde_json::to_string(&ids)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
idx_table.insert(new_id.as_str(), new_json.as_str())?;
|
||||
}
|
||||
}
|
||||
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(request)
|
||||
}
|
||||
|
||||
pub fn delete(&self, id: &str) -> DbResult<()> {
|
||||
let request = self.get(id)?;
|
||||
|
||||
let write_txn = self.db.begin_write()?;
|
||||
|
||||
// Remove from requests table
|
||||
{
|
||||
let mut table = write_txn.open_table(REQUESTS)?;
|
||||
table.remove(id)?;
|
||||
}
|
||||
|
||||
// Update workspace index
|
||||
{
|
||||
let mut idx_table = write_txn.open_table(REQUESTS_BY_WORKSPACE)?;
|
||||
let ids_json = idx_table
|
||||
.get(request.workspace_id.as_str())?
|
||||
.map(|v| v.value().to_string())
|
||||
.unwrap_or_else(|| "[]".to_string());
|
||||
let mut ids: Vec<String> = serde_json::from_str(&ids_json)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
ids.retain(|i| i != id);
|
||||
let new_json = serde_json::to_string(&ids)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
idx_table.insert(request.workspace_id.as_str(), new_json.as_str())?;
|
||||
}
|
||||
|
||||
// Update collection index if applicable
|
||||
if let Some(ref collection_id) = request.collection_id {
|
||||
let mut idx_table = write_txn.open_table(REQUESTS_BY_COLLECTION)?;
|
||||
let ids_json = idx_table
|
||||
.get(collection_id.as_str())?
|
||||
.map(|v| v.value().to_string())
|
||||
.unwrap_or_else(|| "[]".to_string());
|
||||
let mut ids: Vec<String> = serde_json::from_str(&ids_json)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
ids.retain(|i| i != id);
|
||||
let new_json = serde_json::to_string(&ids)
|
||||
.map_err(|e| DbError::Serialization(e.to_string()))?;
|
||||
idx_table.insert(collection_id.as_str(), new_json.as_str())?;
|
||||
}
|
||||
|
||||
write_txn.commit()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
142
src-tauri/src/requests/types.rs
Normal file
142
src-tauri/src/requests/types.rs
Normal file
@@ -0,0 +1,142 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "UPPERCASE")]
|
||||
pub enum HttpMethod {
|
||||
Get,
|
||||
Post,
|
||||
Put,
|
||||
Patch,
|
||||
Delete,
|
||||
Head,
|
||||
Options,
|
||||
}
|
||||
|
||||
impl Default for HttpMethod {
|
||||
fn default() -> Self {
|
||||
Self::Get
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum BodyType {
|
||||
None,
|
||||
Json,
|
||||
Xml,
|
||||
Text,
|
||||
Html,
|
||||
FormData,
|
||||
#[serde(rename = "x-www-form-urlencoded")]
|
||||
XWwwFormUrlencoded,
|
||||
}
|
||||
|
||||
impl Default for BodyType {
|
||||
fn default() -> Self {
|
||||
Self::None
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RequestHeader {
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct RequestParam {
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct FormDataItem {
|
||||
pub key: String,
|
||||
pub value: String,
|
||||
#[serde(rename = "type")]
|
||||
pub item_type: String, // "text" or "file"
|
||||
pub enabled: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct Request {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
pub method: HttpMethod,
|
||||
pub url: String,
|
||||
#[serde(default)]
|
||||
pub headers: Vec<RequestHeader>,
|
||||
#[serde(default)]
|
||||
pub params: Vec<RequestParam>,
|
||||
#[serde(default)]
|
||||
pub body_type: BodyType,
|
||||
#[serde(default)]
|
||||
pub body: String,
|
||||
#[serde(default)]
|
||||
pub form_data: Vec<FormDataItem>,
|
||||
pub collection_id: Option<String>,
|
||||
pub workspace_id: String,
|
||||
#[serde(default = "Utc::now")]
|
||||
pub created_at: DateTime<Utc>,
|
||||
#[serde(default = "Utc::now")]
|
||||
pub updated_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
impl Request {
|
||||
pub fn new(name: String, method: HttpMethod, workspace_id: String) -> Self {
|
||||
let now = Utc::now();
|
||||
Self {
|
||||
id: Uuid::new_v4().to_string(),
|
||||
name,
|
||||
method,
|
||||
url: String::new(),
|
||||
headers: Vec::new(),
|
||||
params: Vec::new(),
|
||||
body_type: BodyType::None,
|
||||
body: String::new(),
|
||||
form_data: Vec::new(),
|
||||
collection_id: None,
|
||||
workspace_id,
|
||||
created_at: now,
|
||||
updated_at: now,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct CreateRequestInput {
|
||||
pub name: String,
|
||||
pub method: HttpMethod,
|
||||
#[serde(default)]
|
||||
pub url: String,
|
||||
#[serde(default)]
|
||||
pub headers: Vec<RequestHeader>,
|
||||
#[serde(default)]
|
||||
pub params: Vec<RequestParam>,
|
||||
#[serde(default)]
|
||||
pub body_type: BodyType,
|
||||
#[serde(default)]
|
||||
pub body: String,
|
||||
#[serde(default)]
|
||||
pub form_data: Vec<FormDataItem>,
|
||||
pub collection_id: Option<String>,
|
||||
pub workspace_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct UpdateRequestInput {
|
||||
pub id: String,
|
||||
pub name: Option<String>,
|
||||
pub method: Option<HttpMethod>,
|
||||
pub url: Option<String>,
|
||||
pub headers: Option<Vec<RequestHeader>>,
|
||||
pub params: Option<Vec<RequestParam>>,
|
||||
pub body_type: Option<BodyType>,
|
||||
pub body: Option<String>,
|
||||
pub form_data: Option<Vec<FormDataItem>>,
|
||||
pub collection_id: Option<Option<String>>,
|
||||
}
|
||||
Reference in New Issue
Block a user