Merge pull request #256

* Update environment model to get ready for request/folder environments

* Folder environments in UI

* Folder environments working

* Tweaks and fixes

* Tweak environment encryption UX

* Tweak environment encryption UX

* Address comments

* Update fn name

* Add tsc back to lint rules

* Update src-web/components/EnvironmentEditor.tsx

* Merge remote-tracking branch 'origin/folder-environments' into folder…
This commit is contained in:
Gregory Schier
2025-09-21 07:54:26 -07:00
committed by GitHub
parent 46b049c72b
commit eb3d1c409b
85 changed files with 776 additions and 534 deletions

View File

@@ -43,14 +43,14 @@ pub async fn send_http_request<R: Runtime>(
) -> Result<HttpResponse> {
let app_handle = window.app_handle().clone();
let plugin_manager = app_handle.state::<PluginManager>();
let (settings, workspace) = {
let db = window.db();
let settings = db.get_settings();
let workspace = db.get_workspace(&unrendered_request.workspace_id)?;
(settings, workspace)
};
let base_environment =
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
let settings = window.db().get_settings();
let workspace = window.db().get_workspace(&unrendered_request.workspace_id)?;
let environment_id = environment.map(|e| e.id);
let environment_chain = window.db().resolve_environments(
&unrendered_request.workspace_id,
unrendered_request.folder_id.as_deref(),
environment_id.as_deref(),
)?;
let response_id = og_response.id.clone();
let response = Arc::new(Mutex::new(og_response.clone()));
@@ -76,20 +76,17 @@ pub async fn send_http_request<R: Runtime>(
RenderPurpose::Send,
);
let request =
match render_http_request(&resolved_request, &base_environment, environment.as_ref(), &cb)
.await
{
Ok(r) => r,
Err(e) => {
return Ok(response_err(
&app_handle,
&*response.lock().await,
e.to_string(),
&update_source,
));
}
};
let request = match render_http_request(&resolved_request, environment_chain, &cb).await {
Ok(r) => r,
Err(e) => {
return Ok(response_err(
&app_handle,
&*response.lock().await,
e.to_string(),
&update_source,
));
}
};
let mut url_string = request.url.clone();

View File

@@ -30,8 +30,9 @@ use yaak_common::window::WorkspaceWindowTrait;
use yaak_grpc::manager::{DynamicMessage, GrpcHandle};
use yaak_grpc::{Code, ServiceDefinition, deserialize_message, serialize_message};
use yaak_models::models::{
CookieJar, Environment, GrpcConnection, GrpcConnectionState, GrpcEvent, GrpcEventType,
GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, Plugin, Workspace, WorkspaceMeta,
AnyModel, CookieJar, Environment, GrpcConnection, GrpcConnectionState, GrpcEvent,
GrpcEventType, GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, Plugin, Workspace,
WorkspaceMeta,
};
use yaak_models::query_manager::QueryManagerExt;
use yaak_models::util::{BatchUpsertResult, UpdateSource, get_workspace_export_resources};
@@ -110,15 +111,11 @@ async fn cmd_render_template<R: Runtime>(
workspace_id: &str,
environment_id: Option<&str>,
) -> YaakResult<String> {
let environment = match environment_id {
Some(id) => app_handle.db().get_environment(id).ok(),
None => None,
};
let base_environment = app_handle.db().get_base_environment(&workspace_id)?;
let environment_chain =
app_handle.db().resolve_environments(workspace_id, None, environment_id)?;
let result = render_template(
template,
&base_environment,
environment.as_ref(),
environment_chain,
&PluginTemplateCallback::new(
&app_handle,
&PluginWindowContext::new(&window),
@@ -147,21 +144,19 @@ async fn cmd_grpc_reflect<R: Runtime>(
app_handle: AppHandle<R>,
grpc_handle: State<'_, Mutex<GrpcHandle>>,
) -> YaakResult<Vec<ServiceDefinition>> {
let environment = match environment_id {
Some(id) => app_handle.db().get_environment(id).ok(),
None => None,
};
let unrendered_request = app_handle.db().get_grpc_request(request_id)?;
let (resolved_request, auth_context_id) = resolve_grpc_request(&window, &unrendered_request)?;
let base_environment =
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
let environment_chain = app_handle.db().resolve_environments(
&unrendered_request.workspace_id,
unrendered_request.folder_id.as_deref(),
environment_id,
)?;
let workspace = app_handle.db().get_workspace(&unrendered_request.workspace_id)?;
let req = render_grpc_request(
&resolved_request,
&base_environment,
environment.as_ref(),
environment_chain,
&PluginTemplateCallback::new(
&app_handle,
&PluginWindowContext::new(&window),
@@ -196,20 +191,18 @@ async fn cmd_grpc_go<R: Runtime>(
window: WebviewWindow<R>,
grpc_handle: State<'_, Mutex<GrpcHandle>>,
) -> YaakResult<String> {
let environment = match environment_id {
Some(id) => app_handle.db().get_environment(id).ok(),
None => None,
};
let unrendered_request = app_handle.db().get_grpc_request(request_id)?;
let (resolved_request, auth_context_id) = resolve_grpc_request(&window, &unrendered_request)?;
let base_environment =
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
let environment_chain = app_handle.db().resolve_environments(
&unrendered_request.workspace_id,
unrendered_request.folder_id.as_deref(),
environment_id,
)?;
let workspace = app_handle.db().get_workspace(&unrendered_request.workspace_id)?;
let request = render_grpc_request(
&resolved_request,
&base_environment,
environment.as_ref(),
environment_chain.clone(),
&PluginTemplateCallback::new(
&app_handle,
&PluginWindowContext::new(&window),
@@ -300,9 +293,8 @@ async fn cmd_grpc_go<R: Runtime>(
let cb = {
let cancelled_rx = cancelled_rx.clone();
let app_handle = app_handle.clone();
let environment_chain = environment_chain.clone();
let window = window.clone();
let base_environment = base_environment.clone();
let environment = environment.clone();
let base_msg = base_msg.clone();
let method_desc = method_desc.clone();
@@ -327,12 +319,12 @@ async fn cmd_grpc_go<R: Runtime>(
let app_handle = app_handle.clone();
let base_msg = base_msg.clone();
let method_desc = method_desc.clone();
let environment_chain = environment_chain.clone();
let msg = block_in_place(|| {
tauri::async_runtime::block_on(async {
render_template(
msg.as_str(),
&base_environment,
environment.as_ref(),
environment_chain,
&PluginTemplateCallback::new(
&app_handle,
&PluginWindowContext::new(&window),
@@ -396,12 +388,12 @@ async fn cmd_grpc_go<R: Runtime>(
let window = window.clone();
let app_handle = app_handle.clone();
let base_event = base_msg.clone();
let environment_chain = environment_chain.clone();
let req = request.clone();
let msg = if req.message.is_empty() { "{}".to_string() } else { req.message };
let msg = render_template(
msg.as_str(),
&base_environment.clone(),
environment.as_ref(),
environment_chain,
&PluginTemplateCallback::new(
&app_handle,
&PluginWindowContext::new(&window),
@@ -833,30 +825,25 @@ async fn cmd_get_http_authentication_config<R: Runtime>(
plugin_manager: State<'_, PluginManager>,
auth_name: &str,
values: HashMap<String, JsonPrimitive>,
request_id: &str,
request: AnyModel,
environment_id: Option<&str>,
workspace_id: &str,
) -> YaakResult<GetHttpAuthenticationConfigResponse> {
let base_environment = window.db().get_base_environment(&workspace_id)?;
let environment = match environment_id {
Some(id) => match window.db().get_environment(id) {
Ok(env) => Some(env),
Err(e) => {
warn!("Failed to find environment by id {id} {}", e);
None
}
let (workspace_id, folder_id) = match request.clone() {
AnyModel::HttpRequest(m) => (m.workspace_id, m.folder_id),
AnyModel::GrpcRequest(m) => (m.workspace_id, m.folder_id),
AnyModel::WebsocketRequest(m) => (m.workspace_id, m.folder_id),
AnyModel::Folder(m) => (m.workspace_id, m.folder_id),
AnyModel::Workspace(m) => (m.id, None),
m => {
return Err(GenericError(format!("Unsupported model to call auth config {m:?}")));
},
None => None,
};
let environment_chain =
window.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
Ok(plugin_manager
.get_http_authentication_config(
&window,
&base_environment,
environment.as_ref(),
auth_name,
values,
request_id,
)
.get_http_authentication_config(&window, environment_chain, auth_name, values, request.id())
.await?)
}
@@ -907,30 +894,29 @@ async fn cmd_call_http_authentication_action<R: Runtime>(
auth_name: &str,
action_index: i32,
values: HashMap<String, JsonPrimitive>,
model_id: &str,
workspace_id: &str,
model: AnyModel,
environment_id: Option<&str>,
) -> YaakResult<()> {
let base_environment = window.db().get_base_environment(&workspace_id)?;
let environment = match environment_id {
Some(id) => match window.db().get_environment(id) {
Ok(env) => Some(env),
Err(e) => {
warn!("Failed to find environment by id {id} {}", e);
None
}
},
None => None,
let (workspace_id, folder_id) = match model.clone() {
AnyModel::HttpRequest(m) => (m.workspace_id, m.folder_id),
AnyModel::GrpcRequest(m) => (m.workspace_id, m.folder_id),
AnyModel::WebsocketRequest(m) => (m.workspace_id, m.folder_id),
AnyModel::Folder(m) => (m.workspace_id, m.folder_id),
AnyModel::Workspace(m) => (m.id, None),
m => {
return Err(GenericError(format!("Unsupported model to call auth {m:?}")));
}
};
let environment_chain =
window.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
Ok(plugin_manager
.call_http_authentication_action(
&window,
&base_environment,
environment.as_ref(),
environment_chain,
auth_name,
action_index,
values,
model_id,
&model.id(),
)
.await?)
}

View File

@@ -74,20 +74,15 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
let workspace =
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
let environment = environment_from_window(&window);
let base_environment = app_handle
let environment_id = environment_from_window(&window).map(|e| e.id);
let environment_chain = window
.db()
.get_base_environment(&workspace.id)
.expect("Failed to get base environment");
.resolve_environments(&workspace.id, None, environment_id.as_deref())
.expect("Failed to resolve environments");
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
let grpc_request = render_grpc_request(
&req.grpc_request,
&base_environment,
environment.as_ref(),
&cb,
)
.await
.expect("Failed to render grpc request");
let grpc_request = render_grpc_request(&req.grpc_request, environment_chain, &cb)
.await
.expect("Failed to render grpc request");
Some(InternalEventPayload::RenderGrpcRequestResponse(RenderGrpcRequestResponse {
grpc_request,
}))
@@ -98,20 +93,15 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
let workspace =
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
let environment = environment_from_window(&window);
let base_environment = app_handle
let environment_id = environment_from_window(&window).map(|e| e.id);
let environment_chain = window
.db()
.get_base_environment(&workspace.id)
.expect("Failed to get base environment");
.resolve_environments(&workspace.id, None, environment_id.as_deref())
.expect("Failed to resolve environments");
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
let http_request = render_http_request(
&req.http_request,
&base_environment,
environment.as_ref(),
&cb,
)
.await
.expect("Failed to render http request");
let http_request = render_http_request(&req.http_request, environment_chain, &cb)
.await
.expect("Failed to render http request");
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
http_request,
}))
@@ -122,13 +112,13 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
let workspace =
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
let environment = environment_from_window(&window);
let base_environment = app_handle
let environment_id = environment_from_window(&window).map(|e| e.id);
let environment_chain = window
.db()
.get_base_environment(&workspace.id)
.expect("Failed to get base environment");
.resolve_environments(&workspace.id, None, environment_id.as_deref())
.expect("Failed to resolve environments");
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
let data = render_json_value(req.data, &base_environment, environment.as_ref(), &cb)
let data = render_json_value(req.data, environment_chain, &cb)
.await
.expect("Failed to render template");
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))

View File

@@ -5,35 +5,32 @@ use yaak_models::models::{
Environment, GrpcRequest, HttpRequest, HttpRequestHeader, HttpUrlParameter,
};
use yaak_models::render::make_vars_hashmap;
use yaak_templates::{parse_and_render, render_json_value_raw, TemplateCallback};
use yaak_templates::{TemplateCallback, parse_and_render, render_json_value_raw};
pub async fn render_template<T: TemplateCallback>(
template: &str,
base_environment: &Environment,
environment: Option<&Environment>,
environment_chain: Vec<Environment>,
cb: &T,
) -> yaak_templates::error::Result<String> {
let vars = &make_vars_hashmap(base_environment, environment);
let vars = &make_vars_hashmap(environment_chain);
render(template, vars, cb).await
}
pub async fn render_json_value<T: TemplateCallback>(
value: Value,
base_environment: &Environment,
environment: Option<&Environment>,
environment_chain: Vec<Environment>,
cb: &T,
) -> yaak_templates::error::Result<Value> {
let vars = &make_vars_hashmap(base_environment, environment);
let vars = &make_vars_hashmap(environment_chain);
render_json_value_raw(value, vars, cb).await
}
pub async fn render_grpc_request<T: TemplateCallback>(
r: &GrpcRequest,
base_environment: &Environment,
environment: Option<&Environment>,
environment_chain: Vec<Environment>,
cb: &T,
) -> yaak_templates::error::Result<GrpcRequest> {
let vars = &make_vars_hashmap(base_environment, environment);
let vars = &make_vars_hashmap(environment_chain);
let mut metadata = Vec::new();
for p in r.metadata.clone() {
@@ -62,11 +59,10 @@ pub async fn render_grpc_request<T: TemplateCallback>(
pub async fn render_http_request<T: TemplateCallback>(
r: &HttpRequest,
base_environment: &Environment,
environment: Option<&Environment>,
environment_chain: Vec<Environment>,
cb: &T,
) -> yaak_templates::error::Result<HttpRequest> {
let vars = &make_vars_hashmap(base_environment, environment);
let vars = &make_vars_hashmap(environment_chain);
let mut url_parameters = Vec::new();
for p in r.url_parameters.clone() {

View File

@@ -1,6 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, base: boolean, variables: Array<EnvironmentVariable>, color: string | null, };
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };

View File

@@ -24,7 +24,7 @@ pub(crate) fn find_ssh_key() -> Option<PathBuf> {
None
}
pub(crate) fn get_current_branch(repo: &Repository) -> Result<Option<Branch>> {
pub(crate) fn get_current_branch(repo: &Repository) -> Result<Option<Branch<'_>>> {
for b in repo.branches(None)? {
let branch = b?.0;
if branch.is_head() {
@@ -101,7 +101,7 @@ pub(crate) fn get_default_remote_in_repo(repo: &Repository) -> Result<String> {
return Ok(DEFAULT_REMOTE_NAME.into());
}
// if only one remote exists pick that
// if only one remote exists, pick that
if remotes.len() == 1 {
let first_remote = remotes
.iter()

View File

@@ -14,7 +14,7 @@ export type EditorKeymap = "default" | "vim" | "vscode" | "emacs";
export type EncryptedKey = { encryptedKey: string, };
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, base: boolean, variables: Array<EnvironmentVariable>, color: string | null, };
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };

View File

@@ -0,0 +1,62 @@
-- Create temporary table for migration
CREATE TABLE environments__new
(
id TEXT NOT NULL PRIMARY KEY,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
deleted_at DATETIME,
workspace_id TEXT NOT NULL
REFERENCES workspaces ON DELETE CASCADE,
name TEXT NOT NULL,
variables TEXT DEFAULT '[]' NOT NULL,
model TEXT DEFAULT 'environment',
public BOOLEAN DEFAULT FALSE,
color TEXT,
-- NEW
parent_model TEXT DEFAULT 'workspace' NOT NULL,
parent_id TEXT
);
-- Backfill the data from the old table
-- - base=1 -> (workspace, NULL)
-- - base=0 -> (environment, id_of_workspace_base) (fallback to workspace,NULL if none)
INSERT INTO environments__new
(id, created_at, updated_at, deleted_at, workspace_id, name, variables, model, public, color, parent_model, parent_id)
SELECT
e.id,
e.created_at,
e.updated_at,
e.deleted_at,
e.workspace_id,
e.name,
e.variables,
e.model,
e.public,
e.color,
CASE
WHEN e.base = 1 THEN 'workspace'
WHEN (
SELECT COUNT(1)
FROM environments b
WHERE b.workspace_id = e.workspace_id AND b.base = 1
) > 0 THEN 'environment'
ELSE 'workspace'
END AS parent_model,
CASE
WHEN e.base = 1 THEN NULL
ELSE (
SELECT b.id
FROM environments b
WHERE b.workspace_id = e.workspace_id AND b.base = 1
ORDER BY b.created_at ASC, b.id ASC
LIMIT 1
)
END AS parent_id
FROM environments e;
-- Move everything to the new table
DROP TABLE environments;
ALTER TABLE environments__new
RENAME TO environments;

View File

@@ -89,7 +89,7 @@ impl<'a> DbContext<'a> {
col: impl IntoColumnRef,
value: impl Into<SimpleExpr>,
limit: Option<u64>,
) -> crate::error::Result<Vec<M>>
) -> Result<Vec<M>>
where
M: Into<AnyModel> + Clone + UpsertModelInfo,
{

View File

@@ -29,6 +29,9 @@ pub enum Error {
#[error("Multiple base environments for {0}. Delete duplicates before continuing.")]
MultipleBaseEnvironments(String),
#[error("Multiple folder environments for {0}. Delete duplicates before continuing.")]
MultipleFolderEnvironments(String),
#[error("unknown error")]
Unknown,

View File

@@ -533,9 +533,10 @@ pub struct Environment {
pub name: String,
pub public: bool,
pub base: bool,
pub variables: Vec<EnvironmentVariable>,
pub color: Option<String>,
pub parent_model: String,
pub parent_id: Option<String>,
}
impl UpsertModelInfo for Environment {
@@ -568,7 +569,8 @@ impl UpsertModelInfo for Environment {
(CreatedAt, upsert_date(source, self.created_at)),
(UpdatedAt, upsert_date(source, self.updated_at)),
(WorkspaceId, self.workspace_id.into()),
(Base, self.base.into()),
(ParentId, self.parent_id.into()),
(ParentModel, self.parent_model.into()),
(Color, self.color.into()),
(Name, self.name.trim().into()),
(Public, self.public.into()),
@@ -579,7 +581,8 @@ impl UpsertModelInfo for Environment {
fn update_columns() -> Vec<impl IntoIden> {
vec![
EnvironmentIden::UpdatedAt,
EnvironmentIden::Base,
EnvironmentIden::ParentId,
EnvironmentIden::ParentModel,
EnvironmentIden::Color,
EnvironmentIden::Name,
EnvironmentIden::Public,
@@ -598,7 +601,8 @@ impl UpsertModelInfo for Environment {
workspace_id: row.get("workspace_id")?,
created_at: row.get("created_at")?,
updated_at: row.get("updated_at")?,
base: row.get("base")?,
parent_id: row.get("parent_id")?,
parent_model: row.get("parent_model")?,
color: row.get("color")?,
name: row.get("name")?,
public: row.get("public")?,
@@ -2072,6 +2076,17 @@ macro_rules! define_any_model {
)*
}
impl AnyModel {
#[inline]
pub fn id(&self) -> &str {
match self {
$(
AnyModel::$type(inner) => &inner.id,
)*
}
}
}
$(
impl From<$type> for AnyModel {
fn from(value: $type) -> Self {

View File

@@ -1,5 +1,7 @@
use crate::db_context::DbContext;
use crate::error::Error::{MissingBaseEnvironment, MultipleBaseEnvironments};
use crate::error::Error::{
MissingBaseEnvironment, MultipleBaseEnvironments, MultipleFolderEnvironments,
};
use crate::error::Result;
use crate::models::{Environment, EnvironmentIden, EnvironmentVariable};
use crate::util::UpdateSource;
@@ -10,21 +12,31 @@ impl<'a> DbContext<'a> {
self.find_one(EnvironmentIden::Id, id)
}
pub fn get_environment_by_folder_id(&self, folder_id: &str) -> Result<Option<Environment>> {
let environments: Vec<Environment> =
self.find_many(EnvironmentIden::ParentId, folder_id, None)?;
if environments.len() > 1 {
return Err(MultipleFolderEnvironments(folder_id.to_string()));
}
Ok(environments.get(0).cloned())
}
pub fn get_base_environment(&self, workspace_id: &str) -> Result<Environment> {
let environments = self.list_environments_ensure_base(workspace_id)?;
let base_environments =
environments.into_iter().filter(|e| e.base).collect::<Vec<Environment>>();
let base_environments = environments
.into_iter()
.filter(|e| e.parent_id.is_none())
.collect::<Vec<Environment>>();
if base_environments.len() > 1 {
return Err(MultipleBaseEnvironments(workspace_id.to_string()));
}
let base_environment = base_environments.into_iter().find(|e| e.base).ok_or(
Ok(base_environments.first().cloned().ok_or(
// Should never happen because one should be created above if it does not exist
MissingBaseEnvironment(workspace_id.to_string()),
)?;
Ok(base_environment)
)?)
}
/// Lists environments and will create a base environment if one doesn't exist
@@ -32,13 +44,12 @@ impl<'a> DbContext<'a> {
let mut environments =
self.find_many::<Environment>(EnvironmentIden::WorkspaceId, workspace_id, None)?;
let base_environment = environments.iter().find(|e| e.base);
let base_environment = environments.iter().find(|e| e.parent_id.is_none());
if let None = base_environment {
let e = self.upsert_environment(
&Environment {
workspace_id: workspace_id.to_string(),
base: true,
name: "Global Variables".to_string(),
..Default::default()
},
@@ -98,4 +109,43 @@ impl<'a> DbContext<'a> {
source,
)
}
pub fn resolve_environments(
&self,
workspace_id: &str,
folder_id: Option<&str>,
active_environment_id: Option<&str>,
) -> Result<Vec<Environment>> {
let mut environments = Vec::new();
if let Some(folder_id) = folder_id {
let folder = self.get_folder(folder_id)?;
// Add current folder's environment
if let Some(e) = self.get_environment_by_folder_id(folder_id)? {
environments.push(e);
};
// Recurse up
let ancestors = self.resolve_environments(
workspace_id,
folder.folder_id.as_deref(),
active_environment_id,
)?;
environments.extend(ancestors);
} else {
// Add active and base environments
if let Some(id) = active_environment_id {
if let Ok(e) = self.get_environment(&id) {
// Add active sub environment
environments.push(e);
};
};
// Add the base environment
environments.push(self.get_base_environment(workspace_id)?);
}
Ok(environments)
}
}

View File

@@ -1,10 +1,7 @@
use crate::connection_or_tx::ConnectionOrTx;
use crate::db_context::DbContext;
use crate::error::Result;
use crate::models::{
Folder, FolderIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestHeader,
HttpRequestIden, WebsocketRequest, WebsocketRequestIden,
};
use crate::models::{Environment, EnvironmentIden, Folder, FolderIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestHeader, HttpRequestIden, WebsocketRequest, WebsocketRequestIden};
use crate::util::UpdateSource;
use serde_json::Value;
use std::collections::BTreeMap;
@@ -37,6 +34,10 @@ impl<'a> DbContext<'a> {
self.delete_websocket_request(&m, source)?;
}
for e in self.find_many(EnvironmentIden::ParentId, fid, None)? {
self.delete_environment(&e, source)?;
}
// Recurse down into child folders
for folder in self.find_many::<Folder>(FolderIden::FolderId, fid, None)? {
self.delete_folder(&folder, source)?;
@@ -99,6 +100,17 @@ impl<'a> DbContext<'a> {
)?;
}
for m in self.find_many::<Environment>(EnvironmentIden::ParentId, fid, None)? {
self.upsert_environment(
&Environment {
id: "".into(),
parent_id: Some(new_folder.id.clone()),
..m
},
source,
)?;
}
for m in self.find_many::<Folder>(FolderIden::FolderId, fid, None)? {
// Recurse down
self.duplicate_folder(

View File

@@ -64,7 +64,7 @@ impl QueryManager {
}
}
pub fn connect(&self) -> DbContext {
pub fn connect(&self) -> DbContext<'_> {
let conn = self
.pool
.lock()

View File

@@ -1,14 +1,10 @@
use std::collections::HashMap;
use crate::models::{Environment, EnvironmentVariable};
use std::collections::HashMap;
pub fn make_vars_hashmap(
base_environment: &Environment,
environment: Option<&Environment>,
) -> HashMap<String, String> {
pub fn make_vars_hashmap(environment_chain: Vec<Environment>) -> HashMap<String, String> {
let mut variables = HashMap::new();
variables = add_variable_to_map(variables, &base_environment.variables);
if let Some(e) = environment {
for e in environment_chain.iter().rev() {
variables = add_variable_to_map(variables, &e.variables);
}
@@ -31,4 +27,3 @@ fn add_variable_to_map(
map
}

View File

@@ -1,6 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, base: boolean, variables: Array<EnvironmentVariable>, color: string | null, };
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };

View File

@@ -584,8 +584,7 @@ impl PluginManager {
pub async fn get_http_authentication_config<R: Runtime>(
&self,
window: &WebviewWindow<R>,
base_environment: &Environment,
environment: Option<&Environment>,
environment_chain: Vec<Environment>,
auth_name: &str,
values: HashMap<String, JsonPrimitive>,
request_id: &str,
@@ -596,7 +595,7 @@ impl PluginManager {
.find_map(|(p, r)| if r.name == auth_name { Some(p) } else { None })
.ok_or(PluginNotFoundErr(auth_name.into()))?;
let vars = &make_vars_hashmap(&base_environment, environment);
let vars = &make_vars_hashmap(environment_chain);
let cb = PluginTemplateCallback::new(
window.app_handle(),
&PluginWindowContext::new(&window),
@@ -629,14 +628,13 @@ impl PluginManager {
pub async fn call_http_authentication_action<R: Runtime>(
&self,
window: &WebviewWindow<R>,
base_environment: &Environment,
environment: Option<&Environment>,
environment_chain: Vec<Environment>,
auth_name: &str,
action_index: i32,
values: HashMap<String, JsonPrimitive>,
model_id: &str,
) -> Result<()> {
let vars = &make_vars_hashmap(&base_environment, environment);
let vars = &make_vars_hashmap(environment_chain);
let rendered_values = render_json_value_raw(
json!(values),
vars,

View File

@@ -1,20 +1,12 @@
use crate::error::Result;
use log::{info, warn};
use serde;
use serde::Deserialize;
use std::net::SocketAddr;
use tauri::path::BaseDirectory;
use tauri::{AppHandle, Manager, Runtime};
use tauri_plugin_shell::ShellExt;
use tauri_plugin_shell::process::CommandEvent;
use tauri_plugin_shell::ShellExt;
use tokio::sync::watch::Receiver;
#[derive(Deserialize, Default)]
#[serde(default, rename_all = "camelCase")]
struct PortFile {
port: i32,
}
pub async fn start_nodejs_plugin_runtime<R: Runtime>(
app: &AppHandle<R>,
addr: SocketAddr,

View File

@@ -1,6 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, base: boolean, variables: Array<EnvironmentVariable>, color: string | null, };
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, variables: Array<EnvironmentVariable>, color: string | null, parentModel: string, parentId: string | null, };
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };

View File

@@ -109,24 +109,18 @@ pub(crate) async fn send<R: Runtime>(
window: WebviewWindow<R>,
ws_manager: State<'_, Mutex<WebsocketManager>>,
) -> Result<WebsocketConnection> {
let (connection, unrendered_request) = {
let db = app_handle.db();
let connection = db.get_websocket_connection(connection_id)?;
let unrendered_request = db.get_websocket_request(&connection.request_id)?;
(connection, unrendered_request)
};
let environment = match environment_id {
Some(id) => Some(app_handle.db().get_environment(id)?),
None => None,
};
let base_environment =
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
let connection = app_handle.db().get_websocket_connection(connection_id)?;
let unrendered_request = app_handle.db().get_websocket_request(&connection.request_id)?;
let environment_chain = app_handle.db().resolve_environments(
&unrendered_request.workspace_id,
unrendered_request.folder_id.as_deref(),
environment_id,
)?;
let (resolved_request, _auth_context_id) =
resolve_websocket_request(&window, &unrendered_request)?;
let request = render_websocket_request(
&resolved_request,
&base_environment,
environment.as_ref(),
environment_chain,
&PluginTemplateCallback::new(
&app_handle,
&PluginWindowContext::new(&window),
@@ -192,19 +186,17 @@ pub(crate) async fn connect<R: Runtime>(
ws_manager: State<'_, Mutex<WebsocketManager>>,
) -> Result<WebsocketConnection> {
let unrendered_request = app_handle.db().get_websocket_request(request_id)?;
let environment = match environment_id {
Some(id) => Some(app_handle.db().get_environment(id)?),
None => None,
};
let base_environment =
app_handle.db().get_base_environment(&unrendered_request.workspace_id)?;
let environment_chain = app_handle.db().resolve_environments(
&unrendered_request.workspace_id,
unrendered_request.folder_id.as_deref(),
environment_id,
)?;
let workspace = app_handle.db().get_workspace(&unrendered_request.workspace_id)?;
let (resolved_request, auth_context_id) =
resolve_websocket_request(&window, &unrendered_request)?;
let request = render_websocket_request(
&resolved_request,
&base_environment,
environment.as_ref(),
environment_chain,
&PluginTemplateCallback::new(
&app_handle,
&PluginWindowContext::new(&window),
@@ -305,7 +297,7 @@ pub(crate) async fn connect<R: Runtime>(
// Add cookies to WS HTTP Upgrade
if let Some(id) = cookie_jar_id {
let cookie_jar = app_handle.db().get_cookie_jar(id)?;
let cookie_jar = app_handle.db().get_cookie_jar(&id)?;
let cookies = cookie_jar
.cookies

View File

@@ -6,11 +6,10 @@ use yaak_templates::{parse_and_render, render_json_value_raw, TemplateCallback};
pub async fn render_websocket_request<T: TemplateCallback>(
r: &WebsocketRequest,
base_environment: &Environment,
environment: Option<&Environment>,
environment_chain: Vec<Environment>,
cb: &T,
) -> Result<WebsocketRequest> {
let vars = &make_vars_hashmap(base_environment, environment);
let vars = &make_vars_hashmap(environment_chain);
let mut headers = Vec::new();
for p in r.headers.clone() {