mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-01-11 22:40:26 +01:00
Dynamic template function args and TTL option for request chaining (#266)
This commit is contained in:
@@ -361,7 +361,11 @@ export type GetKeyValueRequest = { key: string, };
|
||||
|
||||
export type GetKeyValueResponse = { value?: string, };
|
||||
|
||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
export type GetTemplateFunctionConfigRequest = { contextId: string, name: string, values: { [key in string]?: JsonPrimitive }, };
|
||||
|
||||
export type GetTemplateFunctionConfigResponse = { function: TemplateFunction, pluginRefId: string, };
|
||||
|
||||
export type GetTemplateFunctionSummaryResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
|
||||
export type GetThemesRequest = Record<string, never>;
|
||||
|
||||
@@ -385,7 +389,7 @@ export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: PluginWindowContext, payload: InternalEventPayload, };
|
||||
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse;
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_function_summary_request" } & EmptyPayload | { "type": "get_template_function_summary_response" } & GetTemplateFunctionSummaryResponse | { "type": "get_template_function_config_request" } & GetTemplateFunctionConfigRequest | { "type": "get_template_function_config_response" } & GetTemplateFunctionConfigResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse;
|
||||
|
||||
export type JsonPrimitive = string | number | boolean | null;
|
||||
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import {
|
||||
CallTemplateFunctionArgs,
|
||||
FormInput,
|
||||
GetHttpAuthenticationConfigRequest,
|
||||
TemplateFunction,
|
||||
} from "../bindings/gen_events";
|
||||
import { Context } from "./Context";
|
||||
TemplateFunctionArg,
|
||||
} from '../bindings/gen_events';
|
||||
import { MaybePromise } from '../helpers';
|
||||
import { Context } from './Context';
|
||||
|
||||
export type DynamicTemplateFunctionArg = FormInput & {
|
||||
dynamic(
|
||||
ctx: Context,
|
||||
args: GetHttpAuthenticationConfigRequest,
|
||||
): MaybePromise<Partial<FormInput> | undefined | null>;
|
||||
};
|
||||
|
||||
export type TemplateFunctionPlugin = TemplateFunction & {
|
||||
onRender(
|
||||
ctx: Context,
|
||||
args: CallTemplateFunctionArgs,
|
||||
): Promise<string | null>;
|
||||
args: (TemplateFunctionArg | DynamicTemplateFunctionArg)[];
|
||||
onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null>;
|
||||
};
|
||||
|
||||
@@ -186,20 +186,55 @@ export class PluginInstance {
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'get_template_functions_request' &&
|
||||
payload.type === 'get_template_function_summary_request' &&
|
||||
Array.isArray(this.#mod?.templateFunctions)
|
||||
) {
|
||||
const reply: TemplateFunction[] = this.#mod.templateFunctions.map((templateFunction) => {
|
||||
return {
|
||||
...migrateTemplateFunctionSelectOptions(templateFunction),
|
||||
// Add everything except render
|
||||
onRender: undefined,
|
||||
};
|
||||
});
|
||||
const functions: TemplateFunction[] = this.#mod.templateFunctions.map(
|
||||
(templateFunction) => {
|
||||
return {
|
||||
...migrateTemplateFunctionSelectOptions(templateFunction),
|
||||
// Add everything except render
|
||||
onRender: undefined,
|
||||
};
|
||||
},
|
||||
);
|
||||
const replyPayload: InternalEventPayload = {
|
||||
type: 'get_template_functions_response',
|
||||
type: 'get_template_function_summary_response',
|
||||
pluginRefId: this.#workerData.pluginRefId,
|
||||
functions: reply,
|
||||
functions,
|
||||
};
|
||||
this.#sendPayload(windowContext, replyPayload, replyId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'get_template_function_config_request' &&
|
||||
Array.isArray(this.#mod?.templateFunctions)
|
||||
) {
|
||||
let templateFunction = this.#mod.templateFunctions.find((f) => f.name === payload.name);
|
||||
if (templateFunction == null) {
|
||||
this.#sendEmpty(windowContext, replyId);
|
||||
return;
|
||||
}
|
||||
|
||||
templateFunction = migrateTemplateFunctionSelectOptions(templateFunction);
|
||||
// @ts-ignore
|
||||
delete templateFunction.onRender;
|
||||
const resolvedArgs: TemplateFunctionArg[] = [];
|
||||
for (const arg of templateFunction.args) {
|
||||
if (arg && 'dynamic' in arg) {
|
||||
const dynamicAttrs = await arg.dynamic(ctx, payload);
|
||||
const { dynamic, ...other } = arg;
|
||||
resolvedArgs.push({ ...other, ...dynamicAttrs } as TemplateFunctionArg);
|
||||
} else if (arg) {
|
||||
resolvedArgs.push(arg);
|
||||
}
|
||||
templateFunction.args = resolvedArgs;
|
||||
}
|
||||
const replyPayload: InternalEventPayload = {
|
||||
type: 'get_template_function_config_response',
|
||||
pluginRefId: this.#workerData.pluginRefId,
|
||||
function: templateFunction,
|
||||
};
|
||||
this.#sendPayload(windowContext, replyPayload, replyId);
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { TemplateFunction } from '@yaakapp/api';
|
||||
import { TemplateFunctionPlugin } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
|
||||
|
||||
export function migrateTemplateFunctionSelectOptions(f: TemplateFunction): TemplateFunction {
|
||||
export function migrateTemplateFunctionSelectOptions(
|
||||
f: TemplateFunctionPlugin,
|
||||
): TemplateFunctionPlugin {
|
||||
const migratedArgs = f.args.map((a) => {
|
||||
if (a.type === 'select') {
|
||||
a.options = a.options.map((o) => ({
|
||||
|
||||
@@ -3,25 +3,44 @@ import type {
|
||||
CallTemplateFunctionArgs,
|
||||
Context,
|
||||
FormInput,
|
||||
GetHttpAuthenticationConfigRequest,
|
||||
HttpResponse,
|
||||
PluginDefinition,
|
||||
RenderPurpose,
|
||||
} from '@yaakapp/api';
|
||||
import type { DynamicTemplateFunctionArg } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
|
||||
import { JSONPath } from 'jsonpath-plus';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import xpath from 'xpath';
|
||||
|
||||
const BEHAVIOR_TTL = 'ttl';
|
||||
const BEHAVIOR_ALWAYS = 'always';
|
||||
const BEHAVIOR_SMART = 'smart';
|
||||
|
||||
const behaviorArg: FormInput = {
|
||||
type: 'select',
|
||||
name: 'behavior',
|
||||
label: 'Sending Behavior',
|
||||
defaultValue: 'smart',
|
||||
options: [
|
||||
{ label: 'When no responses', value: 'smart' },
|
||||
{ label: 'Always', value: 'always' },
|
||||
{ label: 'When no responses', value: BEHAVIOR_SMART },
|
||||
{ label: 'Always', value: BEHAVIOR_ALWAYS },
|
||||
{ label: 'When expired', value: BEHAVIOR_TTL },
|
||||
],
|
||||
};
|
||||
|
||||
const ttlArg: DynamicTemplateFunctionArg = {
|
||||
type: 'text',
|
||||
name: 'ttl',
|
||||
label: 'Expiration Time (seconds)',
|
||||
placeholder: '0',
|
||||
description: 'Resend the request when the latest response is older than this many seconds, or if there are no responses yet.',
|
||||
dynamic(_ctx: Context, { values }: GetHttpAuthenticationConfigRequest) {
|
||||
const show = values.behavior === BEHAVIOR_TTL;
|
||||
return { hidden: !show };
|
||||
},
|
||||
};
|
||||
|
||||
const requestArg: FormInput = {
|
||||
type: 'http_request',
|
||||
name: 'request',
|
||||
@@ -42,6 +61,7 @@ export const plugin: PluginDefinition = {
|
||||
placeholder: 'Content-Type',
|
||||
},
|
||||
behaviorArg,
|
||||
ttlArg,
|
||||
],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.request || !args.values.header) return null;
|
||||
@@ -50,6 +70,7 @@ export const plugin: PluginDefinition = {
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
ttl: String(args.values.ttl || ''),
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
@@ -72,6 +93,7 @@ export const plugin: PluginDefinition = {
|
||||
placeholder: '$.books[0].id or /books[0]/id',
|
||||
},
|
||||
behaviorArg,
|
||||
ttlArg,
|
||||
],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.request || !args.values.path) return null;
|
||||
@@ -80,6 +102,7 @@ export const plugin: PluginDefinition = {
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
ttl: String(args.values.ttl || ''),
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
@@ -113,7 +136,7 @@ export const plugin: PluginDefinition = {
|
||||
name: 'response.body.raw',
|
||||
description: 'Access the entire response body, as text',
|
||||
aliases: ['response'],
|
||||
args: [requestArg, behaviorArg],
|
||||
args: [requestArg, behaviorArg, ttlArg],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.request) return null;
|
||||
|
||||
@@ -121,6 +144,7 @@ export const plugin: PluginDefinition = {
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
ttl: String(args.values.ttl || ''),
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
@@ -177,9 +201,11 @@ async function getResponse(
|
||||
requestId,
|
||||
behavior,
|
||||
purpose,
|
||||
ttl,
|
||||
}: {
|
||||
requestId: string;
|
||||
behavior: string | null;
|
||||
ttl: string | null;
|
||||
purpose: RenderPurpose;
|
||||
},
|
||||
): Promise<HttpResponse | null> {
|
||||
@@ -203,7 +229,11 @@ async function getResponse(
|
||||
const finalBehavior = behavior === 'always' && purpose === 'preview' ? 'smart' : behavior;
|
||||
|
||||
// Send if no responses and "smart," or "always"
|
||||
if ((finalBehavior === 'smart' && response == null) || finalBehavior === 'always') {
|
||||
if (
|
||||
(finalBehavior === 'smart' && response == null) ||
|
||||
finalBehavior === 'always' ||
|
||||
(finalBehavior === BEHAVIOR_TTL && shouldSendExpired(response, ttl))
|
||||
) {
|
||||
// NOTE: Render inside this conditional, or we'll get infinite recursion (render->render->...)
|
||||
const renderedHttpRequest = await ctx.httpRequest.render({ httpRequest, purpose });
|
||||
response = await ctx.httpRequest.send({ httpRequest: renderedHttpRequest });
|
||||
@@ -211,3 +241,12 @@ async function getResponse(
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
function shouldSendExpired(response: HttpResponse | null, ttl: string | null): boolean {
|
||||
if (response == null) return true;
|
||||
const ttlSeconds = parseInt(ttl || '0');
|
||||
if (isNaN(ttlSeconds)) throw new Error(`Invalid TTL "${ttl}"`);
|
||||
const nowMillis = Date.now();
|
||||
const respMillis = new Date(response.createdAt + 'Z').getTime();
|
||||
return respMillis + ttlSeconds * 1000 < nowMillis;
|
||||
}
|
||||
|
||||
@@ -38,13 +38,7 @@ use yaak_models::models::{
|
||||
};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::{BatchUpsertResult, UpdateSource, get_workspace_export_resources};
|
||||
use yaak_plugins::events::{
|
||||
CallGrpcRequestActionArgs, CallGrpcRequestActionRequest, CallHttpRequestActionArgs,
|
||||
CallHttpRequestActionRequest, Color, FilterResponse, GetGrpcRequestActionsResponse,
|
||||
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||
GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, InternalEvent,
|
||||
InternalEventPayload, JsonPrimitive, PluginWindowContext, RenderPurpose, ShowToastRequest,
|
||||
};
|
||||
use yaak_plugins::events::{CallGrpcRequestActionArgs, CallGrpcRequestActionRequest, CallHttpRequestActionArgs, CallHttpRequestActionRequest, Color, FilterResponse, GetGrpcRequestActionsResponse, GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse, GetHttpRequestActionsResponse, GetTemplateFunctionSummaryResponse, GetTemplateFunctionConfigResponse, InternalEvent, InternalEventPayload, JsonPrimitive, PluginWindowContext, RenderPurpose, ShowToastRequest};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::plugin_meta::PluginMetadata;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
@@ -827,11 +821,36 @@ async fn cmd_grpc_request_actions<R: Runtime>(
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_template_functions<R: Runtime>(
|
||||
async fn cmd_template_function_summaries<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> YaakResult<Vec<GetTemplateFunctionsResponse>> {
|
||||
Ok(plugin_manager.get_template_functions(&window).await?)
|
||||
) -> YaakResult<Vec<GetTemplateFunctionSummaryResponse>> {
|
||||
let results = plugin_manager.get_template_function_summaries(&window).await?;
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_template_function_config<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
function_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
model: AnyModel,
|
||||
environment_id: Option<&str>,
|
||||
) -> YaakResult<GetTemplateFunctionConfigResponse> {
|
||||
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 template functions {m:?}")));
|
||||
}
|
||||
};
|
||||
let environment_chain =
|
||||
window.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
|
||||
Ok(plugin_manager.get_template_function_config(&window, function_name, environment_chain, values, model.id()).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -849,10 +868,10 @@ async fn cmd_get_http_authentication_config<R: Runtime>(
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
auth_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request: AnyModel,
|
||||
model: AnyModel,
|
||||
environment_id: Option<&str>,
|
||||
) -> YaakResult<GetHttpAuthenticationConfigResponse> {
|
||||
let (workspace_id, folder_id) = match request.clone() {
|
||||
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),
|
||||
@@ -867,7 +886,7 @@ async fn cmd_get_http_authentication_config<R: Runtime>(
|
||||
window.db().resolve_environments(&workspace_id, folder_id.as_deref(), environment_id)?;
|
||||
|
||||
Ok(plugin_manager
|
||||
.get_http_authentication_config(&window, environment_chain, auth_name, values, request.id())
|
||||
.get_http_authentication_config(&window, environment_chain, auth_name, values, model.id())
|
||||
.await?)
|
||||
}
|
||||
|
||||
@@ -1416,7 +1435,8 @@ pub fn run() {
|
||||
cmd_send_ephemeral_request,
|
||||
cmd_send_http_request,
|
||||
cmd_send_folder,
|
||||
cmd_template_functions,
|
||||
cmd_template_function_config,
|
||||
cmd_template_function_summaries,
|
||||
cmd_template_tokens_to_string,
|
||||
//
|
||||
//
|
||||
|
||||
@@ -361,7 +361,11 @@ export type GetKeyValueRequest = { key: string, };
|
||||
|
||||
export type GetKeyValueResponse = { value?: string, };
|
||||
|
||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
export type GetTemplateFunctionConfigRequest = { contextId: string, name: string, values: { [key in string]?: JsonPrimitive }, };
|
||||
|
||||
export type GetTemplateFunctionConfigResponse = { function: TemplateFunction, pluginRefId: string, };
|
||||
|
||||
export type GetTemplateFunctionSummaryResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
|
||||
export type GetThemesRequest = Record<string, never>;
|
||||
|
||||
@@ -385,7 +389,7 @@ export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: PluginWindowContext, payload: InternalEventPayload, };
|
||||
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse;
|
||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } | { "type": "reload_response" } & ReloadResponse | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "type": "get_template_function_summary_request" } & EmptyPayload | { "type": "get_template_function_summary_response" } & GetTemplateFunctionSummaryResponse | { "type": "get_template_function_config_request" } & GetTemplateFunctionConfigRequest | { "type": "get_template_function_config_response" } & GetTemplateFunctionConfigResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "window_close_event" } | { "type": "close_window_request" } & CloseWindowRequest | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse;
|
||||
|
||||
export type JsonPrimitive = string | number | boolean | null;
|
||||
|
||||
|
||||
@@ -99,8 +99,10 @@ pub enum InternalEventPayload {
|
||||
CallGrpcRequestActionRequest(CallGrpcRequestActionRequest),
|
||||
|
||||
// Template Functions
|
||||
GetTemplateFunctionsRequest,
|
||||
GetTemplateFunctionsResponse(GetTemplateFunctionsResponse),
|
||||
GetTemplateFunctionSummaryRequest(EmptyPayload),
|
||||
GetTemplateFunctionSummaryResponse(GetTemplateFunctionSummaryResponse),
|
||||
GetTemplateFunctionConfigRequest(GetTemplateFunctionConfigRequest),
|
||||
GetTemplateFunctionConfigResponse(GetTemplateFunctionConfigResponse),
|
||||
CallTemplateFunctionRequest(CallTemplateFunctionRequest),
|
||||
CallTemplateFunctionResponse(CallTemplateFunctionResponse),
|
||||
|
||||
@@ -673,11 +675,28 @@ pub struct CallHttpAuthenticationResponse {
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetTemplateFunctionsResponse {
|
||||
pub struct GetTemplateFunctionSummaryResponse {
|
||||
pub functions: Vec<TemplateFunction>,
|
||||
pub plugin_ref_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetTemplateFunctionConfigRequest {
|
||||
pub context_id: String,
|
||||
pub name: String,
|
||||
pub values: HashMap<String, JsonPrimitive>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
pub struct GetTemplateFunctionConfigResponse {
|
||||
pub function: TemplateFunction,
|
||||
pub plugin_ref_id: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
#[ts(export, export_to = "gen_events.ts")]
|
||||
|
||||
@@ -10,7 +10,8 @@ use crate::events::{
|
||||
FilterRequest, FilterResponse, GetGrpcRequestActionsResponse,
|
||||
GetHttpAuthenticationConfigRequest, GetHttpAuthenticationConfigResponse,
|
||||
GetHttpAuthenticationSummaryResponse, GetHttpRequestActionsResponse,
|
||||
GetTemplateFunctionsResponse, GetThemesRequest, GetThemesResponse, ImportRequest,
|
||||
GetTemplateFunctionConfigRequest, GetTemplateFunctionConfigResponse,
|
||||
GetTemplateFunctionSummaryResponse, GetThemesRequest, GetThemesResponse, ImportRequest,
|
||||
ImportResponse, InternalEvent, InternalEventPayload, JsonPrimitive, PluginWindowContext,
|
||||
RenderPurpose,
|
||||
};
|
||||
@@ -489,35 +490,59 @@ impl PluginManager {
|
||||
Ok(all_actions)
|
||||
}
|
||||
|
||||
pub async fn get_template_functions<R: Runtime>(
|
||||
pub async fn get_template_function_config<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
||||
self.get_template_functions_with_context(&PluginWindowContext::new(&window)).await
|
||||
}
|
||||
fn_name: &str,
|
||||
environment_chain: Vec<Environment>,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
model_id: &str,
|
||||
) -> Result<GetTemplateFunctionConfigResponse> {
|
||||
let results = self.get_template_function_summaries(window).await?;
|
||||
let r = results
|
||||
.iter()
|
||||
.find(|r| r.functions.iter().any(|f| f.name == fn_name))
|
||||
.ok_or_else(|| PluginNotFoundErr(fn_name.into()))?;
|
||||
let plugin = self
|
||||
.get_plugin_by_ref_id(&r.plugin_ref_id)
|
||||
.await
|
||||
.ok_or_else(|| PluginNotFoundErr(r.plugin_ref_id.clone()))?;
|
||||
|
||||
pub async fn get_template_functions_with_context(
|
||||
&self,
|
||||
window_context: &PluginWindowContext,
|
||||
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
||||
let reply_events = self
|
||||
.send_and_wait(window_context, &InternalEventPayload::GetTemplateFunctionsRequest)
|
||||
let window_context = &PluginWindowContext::new(&window);
|
||||
let vars = &make_vars_hashmap(environment_chain);
|
||||
let cb = PluginTemplateCallback::new(
|
||||
window.app_handle(),
|
||||
&window_context,
|
||||
RenderPurpose::Preview,
|
||||
);
|
||||
// We don't want to fail for this op because the UI will not be able to list any auth types then
|
||||
let render_opt = RenderOptions {
|
||||
error_behavior: RenderErrorBehavior::ReturnEmpty,
|
||||
};
|
||||
let rendered_values = render_json_value_raw(json!(values), vars, &cb, &render_opt).await?;
|
||||
let context_id = format!("{:x}", md5::compute(model_id.to_string()));
|
||||
|
||||
let event = self
|
||||
.send_to_plugin_and_wait(
|
||||
&PluginWindowContext::new(window),
|
||||
&plugin,
|
||||
&InternalEventPayload::GetTemplateFunctionConfigRequest(
|
||||
GetTemplateFunctionConfigRequest {
|
||||
values: serde_json::from_value(rendered_values)?,
|
||||
name: fn_name.to_string(),
|
||||
context_id,
|
||||
},
|
||||
),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut result = Vec::new();
|
||||
for event in reply_events {
|
||||
if let InternalEventPayload::GetTemplateFunctionsResponse(resp) = event.payload {
|
||||
result.push(resp.clone());
|
||||
match event.payload {
|
||||
InternalEventPayload::GetTemplateFunctionConfigResponse(resp) => Ok(resp),
|
||||
InternalEventPayload::EmptyResponse(_) => {
|
||||
Err(PluginErr("Template function plugin returned empty".to_string()))
|
||||
}
|
||||
InternalEventPayload::ErrorResponse(e) => Err(PluginErr(e.error)),
|
||||
e => Err(PluginErr(format!("Template function plugin returned invalid event {:?}", e))),
|
||||
}
|
||||
|
||||
// Add Rust-based functions
|
||||
result.push(GetTemplateFunctionsResponse {
|
||||
plugin_ref_id: "__NATIVE__".to_string(), // Meh
|
||||
functions: vec![template_function_secure(), template_function_keyring()],
|
||||
});
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub async fn call_http_request_action<R: Runtime>(
|
||||
@@ -587,7 +612,7 @@ impl PluginManager {
|
||||
environment_chain: Vec<Environment>,
|
||||
auth_name: &str,
|
||||
values: HashMap<String, JsonPrimitive>,
|
||||
request_id: &str,
|
||||
model_id: &str,
|
||||
) -> Result<GetHttpAuthenticationConfigResponse> {
|
||||
let results = self.get_http_authentication_summaries(window).await?;
|
||||
let plugin = results
|
||||
@@ -606,7 +631,7 @@ impl PluginManager {
|
||||
error_behavior: RenderErrorBehavior::ReturnEmpty,
|
||||
};
|
||||
let rendered_values = render_json_value_raw(json!(values), vars, &cb, &render_opt).await?;
|
||||
let context_id = format!("{:x}", md5::compute(request_id.to_string()));
|
||||
let context_id = format!("{:x}", md5::compute(model_id.to_string()));
|
||||
let event = self
|
||||
.send_to_plugin_and_wait(
|
||||
&PluginWindowContext::new(window),
|
||||
@@ -720,6 +745,34 @@ impl PluginManager {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_template_function_summaries<R: Runtime>(
|
||||
&self,
|
||||
window: &WebviewWindow<R>,
|
||||
) -> Result<Vec<GetTemplateFunctionSummaryResponse>> {
|
||||
let window_context = PluginWindowContext::new(window);
|
||||
let reply_events = self
|
||||
.send_and_wait(
|
||||
&window_context,
|
||||
&InternalEventPayload::GetTemplateFunctionSummaryRequest(EmptyPayload {}),
|
||||
)
|
||||
.await?;
|
||||
|
||||
let mut results = Vec::new();
|
||||
for event in reply_events {
|
||||
if let InternalEventPayload::GetTemplateFunctionSummaryResponse(resp) = event.payload {
|
||||
results.push(resp.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Add Rust-based functions
|
||||
results.push(GetTemplateFunctionSummaryResponse {
|
||||
plugin_ref_id: "__NATIVE__".to_string(), // Meh
|
||||
functions: vec![template_function_secure(), template_function_keyring()],
|
||||
});
|
||||
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
pub async fn call_template_function(
|
||||
&self,
|
||||
window_context: &PluginWindowContext,
|
||||
|
||||
@@ -1,9 +1,17 @@
|
||||
import type {
|
||||
Folder,
|
||||
GrpcRequest,
|
||||
HttpRequest,
|
||||
WebsocketRequest,
|
||||
Workspace,
|
||||
} from '@yaakapp-internal/models';
|
||||
import type { TemplateFunction } from '@yaakapp-internal/plugins';
|
||||
import type { FnArg, Tokens } from '@yaakapp-internal/templates';
|
||||
import classNames from 'classnames';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { useDebouncedValue } from '../hooks/useDebouncedValue';
|
||||
import { useRenderTemplate } from '../hooks/useRenderTemplate';
|
||||
import { useTemplateFunctionConfig } from '../hooks/useTemplateFunctionConfig';
|
||||
import {
|
||||
templateTokensToString,
|
||||
useTemplateTokensToString,
|
||||
@@ -24,6 +32,7 @@ interface Props {
|
||||
initialTokens: Tokens;
|
||||
hide: () => void;
|
||||
onChange: (insert: string) => void;
|
||||
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace;
|
||||
}
|
||||
|
||||
export function TemplateFunctionDialog({ initialTokens, templateFunction, ...props }: Props) {
|
||||
@@ -84,14 +93,15 @@ export function TemplateFunctionDialog({ initialTokens, templateFunction, ...pro
|
||||
}
|
||||
|
||||
function InitializedTemplateFunctionDialog({
|
||||
templateFunction,
|
||||
hide,
|
||||
templateFunction: { name },
|
||||
initialArgValues,
|
||||
hide,
|
||||
onChange,
|
||||
model,
|
||||
}: Omit<Props, 'initialTokens'> & {
|
||||
initialArgValues: Record<string, string | boolean>;
|
||||
}) {
|
||||
const enablePreview = templateFunction.name !== 'secure';
|
||||
const enablePreview = name !== 'secure';
|
||||
const [showSecretsInPreview, toggleShowSecretsInPreview] = useToggle(false);
|
||||
const [argValues, setArgValues] = useState<Record<string, string | boolean>>(initialArgValues);
|
||||
|
||||
@@ -112,15 +122,16 @@ function InitializedTemplateFunctionDialog({
|
||||
type: 'tag',
|
||||
val: {
|
||||
type: 'fn',
|
||||
name: templateFunction.name,
|
||||
name,
|
||||
args: argTokens,
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}, [argValues, templateFunction.name]);
|
||||
}, [argValues, name]);
|
||||
|
||||
const tagText = useTemplateTokensToString(tokens);
|
||||
const templateFunction = useTemplateFunctionConfig(name, argValues, model).data;
|
||||
|
||||
const handleDone = () => {
|
||||
if (tagText.data) {
|
||||
@@ -134,7 +145,7 @@ function InitializedTemplateFunctionDialog({
|
||||
const tooLarge = rendered.data ? rendered.data.length > 10000 : false;
|
||||
const dataContainsSecrets = useMemo(() => {
|
||||
for (const [name, value] of Object.entries(argValues)) {
|
||||
const arg = templateFunction.args.find((a) => 'name' in a && a.name === name);
|
||||
const arg = templateFunction?.args.find((a) => 'name' in a && a.name === name);
|
||||
const isTextPassword = arg?.type === 'text' && arg.password;
|
||||
if (isTextPassword && typeof value === 'string' && value && rendered.data?.includes(value)) {
|
||||
return true;
|
||||
@@ -145,6 +156,8 @@ function InitializedTemplateFunctionDialog({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [rendered.data]);
|
||||
|
||||
if (templateFunction == null) return null;
|
||||
|
||||
return (
|
||||
<VStack
|
||||
as="form"
|
||||
@@ -155,7 +168,7 @@ function InitializedTemplateFunctionDialog({
|
||||
handleDone();
|
||||
}}
|
||||
>
|
||||
{templateFunction.name === 'secure' ? (
|
||||
{name === 'secure' ? (
|
||||
<PlainInput
|
||||
required
|
||||
label="Value"
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react';
|
||||
import { activeWorkspaceAtom } from '../../../hooks/useActiveWorkspace';
|
||||
import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables';
|
||||
import { useEnvironmentVariables } from '../../../hooks/useEnvironmentVariables';
|
||||
import { useRandomKey } from '../../../hooks/useRandomKey';
|
||||
@@ -36,6 +37,7 @@ import { useTemplateFunctionCompletionOptions } from '../../../hooks/useTemplate
|
||||
import { showDialog } from '../../../lib/dialog';
|
||||
import { editEnvironment } from '../../../lib/editEnvironment';
|
||||
import { tryFormatJson, tryFormatXml } from '../../../lib/formatters';
|
||||
import { jotaiStore } from '../../../lib/jotai';
|
||||
import { withEncryptionEnabled } from '../../../lib/setupOrConfigureEncryption';
|
||||
import { TemplateFunctionDialog } from '../../TemplateFunctionDialog';
|
||||
import { TemplateVariableDialog } from '../../TemplateVariableDialog';
|
||||
@@ -292,18 +294,22 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
||||
size: 'md',
|
||||
title: <InlineCode>{fn.name}(…)</InlineCode>,
|
||||
description: fn.description,
|
||||
render: ({ hide }) => (
|
||||
<TemplateFunctionDialog
|
||||
templateFunction={fn}
|
||||
hide={hide}
|
||||
initialTokens={initialTokens}
|
||||
onChange={(insert) => {
|
||||
cm.current?.view.dispatch({
|
||||
changes: [{ from: startPos, to: startPos + tagValue.length, insert }],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
),
|
||||
render: ({ hide }) => {
|
||||
const model = jotaiStore.get(activeWorkspaceAtom)!;
|
||||
return (
|
||||
<TemplateFunctionDialog
|
||||
templateFunction={fn}
|
||||
model={model}
|
||||
hide={hide}
|
||||
initialTokens={initialTokens}
|
||||
onChange={(insert) => {
|
||||
cm.current?.view.dispatch({
|
||||
changes: [{ from: startPos, to: startPos + tagValue.length, insert }],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
if (fn.name === 'secure') {
|
||||
|
||||
@@ -18,7 +18,7 @@ import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
export function useHttpAuthenticationConfig(
|
||||
authName: string | null,
|
||||
values: Record<string, JsonPrimitive>,
|
||||
request: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
||||
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
||||
) {
|
||||
const workspaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const environmentId = useAtomValue(activeEnvironmentIdAtom);
|
||||
@@ -37,7 +37,7 @@ export function useHttpAuthenticationConfig(
|
||||
return useQuery({
|
||||
queryKey: [
|
||||
'http_authentication_config',
|
||||
request,
|
||||
model,
|
||||
authName,
|
||||
values,
|
||||
responseKey,
|
||||
@@ -53,7 +53,7 @@ export function useHttpAuthenticationConfig(
|
||||
{
|
||||
authName,
|
||||
values,
|
||||
request,
|
||||
model,
|
||||
environmentId,
|
||||
},
|
||||
);
|
||||
|
||||
60
src-web/hooks/useTemplateFunctionConfig.ts
Normal file
60
src-web/hooks/useTemplateFunctionConfig.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type {
|
||||
Folder,
|
||||
GrpcRequest,
|
||||
HttpRequest,
|
||||
WebsocketRequest,
|
||||
Workspace,
|
||||
} from '@yaakapp-internal/models';
|
||||
import { httpResponsesAtom } from '@yaakapp-internal/models';
|
||||
import type { GetTemplateFunctionConfigResponse, JsonPrimitive } from '@yaakapp-internal/plugins';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { md5 } from 'js-md5';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
import { activeEnvironmentIdAtom } from './useActiveEnvironment';
|
||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
||||
|
||||
export function useTemplateFunctionConfig(
|
||||
functionName: string | null,
|
||||
values: Record<string, JsonPrimitive>,
|
||||
model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace,
|
||||
) {
|
||||
const workspaceId = useAtomValue(activeWorkspaceIdAtom);
|
||||
const environmentId = useAtomValue(activeEnvironmentIdAtom);
|
||||
const responses = useAtomValue(httpResponsesAtom);
|
||||
|
||||
// Some auth handlers like OAuth 2.0 show the current token after a successful request. To
|
||||
// handle that, we'll force the auth to re-fetch after each new response closes
|
||||
const responseKey = md5(
|
||||
responses
|
||||
.filter((r) => r.state === 'closed')
|
||||
.map((r) => r.id)
|
||||
.join(':'),
|
||||
);
|
||||
|
||||
return useQuery({
|
||||
queryKey: [
|
||||
'template_function_config',
|
||||
model,
|
||||
functionName,
|
||||
values,
|
||||
responseKey,
|
||||
workspaceId,
|
||||
environmentId,
|
||||
],
|
||||
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||
queryFn: async () => {
|
||||
if (functionName == null) return null;
|
||||
const config = await invokeCmd<GetTemplateFunctionConfigResponse>(
|
||||
'cmd_template_function_config',
|
||||
{
|
||||
functionName: functionName,
|
||||
values,
|
||||
model,
|
||||
environmentId,
|
||||
},
|
||||
);
|
||||
return config.function;
|
||||
},
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,9 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { GetTemplateFunctionsResponse, TemplateFunction } from '@yaakapp-internal/plugins';
|
||||
import { atom, useAtomValue , useSetAtom } from 'jotai';
|
||||
import type {
|
||||
GetTemplateFunctionSummaryResponse,
|
||||
TemplateFunction,
|
||||
} from '@yaakapp-internal/plugins';
|
||||
import { atom, useAtomValue, useSetAtom } from 'jotai';
|
||||
import { useMemo, useState } from 'react';
|
||||
import type { TwigCompletionOption } from '../components/core/Editor/twig/completion';
|
||||
import { invokeCmd } from '../lib/tauri';
|
||||
@@ -55,7 +58,9 @@ export function useSubscribeTemplateFunctions() {
|
||||
refetchInterval: numFns > 0 ? Infinity : 1000,
|
||||
refetchOnMount: true,
|
||||
queryFn: async () => {
|
||||
const result = await invokeCmd<GetTemplateFunctionsResponse[]>('cmd_template_functions');
|
||||
const result = await invokeCmd<GetTemplateFunctionSummaryResponse[]>(
|
||||
'cmd_template_function_summaries',
|
||||
);
|
||||
setNumFns(result.length);
|
||||
const functions = result.flatMap((r) => r.functions) ?? [];
|
||||
setAtom(functions);
|
||||
|
||||
@@ -40,7 +40,8 @@ type TauriCmd =
|
||||
| 'cmd_send_folder'
|
||||
| 'cmd_send_http_request'
|
||||
| 'cmd_show_workspace_key'
|
||||
| 'cmd_template_functions'
|
||||
| 'cmd_template_function_summaries'
|
||||
| 'cmd_template_function_config'
|
||||
| 'cmd_template_tokens_to_string';
|
||||
|
||||
export async function invokeCmd<T>(cmd: TauriCmd, args?: InvokeArgs): Promise<T> {
|
||||
|
||||
Reference in New Issue
Block a user