mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-18 06:49:50 +02:00
Request Inheritance (#209)
This commit is contained in:
@@ -219,8 +219,13 @@ pub struct Workspace {
|
||||
pub id: String,
|
||||
pub created_at: NaiveDateTime,
|
||||
pub updated_at: NaiveDateTime,
|
||||
pub name: String,
|
||||
|
||||
#[ts(type = "Record<string, any>")]
|
||||
pub authentication: BTreeMap<String, Value>,
|
||||
pub authentication_type: Option<String>,
|
||||
pub description: String,
|
||||
pub headers: Vec<HttpRequestHeader>,
|
||||
pub name: String,
|
||||
pub encryption_key_challenge: Option<String>,
|
||||
|
||||
// Settings
|
||||
@@ -261,6 +266,9 @@ impl UpsertModelInfo for Workspace {
|
||||
(CreatedAt, upsert_date(source, self.created_at)),
|
||||
(UpdatedAt, upsert_date(source, self.updated_at)),
|
||||
(Name, self.name.trim().into()),
|
||||
(Authentication, serde_json::to_string(&self.authentication)?.into()),
|
||||
(AuthenticationType, self.authentication_type.into()),
|
||||
(Headers, serde_json::to_string(&self.headers)?.into()),
|
||||
(Description, self.description.into()),
|
||||
(EncryptionKeyChallenge, self.encryption_key_challenge.into()),
|
||||
(SettingFollowRedirects, self.setting_follow_redirects.into()),
|
||||
@@ -273,6 +281,9 @@ impl UpsertModelInfo for Workspace {
|
||||
vec![
|
||||
WorkspaceIden::UpdatedAt,
|
||||
WorkspaceIden::Name,
|
||||
WorkspaceIden::Authentication,
|
||||
WorkspaceIden::AuthenticationType,
|
||||
WorkspaceIden::Headers,
|
||||
WorkspaceIden::Description,
|
||||
WorkspaceIden::EncryptionKeyChallenge,
|
||||
WorkspaceIden::SettingRequestTimeout,
|
||||
@@ -286,6 +297,8 @@ impl UpsertModelInfo for Workspace {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let headers: String = row.get("headers")?;
|
||||
let authentication: String = row.get("authentication")?;
|
||||
Ok(Self {
|
||||
id: row.get("id")?,
|
||||
model: row.get("model")?,
|
||||
@@ -294,6 +307,9 @@ impl UpsertModelInfo for Workspace {
|
||||
name: row.get("name")?,
|
||||
description: row.get("description")?,
|
||||
encryption_key_challenge: row.get("encryption_key_challenge")?,
|
||||
headers: serde_json::from_str(&headers).unwrap_or_default(),
|
||||
authentication: serde_json::from_str(&authentication).unwrap_or_default(),
|
||||
authentication_type: row.get("authentication_type")?,
|
||||
setting_follow_redirects: row.get("setting_follow_redirects")?,
|
||||
setting_request_timeout: row.get("setting_request_timeout")?,
|
||||
setting_validate_certificates: row.get("setting_validate_certificates")?,
|
||||
@@ -581,6 +597,22 @@ pub struct EnvironmentVariable {
|
||||
pub id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct ParentAuthentication {
|
||||
#[ts(type = "Record<string, any>")]
|
||||
pub authentication: BTreeMap<String, Value>,
|
||||
pub authentication_type: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct ParentHeaders {
|
||||
pub headers: Vec<HttpRequestHeader>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
@@ -594,8 +626,12 @@ pub struct Folder {
|
||||
pub workspace_id: String,
|
||||
pub folder_id: Option<String>,
|
||||
|
||||
pub name: String,
|
||||
#[ts(type = "Record<string, any>")]
|
||||
pub authentication: BTreeMap<String, Value>,
|
||||
pub authentication_type: Option<String>,
|
||||
pub description: String,
|
||||
pub headers: Vec<HttpRequestHeader>,
|
||||
pub name: String,
|
||||
pub sort_priority: f32,
|
||||
}
|
||||
|
||||
@@ -630,8 +666,11 @@ impl UpsertModelInfo for Folder {
|
||||
(UpdatedAt, upsert_date(source, self.updated_at)),
|
||||
(WorkspaceId, self.workspace_id.into()),
|
||||
(FolderId, self.folder_id.into()),
|
||||
(Name, self.name.trim().into()),
|
||||
(Authentication, serde_json::to_string(&self.authentication)?.into()),
|
||||
(AuthenticationType, self.authentication_type.into()),
|
||||
(Headers, serde_json::to_string(&self.headers)?.into()),
|
||||
(Description, self.description.into()),
|
||||
(Name, self.name.trim().into()),
|
||||
(SortPriority, self.sort_priority.into()),
|
||||
])
|
||||
}
|
||||
@@ -640,6 +679,9 @@ impl UpsertModelInfo for Folder {
|
||||
vec![
|
||||
FolderIden::UpdatedAt,
|
||||
FolderIden::Name,
|
||||
FolderIden::Authentication,
|
||||
FolderIden::AuthenticationType,
|
||||
FolderIden::Headers,
|
||||
FolderIden::Description,
|
||||
FolderIden::FolderId,
|
||||
FolderIden::SortPriority,
|
||||
@@ -650,6 +692,8 @@ impl UpsertModelInfo for Folder {
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let headers: String = row.get("headers")?;
|
||||
let authentication: String = row.get("authentication")?;
|
||||
Ok(Self {
|
||||
id: row.get("id")?,
|
||||
model: row.get("model")?,
|
||||
@@ -660,6 +704,9 @@ impl UpsertModelInfo for Folder {
|
||||
folder_id: row.get("folder_id")?,
|
||||
name: row.get("name")?,
|
||||
description: row.get("description")?,
|
||||
headers: serde_json::from_str(&headers).unwrap_or_default(),
|
||||
authentication_type: row.get("authentication_type")?,
|
||||
authentication: serde_json::from_str(&authentication).unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -782,28 +829,28 @@ impl UpsertModelInfo for HttpRequest {
|
||||
]
|
||||
}
|
||||
|
||||
fn from_row(r: &Row) -> rusqlite::Result<Self> {
|
||||
let url_parameters: String = r.get("url_parameters")?;
|
||||
let body: String = r.get("body")?;
|
||||
let authentication: String = r.get("authentication")?;
|
||||
let headers: String = r.get("headers")?;
|
||||
fn from_row(row: &Row) -> rusqlite::Result<Self> {
|
||||
let url_parameters: String = row.get("url_parameters")?;
|
||||
let body: String = row.get("body")?;
|
||||
let authentication: String = row.get("authentication")?;
|
||||
let headers: String = row.get("headers")?;
|
||||
Ok(Self {
|
||||
id: r.get("id")?,
|
||||
model: r.get("model")?,
|
||||
workspace_id: r.get("workspace_id")?,
|
||||
created_at: r.get("created_at")?,
|
||||
updated_at: r.get("updated_at")?,
|
||||
id: row.get("id")?,
|
||||
model: row.get("model")?,
|
||||
workspace_id: row.get("workspace_id")?,
|
||||
created_at: row.get("created_at")?,
|
||||
updated_at: row.get("updated_at")?,
|
||||
authentication: serde_json::from_str(authentication.as_str()).unwrap_or_default(),
|
||||
authentication_type: r.get("authentication_type")?,
|
||||
authentication_type: row.get("authentication_type")?,
|
||||
body: serde_json::from_str(body.as_str()).unwrap_or_default(),
|
||||
body_type: r.get("body_type")?,
|
||||
description: r.get("description")?,
|
||||
folder_id: r.get("folder_id")?,
|
||||
body_type: row.get("body_type")?,
|
||||
description: row.get("description")?,
|
||||
folder_id: row.get("folder_id")?,
|
||||
headers: serde_json::from_str(headers.as_str()).unwrap_or_default(),
|
||||
method: r.get("method")?,
|
||||
name: r.get("name")?,
|
||||
sort_priority: r.get("sort_priority")?,
|
||||
url: r.get("url")?,
|
||||
method: row.get("method")?,
|
||||
name: row.get("name")?,
|
||||
sort_priority: row.get("sort_priority")?,
|
||||
url: row.get("url")?,
|
||||
url_parameters: serde_json::from_str(url_parameters.as_str()).unwrap_or_default(),
|
||||
})
|
||||
}
|
||||
@@ -992,7 +1039,7 @@ impl UpsertModelInfo for WebsocketRequest {
|
||||
(WorkspaceId, self.workspace_id.into()),
|
||||
(FolderId, self.folder_id.as_ref().map(|s| s.as_str()).into()),
|
||||
(Authentication, serde_json::to_string(&self.authentication)?.into()),
|
||||
(AuthenticationType, self.authentication_type.as_ref().map(|s| s.as_str()).into()),
|
||||
(AuthenticationType, self.authentication_type.into()),
|
||||
(Description, self.description.into()),
|
||||
(Headers, serde_json::to_string(&self.headers)?.into()),
|
||||
(Message, self.message.into()),
|
||||
@@ -1295,19 +1342,6 @@ impl UpsertModelInfo for HttpResponse {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
pub struct GrpcMetadataEntry {
|
||||
#[serde(default = "default_true")]
|
||||
#[ts(optional, as = "Option<bool>")]
|
||||
pub enabled: bool,
|
||||
pub name: String,
|
||||
pub value: String,
|
||||
#[ts(optional, as = "Option<String>")]
|
||||
pub id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_models.ts")]
|
||||
@@ -1326,7 +1360,7 @@ pub struct GrpcRequest {
|
||||
pub authentication: BTreeMap<String, Value>,
|
||||
pub description: String,
|
||||
pub message: String,
|
||||
pub metadata: Vec<GrpcMetadataEntry>,
|
||||
pub metadata: Vec<HttpRequestHeader>,
|
||||
pub method: Option<String>,
|
||||
pub name: String,
|
||||
pub service: Option<String>,
|
||||
|
||||
@@ -2,10 +2,12 @@ use crate::connection_or_tx::ConnectionOrTx;
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{
|
||||
Folder, FolderIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestIden,
|
||||
WebsocketRequest, WebsocketRequestIden,
|
||||
Folder, FolderIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestHeader,
|
||||
HttpRequestIden, WebsocketRequest, WebsocketRequestIden,
|
||||
};
|
||||
use crate::util::UpdateSource;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl<'a> DbContext<'a> {
|
||||
pub fn get_folder(&self, id: &str) -> Result<Folder> {
|
||||
@@ -110,4 +112,40 @@ impl<'a> DbContext<'a> {
|
||||
|
||||
Ok(new_folder)
|
||||
}
|
||||
|
||||
pub fn resolve_auth_for_folder(
|
||||
&self,
|
||||
folder: Folder,
|
||||
) -> Result<(Option<String>, BTreeMap<String, Value>)> {
|
||||
if let Some(at) = folder.authentication_type {
|
||||
return Ok((Some(at), folder.authentication));
|
||||
}
|
||||
|
||||
if let Some(folder_id) = folder.folder_id {
|
||||
let folder = self.get_folder(&folder_id)?;
|
||||
return self.resolve_auth_for_folder(folder);
|
||||
}
|
||||
|
||||
let workspace = self.get_workspace(&folder.workspace_id)?;
|
||||
Ok(self.resolve_auth_for_workspace(&workspace))
|
||||
}
|
||||
|
||||
pub fn resolve_headers_for_folder(&self, folder: &Folder) -> Result<Vec<HttpRequestHeader>> {
|
||||
let mut headers = Vec::new();
|
||||
|
||||
if let Some(folder_id) = folder.folder_id.clone() {
|
||||
let parent_folder = self.get_folder(&folder_id)?;
|
||||
let mut folder_headers = self.resolve_headers_for_folder(&parent_folder)?;
|
||||
// NOTE: Add parent headers first, so overrides are logical
|
||||
headers.append(&mut folder_headers);
|
||||
} else {
|
||||
let workspace = self.get_workspace(&folder.workspace_id)?;
|
||||
let mut workspace_headers = self.resolve_headers_for_workspace(&workspace);
|
||||
headers.append(&mut workspace_headers);
|
||||
}
|
||||
|
||||
headers.append(&mut folder.headers.clone());
|
||||
|
||||
Ok(headers)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{GrpcRequest, GrpcRequestIden};
|
||||
use crate::models::{GrpcRequest, GrpcRequestIden, HttpRequestHeader};
|
||||
use crate::util::UpdateSource;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl<'a> DbContext<'a> {
|
||||
pub fn get_grpc_request(&self, id: &str) -> Result<GrpcRequest> {
|
||||
@@ -48,4 +50,43 @@ impl<'a> DbContext<'a> {
|
||||
) -> Result<GrpcRequest> {
|
||||
self.upsert(grpc_request, source)
|
||||
}
|
||||
|
||||
pub fn resolve_auth_for_grpc_request(
|
||||
&self,
|
||||
grpc_request: &GrpcRequest,
|
||||
) -> Result<(Option<String>, BTreeMap<String, Value>)> {
|
||||
if let Some(at) = grpc_request.authentication_type.clone() {
|
||||
return Ok((Some(at), grpc_request.authentication.clone()));
|
||||
}
|
||||
|
||||
if let Some(folder_id) = grpc_request.folder_id.clone() {
|
||||
let folder = self.get_folder(&folder_id)?;
|
||||
return self.resolve_auth_for_folder(folder);
|
||||
}
|
||||
|
||||
let workspace = self.get_workspace(&grpc_request.workspace_id)?;
|
||||
Ok(self.resolve_auth_for_workspace(&workspace))
|
||||
}
|
||||
|
||||
pub fn resolve_metadata_for_grpc_request(
|
||||
&self,
|
||||
grpc_request: &GrpcRequest,
|
||||
) -> Result<Vec<HttpRequestHeader>> {
|
||||
// Resolved headers should be from furthest to closest ancestor, to override logically.
|
||||
let mut metadata = Vec::new();
|
||||
|
||||
if let Some(folder_id) = grpc_request.folder_id.clone() {
|
||||
let parent_folder = self.get_folder(&folder_id)?;
|
||||
let mut folder_headers = self.resolve_headers_for_folder(&parent_folder)?;
|
||||
metadata.append(&mut folder_headers);
|
||||
} else {
|
||||
let workspace = self.get_workspace(&grpc_request.workspace_id)?;
|
||||
let mut workspace_metadata = self.resolve_headers_for_workspace(&workspace);
|
||||
metadata.append(&mut workspace_metadata);
|
||||
}
|
||||
|
||||
metadata.append(&mut grpc_request.metadata.clone());
|
||||
|
||||
Ok(metadata)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{HttpRequest, HttpRequestIden};
|
||||
use crate::models::{HttpRequest, HttpRequestHeader, HttpRequestIden};
|
||||
use crate::util::UpdateSource;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl<'a> DbContext<'a> {
|
||||
pub fn get_http_request(&self, id: &str) -> Result<HttpRequest> {
|
||||
@@ -48,4 +50,43 @@ impl<'a> DbContext<'a> {
|
||||
) -> Result<HttpRequest> {
|
||||
self.upsert(http_request, source)
|
||||
}
|
||||
|
||||
pub fn resolve_auth_for_http_request(
|
||||
&self,
|
||||
http_request: &HttpRequest,
|
||||
) -> Result<(Option<String>, BTreeMap<String, Value>)> {
|
||||
if let Some(at) = http_request.authentication_type.clone() {
|
||||
return Ok((Some(at), http_request.authentication.clone()));
|
||||
}
|
||||
|
||||
if let Some(folder_id) = http_request.folder_id.clone() {
|
||||
let folder = self.get_folder(&folder_id)?;
|
||||
return self.resolve_auth_for_folder(folder);
|
||||
}
|
||||
|
||||
let workspace = self.get_workspace(&http_request.workspace_id)?;
|
||||
Ok(self.resolve_auth_for_workspace(&workspace))
|
||||
}
|
||||
|
||||
pub fn resolve_headers_for_http_request(
|
||||
&self,
|
||||
http_request: &HttpRequest,
|
||||
) -> Result<Vec<HttpRequestHeader>> {
|
||||
// Resolved headers should be from furthest to closest ancestor, to override logically.
|
||||
let mut headers = Vec::new();
|
||||
|
||||
if let Some(folder_id) = http_request.folder_id.clone() {
|
||||
let parent_folder = self.get_folder(&folder_id)?;
|
||||
let mut folder_headers = self.resolve_headers_for_folder(&parent_folder)?;
|
||||
headers.append(&mut folder_headers);
|
||||
} else {
|
||||
let workspace = self.get_workspace(&http_request.workspace_id)?;
|
||||
let mut workspace_headers = self.resolve_headers_for_workspace(&workspace);
|
||||
headers.append(&mut workspace_headers);
|
||||
}
|
||||
|
||||
headers.append(&mut http_request.headers.clone());
|
||||
|
||||
Ok(headers)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{WebsocketRequest, WebsocketRequestIden};
|
||||
use crate::models::{HttpRequestHeader, WebsocketRequest, WebsocketRequestIden};
|
||||
use crate::util::UpdateSource;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl<'a> DbContext<'a> {
|
||||
pub fn get_websocket_request(&self, id: &str) -> Result<WebsocketRequest> {
|
||||
@@ -48,4 +50,47 @@ impl<'a> DbContext<'a> {
|
||||
) -> Result<WebsocketRequest> {
|
||||
self.upsert(websocket_request, source)
|
||||
}
|
||||
|
||||
pub fn resolve_auth_for_websocket_request(
|
||||
&self,
|
||||
websocket_request: &WebsocketRequest,
|
||||
) -> Result<(Option<String>, BTreeMap<String, Value>)> {
|
||||
if let Some(at) = websocket_request.authentication_type.clone() {
|
||||
return Ok((Some(at), websocket_request.authentication.clone()));
|
||||
}
|
||||
|
||||
if let Some(folder_id) = websocket_request.folder_id.clone() {
|
||||
let folder = self.get_folder(&folder_id)?;
|
||||
return self.resolve_auth_for_folder(folder);
|
||||
}
|
||||
|
||||
let workspace = self.get_workspace(&websocket_request.workspace_id)?;
|
||||
Ok(self.resolve_auth_for_workspace(&workspace))
|
||||
}
|
||||
|
||||
pub fn resolve_headers_for_websocket_request(
|
||||
&self,
|
||||
websocket_request: &WebsocketRequest,
|
||||
) -> Result<Vec<HttpRequestHeader>> {
|
||||
let workspace = self.get_workspace(&websocket_request.workspace_id)?;
|
||||
|
||||
// Resolved headers should be from furthest to closest ancestor, to override logically.
|
||||
let mut headers = Vec::new();
|
||||
|
||||
headers.append(&mut workspace.headers.clone());
|
||||
|
||||
if let Some(folder_id) = websocket_request.folder_id.clone() {
|
||||
let parent_folder = self.get_folder(&folder_id)?;
|
||||
let mut folder_headers = self.resolve_headers_for_folder(&parent_folder)?;
|
||||
headers.append(&mut folder_headers);
|
||||
} else {
|
||||
let workspace = self.get_workspace(&websocket_request.workspace_id)?;
|
||||
let mut workspace_headers = self.resolve_headers_for_workspace(&workspace);
|
||||
headers.append(&mut workspace_headers);
|
||||
}
|
||||
|
||||
headers.append(&mut websocket_request.headers.clone());
|
||||
|
||||
Ok(headers)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
use crate::db_context::DbContext;
|
||||
use crate::error::Result;
|
||||
use crate::models::{
|
||||
EnvironmentIden, FolderIden, GrpcRequestIden, HttpRequestIden, WebsocketRequestIden, Workspace,
|
||||
WorkspaceIden,
|
||||
EnvironmentIden, FolderIden, GrpcRequestIden, HttpRequestHeader, HttpRequestIden,
|
||||
WebsocketRequestIden, Workspace, WorkspaceIden,
|
||||
};
|
||||
use crate::util::UpdateSource;
|
||||
use serde_json::Value;
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
impl<'a> DbContext<'a> {
|
||||
pub fn get_workspace(&self, id: &str) -> Result<Workspace> {
|
||||
@@ -65,4 +67,15 @@ impl<'a> DbContext<'a> {
|
||||
pub fn upsert_workspace(&self, w: &Workspace, source: &UpdateSource) -> Result<Workspace> {
|
||||
self.upsert(w, source)
|
||||
}
|
||||
|
||||
pub fn resolve_auth_for_workspace(
|
||||
&self,
|
||||
workspace: &Workspace,
|
||||
) -> (Option<String>, BTreeMap<String, Value>) {
|
||||
(workspace.authentication_type.clone(), workspace.authentication.clone())
|
||||
}
|
||||
|
||||
pub fn resolve_headers_for_workspace(&self, workspace: &Workspace) -> Vec<HttpRequestHeader> {
|
||||
workspace.headers.clone()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user