diff --git a/README.md b/README.md index 2a6a9911..aa90c672 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight ## Contribution Policy -Yaak is open source, but only accepting contributions for bug fixes. To get started, +Yaak is open source but only accepting contributions for bug fixes. To get started, visit [`DEVELOPMENT.md`](DEVELOPMENT.md) for tips on setting up your environment. ## Useful Resources diff --git a/packages/plugin-runtime-types/src/bindings/gen_events.ts b/packages/plugin-runtime-types/src/bindings/gen_events.ts index 36d27536..173f5cbe 100644 --- a/packages/plugin-runtime-types/src/bindings/gen_events.ts +++ b/packages/plugin-runtime-types/src/bindings/gen_events.ts @@ -437,7 +437,7 @@ export type SetKeyValueRequest = { key: string, value: string, }; export type SetKeyValueResponse = {}; -export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, }; +export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, timeout?: number, }; export type TemplateFunction = { name: string, description?: string, /** diff --git a/packages/plugin-runtime-types/src/bindings/gen_models.ts b/packages/plugin-runtime-types/src/bindings/gen_models.ts index 1136b85a..b984c7eb 100644 --- a/packages/plugin-runtime-types/src/bindings/gen_models.ts +++ b/packages/plugin-runtime-types/src/bindings/gen_models.ts @@ -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, variables: Array, color: string | null, parentModel: string, parentId: string | null, }; +export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array, color: string | null, }; export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, }; diff --git a/packages/plugin-runtime/src/PluginInstance.ts b/packages/plugin-runtime/src/PluginInstance.ts index 4eef39a8..1f902a20 100644 --- a/packages/plugin-runtime/src/PluginInstance.ts +++ b/packages/plugin-runtime/src/PluginInstance.ts @@ -454,6 +454,8 @@ export class PluginInstance { show: async (args) => { await this.#sendAndWaitForReply(windowContext, { type: 'show_toast_request', + // Handle default here because null/undefined both convert to None in Rust translation + timeout: args.timeout === undefined ? 5000 : args.timeout, ...args, }); }, diff --git a/plugins/auth-oauth2/src/index.ts b/plugins/auth-oauth2/src/index.ts index 90f28255..28e16e86 100644 --- a/plugins/auth-oauth2/src/index.ts +++ b/plugins/auth-oauth2/src/index.ts @@ -271,6 +271,12 @@ export const plugin: PluginDefinition = { label: 'Advanced', inputs: [ { type: 'text', name: 'scope', label: 'Scope', optional: true }, + { + type: 'text', + name: 'headerName', + label: 'Header Name', + defaultValue: 'Authorization', + }, { type: 'text', name: 'headerPrefix', @@ -397,15 +403,9 @@ export const plugin: PluginDefinition = { throw new Error('Invalid grant type ' + grantType); } + const headerName = stringArg(values, 'headerName') || 'Authorization'; const headerValue = `${headerPrefix} ${token.response[tokenName]}`.trim(); - return { - setHeaders: [ - { - name: 'Authorization', - value: headerValue, - }, - ], - }; + return { setHeaders: [{ name: headerName, value: headerValue }] }; }, }, }; diff --git a/plugins/template-function-request/src/index.ts b/plugins/template-function-request/src/index.ts index 28814558..9186e50d 100755 --- a/plugins/template-function-request/src/index.ts +++ b/plugins/template-function-request/src/index.ts @@ -1,5 +1,6 @@ import type { HttpUrlParameter } from '@yaakapp-internal/models'; import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; +import { resolvedModelName } from '@yaakapp/app/lib/resolvedModelName'; export const plugin: PluginDefinition = { templateFunctions: [ @@ -96,5 +97,22 @@ export const plugin: PluginDefinition = { return renderedValue; }, }, + { + name: 'request.name', + args: [ + { + name: 'requestId', + label: 'Http Request', + type: 'http_request', + }, + ], + async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { + const requestId = String(args.values.requestId ?? 'n/a'); + const httpRequest = await ctx.httpRequest.getById({ id: requestId }); + if (httpRequest == null) return null; + + return resolvedModelName(httpRequest); + }, + }, ], }; diff --git a/plugins/template-function-timestamp/src/index.ts b/plugins/template-function-timestamp/src/index.ts index 20d5bf27..1f1b156a 100755 --- a/plugins/template-function-timestamp/src/index.ts +++ b/plugins/template-function-timestamp/src/index.ts @@ -155,7 +155,7 @@ export function formatDatetime(args: { format?: string; in?: ContextFn; }): string { - const { date, format = 'yyyy-MM-dd HH:mm:ss' } = args; + const { date, format } = args; const d = parseDateString(date ?? ''); - return formatDate(d, String(format), { in: args.in }); + return formatDate(d, String(format || 'yyyy-MM-dd HH:mm:ss'), { in: args.in }); } diff --git a/src-tauri/src/error.rs b/src-tauri/src/error.rs index 65365f90..7efbe8cc 100644 --- a/src-tauri/src/error.rs +++ b/src-tauri/src/error.rs @@ -35,6 +35,9 @@ pub enum Error { #[error(transparent)] CommonError(#[from] yaak_common::error::Error), + #[error(transparent)] + ClipboardError(#[from] tauri_plugin_clipboard_manager::Error), + #[error("Updater error: {0}")] UpdaterError(#[from] tauri_plugin_updater::Error), diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 9327ed4e..30fba3b4 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,6 +1,7 @@ extern crate core; use crate::encoding::read_response_body; use crate::error::Error::GenericError; +use crate::error::Result; use crate::grpc::{build_metadata, metadata_to_map, resolve_grpc_request}; use crate::http_request::{resolve_http_request, send_http_request}; use crate::import::import_data; @@ -49,7 +50,7 @@ use yaak_plugins::plugin_meta::PluginMetadata; use yaak_plugins::template_callback::PluginTemplateCallback; use yaak_sse::sse::ServerSentEvent; use yaak_templates::format::format_json; -use yaak_templates::{Tokens, transform_args, RenderOptions, RenderErrorBehavior}; +use yaak_templates::{RenderErrorBehavior, RenderOptions, Tokens, transform_args}; mod commands; mod encoding; @@ -1499,7 +1500,27 @@ fn monitor_plugin_events(app_handle: &AppHandle) { // We might have recursive back-and-forth calls between app and plugin, so we don't // want to block here tauri::async_runtime::spawn(async move { - plugin_events::handle_plugin_event(&app_handle, &event, &plugin).await; + let ev = plugin_events::handle_plugin_event(&app_handle, &event, &plugin).await; + + let ev = match ev { + Ok(Some(ev)) => ev, + Ok(None) => return, + Err(e) => { + warn!("Failed to handle plugin event: {e:?}"); + let _ = app_handle.emit("show_toast", InternalEventPayload::ShowToastRequest(ShowToastRequest { + message: e.to_string(), + color: Some(Color::Danger), + icon: None, + timeout: Some(30000), + })); + return; + }, + }; + + let plugin_manager: State<'_, PluginManager> = app_handle.state(); + if let Err(e) = plugin_manager.reply(&event, &ev).await { + warn!("Failed to reply to plugin manager: {:?}", e) + } }); } plugin_manager.unsubscribe(rx_id.as_str()).await; @@ -1534,11 +1555,16 @@ async fn call_frontend( fn get_window_from_window_context( app_handle: &AppHandle, window_context: &PluginWindowContext, -) -> Option> { +) -> Result> { let label = match window_context { PluginWindowContext::Label { label, .. } => label, PluginWindowContext::None => { - return app_handle.webview_windows().iter().next().map(|(_, w)| w.to_owned()); + return app_handle + .webview_windows() + .iter() + .next() + .map(|(_, w)| w.to_owned()) + .ok_or(GenericError("No windows open".to_string())); } }; @@ -1551,7 +1577,7 @@ fn get_window_from_window_context( error!("Failed to find window by {window_context:?}"); } - window + Ok(window.ok_or(GenericError(format!("Failed to find window for {}", label)))?) } fn workspace_from_window(window: &WebviewWindow) -> Option { diff --git a/src-tauri/src/plugin_events.rs b/src-tauri/src/plugin_events.rs index b3935b2e..fe887531 100644 --- a/src-tauri/src/plugin_events.rs +++ b/src-tauri/src/plugin_events.rs @@ -1,3 +1,4 @@ +use crate::error::Result; use crate::http_request::send_http_request; use crate::render::{render_grpc_request, render_http_request, render_json_value}; use crate::window::{CreateWindowConfig, create_window}; @@ -7,10 +8,12 @@ use crate::{ }; use chrono::Utc; use cookie::Cookie; -use log::{error, warn}; -use tauri::{AppHandle, Emitter, Manager, Runtime, State}; +use log::error; +use tauri::{AppHandle, Emitter, Manager, Runtime}; use tauri_plugin_clipboard_manager::ClipboardExt; +use yaak_common::window::WorkspaceWindowTrait; use yaak_models::models::{HttpResponse, Plugin}; +use yaak_models::queries::any_request::AnyRequest; use yaak_models::query_manager::QueryManagerExt; use yaak_models::util::UpdateSource; use yaak_plugins::events::{ @@ -20,7 +23,6 @@ use yaak_plugins::events::{ RenderHttpRequestResponse, SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest, TemplateRenderResponse, WindowNavigateEvent, }; -use yaak_plugins::manager::PluginManager; use yaak_plugins::plugin_handle::PluginHandle; use yaak_plugins::template_callback::PluginTemplateCallback; use yaak_templates::{RenderErrorBehavior, RenderOptions}; @@ -29,109 +31,111 @@ pub(crate) async fn handle_plugin_event( app_handle: &AppHandle, event: &InternalEvent, plugin_handle: &PluginHandle, -) { +) -> Result> { // debug!("Got event to app {event:?}"); let window_context = event.window_context.to_owned(); - let response_event: Option = match event.clone().payload { + match event.clone().payload { InternalEventPayload::CopyTextRequest(req) => { - app_handle - .clipboard() - .write_text(req.text.as_str()) - .expect("Failed to write text to clipboard"); - Some(InternalEventPayload::CopyTextResponse(EmptyPayload {})) + app_handle.clipboard().write_text(req.text.as_str())?; + Ok(Some(InternalEventPayload::CopyTextResponse(EmptyPayload {}))) } InternalEventPayload::ShowToastRequest(req) => { match window_context { - PluginWindowContext::Label { label, .. } => app_handle - .emit_to(label, "show_toast", req) - .expect("Failed to emit show_toast to window"), - _ => app_handle.emit("show_toast", req).expect("Failed to emit show_toast"), + PluginWindowContext::Label { label, .. } => { + app_handle.emit_to(label, "show_toast", req)? + } + _ => app_handle.emit("show_toast", req)?, }; - Some(InternalEventPayload::ShowToastResponse(EmptyPayload {})) + Ok(Some(InternalEventPayload::ShowToastResponse(EmptyPayload {}))) } InternalEventPayload::PromptTextRequest(_) => { - let window = get_window_from_window_context(app_handle, &window_context) - .expect("Failed to find window for render"); - call_frontend(&window, event).await + let window = get_window_from_window_context(app_handle, &window_context)?; + Ok(call_frontend(&window, event).await) } InternalEventPayload::FindHttpResponsesRequest(req) => { let http_responses = app_handle .db() .list_http_responses_for_request(&req.request_id, req.limit.map(|l| l as u64)) .unwrap_or_default(); - Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse { + Ok(Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse { http_responses, - })) + }))) } InternalEventPayload::GetHttpRequestByIdRequest(req) => { let http_request = app_handle.db().get_http_request(&req.id).ok(); - Some(InternalEventPayload::GetHttpRequestByIdResponse(GetHttpRequestByIdResponse { + Ok(Some(InternalEventPayload::GetHttpRequestByIdResponse(GetHttpRequestByIdResponse { http_request, - })) + }))) } InternalEventPayload::RenderGrpcRequestRequest(req) => { - let window = get_window_from_window_context(app_handle, &window_context) - .expect("Failed to find window for render grpc request"); + let window = get_window_from_window_context(app_handle, &window_context)?; let workspace = workspace_from_window(&window).expect("Failed to get workspace_id from window URL"); let environment_id = environment_from_window(&window).map(|e| e.id); - let environment_chain = window - .db() - .resolve_environments(&workspace.id, None, environment_id.as_deref()) - .expect("Failed to resolve environments"); + let environment_chain = window.db().resolve_environments( + &workspace.id, + req.grpc_request.folder_id.as_deref(), + environment_id.as_deref(), + )?; let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose); let opt = RenderOptions { error_behavior: RenderErrorBehavior::Throw, }; - let grpc_request = render_grpc_request(&req.grpc_request, environment_chain, &cb, &opt) - .await - .expect("Failed to render grpc request"); - Some(InternalEventPayload::RenderGrpcRequestResponse(RenderGrpcRequestResponse { + let grpc_request = + render_grpc_request(&req.grpc_request, environment_chain, &cb, &opt).await?; + Ok(Some(InternalEventPayload::RenderGrpcRequestResponse(RenderGrpcRequestResponse { grpc_request, - })) + }))) } InternalEventPayload::RenderHttpRequestRequest(req) => { - let window = get_window_from_window_context(app_handle, &window_context) - .expect("Failed to find window for render http request"); + let window = get_window_from_window_context(app_handle, &window_context)?; let workspace = workspace_from_window(&window).expect("Failed to get workspace_id from window URL"); let environment_id = environment_from_window(&window).map(|e| e.id); - let environment_chain = window - .db() - .resolve_environments(&workspace.id, None, environment_id.as_deref()) - .expect("Failed to resolve environments"); + let environment_chain = window.db().resolve_environments( + &workspace.id, + req.http_request.folder_id.as_deref(), + environment_id.as_deref(), + )?; let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose); let opt = &RenderOptions { error_behavior: RenderErrorBehavior::Throw, }; - let http_request = render_http_request(&req.http_request, environment_chain, &cb, &opt) - .await - .expect("Failed to render http request"); - Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse { + let http_request = + render_http_request(&req.http_request, environment_chain, &cb, &opt).await?; + Ok(Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse { http_request, - })) + }))) } InternalEventPayload::TemplateRenderRequest(req) => { - let window = get_window_from_window_context(app_handle, &window_context) - .expect("Failed to find window for render"); + let window = get_window_from_window_context(app_handle, &window_context)?; let workspace = workspace_from_window(&window).expect("Failed to get workspace_id from window URL"); let environment_id = environment_from_window(&window).map(|e| e.id); - let environment_chain = window - .db() - .resolve_environments(&workspace.id, None, environment_id.as_deref()) - .expect("Failed to resolve environments"); + let folder_id = if let Some(id) = window.request_id() { + match window.db().get_any_request(&id) { + Ok(AnyRequest::HttpRequest(r)) => r.folder_id, + Ok(AnyRequest::GrpcRequest(r)) => r.folder_id, + Ok(AnyRequest::WebsocketRequest(r)) => r.folder_id, + Err(_) => None, + } + } else { + None + }; + let environment_chain = window.db().resolve_environments( + &workspace.id, + folder_id.as_deref(), + environment_id.as_deref(), + )?; let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose); let opt = RenderOptions { error_behavior: RenderErrorBehavior::Throw, }; - let data = render_json_value(req.data, environment_chain, &cb, &opt) - .await - .expect("Failed to render template"); - Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data })) + let data = render_json_value(req.data, environment_chain, &cb, &opt).await?; + Ok(Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))) } InternalEventPayload::ErrorResponse(resp) => { error!("Plugin error: {}: {:?}", resp.error, resp); @@ -144,16 +148,15 @@ pub(crate) async fn handle_plugin_event( resp.error ), color: Some(Color::Danger), - timeout: None, + timeout: Some(30000), ..Default::default() }), None, ); - Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await; - None + Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await } InternalEventPayload::ReloadResponse(req) => { - let plugins = app_handle.db().list_plugins().unwrap(); + let plugins = app_handle.db().list_plugins()?; for plugin in plugins { if plugin.directory != plugin_handle.dir { continue; @@ -163,7 +166,7 @@ pub(crate) async fn handle_plugin_event( updated_at: Utc::now().naive_utc(), // TODO: Add reloaded_at field to use instead ..plugin }; - app_handle.db().upsert_plugin(&new_plugin, &UpdateSource::Plugin).unwrap(); + app_handle.db().upsert_plugin(&new_plugin, &UpdateSource::Plugin)?; } if !req.silent { @@ -178,13 +181,13 @@ pub(crate) async fn handle_plugin_event( }), None, ); - Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await; + Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await + } else { + Ok(None) } - None } InternalEventPayload::SendHttpRequestRequest(req) => { - let window = get_window_from_window_context(app_handle, &window_context) - .expect("Failed to find window for sending HTTP request"); + let window = get_window_from_window_context(app_handle, &window_context)?; let mut http_request = req.http_request; let workspace = workspace_from_window(&window).expect("Failed to get workspace_id from window URL"); @@ -198,20 +201,17 @@ pub(crate) async fn handle_plugin_event( let http_response = if http_request.id.is_empty() { HttpResponse::default() } else { - window - .db() - .upsert_http_response( - &HttpResponse { - request_id: http_request.id.clone(), - workspace_id: http_request.workspace_id.clone(), - ..Default::default() - }, - &UpdateSource::Plugin, - ) - .unwrap() + window.db().upsert_http_response( + &HttpResponse { + request_id: http_request.id.clone(), + workspace_id: http_request.workspace_id.clone(), + ..Default::default() + }, + &UpdateSource::Plugin, + )? }; - let result = send_http_request( + let http_response = send_http_request( &window, &http_request, &http_response, @@ -219,16 +219,11 @@ pub(crate) async fn handle_plugin_event( cookie_jar, &mut tokio::sync::watch::channel(false).1, // No-op cancel channel ) - .await; + .await?; - let http_response = match result { - Ok(r) => r, - Err(_e) => return, - }; - - Some(InternalEventPayload::SendHttpRequestResponse(SendHttpRequestResponse { + Ok(Some(InternalEventPayload::SendHttpRequestResponse(SendHttpRequestResponse { http_response, - })) + }))) } InternalEventPayload::OpenWindowRequest(req) => { let (navigation_tx, mut navigation_rx) = tokio::sync::mpsc::channel(128); @@ -251,8 +246,8 @@ pub(crate) async fn handle_plugin_event( }), None, ); - Box::pin(handle_plugin_event(app_handle, &error_event, plugin_handle)).await; - return; + return Box::pin(handle_plugin_event(app_handle, &error_event, plugin_handle)) + .await; } { @@ -288,32 +283,33 @@ pub(crate) async fn handle_plugin_event( }); } - None + Ok(None) } InternalEventPayload::CloseWindowRequest(req) => { if let Some(window) = app_handle.webview_windows().get(&req.label) { - window.close().expect("Failed to close window"); + window.close()?; } - None + Ok(None) } InternalEventPayload::SetKeyValueRequest(req) => { let name = plugin_handle.info().name; app_handle.db().set_plugin_key_value(&name, &req.key, &req.value); - Some(InternalEventPayload::SetKeyValueResponse(SetKeyValueResponse {})) + Ok(Some(InternalEventPayload::SetKeyValueResponse(SetKeyValueResponse {}))) } InternalEventPayload::GetKeyValueRequest(req) => { let name = plugin_handle.info().name; let value = app_handle.db().get_plugin_key_value(&name, &req.key).map(|v| v.value); - Some(InternalEventPayload::GetKeyValueResponse(GetKeyValueResponse { value })) + Ok(Some(InternalEventPayload::GetKeyValueResponse(GetKeyValueResponse { value }))) } InternalEventPayload::DeleteKeyValueRequest(req) => { let name = plugin_handle.info().name; - let deleted = app_handle.db().delete_plugin_key_value(&name, &req.key).unwrap(); - Some(InternalEventPayload::DeleteKeyValueResponse(DeleteKeyValueResponse { deleted })) + let deleted = app_handle.db().delete_plugin_key_value(&name, &req.key)?; + Ok(Some(InternalEventPayload::DeleteKeyValueResponse(DeleteKeyValueResponse { + deleted, + }))) } InternalEventPayload::ListCookieNamesRequest(_req) => { - let window = get_window_from_window_context(app_handle, &window_context) - .expect("Failed to find window for listing cookies"); + let window = get_window_from_window_context(app_handle, &window_context)?; let names = match cookie_jar_from_window(&window) { None => Vec::new(), Some(j) => j @@ -322,11 +318,12 @@ pub(crate) async fn handle_plugin_event( .filter_map(|c| Cookie::parse(c.raw_cookie).ok().map(|c| c.name().to_string())) .collect(), }; - Some(InternalEventPayload::ListCookieNamesResponse(ListCookieNamesResponse { names })) + Ok(Some(InternalEventPayload::ListCookieNamesResponse(ListCookieNamesResponse { + names, + }))) } InternalEventPayload::GetCookieValueRequest(req) => { - let window = get_window_from_window_context(app_handle, &window_context) - .expect("Failed to find window for listing cookies"); + let window = get_window_from_window_context(app_handle, &window_context)?; let value = match cookie_jar_from_window(&window) { None => None, Some(j) => j.cookies.into_iter().find_map(|c| match Cookie::parse(c.raw_cookie) { @@ -336,15 +333,8 @@ pub(crate) async fn handle_plugin_event( _ => None, }), }; - Some(InternalEventPayload::GetCookieValueResponse(GetCookieValueResponse { value })) - } - _ => None, - }; - - if let Some(e) = response_event { - let plugin_manager: State<'_, PluginManager> = app_handle.state(); - if let Err(e) = plugin_manager.reply(&event, &e).await { - warn!("Failed to reply to plugin manager: {:?}", e) + Ok(Some(InternalEventPayload::GetCookieValueResponse(GetCookieValueResponse { value }))) } + _ => Ok(None), } } diff --git a/src-tauri/yaak-common/src/window.rs b/src-tauri/yaak-common/src/window.rs index 60bcc076..004ef18f 100644 --- a/src-tauri/yaak-common/src/window.rs +++ b/src-tauri/yaak-common/src/window.rs @@ -5,6 +5,7 @@ pub trait WorkspaceWindowTrait { fn workspace_id(&self) -> Option; fn cookie_jar_id(&self) -> Option; fn environment_id(&self) -> Option; + fn request_id(&self) -> Option; } impl WorkspaceWindowTrait for WebviewWindow { @@ -28,4 +29,10 @@ impl WorkspaceWindowTrait for WebviewWindow { let mut query_pairs = url.query_pairs(); query_pairs.find(|(k, _v)| k == "environment_id").map(|(_k, v)| v.to_string()) } + + fn request_id(&self) -> Option { + let url = self.url().unwrap(); + let mut query_pairs = url.query_pairs(); + query_pairs.find(|(k, _v)| k == "request_id").map(|(_k, v)| v.to_string()) + } } diff --git a/src-tauri/yaak-models/src/queries/any_request.rs b/src-tauri/yaak-models/src/queries/any_request.rs new file mode 100644 index 00000000..ba3b23b9 --- /dev/null +++ b/src-tauri/yaak-models/src/queries/any_request.rs @@ -0,0 +1,23 @@ +use crate::db_context::DbContext; +use crate::error::Result; +use crate::models::{ + GrpcRequest, HttpRequest, WebsocketRequest, +}; + +pub enum AnyRequest { + HttpRequest(HttpRequest), + GrpcRequest(GrpcRequest), + WebsocketRequest(WebsocketRequest), +} + +impl<'a> DbContext<'a> { + pub fn get_any_request(&self, id: &str) -> Result { + if let Ok(http_request) = self.get_http_request(id) { + Ok(AnyRequest::HttpRequest(http_request)) + } else if let Ok(grpc_request) = self.get_grpc_request(id) { + Ok(AnyRequest::GrpcRequest(grpc_request)) + } else { + Ok(AnyRequest::WebsocketRequest(self.get_websocket_request(id)?)) + } + } +} diff --git a/src-tauri/yaak-models/src/queries/mod.rs b/src-tauri/yaak-models/src/queries/mod.rs index 331638fb..983ac621 100644 --- a/src-tauri/yaak-models/src/queries/mod.rs +++ b/src-tauri/yaak-models/src/queries/mod.rs @@ -1,3 +1,4 @@ +pub mod any_request; mod batch; mod cookie_jars; mod environments; diff --git a/src-tauri/yaak-plugins/src/events.rs b/src-tauri/yaak-plugins/src/events.rs index 3840d0f8..4437c3bf 100644 --- a/src-tauri/yaak-plugins/src/events.rs +++ b/src-tauri/yaak-plugins/src/events.rs @@ -123,6 +123,9 @@ pub enum InternalEventPayload { RenderGrpcRequestRequest(RenderGrpcRequestRequest), RenderGrpcRequestResponse(RenderGrpcRequestResponse), + TemplateRenderRequest(TemplateRenderRequest), + TemplateRenderResponse(TemplateRenderResponse), + GetKeyValueRequest(GetKeyValueRequest), GetKeyValueResponse(GetKeyValueResponse), SetKeyValueRequest(SetKeyValueRequest), @@ -135,9 +138,6 @@ pub enum InternalEventPayload { WindowCloseEvent, CloseWindowRequest(CloseWindowRequest), - TemplateRenderRequest(TemplateRenderRequest), - TemplateRenderResponse(TemplateRenderResponse), - ShowToastRequest(ShowToastRequest), ShowToastResponse(EmptyPayload), diff --git a/src-web/components/HttpAuthenticationEditor.tsx b/src-web/components/HttpAuthenticationEditor.tsx index c8c15068..e9939f78 100644 --- a/src-web/components/HttpAuthenticationEditor.tsx +++ b/src-web/components/HttpAuthenticationEditor.tsx @@ -67,7 +67,7 @@ export function HttpAuthenticationEditor({ model }: Props) { ); } else { - return Authentication not configured; + return No authentication; } } diff --git a/src-web/components/HttpRequestPane.tsx b/src-web/components/HttpRequestPane.tsx index e2113cf9..8ab74fa1 100644 --- a/src-web/components/HttpRequestPane.tsx +++ b/src-web/components/HttpRequestPane.tsx @@ -351,7 +351,7 @@ export function HttpRequestPane({ style, fullHeight, className, activeRequest }: label="Request" onChangeValue={setActiveTab} tabs={tabs} - tabListClassName="mt-1 !mb-1.5" + tabListClassName="mt-1 mb-1.5" > diff --git a/src-web/components/core/Tabs/Tabs.tsx b/src-web/components/core/Tabs/Tabs.tsx index 30383f7c..a34e0b9b 100644 --- a/src-web/components/core/Tabs/Tabs.tsx +++ b/src-web/components/core/Tabs/Tabs.tsx @@ -84,7 +84,8 @@ export function Tabs({ className={classNames( tabListClassName, addBorders && '!-ml-1', - 'flex items-center hide-scrollbars mb-2', + addBorders && layout === 'vertical' && 'mb-2', + 'flex items-center hide-scrollbars', layout === 'horizontal' && 'h-full overflow-auto p-2 -mr-2', layout === 'vertical' && 'overflow-x-auto overflow-y-visible ', // Give space for button focus states within overflow boundary. diff --git a/src-web/lib/contentType.ts b/src-web/lib/contentType.ts index 909b949f..1268fafd 100644 --- a/src-web/lib/contentType.ts +++ b/src-web/lib/contentType.ts @@ -21,6 +21,8 @@ export function languageFromContentType( } else if (justContentType.includes('javascript')) { // Sometimes `application/javascript` returns JSON, so try detecting that return detectFromContent(content, 'javascript'); + } else if (justContentType.includes('markdown')) { + return 'markdown'; } return detectFromContent(content, 'text');