mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-21 08:21:19 +02:00
Don't show plugin error for response filter
https://feedback.yaak.app/p/increase-debounce-time-for-jsonpath-xpath-filter https://feedback.yaak.app/p/possibility-to-cancel-request
This commit is contained in:
@@ -65,7 +65,7 @@ extensions: Array<string>, };
|
|||||||
|
|
||||||
export type FilterRequest = { content: string, filter: string, };
|
export type FilterRequest = { content: string, filter: string, };
|
||||||
|
|
||||||
export type FilterResponse = { content: string, };
|
export type FilterResponse = { content: string, error?: string, };
|
||||||
|
|
||||||
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
|
import { FilterResponse } from '../bindings/gen_events';
|
||||||
import type { Context } from './Context';
|
import type { Context } from './Context';
|
||||||
|
|
||||||
type FilterPluginResponse = { filtered: string };
|
|
||||||
|
|
||||||
export type FilterPlugin = {
|
export type FilterPlugin = {
|
||||||
name: string;
|
name: string;
|
||||||
description?: string;
|
description?: string;
|
||||||
onFilter(
|
onFilter(
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
args: { payload: string; filter: string; mimeType: string },
|
args: { payload: string; filter: string; mimeType: string },
|
||||||
): Promise<FilterPluginResponse> | FilterPluginResponse;
|
): Promise<FilterResponse> | FilterResponse;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,7 +7,8 @@ import {
|
|||||||
GetCookieValueRequest,
|
GetCookieValueRequest,
|
||||||
GetCookieValueResponse,
|
GetCookieValueResponse,
|
||||||
GetHttpRequestByIdResponse,
|
GetHttpRequestByIdResponse,
|
||||||
GetKeyValueResponse, GrpcRequestAction,
|
GetKeyValueResponse,
|
||||||
|
GrpcRequestAction,
|
||||||
HttpAuthenticationAction,
|
HttpAuthenticationAction,
|
||||||
HttpRequestAction,
|
HttpRequestAction,
|
||||||
InternalEvent,
|
InternalEvent,
|
||||||
@@ -15,8 +16,8 @@ import {
|
|||||||
ListCookieNamesResponse,
|
ListCookieNamesResponse,
|
||||||
PluginWindowContext,
|
PluginWindowContext,
|
||||||
PromptTextResponse,
|
PromptTextResponse,
|
||||||
RenderHttpRequestResponse,
|
|
||||||
RenderGrpcRequestResponse,
|
RenderGrpcRequestResponse,
|
||||||
|
RenderHttpRequestResponse,
|
||||||
SendHttpRequestResponse,
|
SendHttpRequestResponse,
|
||||||
TemplateFunction,
|
TemplateFunction,
|
||||||
TemplateFunctionArg,
|
TemplateFunctionArg,
|
||||||
@@ -138,11 +139,7 @@ export class PluginInstance {
|
|||||||
payload: payload.content,
|
payload: payload.content,
|
||||||
mimeType: payload.type,
|
mimeType: payload.type,
|
||||||
});
|
});
|
||||||
const replyPayload: InternalEventPayload = {
|
this.#sendPayload(windowContext, { type: 'filter_response', ...reply }, replyId);
|
||||||
type: 'filter_response',
|
|
||||||
content: reply.filtered,
|
|
||||||
};
|
|
||||||
this.#sendPayload(windowContext, replyPayload, replyId);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { PluginDefinition } from '@yaakapp/api';
|
import type { PluginDefinition } from '@yaakapp/api';
|
||||||
import { JSONPath } from 'jsonpath-plus';
|
import { JSONPath } from 'jsonpath-plus';
|
||||||
|
|
||||||
export const plugin: PluginDefinition = {
|
export const plugin: PluginDefinition = {
|
||||||
@@ -7,8 +7,12 @@ export const plugin: PluginDefinition = {
|
|||||||
description: 'Filter JSONPath',
|
description: 'Filter JSONPath',
|
||||||
onFilter(_ctx, args) {
|
onFilter(_ctx, args) {
|
||||||
const parsed = JSON.parse(args.payload);
|
const parsed = JSON.parse(args.payload);
|
||||||
const filtered = JSONPath({ path: args.filter, json: parsed });
|
try {
|
||||||
return { filtered: JSON.stringify(filtered, null, 2) };
|
const filtered = JSONPath({ path: args.filter, json: parsed });
|
||||||
|
return { content: JSON.stringify(filtered, null, 2) };
|
||||||
|
} catch (err) {
|
||||||
|
return { content: '', error: `Invalid filter: ${err}` };
|
||||||
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { DOMParser } from '@xmldom/xmldom';
|
import { DOMParser } from '@xmldom/xmldom';
|
||||||
import { PluginDefinition } from '@yaakapp/api';
|
import type { PluginDefinition } from '@yaakapp/api';
|
||||||
import xpath from 'xpath';
|
import xpath from 'xpath';
|
||||||
|
|
||||||
export const plugin: PluginDefinition = {
|
export const plugin: PluginDefinition = {
|
||||||
@@ -8,13 +8,16 @@ export const plugin: PluginDefinition = {
|
|||||||
description: 'Filter XPath',
|
description: 'Filter XPath',
|
||||||
onFilter(_ctx, args) {
|
onFilter(_ctx, args) {
|
||||||
const doc = new DOMParser().parseFromString(args.payload, 'text/xml');
|
const doc = new DOMParser().parseFromString(args.payload, 'text/xml');
|
||||||
const result = xpath.select(args.filter, doc, false);
|
try {
|
||||||
|
const result = xpath.select(args.filter, doc, false);
|
||||||
if (Array.isArray(result)) {
|
if (Array.isArray(result)) {
|
||||||
return { filtered: result.map(r => String(r)).join('\n') };
|
return { content: result.map((r) => String(r)).join('\n') };
|
||||||
} else {
|
} else {
|
||||||
// Not sure what cases this happens in (?)
|
// Not sure what cases this happens in (?)
|
||||||
return { filtered: String(result) };
|
return { content: String(result) };
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
return { content: '', error: `Invalid filter: ${err}` };
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -36,12 +36,11 @@ use yaak_models::models::{
|
|||||||
use yaak_models::query_manager::QueryManagerExt;
|
use yaak_models::query_manager::QueryManagerExt;
|
||||||
use yaak_models::util::{BatchUpsertResult, UpdateSource, get_workspace_export_resources};
|
use yaak_models::util::{BatchUpsertResult, UpdateSource, get_workspace_export_resources};
|
||||||
use yaak_plugins::events::{
|
use yaak_plugins::events::{
|
||||||
CallGrpcRequestActionArgs, CallGrpcRequestActionRequest, CallHttpAuthenticationActionArgs,
|
CallGrpcRequestActionArgs, CallGrpcRequestActionRequest, CallHttpRequestActionArgs,
|
||||||
CallHttpRequestActionArgs, CallHttpRequestActionRequest, FilterResponse,
|
CallHttpRequestActionRequest, FilterResponse, GetGrpcRequestActionsResponse,
|
||||||
GetGrpcRequestActionsResponse, GetHttpAuthenticationConfigResponse,
|
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||||
GetHttpAuthenticationSummaryResponse, GetHttpRequestActionsResponse,
|
GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, InternalEvent,
|
||||||
GetTemplateFunctionsResponse, InternalEvent, InternalEventPayload, JsonPrimitive,
|
InternalEventPayload, JsonPrimitive, PluginWindowContext, RenderPurpose,
|
||||||
PluginWindowContext, RenderPurpose,
|
|
||||||
};
|
};
|
||||||
use yaak_plugins::manager::PluginManager;
|
use yaak_plugins::manager::PluginManager;
|
||||||
use yaak_plugins::plugin_meta::PluginMetadata;
|
use yaak_plugins::plugin_meta::PluginMetadata;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use crate::http_request::send_http_request;
|
use crate::http_request::send_http_request;
|
||||||
use crate::render::{render_grpc_request, render_http_request, render_json_value};
|
use crate::render::{render_grpc_request, render_http_request, render_json_value};
|
||||||
use crate::window::{create_window, CreateWindowConfig};
|
use crate::window::{CreateWindowConfig, create_window};
|
||||||
use crate::{
|
use crate::{
|
||||||
call_frontend, cookie_jar_from_window, environment_from_window, get_window_from_window_context,
|
call_frontend, cookie_jar_from_window, environment_from_window, get_window_from_window_context,
|
||||||
workspace_from_window,
|
workspace_from_window,
|
||||||
@@ -13,7 +13,13 @@ use tauri_plugin_clipboard_manager::ClipboardExt;
|
|||||||
use yaak_models::models::{HttpResponse, Plugin};
|
use yaak_models::models::{HttpResponse, Plugin};
|
||||||
use yaak_models::query_manager::QueryManagerExt;
|
use yaak_models::query_manager::QueryManagerExt;
|
||||||
use yaak_models::util::UpdateSource;
|
use yaak_models::util::UpdateSource;
|
||||||
use yaak_plugins::events::{Color, DeleteKeyValueResponse, EmptyPayload, FindHttpResponsesResponse, GetCookieValueResponse, GetHttpRequestByIdResponse, GetKeyValueResponse, Icon, InternalEvent, InternalEventPayload, ListCookieNamesResponse, PluginWindowContext, RenderGrpcRequestResponse, RenderHttpRequestResponse, SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest, TemplateRenderResponse, WindowNavigateEvent};
|
use yaak_plugins::events::{
|
||||||
|
Color, DeleteKeyValueResponse, EmptyPayload, FindHttpResponsesResponse, GetCookieValueResponse,
|
||||||
|
GetHttpRequestByIdResponse, GetKeyValueResponse, Icon, InternalEvent, InternalEventPayload,
|
||||||
|
ListCookieNamesResponse, PluginWindowContext, RenderGrpcRequestResponse,
|
||||||
|
RenderHttpRequestResponse, SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest,
|
||||||
|
TemplateRenderResponse, WindowNavigateEvent,
|
||||||
|
};
|
||||||
use yaak_plugins::manager::PluginManager;
|
use yaak_plugins::manager::PluginManager;
|
||||||
use yaak_plugins::plugin_handle::PluginHandle;
|
use yaak_plugins::plugin_handle::PluginHandle;
|
||||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||||
@@ -80,8 +86,8 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
|||||||
environment.as_ref(),
|
environment.as_ref(),
|
||||||
&cb,
|
&cb,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to render grpc request");
|
.expect("Failed to render grpc request");
|
||||||
Some(InternalEventPayload::RenderGrpcRequestResponse(RenderGrpcRequestResponse {
|
Some(InternalEventPayload::RenderGrpcRequestResponse(RenderGrpcRequestResponse {
|
||||||
grpc_request,
|
grpc_request,
|
||||||
}))
|
}))
|
||||||
@@ -104,8 +110,8 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
|||||||
environment.as_ref(),
|
environment.as_ref(),
|
||||||
&cb,
|
&cb,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to render http request");
|
.expect("Failed to render http request");
|
||||||
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
||||||
http_request,
|
http_request,
|
||||||
}))
|
}))
|
||||||
@@ -206,7 +212,7 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
|||||||
cookie_jar,
|
cookie_jar,
|
||||||
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
|
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let http_response = match result {
|
let http_response = match result {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ extensions: Array<string>, };
|
|||||||
|
|
||||||
export type FilterRequest = { content: string, filter: string, };
|
export type FilterRequest = { content: string, filter: string, };
|
||||||
|
|
||||||
export type FilterResponse = { content: string, };
|
export type FilterResponse = { content: string, error?: string, };
|
||||||
|
|
||||||
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
||||||
|
|
||||||
|
|||||||
@@ -216,6 +216,8 @@ pub struct FilterRequest {
|
|||||||
#[ts(export, export_to = "gen_events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FilterResponse {
|
pub struct FilterResponse {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
|
#[ts(optional)]
|
||||||
|
pub error: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import classNames from 'classnames';
|
|||||||
import type { CSSProperties, ReactNode } from 'react';
|
import type { CSSProperties, ReactNode } from 'react';
|
||||||
import React, { useCallback, useMemo } from 'react';
|
import React, { useCallback, useMemo } from 'react';
|
||||||
import { useLocalStorage } from 'react-use';
|
import { useLocalStorage } from 'react-use';
|
||||||
|
import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse';
|
||||||
import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse';
|
import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse';
|
||||||
import { useResponseViewMode } from '../hooks/useResponseViewMode';
|
import { useResponseViewMode } from '../hooks/useResponseViewMode';
|
||||||
import { getMimeTypeFromContentType } from '../lib/contentType';
|
import { getMimeTypeFromContentType } from '../lib/contentType';
|
||||||
@@ -14,7 +15,7 @@ import { HttpResponseDurationTag } from './core/HttpResponseDurationTag';
|
|||||||
import { HotKeyList } from './core/HotKeyList';
|
import { HotKeyList } from './core/HotKeyList';
|
||||||
import { LoadingIcon } from './core/LoadingIcon';
|
import { LoadingIcon } from './core/LoadingIcon';
|
||||||
import { SizeTag } from './core/SizeTag';
|
import { SizeTag } from './core/SizeTag';
|
||||||
import { HStack } from './core/Stacks';
|
import { HStack, VStack } from './core/Stacks';
|
||||||
import { HttpStatusTag } from './core/HttpStatusTag';
|
import { HttpStatusTag } from './core/HttpStatusTag';
|
||||||
import type { TabItem } from './core/Tabs/Tabs';
|
import type { TabItem } from './core/Tabs/Tabs';
|
||||||
import { TabContent, Tabs } from './core/Tabs/Tabs';
|
import { TabContent, Tabs } from './core/Tabs/Tabs';
|
||||||
@@ -31,6 +32,7 @@ import { PdfViewer } from './responseViewers/PdfViewer';
|
|||||||
import { SvgViewer } from './responseViewers/SvgViewer';
|
import { SvgViewer } from './responseViewers/SvgViewer';
|
||||||
import { VideoViewer } from './responseViewers/VideoViewer';
|
import { VideoViewer } from './responseViewers/VideoViewer';
|
||||||
import { ErrorBoundary } from './ErrorBoundary';
|
import { ErrorBoundary } from './ErrorBoundary';
|
||||||
|
import { Button } from './core/Button';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
style?: CSSProperties;
|
style?: CSSProperties;
|
||||||
@@ -90,6 +92,8 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
|||||||
[activeRequestId, setActiveTabs],
|
[activeRequestId, setActiveTabs],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const cancel = useCancelHttpResponse(activeResponse?.id ?? null);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
style={style}
|
style={style}
|
||||||
@@ -160,7 +164,13 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) {
|
|||||||
<ConfirmLargeResponse response={activeResponse}>
|
<ConfirmLargeResponse response={activeResponse}>
|
||||||
{activeResponse.state === 'initialized' ? (
|
{activeResponse.state === 'initialized' ? (
|
||||||
<EmptyStateText>
|
<EmptyStateText>
|
||||||
<LoadingIcon size="xl" className="text-text-subtlest" />
|
<VStack space={3}>
|
||||||
|
<HStack space={3} className="text-lg">
|
||||||
|
<LoadingIcon size="lg" className="text-text-subtlest" />
|
||||||
|
Sending Request
|
||||||
|
</HStack>
|
||||||
|
<Button variant="border" onClick={() => cancel.mutate()}>Cancel</Button>
|
||||||
|
</VStack>
|
||||||
</EmptyStateText>
|
</EmptyStateText>
|
||||||
) : activeResponse.state === 'closed' && activeResponse.contentLength === 0 ? (
|
) : activeResponse.state === 'closed' && activeResponse.contentLength === 0 ? (
|
||||||
<EmptyStateText>Empty </EmptyStateText>
|
<EmptyStateText>Empty </EmptyStateText>
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ const useFilterText = createGlobalState<Record<string, string | null>>({});
|
|||||||
export function TextViewer({ language, text, responseId, requestId, pretty, className }: Props) {
|
export function TextViewer({ language, text, responseId, requestId, pretty, className }: Props) {
|
||||||
const [filterTextMap, setFilterTextMap] = useFilterText();
|
const [filterTextMap, setFilterTextMap] = useFilterText();
|
||||||
const filterText = filterTextMap[requestId] ?? null;
|
const filterText = filterTextMap[requestId] ?? null;
|
||||||
const debouncedFilterText = useDebouncedValue(filterText, 200);
|
const debouncedFilterText = useDebouncedValue(filterText);
|
||||||
const setFilterText = useCallback(
|
const setFilterText = useCallback(
|
||||||
(v: string | null) => {
|
(v: string | null) => {
|
||||||
setFilterTextMap((m) => ({ ...m, [requestId]: v }));
|
setFilterTextMap((m) => ({ ...m, [requestId]: v }));
|
||||||
@@ -58,7 +58,7 @@ export function TextViewer({ language, text, responseId, requestId, pretty, clas
|
|||||||
<div key="input" className="w-full !opacity-100">
|
<div key="input" className="w-full !opacity-100">
|
||||||
<Input
|
<Input
|
||||||
key={requestId}
|
key={requestId}
|
||||||
validate={!filteredResponse.error}
|
validate={!(filteredResponse.error || filteredResponse.data?.error)}
|
||||||
hideLabel
|
hideLabel
|
||||||
autoFocus
|
autoFocus
|
||||||
containerClassName="bg-surface"
|
containerClassName="bg-surface"
|
||||||
@@ -79,6 +79,7 @@ export function TextViewer({ language, text, responseId, requestId, pretty, clas
|
|||||||
<IconButton
|
<IconButton
|
||||||
key="icon"
|
key="icon"
|
||||||
size="sm"
|
size="sm"
|
||||||
|
isLoading={filteredResponse.isPending}
|
||||||
icon={isSearching ? 'x' : 'filter'}
|
icon={isSearching ? 'x' : 'filter'}
|
||||||
title={isSearching ? 'Close filter' : 'Filter response'}
|
title={isSearching ? 'Close filter' : 'Filter response'}
|
||||||
onClick={toggleSearch}
|
onClick={toggleSearch}
|
||||||
@@ -90,7 +91,9 @@ export function TextViewer({ language, text, responseId, requestId, pretty, clas
|
|||||||
}, [
|
}, [
|
||||||
canFilter,
|
canFilter,
|
||||||
filterText,
|
filterText,
|
||||||
|
filteredResponse.data?.error,
|
||||||
filteredResponse.error,
|
filteredResponse.error,
|
||||||
|
filteredResponse.isPending,
|
||||||
isSearching,
|
isSearching,
|
||||||
language,
|
language,
|
||||||
requestId,
|
requestId,
|
||||||
@@ -109,7 +112,7 @@ export function TextViewer({ language, text, responseId, requestId, pretty, clas
|
|||||||
if (filteredResponse.error) {
|
if (filteredResponse.error) {
|
||||||
body = '';
|
body = '';
|
||||||
} else {
|
} else {
|
||||||
body = filteredResponse.data != null ? filteredResponse.data : '';
|
body = filteredResponse.data?.content != null ? filteredResponse.data.content : '';
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
body = formattedBody;
|
body = formattedBody;
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ export function useFilterResponse({
|
|||||||
responseId: string | null;
|
responseId: string | null;
|
||||||
filter: string;
|
filter: string;
|
||||||
}) {
|
}) {
|
||||||
return useQuery<string | null, string>({
|
return useQuery({
|
||||||
queryKey: ['filter_response', responseId, filter],
|
queryKey: ['filter_response', responseId, filter],
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
if (filter === '') {
|
if (filter === '') {
|
||||||
@@ -21,7 +21,7 @@ export function useFilterResponse({
|
|||||||
filter,
|
filter,
|
||||||
})) as FilterResponse;
|
})) as FilterResponse;
|
||||||
|
|
||||||
return result.content;
|
return result;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user