mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-23 01:08:28 +02:00
Auth plugins (#155)
This commit is contained in:
2
package-lock.json
generated
2
package-lock.json
generated
@@ -15808,7 +15808,7 @@
|
|||||||
},
|
},
|
||||||
"packages/plugin-runtime-types": {
|
"packages/plugin-runtime-types": {
|
||||||
"name": "@yaakapp/api",
|
"name": "@yaakapp/api",
|
||||||
"version": "0.2.17",
|
"version": "0.2.25",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@types/node": "^22.5.4"
|
"@types/node": "^22.5.4"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@yaakapp/api",
|
"name": "@yaakapp/api",
|
||||||
"version": "0.2.17",
|
"version": "0.2.26",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"typings": "./lib/index.d.ts",
|
"typings": "./lib/index.d.ts",
|
||||||
"files": [
|
"files": [
|
||||||
@@ -11,8 +11,9 @@
|
|||||||
"build": "run-s build:copy-types build:tsc",
|
"build": "run-s build:copy-types build:tsc",
|
||||||
"build:tsc": "tsc",
|
"build:tsc": "tsc",
|
||||||
"build:copy-types": "run-p build:copy-types:*",
|
"build:copy-types": "run-p build:copy-types:*",
|
||||||
"build:copy-types:root": "cpy --flat ../../src-tauri/yaak-plugin-runtime/bindings/*.ts ./src/bindings",
|
"build:copy-types:root": "cpy --flat ../../src-tauri/yaak-plugins/bindings/*.ts ./src/bindings",
|
||||||
"build:copy-types:next": "cpy --flat ../../src-tauri/yaak-plugin-runtime/bindings/serde_json/*.ts ./src/bindings/serde_json",
|
"build:copy-types:next": "cpy --flat ../../src-tauri/yaak-plugins/bindings/serde_json/*.ts ./src/bindings/serde_json",
|
||||||
|
"publish": "npm publish",
|
||||||
"prepublishOnly": "npm run build"
|
"prepublishOnly": "npm run build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|||||||
@@ -1,338 +1,223 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type {
|
import type { Environment } from "./models";
|
||||||
Environment,
|
import type { Folder } from "./models";
|
||||||
Folder,
|
import type { GrpcRequest } from "./models";
|
||||||
GrpcRequest,
|
import type { HttpRequest } from "./models";
|
||||||
HttpRequest,
|
import type { HttpResponse } from "./models";
|
||||||
HttpResponse,
|
import type { JsonValue } from "./serde_json/JsonValue";
|
||||||
Workspace,
|
import type { Workspace } from "./models";
|
||||||
} from './models';
|
|
||||||
import type { JsonValue } from './serde_json/JsonValue';
|
|
||||||
|
|
||||||
export type BootRequest = { dir: string; watch: boolean };
|
export type BootRequest = { dir: string, watch: boolean, };
|
||||||
|
|
||||||
export type BootResponse = { name: string; version: string; capabilities: Array<string> };
|
export type BootResponse = { name: string, version: string, capabilities: Array<string>, };
|
||||||
|
|
||||||
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest };
|
export type CallHttpAuthenticationRequest = { config: { [key in string]?: JsonValue }, method: string, url: string, headers: Array<HttpHeader>, };
|
||||||
|
|
||||||
export type CallHttpRequestActionRequest = {
|
export type CallHttpAuthenticationResponse = { url: string, headers: Array<HttpHeader>, };
|
||||||
key: string;
|
|
||||||
pluginRefId: string;
|
|
||||||
args: CallHttpRequestActionArgs;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CallTemplateFunctionArgs = {
|
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
|
||||||
purpose: RenderPurpose;
|
|
||||||
values: { [key in string]?: string };
|
|
||||||
};
|
|
||||||
|
|
||||||
export type CallTemplateFunctionRequest = { name: string; args: CallTemplateFunctionArgs };
|
export type CallHttpRequestActionRequest = { key: string, pluginRefId: string, args: CallHttpRequestActionArgs, };
|
||||||
|
|
||||||
export type CallTemplateFunctionResponse = { value: string | null };
|
export type CallTemplateFunctionArgs = { purpose: RenderPurpose, values: { [key in string]?: string }, };
|
||||||
|
|
||||||
export type Color =
|
export type CallTemplateFunctionRequest = { name: string, args: CallTemplateFunctionArgs, };
|
||||||
| 'custom'
|
|
||||||
| 'default'
|
|
||||||
| 'primary'
|
|
||||||
| 'secondary'
|
|
||||||
| 'info'
|
|
||||||
| 'success'
|
|
||||||
| 'notice'
|
|
||||||
| 'warning'
|
|
||||||
| 'danger';
|
|
||||||
|
|
||||||
export type CopyTextRequest = { text: string };
|
export type CallTemplateFunctionResponse = { value: string | null, };
|
||||||
|
|
||||||
export type ExportHttpRequestRequest = { httpRequest: HttpRequest };
|
export type Color = "custom" | "default" | "primary" | "secondary" | "info" | "success" | "notice" | "warning" | "danger";
|
||||||
|
|
||||||
export type ExportHttpRequestResponse = { content: string };
|
export type CopyTextRequest = { text: string, };
|
||||||
|
|
||||||
export type FilterRequest = { content: string; filter: string };
|
export type EmptyPayload = {};
|
||||||
|
|
||||||
export type FilterResponse = { content: string };
|
export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
export type FindHttpResponsesRequest = { requestId: string; limit?: number };
|
export type ExportHttpRequestResponse = { content: string, };
|
||||||
|
|
||||||
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse> };
|
export type FileFilter = { name: string,
|
||||||
|
/**
|
||||||
|
* File extensions to require
|
||||||
|
*/
|
||||||
|
extensions: Array<string>, };
|
||||||
|
|
||||||
|
export type FilterRequest = { content: string, filter: string, };
|
||||||
|
|
||||||
|
export type FilterResponse = { content: string, };
|
||||||
|
|
||||||
|
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
||||||
|
|
||||||
|
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
||||||
|
|
||||||
|
export type FormInput = { "type": "text" } & FormInputText | { "type": "select" } & FormInputSelect | { "type": "checkbox" } & FormInputCheckbox | { "type": "file" } & FormInputFile | { "type": "http_request" } & FormInputHttpRequest;
|
||||||
|
|
||||||
|
export type FormInputBase = { name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputCheckbox = { name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputFile = {
|
||||||
|
/**
|
||||||
|
* The title of the file selection window
|
||||||
|
*/
|
||||||
|
title: string,
|
||||||
|
/**
|
||||||
|
* Allow selecting multiple files
|
||||||
|
*/
|
||||||
|
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<FileFilter>, name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputHttpRequest = { name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputSelect = {
|
||||||
|
/**
|
||||||
|
* The options that will be available in the select input
|
||||||
|
*/
|
||||||
|
options: Array<FormInputSelectOption>, name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputSelectOption = { name: string, value: string, };
|
||||||
|
|
||||||
|
export type FormInputText = {
|
||||||
|
/**
|
||||||
|
* Placeholder for the text input
|
||||||
|
*/
|
||||||
|
placeholder?: string | null, name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type GetHttpAuthenticationResponse = { name: string, pluginName: string, config: Array<FormInput>, };
|
||||||
|
|
||||||
export type GetHttpRequestActionsRequest = Record<string, never>;
|
export type GetHttpRequestActionsRequest = Record<string, never>;
|
||||||
|
|
||||||
export type GetHttpRequestActionsResponse = {
|
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
|
||||||
actions: Array<HttpRequestAction>;
|
|
||||||
pluginRefId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type GetHttpRequestByIdRequest = { id: string };
|
export type GetHttpRequestByIdRequest = { id: string, };
|
||||||
|
|
||||||
export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null };
|
export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, };
|
||||||
|
|
||||||
export type GetTemplateFunctionsResponse = {
|
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||||
functions: Array<TemplateFunction>;
|
|
||||||
pluginRefId: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type HttpRequestAction = { key: string; label: string; icon?: Icon };
|
export type HttpHeader = { name: string, value: string, };
|
||||||
|
|
||||||
export type Icon = 'copy' | 'info' | 'check_circle' | 'alert_triangle' | '_unknown';
|
export type HttpRequestAction = { key: string, label: string, icon?: Icon, };
|
||||||
|
|
||||||
export type ImportRequest = { content: string };
|
export type Icon = "copy" | "info" | "check_circle" | "alert_triangle" | "_unknown";
|
||||||
|
|
||||||
export type ImportResources = {
|
export type ImportRequest = { content: string, };
|
||||||
workspaces: Array<Workspace>;
|
|
||||||
environments: Array<Environment>;
|
|
||||||
folders: Array<Folder>;
|
|
||||||
httpRequests: Array<HttpRequest>;
|
|
||||||
grpcRequests: Array<GrpcRequest>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type ImportResponse = { resources: ImportResources };
|
export type ImportResources = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, };
|
||||||
|
|
||||||
export type InternalEvent = {
|
export type ImportResponse = { resources: ImportResources, };
|
||||||
id: string;
|
|
||||||
pluginRefId: string;
|
|
||||||
replyId: string | null;
|
|
||||||
payload: InternalEventPayload;
|
|
||||||
windowContext: WindowContext;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type InternalEventPayload =
|
export type InternalEvent = { id: string, pluginRefId: string, replyId: string | null, payload: InternalEventPayload, windowContext: WindowContext, };
|
||||||
| ({ type: 'boot_request' } & BootRequest)
|
|
||||||
| ({ type: 'boot_response' } & BootResponse)
|
|
||||||
| { type: 'reload_request' }
|
|
||||||
| { type: 'reload_response' }
|
|
||||||
| { 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: 'get_http_request_actions_request' } & GetHttpRequestActionsRequest)
|
|
||||||
| ({ type: 'get_http_request_actions_response' } & GetHttpRequestActionsResponse)
|
|
||||||
| ({ type: 'call_http_request_action_request' } & CallHttpRequestActionRequest)
|
|
||||||
| { type: 'get_template_functions_request' }
|
|
||||||
| ({ type: 'get_template_functions_response' } & GetTemplateFunctionsResponse)
|
|
||||||
| ({ type: 'call_template_function_request' } & CallTemplateFunctionRequest)
|
|
||||||
| ({ type: 'call_template_function_response' } & CallTemplateFunctionResponse)
|
|
||||||
| ({ type: 'copy_text_request' } & CopyTextRequest)
|
|
||||||
| ({ type: 'render_http_request_request' } & RenderHttpRequestRequest)
|
|
||||||
| ({ type: 'render_http_request_response' } & RenderHttpRequestResponse)
|
|
||||||
| ({ type: 'template_render_request' } & TemplateRenderRequest)
|
|
||||||
| ({ type: 'template_render_response' } & TemplateRenderResponse)
|
|
||||||
| ({ type: 'show_toast_request' } & ShowToastRequest)
|
|
||||||
| ({ 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: 'empty_response' };
|
|
||||||
|
|
||||||
export type OpenFileFilter = {
|
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } & EmptyPayload | { "type": "reload_response" } & EmptyPayload | { "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": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "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_request" } & EmptyPayload | { "type": "get_http_authentication_response" } & GetHttpAuthenticationResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "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": "empty_response" } & EmptyPayload;
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* File extensions to require
|
|
||||||
*/
|
|
||||||
extensions: Array<string>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PromptTextRequest = {
|
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||||
id: string;
|
/**
|
||||||
title: string;
|
* Text to add to the confirmation button
|
||||||
label: string;
|
*/
|
||||||
description?: string;
|
confirmText?: string,
|
||||||
defaultValue?: string;
|
/**
|
||||||
placeholder?: string;
|
* Text to add to the cancel button
|
||||||
/**
|
*/
|
||||||
* Text to add to the confirmation button
|
cancelText?: string,
|
||||||
*/
|
/**
|
||||||
confirmText?: string;
|
* Require the user to enter a non-empty value
|
||||||
/**
|
*/
|
||||||
* Text to add to the cancel button
|
require?: boolean, };
|
||||||
*/
|
|
||||||
cancelText?: string;
|
|
||||||
/**
|
|
||||||
* Require the user to enter a non-empty value
|
|
||||||
*/
|
|
||||||
require?: boolean;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type PromptTextResponse = { value: string | null };
|
export type PromptTextResponse = { value: string | null, };
|
||||||
|
|
||||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest; purpose: RenderPurpose };
|
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||||
|
|
||||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest };
|
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
export type RenderPurpose = 'send' | 'preview';
|
export type RenderPurpose = "send" | "preview";
|
||||||
|
|
||||||
export type SendHttpRequestRequest = { httpRequest: HttpRequest };
|
export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse };
|
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||||
|
|
||||||
export type ShowToastRequest = { message: string; color?: Color; icon?: Icon };
|
export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, };
|
||||||
|
|
||||||
export type TemplateFunction = {
|
export type TemplateFunction = { name: string, description?: string,
|
||||||
name: string;
|
/**
|
||||||
description?: string;
|
* Also support alternative names. This is useful for not breaking existing
|
||||||
/**
|
* tags when changing the `name` property
|
||||||
* Also support alternative names. This is useful for not breaking existing
|
*/
|
||||||
* tags when changing the `name` property
|
aliases?: Array<string>, args: Array<FormInput>, };
|
||||||
*/
|
|
||||||
aliases?: Array<string>;
|
|
||||||
args: Array<TemplateFunctionArg>;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TemplateFunctionArg =
|
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
||||||
| ({ type: 'text' } & TemplateFunctionTextArg)
|
|
||||||
| ({
|
|
||||||
type: 'select';
|
|
||||||
} & TemplateFunctionSelectArg)
|
|
||||||
| ({ type: 'checkbox' } & TemplateFunctionCheckboxArg)
|
|
||||||
| ({
|
|
||||||
type: 'http_request';
|
|
||||||
} & TemplateFunctionHttpRequestArg)
|
|
||||||
| ({ type: 'file' } & TemplateFunctionFileArg);
|
|
||||||
|
|
||||||
export type TemplateFunctionBaseArg = {
|
export type TemplateRenderResponse = { data: JsonValue, };
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean;
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TemplateFunctionCheckboxArg = {
|
export type WindowContext = { "type": "none" } | { "type": "label", label: string, };
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean;
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TemplateFunctionFileArg = {
|
|
||||||
/**
|
|
||||||
* The title of the file selection window
|
|
||||||
*/
|
|
||||||
title: string;
|
|
||||||
/**
|
|
||||||
* Allow selecting multiple files
|
|
||||||
*/
|
|
||||||
multiple?: boolean;
|
|
||||||
directory?: boolean;
|
|
||||||
defaultPath?: string;
|
|
||||||
filters?: Array<OpenFileFilter>;
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean;
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TemplateFunctionHttpRequestArg = {
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean;
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TemplateFunctionSelectArg = {
|
|
||||||
/**
|
|
||||||
* The options that will be available in the select input
|
|
||||||
*/
|
|
||||||
options: Array<TemplateFunctionSelectOption>;
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean;
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TemplateFunctionSelectOption = { label: string; value: string };
|
|
||||||
|
|
||||||
export type TemplateFunctionTextArg = {
|
|
||||||
/**
|
|
||||||
* Placeholder for the text input
|
|
||||||
*/
|
|
||||||
placeholder?: string;
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string;
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean;
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string;
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string;
|
|
||||||
};
|
|
||||||
|
|
||||||
export type TemplateRenderRequest = { data: JsonValue; purpose: RenderPurpose };
|
|
||||||
|
|
||||||
export type TemplateRenderResponse = { data: JsonValue };
|
|
||||||
|
|
||||||
export type WindowContext = { type: 'none' } | { type: 'label'; label: string };
|
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
|
|
||||||
export type Environment = { model: "environment", id: string, workspaceId: string, environmentId: string | null, createdAt: string, updatedAt: string, name: string, variables: Array<EnvironmentVariable>, };
|
export type Environment = { model: "environment", id: string, workspaceId: string, environmentId: string | null, createdAt: string, updatedAt: string, name: string, variables: Array<EnvironmentVariable>, };
|
||||||
|
|
||||||
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, };
|
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id: string, };
|
||||||
|
|
||||||
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
|
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
|
||||||
|
|
||||||
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, };
|
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
||||||
|
|
||||||
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
||||||
|
|
||||||
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, };
|
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
|
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
|
||||||
|
|
||||||
@@ -20,6 +20,6 @@ export type HttpResponseHeader = { name: string, value: string, };
|
|||||||
|
|
||||||
export type HttpResponseState = "initialized" | "connected" | "closed";
|
export type HttpResponseState = "initialized" | "connected" | "closed";
|
||||||
|
|
||||||
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, };
|
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
|
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import {CallHttpAuthenticationRequest, CallHttpAuthenticationResponse, GetHttpAuthenticationResponse} from '..';
|
||||||
|
import type { Context } from './Context';
|
||||||
|
|
||||||
|
export type AuthenticationPlugin = Omit<GetHttpAuthenticationResponse, 'pluginName'> & {
|
||||||
|
onApply(
|
||||||
|
ctx: Context,
|
||||||
|
args: CallHttpAuthenticationRequest,
|
||||||
|
): Promise<CallHttpAuthenticationResponse> | CallHttpAuthenticationResponse;
|
||||||
|
};
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { AuthenticationPlugin } from './AuthenticationPlugin';
|
||||||
import type { FilterPlugin } from './FilterPlugin';
|
import type { FilterPlugin } from './FilterPlugin';
|
||||||
import type { HttpRequestActionPlugin } from './HttpRequestActionPlugin';
|
import type { HttpRequestActionPlugin } from './HttpRequestActionPlugin';
|
||||||
import type { ImporterPlugin } from './ImporterPlugin';
|
import type { ImporterPlugin } from './ImporterPlugin';
|
||||||
@@ -13,6 +14,7 @@ export type PluginDefinition = {
|
|||||||
importer?: ImporterPlugin;
|
importer?: ImporterPlugin;
|
||||||
theme?: ThemePlugin;
|
theme?: ThemePlugin;
|
||||||
filter?: FilterPlugin;
|
filter?: FilterPlugin;
|
||||||
|
authentication?: AuthenticationPlugin;
|
||||||
httpRequestActions?: HttpRequestActionPlugin[];
|
httpRequestActions?: HttpRequestActionPlugin[];
|
||||||
templateFunctions?: TemplateFunctionPlugin[];
|
templateFunctions?: TemplateFunctionPlugin[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import type { HttpRequestActionPlugin } from '@yaakapp/api/lib/plugins/HttpReque
|
|||||||
import type { TemplateFunctionPlugin } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
|
import type { TemplateFunctionPlugin } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin';
|
||||||
import interceptStdout from 'intercept-stdout';
|
import interceptStdout from 'intercept-stdout';
|
||||||
import * as console from 'node:console';
|
import * as console from 'node:console';
|
||||||
import type { Stats} from 'node:fs';
|
import type { Stats } from 'node:fs';
|
||||||
import { readFileSync, statSync, watch } from 'node:fs';
|
import { readFileSync, statSync, watch } from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import * as util from 'node:util';
|
import * as util from 'node:util';
|
||||||
@@ -303,6 +303,33 @@ async function initialize() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (payload.type === 'get_http_authentication_request' && mod.plugin?.authentication) {
|
||||||
|
const replyPayload: InternalEventPayload = {
|
||||||
|
type: 'get_http_authentication_response',
|
||||||
|
name: mod.plugin.authentication.name,
|
||||||
|
pluginName: pkg.name,
|
||||||
|
config: mod.plugin.authentication.config,
|
||||||
|
};
|
||||||
|
sendPayload(windowContext, replyPayload, replyId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.type === 'call_http_authentication_request' && mod.plugin?.authentication) {
|
||||||
|
const auth = mod.plugin.authentication;
|
||||||
|
if (typeof auth?.onApply === 'function') {
|
||||||
|
const result = await auth.onApply(ctx, payload);
|
||||||
|
sendPayload(
|
||||||
|
windowContext,
|
||||||
|
{
|
||||||
|
...result,
|
||||||
|
type: 'call_http_authentication_response',
|
||||||
|
},
|
||||||
|
replyId,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'call_http_request_action_request' &&
|
payload.type === 'call_http_request_action_request' &&
|
||||||
Array.isArray(mod.plugin?.httpRequestActions)
|
Array.isArray(mod.plugin?.httpRequestActions)
|
||||||
|
|||||||
494
src-tauri/Cargo.lock
generated
494
src-tauri/Cargo.lock
generated
@@ -392,34 +392,6 @@ version = "1.3.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "axum"
|
|
||||||
version = "0.6.20"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"axum-core 0.3.4",
|
|
||||||
"bitflags 1.3.2",
|
|
||||||
"bytes",
|
|
||||||
"futures-util",
|
|
||||||
"http 0.2.12",
|
|
||||||
"http-body 0.4.6",
|
|
||||||
"hyper 0.14.30",
|
|
||||||
"itoa 1.0.11",
|
|
||||||
"matchit",
|
|
||||||
"memchr",
|
|
||||||
"mime",
|
|
||||||
"percent-encoding",
|
|
||||||
"pin-project-lite",
|
|
||||||
"rustversion",
|
|
||||||
"serde",
|
|
||||||
"sync_wrapper 0.1.2",
|
|
||||||
"tower 0.4.13",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum"
|
name = "axum"
|
||||||
version = "0.7.5"
|
version = "0.7.5"
|
||||||
@@ -427,11 +399,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
|
checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core 0.4.3",
|
"axum-core",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"http-body 1.0.1",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"itoa 1.0.11",
|
"itoa 1.0.11",
|
||||||
"matchit",
|
"matchit",
|
||||||
@@ -447,23 +419,6 @@ dependencies = [
|
|||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "axum-core"
|
|
||||||
version = "0.3.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c"
|
|
||||||
dependencies = [
|
|
||||||
"async-trait",
|
|
||||||
"bytes",
|
|
||||||
"futures-util",
|
|
||||||
"http 0.2.12",
|
|
||||||
"http-body 0.4.6",
|
|
||||||
"mime",
|
|
||||||
"rustversion",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "axum-core"
|
name = "axum-core"
|
||||||
version = "0.4.3"
|
version = "0.4.3"
|
||||||
@@ -473,8 +428,8 @@ dependencies = [
|
|||||||
"async-trait",
|
"async-trait",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"http-body 1.0.1",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"mime",
|
"mime",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
@@ -1528,17 +1483,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "eventsource-client"
|
name = "eventsource-client"
|
||||||
version = "0.13.0"
|
version = "0.14.0"
|
||||||
source = "git+https://github.com/yaakapp/rust-eventsource-client#e9e1e52421f11f0409179389b997aa49275a8461"
|
source = "git+https://github.com/yaakapp/rust-eventsource-client#60e0e3ac5038149c4778dc4979b09b152214f9a8"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures",
|
|
||||||
"hyper 0.14.30",
|
|
||||||
"hyper-rustls 0.24.2",
|
|
||||||
"hyper-timeout 0.4.1",
|
|
||||||
"log",
|
"log",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"rand 0.8.5",
|
|
||||||
"tokio",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1709,21 +1658,6 @@ dependencies = [
|
|||||||
"new_debug_unreachable",
|
"new_debug_unreachable",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "futures"
|
|
||||||
version = "0.3.30"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0"
|
|
||||||
dependencies = [
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-executor",
|
|
||||||
"futures-io",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-task",
|
|
||||||
"futures-util",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-channel"
|
name = "futures-channel"
|
||||||
version = "0.3.30"
|
version = "0.3.30"
|
||||||
@@ -1810,7 +1744,6 @@ version = "0.3.30"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-io",
|
"futures-io",
|
||||||
"futures-macro",
|
"futures-macro",
|
||||||
@@ -2136,25 +2069,6 @@ dependencies = [
|
|||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "h2"
|
|
||||||
version = "0.3.26"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"fnv",
|
|
||||||
"futures-core",
|
|
||||||
"futures-sink",
|
|
||||||
"futures-util",
|
|
||||||
"http 0.2.12",
|
|
||||||
"indexmap 2.3.0",
|
|
||||||
"slab",
|
|
||||||
"tokio",
|
|
||||||
"tokio-util",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "h2"
|
name = "h2"
|
||||||
version = "0.4.5"
|
version = "0.4.5"
|
||||||
@@ -2166,7 +2080,7 @@ dependencies = [
|
|||||||
"fnv",
|
"fnv",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-sink",
|
"futures-sink",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"indexmap 2.3.0",
|
"indexmap 2.3.0",
|
||||||
"slab",
|
"slab",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -2284,37 +2198,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http"
|
name = "http"
|
||||||
version = "0.2.12"
|
version = "1.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1"
|
checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"fnv",
|
"fnv",
|
||||||
"itoa 1.0.11",
|
"itoa 1.0.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http"
|
|
||||||
version = "1.1.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"fnv",
|
|
||||||
"itoa 1.0.11",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "http-body"
|
|
||||||
version = "0.4.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"http 0.2.12",
|
|
||||||
"pin-project-lite",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "http-body"
|
name = "http-body"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -2322,7 +2214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -2333,8 +2225,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"http-body 1.0.1",
|
"http-body",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -2358,40 +2250,16 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper"
|
name = "hyper"
|
||||||
version = "0.14.30"
|
version = "1.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9"
|
checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0"
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"futures-channel",
|
|
||||||
"futures-core",
|
|
||||||
"futures-util",
|
|
||||||
"h2 0.3.26",
|
|
||||||
"http 0.2.12",
|
|
||||||
"http-body 0.4.6",
|
|
||||||
"httparse",
|
|
||||||
"httpdate",
|
|
||||||
"itoa 1.0.11",
|
|
||||||
"pin-project-lite",
|
|
||||||
"socket2",
|
|
||||||
"tokio",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
"want",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper"
|
|
||||||
version = "1.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05"
|
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.4.5",
|
"h2",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"http-body 1.0.1",
|
"http-body",
|
||||||
"httparse",
|
"httparse",
|
||||||
"httpdate",
|
"httpdate",
|
||||||
"itoa 1.0.11",
|
"itoa 1.0.11",
|
||||||
@@ -2403,57 +2271,30 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-rustls"
|
name = "hyper-rustls"
|
||||||
version = "0.24.2"
|
version = "0.27.5"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
|
checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 0.2.12",
|
"http",
|
||||||
"hyper 0.14.30",
|
"hyper",
|
||||||
"log",
|
|
||||||
"rustls 0.21.12",
|
|
||||||
"rustls-native-certs 0.6.3",
|
|
||||||
"tokio",
|
|
||||||
"tokio-rustls 0.24.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper-rustls"
|
|
||||||
version = "0.27.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333"
|
|
||||||
dependencies = [
|
|
||||||
"futures-util",
|
|
||||||
"http 1.1.0",
|
|
||||||
"hyper 1.4.1",
|
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"rustls 0.23.21",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
"rustls-platform-verifier",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-rustls 0.26.0",
|
"tokio-rustls",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"webpki-roots",
|
"webpki-roots",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "hyper-timeout"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1"
|
|
||||||
dependencies = [
|
|
||||||
"hyper 0.14.30",
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio",
|
|
||||||
"tokio-io-timeout",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hyper-timeout"
|
name = "hyper-timeout"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793"
|
checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"hyper 1.4.1",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -2468,7 +2309,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper 1.4.1",
|
"hyper",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"native-tls",
|
"native-tls",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -2485,9 +2326,9 @@ dependencies = [
|
|||||||
"bytes",
|
"bytes",
|
||||||
"futures-channel",
|
"futures-channel",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"http-body 1.0.1",
|
"http-body",
|
||||||
"hyper 1.4.1",
|
"hyper",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -2672,15 +2513,6 @@ dependencies = [
|
|||||||
"nom 4.2.3",
|
"nom 4.2.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "itertools"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
|
|
||||||
dependencies = [
|
|
||||||
"either",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itertools"
|
name = "itertools"
|
||||||
version = "0.13.0"
|
version = "0.13.0"
|
||||||
@@ -4114,22 +3946,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost"
|
name = "prost"
|
||||||
version = "0.12.6"
|
version = "0.13.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29"
|
checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"prost-derive 0.12.6",
|
"prost-derive",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "prost"
|
|
||||||
version = "0.13.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f"
|
|
||||||
dependencies = [
|
|
||||||
"bytes",
|
|
||||||
"prost-derive 0.13.3",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4140,14 +3962,14 @@ checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"bytes",
|
"bytes",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"itertools 0.13.0",
|
"itertools",
|
||||||
"log",
|
"log",
|
||||||
"multimap",
|
"multimap",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"petgraph",
|
"petgraph",
|
||||||
"prettyplease",
|
"prettyplease",
|
||||||
"prost 0.13.3",
|
"prost",
|
||||||
"prost-types 0.13.3",
|
"prost-types",
|
||||||
"regex",
|
"regex",
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
@@ -4155,25 +3977,12 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-derive"
|
name = "prost-derive"
|
||||||
version = "0.12.6"
|
version = "0.13.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1"
|
checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"itertools 0.12.1",
|
"itertools",
|
||||||
"proc-macro2",
|
|
||||||
"quote",
|
|
||||||
"syn 2.0.87",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "prost-derive"
|
|
||||||
version = "0.13.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5"
|
|
||||||
dependencies = [
|
|
||||||
"anyhow",
|
|
||||||
"itertools 0.13.0",
|
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
@@ -4181,24 +3990,24 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-reflect"
|
name = "prost-reflect"
|
||||||
version = "0.12.0"
|
version = "0.14.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "057237efdb71cf4b3f9396302a3d6599a92fa94063ba537b66130980ea9909f3"
|
checksum = "bc9647f03b808b79abca8408b1609be9887ba90453c940d00332a60eeb6f5748"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.7",
|
"base64 0.22.1",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"prost 0.12.6",
|
"prost",
|
||||||
"prost-reflect-derive",
|
"prost-reflect-derive",
|
||||||
"prost-types 0.12.6",
|
"prost-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde-value",
|
"serde-value",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-reflect-derive"
|
name = "prost-reflect-derive"
|
||||||
version = "0.12.0"
|
version = "0.14.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "172da1212c02be2c94901440cb27183cd92bff00ebacca5c323bf7520b8f9c04"
|
checksum = "f4fce6b22f15cc8d8d400a2b98ad29202b33bd56c7d9ddd815bc803a807ecb65"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -4207,20 +4016,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "prost-types"
|
name = "prost-types"
|
||||||
version = "0.12.6"
|
version = "0.13.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0"
|
checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"prost 0.12.6",
|
"prost",
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "prost-types"
|
|
||||||
version = "0.13.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670"
|
|
||||||
dependencies = [
|
|
||||||
"prost 0.13.3",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -4288,7 +4088,7 @@ dependencies = [
|
|||||||
"quinn-proto",
|
"quinn-proto",
|
||||||
"quinn-udp",
|
"quinn-udp",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls 0.23.21",
|
"rustls",
|
||||||
"socket2",
|
"socket2",
|
||||||
"thiserror 1.0.63",
|
"thiserror 1.0.63",
|
||||||
"tokio",
|
"tokio",
|
||||||
@@ -4305,7 +4105,7 @@ dependencies = [
|
|||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"ring",
|
"ring",
|
||||||
"rustc-hash",
|
"rustc-hash",
|
||||||
"rustls 0.23.21",
|
"rustls",
|
||||||
"slab",
|
"slab",
|
||||||
"thiserror 1.0.63",
|
"thiserror 1.0.63",
|
||||||
"tinyvec",
|
"tinyvec",
|
||||||
@@ -4536,12 +4336,12 @@ dependencies = [
|
|||||||
"encoding_rs",
|
"encoding_rs",
|
||||||
"futures-core",
|
"futures-core",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"h2 0.4.5",
|
"h2",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"http-body 1.0.1",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper 1.4.1",
|
"hyper",
|
||||||
"hyper-rustls 0.27.3",
|
"hyper-rustls",
|
||||||
"hyper-tls",
|
"hyper-tls",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
@@ -4554,8 +4354,8 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"quinn",
|
"quinn",
|
||||||
"rustls 0.23.21",
|
"rustls",
|
||||||
"rustls-pemfile 2.1.3",
|
"rustls-pemfile",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -4564,7 +4364,7 @@ dependencies = [
|
|||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-native-tls",
|
"tokio-native-tls",
|
||||||
"tokio-rustls 0.26.0",
|
"tokio-rustls",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"tower 0.5.2",
|
"tower 0.5.2",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
@@ -4741,18 +4541,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls"
|
|
||||||
version = "0.21.12"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e"
|
|
||||||
dependencies = [
|
|
||||||
"log",
|
|
||||||
"ring",
|
|
||||||
"rustls-webpki 0.101.7",
|
|
||||||
"sct",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls"
|
name = "rustls"
|
||||||
version = "0.23.21"
|
version = "0.23.21"
|
||||||
@@ -4762,23 +4550,11 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"ring",
|
"ring",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"rustls-webpki 0.102.8",
|
"rustls-webpki",
|
||||||
"subtle",
|
"subtle",
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls-native-certs"
|
|
||||||
version = "0.6.3"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00"
|
|
||||||
dependencies = [
|
|
||||||
"openssl-probe",
|
|
||||||
"rustls-pemfile 1.0.4",
|
|
||||||
"schannel",
|
|
||||||
"security-framework 2.11.1",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-native-certs"
|
name = "rustls-native-certs"
|
||||||
version = "0.8.0"
|
version = "0.8.0"
|
||||||
@@ -4786,21 +4562,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a"
|
checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"openssl-probe",
|
"openssl-probe",
|
||||||
"rustls-pemfile 2.1.3",
|
"rustls-pemfile",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"schannel",
|
"schannel",
|
||||||
"security-framework 2.11.1",
|
"security-framework 2.11.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls-pemfile"
|
|
||||||
version = "1.0.4"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c"
|
|
||||||
dependencies = [
|
|
||||||
"base64 0.21.7",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pemfile"
|
name = "rustls-pemfile"
|
||||||
version = "2.1.3"
|
version = "2.1.3"
|
||||||
@@ -4828,10 +4595,10 @@ dependencies = [
|
|||||||
"jni",
|
"jni",
|
||||||
"log",
|
"log",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustls 0.23.21",
|
"rustls",
|
||||||
"rustls-native-certs 0.8.0",
|
"rustls-native-certs",
|
||||||
"rustls-platform-verifier-android",
|
"rustls-platform-verifier-android",
|
||||||
"rustls-webpki 0.102.8",
|
"rustls-webpki",
|
||||||
"security-framework 3.2.0",
|
"security-framework 3.2.0",
|
||||||
"security-framework-sys",
|
"security-framework-sys",
|
||||||
"webpki-root-certs",
|
"webpki-root-certs",
|
||||||
@@ -4844,16 +4611,6 @@ version = "0.1.1"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "rustls-webpki"
|
|
||||||
version = "0.101.7"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
|
|
||||||
dependencies = [
|
|
||||||
"ring",
|
|
||||||
"untrusted",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-webpki"
|
name = "rustls-webpki"
|
||||||
version = "0.102.8"
|
version = "0.102.8"
|
||||||
@@ -4943,16 +4700,6 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "sct"
|
|
||||||
version = "0.7.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
|
|
||||||
dependencies = [
|
|
||||||
"ring",
|
|
||||||
"untrusted",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sea-query"
|
name = "sea-query"
|
||||||
version = "0.32.1"
|
version = "0.32.1"
|
||||||
@@ -5445,8 +5192,8 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
"paste",
|
"paste",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rustls 0.23.21",
|
"rustls",
|
||||||
"rustls-pemfile 2.1.3",
|
"rustls-pemfile",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
@@ -5854,7 +5601,7 @@ dependencies = [
|
|||||||
"glob",
|
"glob",
|
||||||
"gtk",
|
"gtk",
|
||||||
"heck 0.5.0",
|
"heck 0.5.0",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"http-range",
|
"http-range",
|
||||||
"jni",
|
"jni",
|
||||||
"libc",
|
"libc",
|
||||||
@@ -6133,7 +5880,7 @@ dependencies = [
|
|||||||
"dirs",
|
"dirs",
|
||||||
"flate2",
|
"flate2",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"infer",
|
"infer",
|
||||||
"minisign-verify",
|
"minisign-verify",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
@@ -6176,7 +5923,7 @@ checksum = "2274ef891ccc0a8d318deffa9d70053f947664d12d58b9c0d1ae5e89237e01f7"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"dpi",
|
"dpi",
|
||||||
"gtk",
|
"gtk",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"jni",
|
"jni",
|
||||||
"raw-window-handle",
|
"raw-window-handle",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -6194,7 +5941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "3707b40711d3b9f6519150869e358ffbde7c57567fb9b5a8b51150606939b2a0"
|
checksum = "3707b40711d3b9f6519150869e358ffbde7c57567fb9b5a8b51150606939b2a0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"gtk",
|
"gtk",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"jni",
|
"jni",
|
||||||
"log",
|
"log",
|
||||||
"objc2",
|
"objc2",
|
||||||
@@ -6225,7 +5972,7 @@ dependencies = [
|
|||||||
"dunce",
|
"dunce",
|
||||||
"glob",
|
"glob",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"infer",
|
"infer",
|
||||||
"json-patch",
|
"json-patch",
|
||||||
"kuchikiki",
|
"kuchikiki",
|
||||||
@@ -6416,16 +6163,6 @@ dependencies = [
|
|||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-io-timeout"
|
|
||||||
version = "1.2.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf"
|
|
||||||
dependencies = [
|
|
||||||
"pin-project-lite",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-macros"
|
name = "tokio-macros"
|
||||||
version = "2.4.0"
|
version = "2.4.0"
|
||||||
@@ -6447,23 +6184,13 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tokio-rustls"
|
|
||||||
version = "0.24.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
|
|
||||||
dependencies = [
|
|
||||||
"rustls 0.21.12",
|
|
||||||
"tokio",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio-rustls"
|
name = "tokio-rustls"
|
||||||
version = "0.26.0"
|
version = "0.26.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"rustls 0.23.21",
|
"rustls",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
@@ -6564,52 +6291,25 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tonic"
|
name = "tonic"
|
||||||
version = "0.10.2"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e"
|
checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-stream",
|
"async-stream",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum 0.6.20",
|
"axum",
|
||||||
"base64 0.21.7",
|
|
||||||
"bytes",
|
|
||||||
"h2 0.3.26",
|
|
||||||
"http 0.2.12",
|
|
||||||
"http-body 0.4.6",
|
|
||||||
"hyper 0.14.30",
|
|
||||||
"hyper-timeout 0.4.1",
|
|
||||||
"percent-encoding",
|
|
||||||
"pin-project",
|
|
||||||
"prost 0.12.6",
|
|
||||||
"tokio",
|
|
||||||
"tokio-stream",
|
|
||||||
"tower 0.4.13",
|
|
||||||
"tower-layer",
|
|
||||||
"tower-service",
|
|
||||||
"tracing",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tonic"
|
|
||||||
version = "0.12.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401"
|
|
||||||
dependencies = [
|
|
||||||
"async-stream",
|
|
||||||
"async-trait",
|
|
||||||
"axum 0.7.5",
|
|
||||||
"base64 0.22.1",
|
"base64 0.22.1",
|
||||||
"bytes",
|
"bytes",
|
||||||
"h2 0.4.5",
|
"h2",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"http-body 1.0.1",
|
"http-body",
|
||||||
"http-body-util",
|
"http-body-util",
|
||||||
"hyper 1.4.1",
|
"hyper",
|
||||||
"hyper-timeout 0.5.1",
|
"hyper-timeout",
|
||||||
"hyper-util",
|
"hyper-util",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"prost 0.13.3",
|
"prost",
|
||||||
"socket2",
|
"socket2",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
@@ -6634,15 +6334,15 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tonic-reflection"
|
name = "tonic-reflection"
|
||||||
version = "0.10.2"
|
version = "0.12.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3fa37c513df1339d197f4ba21d28c918b9ef1ac1768265f11ecb6b7f1cba1b76"
|
checksum = "878d81f52e7fcfd80026b7fdb6a9b578b3c3653ba987f87f0dce4b64043cba27"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"prost 0.12.6",
|
"prost",
|
||||||
"prost-types 0.12.6",
|
"prost-types",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tonic 0.10.2",
|
"tonic",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7703,7 +7403,7 @@ dependencies = [
|
|||||||
"gdkx11",
|
"gdkx11",
|
||||||
"gtk",
|
"gtk",
|
||||||
"html5ever",
|
"html5ever",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"javascriptcore-rs",
|
"javascriptcore-rs",
|
||||||
"jni",
|
"jni",
|
||||||
"kuchikiki",
|
"kuchikiki",
|
||||||
@@ -7803,13 +7503,12 @@ dependencies = [
|
|||||||
name = "yaak-app"
|
name = "yaak-app"
|
||||||
version = "0.0.0"
|
version = "0.0.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.22.1",
|
|
||||||
"chrono",
|
"chrono",
|
||||||
"cocoa 0.26.0",
|
"cocoa 0.26.0",
|
||||||
"datetime",
|
"datetime",
|
||||||
"eventsource-client",
|
"eventsource-client",
|
||||||
"hex_color",
|
"hex_color",
|
||||||
"http 1.1.0",
|
"http",
|
||||||
"log",
|
"log",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
"objc",
|
"objc",
|
||||||
@@ -7818,7 +7517,7 @@ dependencies = [
|
|||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"reqwest_cookie_store",
|
"reqwest_cookie_store",
|
||||||
"rustls 0.23.21",
|
"rustls",
|
||||||
"rustls-platform-verifier",
|
"rustls-platform-verifier",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
@@ -7855,20 +7554,21 @@ dependencies = [
|
|||||||
"anyhow",
|
"anyhow",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
"dunce",
|
"dunce",
|
||||||
"hyper 0.14.30",
|
"hyper",
|
||||||
"hyper-rustls 0.24.2",
|
"hyper-rustls",
|
||||||
|
"hyper-util",
|
||||||
"log",
|
"log",
|
||||||
"md5",
|
"md5",
|
||||||
"prost 0.12.6",
|
"prost",
|
||||||
"prost-reflect",
|
"prost-reflect",
|
||||||
"prost-types 0.12.6",
|
"prost-types",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin-shell",
|
"tauri-plugin-shell",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-stream",
|
"tokio-stream",
|
||||||
"tonic 0.10.2",
|
"tonic",
|
||||||
"tonic-reflection",
|
"tonic-reflection",
|
||||||
"uuid",
|
"uuid",
|
||||||
]
|
]
|
||||||
@@ -7916,7 +7616,7 @@ dependencies = [
|
|||||||
"dunce",
|
"dunce",
|
||||||
"log",
|
"log",
|
||||||
"path-slash",
|
"path-slash",
|
||||||
"prost 0.13.3",
|
"prost",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
"serde",
|
"serde",
|
||||||
@@ -7925,7 +7625,7 @@ dependencies = [
|
|||||||
"tauri-plugin-shell",
|
"tauri-plugin-shell",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.7",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tonic 0.12.1",
|
"tonic",
|
||||||
"tonic-build",
|
"tonic-build",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"yaak-models",
|
"yaak-models",
|
||||||
|
|||||||
@@ -38,12 +38,11 @@ cocoa = "0.26.0"
|
|||||||
openssl-sys = { version = "0.9", features = ["vendored"] } # For Ubuntu installation to work
|
openssl-sys = { version = "0.9", features = ["vendored"] } # For Ubuntu installation to work
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
base64 = "0.22.0"
|
|
||||||
chrono = { version = "0.4.31", features = ["serde"] }
|
chrono = { version = "0.4.31", features = ["serde"] }
|
||||||
datetime = "0.5.2"
|
datetime = "0.5.2"
|
||||||
eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.13.0" }
|
eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.14.0" }
|
||||||
hex_color = "3.0.0"
|
hex_color = "3.0.0"
|
||||||
http = "1"
|
http = { version = "1.2.0", default-features = false }
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
regex = "1.10.2"
|
regex = "1.10.2"
|
||||||
|
|||||||
@@ -1,10 +1,8 @@
|
|||||||
use crate::render::render_http_request;
|
use crate::render::render_http_request;
|
||||||
use crate::response_err;
|
use crate::response_err;
|
||||||
use crate::template_callback::PluginTemplateCallback;
|
use crate::template_callback::PluginTemplateCallback;
|
||||||
use base64::prelude::BASE64_STANDARD;
|
|
||||||
use base64::Engine;
|
|
||||||
use http::header::{ACCEPT, USER_AGENT};
|
use http::header::{ACCEPT, USER_AGENT};
|
||||||
use http::{HeaderMap, HeaderName, HeaderValue};
|
use http::{HeaderMap, HeaderName, HeaderValue, Uri};
|
||||||
use log::{debug, error, warn};
|
use log::{debug, error, warn};
|
||||||
use mime_guess::Mime;
|
use mime_guess::Mime;
|
||||||
use reqwest::redirect::Policy;
|
use reqwest::redirect::Policy;
|
||||||
@@ -32,7 +30,8 @@ use yaak_models::queries::{
|
|||||||
get_base_environment, get_http_response, get_or_create_settings, get_workspace,
|
get_base_environment, get_http_response, get_or_create_settings, get_workspace,
|
||||||
update_response_if_id, upsert_cookie_jar, UpdateSource,
|
update_response_if_id, upsert_cookie_jar, UpdateSource,
|
||||||
};
|
};
|
||||||
use yaak_plugins::events::{RenderPurpose, WindowContext};
|
use yaak_plugins::events::{CallHttpAuthenticationRequest, HttpHeader, RenderPurpose, WindowContext};
|
||||||
|
use yaak_plugins::manager::PluginManager;
|
||||||
|
|
||||||
pub async fn send_http_request<R: Runtime>(
|
pub async fn send_http_request<R: Runtime>(
|
||||||
window: &WebviewWindow<R>,
|
window: &WebviewWindow<R>,
|
||||||
@@ -42,6 +41,7 @@ pub async fn send_http_request<R: Runtime>(
|
|||||||
cookie_jar: Option<CookieJar>,
|
cookie_jar: Option<CookieJar>,
|
||||||
cancelled_rx: &mut Receiver<bool>,
|
cancelled_rx: &mut Receiver<bool>,
|
||||||
) -> Result<HttpResponse, String> {
|
) -> Result<HttpResponse, String> {
|
||||||
|
let plugin_manager = window.state::<PluginManager>();
|
||||||
let workspace =
|
let workspace =
|
||||||
get_workspace(window, &request.workspace_id).await.expect("Failed to get Workspace");
|
get_workspace(window, &request.workspace_id).await.expect("Failed to get Workspace");
|
||||||
let base_environment = get_base_environment(window, &request.workspace_id)
|
let base_environment = get_base_environment(window, &request.workspace_id)
|
||||||
@@ -160,7 +160,7 @@ pub async fn send_http_request<R: Runtime>(
|
|||||||
query_params.push((p.name, p.value));
|
query_params.push((p.name, p.value));
|
||||||
}
|
}
|
||||||
|
|
||||||
let uri = match http::Uri::from_str(url_string.as_str()) {
|
let uri = match Uri::from_str(url_string.as_str()) {
|
||||||
Ok(u) => u,
|
Ok(u) => u,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
return Ok(response_err(
|
return Ok(response_err(
|
||||||
@@ -234,29 +234,6 @@ pub async fn send_http_request<R: Runtime>(
|
|||||||
headers.insert(header_name, header_value);
|
headers.insert(header_name, header_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(b) = &rendered_request.authentication_type {
|
|
||||||
let empty_value = &serde_json::to_value("").unwrap();
|
|
||||||
let a = rendered_request.authentication;
|
|
||||||
|
|
||||||
if b == "basic" {
|
|
||||||
let username = a.get("username").unwrap_or(empty_value).as_str().unwrap_or_default();
|
|
||||||
let password = a.get("password").unwrap_or(empty_value).as_str().unwrap_or_default();
|
|
||||||
|
|
||||||
let auth = format!("{username}:{password}");
|
|
||||||
let encoded = BASE64_STANDARD.encode(auth);
|
|
||||||
headers.insert(
|
|
||||||
"Authorization",
|
|
||||||
HeaderValue::from_str(&format!("Basic {}", encoded)).unwrap(),
|
|
||||||
);
|
|
||||||
} else if b == "bearer" {
|
|
||||||
let token = a.get("token").unwrap_or(empty_value).as_str().unwrap_or_default();
|
|
||||||
headers.insert(
|
|
||||||
"Authorization",
|
|
||||||
HeaderValue::from_str(&format!("Bearer {token}")).unwrap(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let request_body = rendered_request.body;
|
let request_body = rendered_request.body;
|
||||||
if let Some(body_type) = &rendered_request.body_type {
|
if let Some(body_type) = &rendered_request.body_type {
|
||||||
if body_type == "graphql" {
|
if body_type == "graphql" {
|
||||||
@@ -383,7 +360,7 @@ pub async fn send_http_request<R: Runtime>(
|
|||||||
// Add headers last, because previous steps may modify them
|
// Add headers last, because previous steps may modify them
|
||||||
request_builder = request_builder.headers(headers);
|
request_builder = request_builder.headers(headers);
|
||||||
|
|
||||||
let sendable_req = match request_builder.build() {
|
let mut sendable_req = match request_builder.build() {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
warn!("Failed to build request builder {e:?}");
|
warn!("Failed to build request builder {e:?}");
|
||||||
@@ -391,6 +368,56 @@ pub async fn send_http_request<R: Runtime>(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Apply authentication
|
||||||
|
|
||||||
|
// Map legacy auth name values from before they were plugins
|
||||||
|
let auth_plugin_name = match request.authentication_type.clone() {
|
||||||
|
Some(s) if s == "basic" => Some("@yaakapp/auth-basic".to_string()),
|
||||||
|
Some(s) if s == "bearer" => Some("@yaakapp/auth-bearer".to_string()),
|
||||||
|
_ => request.authentication_type.to_owned(),
|
||||||
|
};
|
||||||
|
if let Some(plugin_name) = auth_plugin_name {
|
||||||
|
let req = CallHttpAuthenticationRequest {
|
||||||
|
config: serde_json::to_value(&request.authentication)
|
||||||
|
.unwrap()
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned(),
|
||||||
|
method: sendable_req.method().to_string(),
|
||||||
|
url: sendable_req.url().to_string(),
|
||||||
|
headers: sendable_req
|
||||||
|
.headers()
|
||||||
|
.iter()
|
||||||
|
.map(|(name, value)| HttpHeader {
|
||||||
|
name: name.to_string(),
|
||||||
|
value: value.to_str().unwrap_or_default().to_string(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let plugin_result =
|
||||||
|
match plugin_manager.call_http_authentication(window, &plugin_name, req).await {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(e) => {
|
||||||
|
return Ok(response_err(&*response.lock().await, e.to_string(), window).await);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
let url = sendable_req.url_mut();
|
||||||
|
*url = Url::parse(&plugin_result.url).unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let headers = sendable_req.headers_mut();
|
||||||
|
for header in plugin_result.headers {
|
||||||
|
headers.insert(
|
||||||
|
HeaderName::from_str(&header.name).unwrap(),
|
||||||
|
HeaderValue::from_str(&header.value).unwrap(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let (resp_tx, resp_rx) = oneshot::channel::<Result<Response, reqwest::Error>>();
|
let (resp_tx, resp_rx) = oneshot::channel::<Result<Response, reqwest::Error>>();
|
||||||
let (done_tx, done_rx) = oneshot::channel::<HttpResponse>();
|
let (done_tx, done_rx) = oneshot::channel::<HttpResponse>();
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,6 @@ use crate::render::{render_grpc_request, render_http_request, render_json_value,
|
|||||||
use crate::template_callback::PluginTemplateCallback;
|
use crate::template_callback::PluginTemplateCallback;
|
||||||
use crate::updates::{UpdateMode, YaakUpdater};
|
use crate::updates::{UpdateMode, YaakUpdater};
|
||||||
use crate::window_menu::app_menu;
|
use crate::window_menu::app_menu;
|
||||||
use base64::prelude::BASE64_STANDARD;
|
|
||||||
use base64::Engine;
|
|
||||||
use chrono::Utc;
|
use chrono::Utc;
|
||||||
use eventsource_client::{EventParser, SSE};
|
use eventsource_client::{EventParser, SSE};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
@@ -65,11 +63,11 @@ use yaak_models::queries::{
|
|||||||
upsert_workspace_meta, BatchUpsertResult, UpdateSource,
|
upsert_workspace_meta, BatchUpsertResult, UpdateSource,
|
||||||
};
|
};
|
||||||
use yaak_plugins::events::{
|
use yaak_plugins::events::{
|
||||||
BootResponse, CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse,
|
BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, FilterResponse,
|
||||||
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, Icon,
|
FindHttpResponsesResponse, GetHttpAuthenticationResponse, GetHttpRequestActionsResponse,
|
||||||
InternalEvent, InternalEventPayload, PromptTextResponse, RenderHttpRequestResponse,
|
GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, HttpHeader, Icon, InternalEvent,
|
||||||
RenderPurpose, SendHttpRequestResponse, ShowToastRequest, TemplateRenderResponse,
|
InternalEventPayload, PromptTextResponse, RenderHttpRequestResponse, RenderPurpose,
|
||||||
WindowContext,
|
SendHttpRequestResponse, ShowToastRequest, TemplateRenderResponse, WindowContext,
|
||||||
};
|
};
|
||||||
use yaak_plugins::manager::PluginManager;
|
use yaak_plugins::manager::PluginManager;
|
||||||
use yaak_plugins::plugin_handle::PluginHandle;
|
use yaak_plugins::plugin_handle::PluginHandle;
|
||||||
@@ -154,7 +152,7 @@ async fn cmd_render_template<R: Runtime>(
|
|||||||
RenderPurpose::Preview,
|
RenderPurpose::Preview,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
Ok(rendered)
|
Ok(rendered)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -198,6 +196,7 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
environment_id: Option<&str>,
|
environment_id: Option<&str>,
|
||||||
proto_files: Vec<String>,
|
proto_files: Vec<String>,
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
|
plugin_manager: State<'_, PluginManager>,
|
||||||
grpc_handle: State<'_, Mutex<GrpcHandle>>,
|
grpc_handle: State<'_, Mutex<GrpcHandle>>,
|
||||||
) -> Result<String, String> {
|
) -> Result<String, String> {
|
||||||
let environment = match environment_id {
|
let environment = match environment_id {
|
||||||
@@ -210,7 +209,7 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
.ok_or("Failed to find GRPC request")?;
|
.ok_or("Failed to find GRPC request")?;
|
||||||
let base_environment =
|
let base_environment =
|
||||||
get_base_environment(&window, &req.workspace_id).await.map_err(|e| e.to_string())?;
|
get_base_environment(&window, &req.workspace_id).await.map_err(|e| e.to_string())?;
|
||||||
let req = render_grpc_request(
|
let mut req = render_grpc_request(
|
||||||
&req,
|
&req,
|
||||||
&base_environment,
|
&base_environment,
|
||||||
environment.as_ref(),
|
environment.as_ref(),
|
||||||
@@ -220,7 +219,7 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
RenderPurpose::Send,
|
RenderPurpose::Send,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
let mut metadata = BTreeMap::new();
|
let mut metadata = BTreeMap::new();
|
||||||
|
|
||||||
// Add the rest of metadata
|
// Add the rest of metadata
|
||||||
@@ -236,21 +235,37 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
metadata.insert(h.name, h.value);
|
metadata.insert(h.name, h.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(b) = &req.authentication_type {
|
// Map legacy auth name values from before they were plugins
|
||||||
let req = req.clone();
|
let auth_plugin_name = match req.authentication_type.clone() {
|
||||||
let empty_value = &serde_json::to_value("").unwrap();
|
Some(s) if s == "basic" => Some("@yaakapp/auth-basic".to_string()),
|
||||||
let a = req.authentication;
|
Some(s) if s == "bearer" => Some("@yaakapp/auth-bearer".to_string()),
|
||||||
|
_ => req.authentication_type.to_owned(),
|
||||||
|
};
|
||||||
|
if let Some(plugin_name) = auth_plugin_name {
|
||||||
|
let plugin_req = CallHttpAuthenticationRequest {
|
||||||
|
config: serde_json::to_value(&req.authentication)
|
||||||
|
.unwrap()
|
||||||
|
.as_object()
|
||||||
|
.unwrap()
|
||||||
|
.to_owned(),
|
||||||
|
method: "POST".to_string(),
|
||||||
|
url: req.url.clone(),
|
||||||
|
headers: metadata
|
||||||
|
.iter()
|
||||||
|
.map(|(name, value)| HttpHeader {
|
||||||
|
name: name.to_string(),
|
||||||
|
value: value.to_string(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
};
|
||||||
|
let plugin_result = plugin_manager
|
||||||
|
.call_http_authentication(&window, &plugin_name, plugin_req)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
if b == "basic" {
|
req.url = plugin_result.url;
|
||||||
let username = a.get("username").unwrap_or(empty_value).as_str().unwrap_or("");
|
for header in plugin_result.headers {
|
||||||
let password = a.get("password").unwrap_or(empty_value).as_str().unwrap_or("");
|
metadata.insert(header.name, header.value);
|
||||||
|
|
||||||
let auth = format!("{username}:{password}");
|
|
||||||
let encoded = BASE64_STANDARD.encode(auth);
|
|
||||||
metadata.insert("Authorization".to_string(), format!("Basic {}", encoded));
|
|
||||||
} else if b == "bearer" {
|
|
||||||
let token = a.get("token").unwrap_or(empty_value).as_str().unwrap_or("");
|
|
||||||
metadata.insert("Authorization".to_string(), format!("Bearer {token}"));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,8 +284,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?
|
.map_err(|e| e.to_string())?
|
||||||
};
|
};
|
||||||
|
|
||||||
let conn_id = conn.id.clone();
|
let conn_id = conn.id.clone();
|
||||||
@@ -322,8 +337,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
return Ok(conn_id);
|
return Ok(conn_id);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -378,7 +393,7 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
RenderPurpose::Send,
|
RenderPurpose::Send,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
@@ -396,8 +411,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -413,8 +428,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(IncomingMsg::Commit) => {
|
Ok(IncomingMsg::Commit) => {
|
||||||
@@ -446,7 +461,7 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
RenderPurpose::Send,
|
RenderPurpose::Send,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
upsert_grpc_event(
|
upsert_grpc_event(
|
||||||
&window,
|
&window,
|
||||||
@@ -458,8 +473,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
async move {
|
async move {
|
||||||
let (maybe_stream, maybe_msg) =
|
let (maybe_stream, maybe_msg) =
|
||||||
@@ -497,8 +512,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
match maybe_msg {
|
match maybe_msg {
|
||||||
@@ -512,14 +527,14 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
} else {
|
} else {
|
||||||
"Received response with metadata"
|
"Received response with metadata"
|
||||||
}
|
}
|
||||||
.to_string(),
|
.to_string(),
|
||||||
event_type: GrpcEventType::Info,
|
event_type: GrpcEventType::Info,
|
||||||
..base_event.clone()
|
..base_event.clone()
|
||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
upsert_grpc_event(
|
upsert_grpc_event(
|
||||||
&window,
|
&window,
|
||||||
&GrpcEvent {
|
&GrpcEvent {
|
||||||
@@ -529,8 +544,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
upsert_grpc_event(
|
upsert_grpc_event(
|
||||||
&window,
|
&window,
|
||||||
&GrpcEvent {
|
&GrpcEvent {
|
||||||
@@ -541,8 +556,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
upsert_grpc_event(
|
upsert_grpc_event(
|
||||||
@@ -566,8 +581,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
}),
|
}),
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
// Server streaming doesn't return the initial message
|
// Server streaming doesn't return the initial message
|
||||||
@@ -585,14 +600,14 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
} else {
|
} else {
|
||||||
"Received response with metadata"
|
"Received response with metadata"
|
||||||
}
|
}
|
||||||
.to_string(),
|
.to_string(),
|
||||||
event_type: GrpcEventType::Info,
|
event_type: GrpcEventType::Info,
|
||||||
..base_event.clone()
|
..base_event.clone()
|
||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
stream.into_inner()
|
stream.into_inner()
|
||||||
}
|
}
|
||||||
Some(Err(e)) => {
|
Some(Err(e)) => {
|
||||||
@@ -618,8 +633,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
}),
|
}),
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
None => return,
|
None => return,
|
||||||
@@ -638,8 +653,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
Ok(None) => {
|
Ok(None) => {
|
||||||
let trailers =
|
let trailers =
|
||||||
@@ -655,8 +670,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Err(status) => {
|
Err(status) => {
|
||||||
@@ -671,8 +686,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -930,8 +945,8 @@ async fn cmd_import_data<R: Runtime>(
|
|||||||
grpc_requests,
|
grpc_requests,
|
||||||
&UpdateSource::Import,
|
&UpdateSource::Import,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
analytics::track_event(
|
analytics::track_event(
|
||||||
&window,
|
&window,
|
||||||
@@ -939,7 +954,7 @@ async fn cmd_import_data<R: Runtime>(
|
|||||||
AnalyticsAction::Import,
|
AnalyticsAction::Import,
|
||||||
Some(json!({ "plugin": plugin_name })),
|
Some(json!({ "plugin": plugin_name })),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
Ok(upserted)
|
Ok(upserted)
|
||||||
}
|
}
|
||||||
@@ -960,6 +975,14 @@ async fn cmd_template_functions<R: Runtime>(
|
|||||||
plugin_manager.get_template_functions(&window).await.map_err(|e| e.to_string())
|
plugin_manager.get_template_functions(&window).await.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn cmd_get_http_authentication<R: Runtime>(
|
||||||
|
window: WebviewWindow<R>,
|
||||||
|
plugin_manager: State<'_, PluginManager>,
|
||||||
|
) -> Result<Vec<GetHttpAuthenticationResponse>, String> {
|
||||||
|
plugin_manager.get_http_authentication(&window).await.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn cmd_call_http_request_action<R: Runtime>(
|
async fn cmd_call_http_request_action<R: Runtime>(
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
@@ -985,7 +1008,7 @@ async fn cmd_curl_to_request<R: Runtime>(
|
|||||||
AnalyticsAction::Import,
|
AnalyticsAction::Import,
|
||||||
Some(json!({ "plugin": plugin_name })),
|
Some(json!({ "plugin": plugin_name })),
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
import_result.resources.http_requests.get(0).ok_or("No curl command found".to_string()).map(
|
import_result.resources.http_requests.get(0).ok_or("No curl command found".to_string()).map(
|
||||||
|r| {
|
|r| {
|
||||||
@@ -1170,8 +1193,8 @@ async fn cmd_install_plugin<R: Runtime>(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
Ok(plugin)
|
Ok(plugin)
|
||||||
}
|
}
|
||||||
@@ -1222,8 +1245,8 @@ async fn cmd_create_cookie_jar(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -1245,8 +1268,8 @@ async fn cmd_create_environment(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -1268,8 +1291,8 @@ async fn cmd_create_grpc_request(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())
|
.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -1508,8 +1531,8 @@ async fn cmd_list_cookie_jars(
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create CookieJar");
|
.expect("Failed to create CookieJar");
|
||||||
Ok(vec![cookie_jar])
|
Ok(vec![cookie_jar])
|
||||||
} else {
|
} else {
|
||||||
Ok(cookie_jars)
|
Ok(cookie_jars)
|
||||||
@@ -1598,8 +1621,8 @@ async fn cmd_list_workspaces(window: WebviewWindow) -> Result<Vec<Workspace>, St
|
|||||||
},
|
},
|
||||||
&UpdateSource::Window,
|
&UpdateSource::Window,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to create Workspace");
|
.expect("Failed to create Workspace");
|
||||||
Ok(vec![workspace])
|
Ok(vec![workspace])
|
||||||
} else {
|
} else {
|
||||||
Ok(workspaces)
|
Ok(workspaces)
|
||||||
@@ -1853,6 +1876,7 @@ pub fn run() {
|
|||||||
cmd_get_environment,
|
cmd_get_environment,
|
||||||
cmd_get_folder,
|
cmd_get_folder,
|
||||||
cmd_get_grpc_request,
|
cmd_get_grpc_request,
|
||||||
|
cmd_get_http_authentication,
|
||||||
cmd_get_http_request,
|
cmd_get_http_request,
|
||||||
cmd_get_key_value,
|
cmd_get_key_value,
|
||||||
cmd_get_settings,
|
cmd_get_settings,
|
||||||
@@ -1989,7 +2013,7 @@ fn create_main_window(handle: &AppHandle, url: &str) -> WebviewWindow {
|
|||||||
Some(_) => counter += 1,
|
Some(_) => counter += 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.expect("Failed to generate label for new window");
|
.expect("Failed to generate label for new window");
|
||||||
|
|
||||||
let config = CreateWindowConfig {
|
let config = CreateWindowConfig {
|
||||||
url,
|
url,
|
||||||
@@ -2224,8 +2248,8 @@ async fn handle_plugin_event<R: Runtime>(
|
|||||||
req.request_id.as_str(),
|
req.request_id.as_str(),
|
||||||
req.limit.map(|l| l as i64),
|
req.limit.map(|l| l as i64),
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap_or_default();
|
.unwrap_or_default();
|
||||||
Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse {
|
Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse {
|
||||||
http_responses,
|
http_responses,
|
||||||
}))
|
}))
|
||||||
@@ -2254,7 +2278,7 @@ async fn handle_plugin_event<R: Runtime>(
|
|||||||
environment.as_ref(),
|
environment.as_ref(),
|
||||||
&cb,
|
&cb,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
||||||
http_request,
|
http_request,
|
||||||
}))
|
}))
|
||||||
@@ -2275,7 +2299,7 @@ async fn handle_plugin_event<R: Runtime>(
|
|||||||
render_json_value(req.data, &base_environment, environment.as_ref(), &cb).await;
|
render_json_value(req.data, &base_environment, environment.as_ref(), &cb).await;
|
||||||
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))
|
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))
|
||||||
}
|
}
|
||||||
InternalEventPayload::ReloadResponse => {
|
InternalEventPayload::ReloadResponse(_) => {
|
||||||
let window = get_window_from_window_context(app_handle, &window_context)
|
let window = get_window_from_window_context(app_handle, &window_context)
|
||||||
.expect("Failed to find window for plugin reload");
|
.expect("Failed to find window for plugin reload");
|
||||||
let plugins = list_plugins(app_handle).await.unwrap();
|
let plugins = list_plugins(app_handle).await.unwrap();
|
||||||
@@ -2313,8 +2337,8 @@ async fn handle_plugin_event<R: Runtime>(
|
|||||||
req.http_request.id.as_str(),
|
req.http_request.id.as_str(),
|
||||||
&UpdateSource::Plugin,
|
&UpdateSource::Plugin,
|
||||||
)
|
)
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let result = send_http_request(
|
let result = send_http_request(
|
||||||
&window,
|
&window,
|
||||||
@@ -2324,7 +2348,7 @@ 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,
|
||||||
|
|||||||
@@ -312,7 +312,7 @@ mod placeholder_tests {
|
|||||||
name: ":foo".into(),
|
name: ":foo".into(),
|
||||||
value: "xxx".into(),
|
value: "xxx".into(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: "p1".into(),
|
id: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
replace_path_placeholder(&p, "https://example.com/:foo/bar"),
|
replace_path_placeholder(&p, "https://example.com/:foo/bar"),
|
||||||
@@ -326,7 +326,7 @@ mod placeholder_tests {
|
|||||||
name: ":foo".into(),
|
name: ":foo".into(),
|
||||||
value: "xxx".into(),
|
value: "xxx".into(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: "p1".into(),
|
id: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
replace_path_placeholder(&p, "https://example.com/:foo"),
|
replace_path_placeholder(&p, "https://example.com/:foo"),
|
||||||
@@ -340,7 +340,7 @@ mod placeholder_tests {
|
|||||||
name: ":foo".into(),
|
name: ":foo".into(),
|
||||||
value: "xxx".into(),
|
value: "xxx".into(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: "p1".into(),
|
id: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
replace_path_placeholder(&p, "https://example.com/:foo?:foo"),
|
replace_path_placeholder(&p, "https://example.com/:foo?:foo"),
|
||||||
@@ -354,7 +354,7 @@ mod placeholder_tests {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
name: "".to_string(),
|
name: "".to_string(),
|
||||||
value: "".to_string(),
|
value: "".to_string(),
|
||||||
id: "p1".into(),
|
id: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
replace_path_placeholder(&p, "https://example.com/:missing"),
|
replace_path_placeholder(&p, "https://example.com/:missing"),
|
||||||
@@ -368,7 +368,7 @@ mod placeholder_tests {
|
|||||||
enabled: false,
|
enabled: false,
|
||||||
name: ":foo".to_string(),
|
name: ":foo".to_string(),
|
||||||
value: "xxx".to_string(),
|
value: "xxx".to_string(),
|
||||||
id: "p1".into(),
|
id: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
replace_path_placeholder(&p, "https://example.com/:foo"),
|
replace_path_placeholder(&p, "https://example.com/:foo"),
|
||||||
@@ -382,7 +382,7 @@ mod placeholder_tests {
|
|||||||
name: ":foo".into(),
|
name: ":foo".into(),
|
||||||
value: "xxx".into(),
|
value: "xxx".into(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: "p1".into(),
|
id: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
replace_path_placeholder(&p, "https://example.com/:foooo"),
|
replace_path_placeholder(&p, "https://example.com/:foooo"),
|
||||||
@@ -396,7 +396,7 @@ mod placeholder_tests {
|
|||||||
name: ":foo".into(),
|
name: ":foo".into(),
|
||||||
value: "Hello World".into(),
|
value: "Hello World".into(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: "p1".into(),
|
id: None,
|
||||||
};
|
};
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
replace_path_placeholder(&p, "https://example.com/:foo"),
|
replace_path_placeholder(&p, "https://example.com/:foo"),
|
||||||
@@ -413,13 +413,13 @@ mod placeholder_tests {
|
|||||||
name: "b".to_string(),
|
name: "b".to_string(),
|
||||||
value: "bbb".to_string(),
|
value: "bbb".to_string(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: "p1".into(),
|
id: None,
|
||||||
},
|
},
|
||||||
HttpUrlParameter {
|
HttpUrlParameter {
|
||||||
name: ":a".to_string(),
|
name: ":a".to_string(),
|
||||||
value: "aaa".to_string(),
|
value: "aaa".to_string(),
|
||||||
enabled: true,
|
enabled: true,
|
||||||
id: "p2".into(),
|
id: None,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
..Default::default()
|
..Default::default()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tauri::{AppHandle, Manager, Runtime};
|
use tauri::{AppHandle, Manager, Runtime};
|
||||||
use yaak_plugins::events::{RenderPurpose, TemplateFunctionArg, WindowContext};
|
use yaak_plugins::events::{FormInput, RenderPurpose, WindowContext};
|
||||||
use yaak_plugins::manager::PluginManager;
|
use yaak_plugins::manager::PluginManager;
|
||||||
use yaak_templates::TemplateCallback;
|
use yaak_templates::TemplateCallback;
|
||||||
|
|
||||||
@@ -48,11 +48,11 @@ impl TemplateCallback for PluginTemplateCallback {
|
|||||||
// Fill in default values for all args
|
// Fill in default values for all args
|
||||||
for a_def in function.args {
|
for a_def in function.args {
|
||||||
let base = match a_def {
|
let base = match a_def {
|
||||||
TemplateFunctionArg::Text(a) => a.base,
|
FormInput::Text(a) => a.base,
|
||||||
TemplateFunctionArg::Select(a) => a.base,
|
FormInput::Select(a) => a.base,
|
||||||
TemplateFunctionArg::Checkbox(a) => a.base,
|
FormInput::Checkbox(a) => a.base,
|
||||||
TemplateFunctionArg::File(a) => a.base,
|
FormInput::File(a) => a.base,
|
||||||
TemplateFunctionArg::HttpRequest(a) => a.base,
|
FormInput::HttpRequest(a) => a.base,
|
||||||
};
|
};
|
||||||
if let None = args_with_defaults.get(base.name.as_str()) {
|
if let None = args_with_defaults.get(base.name.as_str()) {
|
||||||
args_with_defaults.insert(base.name, base.default_value.unwrap_or_default());
|
args_with_defaults.insert(base.name, base.default_value.unwrap_or_default());
|
||||||
|
|||||||
55
src-tauri/vendored/plugins/auth-basic/build/index.js
generated
Normal file
55
src-tauri/vendored/plugins/auth-basic/build/index.js
generated
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
"use strict";
|
||||||
|
var __defProp = Object.defineProperty;
|
||||||
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||||
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||||
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||||
|
var __export = (target, all) => {
|
||||||
|
for (var name in all)
|
||||||
|
__defProp(target, name, { get: all[name], enumerable: true });
|
||||||
|
};
|
||||||
|
var __copyProps = (to, from, except, desc) => {
|
||||||
|
if (from && typeof from === "object" || typeof from === "function") {
|
||||||
|
for (let key of __getOwnPropNames(from))
|
||||||
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||||
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
};
|
||||||
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||||
|
|
||||||
|
// src/index.ts
|
||||||
|
var src_exports = {};
|
||||||
|
__export(src_exports, {
|
||||||
|
plugin: () => plugin
|
||||||
|
});
|
||||||
|
module.exports = __toCommonJS(src_exports);
|
||||||
|
var plugin = {
|
||||||
|
authentication: {
|
||||||
|
name: "Basic",
|
||||||
|
config: [{
|
||||||
|
type: "text",
|
||||||
|
name: "username",
|
||||||
|
label: "Username",
|
||||||
|
optional: true
|
||||||
|
}, {
|
||||||
|
type: "text",
|
||||||
|
name: "password",
|
||||||
|
label: "Password",
|
||||||
|
optional: true
|
||||||
|
}],
|
||||||
|
async onApply(_ctx, args) {
|
||||||
|
const { username, password } = args.config;
|
||||||
|
return {
|
||||||
|
url: args.url,
|
||||||
|
headers: [{
|
||||||
|
name: "Authorization",
|
||||||
|
value: "Basic " + Buffer.from(`${username}:${password}`).toString("base64")
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Annotate the CommonJS export names for ESM import in node:
|
||||||
|
0 && (module.exports = {
|
||||||
|
plugin
|
||||||
|
});
|
||||||
9
src-tauri/vendored/plugins/auth-basic/package.json
generated
Normal file
9
src-tauri/vendored/plugins/auth-basic/package.json
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "@yaakapp/auth-basic",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"build": "yaakcli build ./src/index.ts",
|
||||||
|
"dev": "yaakcli dev ./src/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src-tauri/vendored/plugins/auth-bearer/build/index.js
generated
Normal file
50
src-tauri/vendored/plugins/auth-bearer/build/index.js
generated
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
"use strict";
|
||||||
|
var __defProp = Object.defineProperty;
|
||||||
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
||||||
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
||||||
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
||||||
|
var __export = (target, all) => {
|
||||||
|
for (var name in all)
|
||||||
|
__defProp(target, name, { get: all[name], enumerable: true });
|
||||||
|
};
|
||||||
|
var __copyProps = (to, from, except, desc) => {
|
||||||
|
if (from && typeof from === "object" || typeof from === "function") {
|
||||||
|
for (let key of __getOwnPropNames(from))
|
||||||
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
||||||
|
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
||||||
|
}
|
||||||
|
return to;
|
||||||
|
};
|
||||||
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
||||||
|
|
||||||
|
// src/index.ts
|
||||||
|
var src_exports = {};
|
||||||
|
__export(src_exports, {
|
||||||
|
plugin: () => plugin
|
||||||
|
});
|
||||||
|
module.exports = __toCommonJS(src_exports);
|
||||||
|
var plugin = {
|
||||||
|
authentication: {
|
||||||
|
name: "Bearer",
|
||||||
|
config: [{
|
||||||
|
type: "text",
|
||||||
|
name: "token",
|
||||||
|
label: "Token",
|
||||||
|
optional: true
|
||||||
|
}],
|
||||||
|
async onApply(_ctx, args) {
|
||||||
|
const { token } = args.config;
|
||||||
|
return {
|
||||||
|
url: args.url,
|
||||||
|
headers: [{
|
||||||
|
name: "Authorization",
|
||||||
|
value: `Bearer ${token}`.trim()
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Annotate the CommonJS export names for ESM import in node:
|
||||||
|
0 && (module.exports = {
|
||||||
|
plugin
|
||||||
|
});
|
||||||
9
src-tauri/vendored/plugins/auth-bearer/package.json
generated
Normal file
9
src-tauri/vendored/plugins/auth-bearer/package.json
generated
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "@yaakapp/auth-bearer",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"build": "yaakcli build ./src/index.ts",
|
||||||
|
"dev": "yaakcli dev ./src/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
3822
src-tauri/vendored/plugins/auth-jwt/build/index.js
generated
Normal file
3822
src-tauri/vendored/plugins/auth-jwt/build/index.js
generated
Normal file
File diff suppressed because it is too large
Load Diff
15
src-tauri/vendored/plugins/auth-jwt/package.json
generated
Normal file
15
src-tauri/vendored/plugins/auth-jwt/package.json
generated
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "@yaakapp/auth-jwt",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"build": "yaakcli build ./src/index.ts",
|
||||||
|
"dev": "yaakcli dev ./src/index.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"jsonwebtoken": "^9.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/jsonwebtoken": "^9.0.7"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,19 +5,20 @@ edition = "2021"
|
|||||||
publish = false
|
publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tonic = "0.10.2"
|
tonic = "0.12.3"
|
||||||
prost = "0.12"
|
prost = "0.13.4"
|
||||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "fs"] }
|
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "fs"] }
|
||||||
tonic-reflection = "0.10.2"
|
tonic-reflection = "0.12.3"
|
||||||
tokio-stream = "0.1.14"
|
tokio-stream = "0.1.14"
|
||||||
prost-types = "0.12.3"
|
prost-types = "0.13.4"
|
||||||
serde = { version = "1.0.196", features = ["derive"] }
|
serde = { version = "1.0.196", features = ["derive"] }
|
||||||
serde_json = "1.0.113"
|
serde_json = "1.0.113"
|
||||||
prost-reflect = { version = "0.12.0", features = ["serde", "derive"] }
|
prost-reflect = { version = "0.14.4", features = ["serde", "derive"] }
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
anyhow = "1.0.79"
|
anyhow = "1.0.79"
|
||||||
hyper = { version = "0.14" }
|
hyper = "1.5.2"
|
||||||
hyper-rustls = { version = "0.24.0", features = ["http2"] }
|
hyper-util = { version = "0.1.10", features = ["client-legacy", "client"] }
|
||||||
|
hyper-rustls = { version = "0.27.5", default-features = false, features = ["http2", "rustls-platform-verifier"] }
|
||||||
uuid = { version = "1.7.0", features = ["v4"] }
|
uuid = { version = "1.7.0", features = ["v4"] }
|
||||||
tauri = { workspace = true }
|
tauri = { workspace = true }
|
||||||
tauri-plugin-shell = { workspace = true }
|
tauri-plugin-shell = { workspace = true }
|
||||||
|
|||||||
172
src-tauri/yaak-grpc/src/client.rs
Normal file
172
src-tauri/yaak-grpc/src/client.rs
Normal file
@@ -0,0 +1,172 @@
|
|||||||
|
use crate::transport::get_transport;
|
||||||
|
use async_recursion::async_recursion;
|
||||||
|
use hyper_rustls::HttpsConnector;
|
||||||
|
use hyper_util::client::legacy::connect::HttpConnector;
|
||||||
|
use hyper_util::client::legacy::Client;
|
||||||
|
use log::debug;
|
||||||
|
use tokio_stream::StreamExt;
|
||||||
|
use tonic::body::BoxBody;
|
||||||
|
use tonic::transport::Uri;
|
||||||
|
use tonic::Request;
|
||||||
|
use tonic_reflection::pb::v1::server_reflection_request::MessageRequest;
|
||||||
|
use tonic_reflection::pb::v1::server_reflection_response::MessageResponse;
|
||||||
|
use tonic_reflection::pb::v1::{
|
||||||
|
ErrorResponse, ExtensionNumberResponse, ListServiceResponse, ServerReflectionRequest,
|
||||||
|
ServiceResponse,
|
||||||
|
};
|
||||||
|
use tonic_reflection::pb::v1::{ExtensionRequest, FileDescriptorResponse};
|
||||||
|
use tonic_reflection::pb::{v1, v1alpha};
|
||||||
|
|
||||||
|
pub struct AutoReflectionClient<T = Client<HttpsConnector<HttpConnector>, BoxBody>> {
|
||||||
|
use_v1alpha: bool,
|
||||||
|
client_v1: v1::server_reflection_client::ServerReflectionClient<T>,
|
||||||
|
client_v1alpha: v1alpha::server_reflection_client::ServerReflectionClient<T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AutoReflectionClient {
|
||||||
|
pub fn new(uri: &Uri) -> Self {
|
||||||
|
let client_v1 = v1::server_reflection_client::ServerReflectionClient::with_origin(
|
||||||
|
get_transport(),
|
||||||
|
uri.clone(),
|
||||||
|
);
|
||||||
|
let client_v1alpha = v1alpha::server_reflection_client::ServerReflectionClient::with_origin(
|
||||||
|
get_transport(),
|
||||||
|
uri.clone(),
|
||||||
|
);
|
||||||
|
AutoReflectionClient {
|
||||||
|
use_v1alpha: false,
|
||||||
|
client_v1,
|
||||||
|
client_v1alpha,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_recursion]
|
||||||
|
pub async fn send_reflection_request(
|
||||||
|
&mut self,
|
||||||
|
message: MessageRequest,
|
||||||
|
) -> Result<MessageResponse, String> {
|
||||||
|
let reflection_request = ServerReflectionRequest {
|
||||||
|
host: "".into(), // Doesn't matter
|
||||||
|
message_request: Some(message.clone()),
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.use_v1alpha {
|
||||||
|
let request = Request::new(tokio_stream::once(to_v1alpha_request(reflection_request)));
|
||||||
|
self.client_v1alpha
|
||||||
|
.server_reflection_info(request)
|
||||||
|
.await
|
||||||
|
.map_err(|e| match e.code() {
|
||||||
|
tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(),
|
||||||
|
tonic::Code::Unauthenticated => "Authentication failed".to_string(),
|
||||||
|
tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(),
|
||||||
|
_ => e.to_string(),
|
||||||
|
})?
|
||||||
|
.into_inner()
|
||||||
|
.next()
|
||||||
|
.await
|
||||||
|
.expect("steamed response")
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
.message_response
|
||||||
|
.ok_or("No reflection response".to_string())
|
||||||
|
.map(|resp| to_v1_msg_response(resp))
|
||||||
|
} else {
|
||||||
|
let request = Request::new(tokio_stream::once(reflection_request));
|
||||||
|
let resp = self.client_v1.server_reflection_info(request).await;
|
||||||
|
match resp {
|
||||||
|
Ok(r) => Ok(r),
|
||||||
|
Err(e) => match e.code().clone() {
|
||||||
|
tonic::Code::Unimplemented => {
|
||||||
|
// If v1 fails, change to v1alpha and try again
|
||||||
|
debug!("gRPC schema reflection falling back to v1alpha");
|
||||||
|
self.use_v1alpha = true;
|
||||||
|
return self.send_reflection_request(message).await;
|
||||||
|
}
|
||||||
|
_ => Err(e),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
.map_err(|e| match e.code() {
|
||||||
|
tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(),
|
||||||
|
tonic::Code::Unauthenticated => "Authentication failed".to_string(),
|
||||||
|
tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(),
|
||||||
|
_ => e.to_string(),
|
||||||
|
})?
|
||||||
|
.into_inner()
|
||||||
|
.next()
|
||||||
|
.await
|
||||||
|
.expect("steamed response")
|
||||||
|
.map_err(|e| e.to_string())?
|
||||||
|
.message_response
|
||||||
|
.ok_or("No reflection response".to_string())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_v1_msg_response(
|
||||||
|
response: v1alpha::server_reflection_response::MessageResponse,
|
||||||
|
) -> MessageResponse {
|
||||||
|
match response {
|
||||||
|
v1alpha::server_reflection_response::MessageResponse::FileDescriptorResponse(v) => {
|
||||||
|
MessageResponse::FileDescriptorResponse(FileDescriptorResponse {
|
||||||
|
file_descriptor_proto: v.file_descriptor_proto,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
v1alpha::server_reflection_response::MessageResponse::AllExtensionNumbersResponse(v) => {
|
||||||
|
MessageResponse::AllExtensionNumbersResponse(ExtensionNumberResponse {
|
||||||
|
extension_number: v.extension_number,
|
||||||
|
base_type_name: v.base_type_name,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
v1alpha::server_reflection_response::MessageResponse::ListServicesResponse(v) => {
|
||||||
|
MessageResponse::ListServicesResponse(ListServiceResponse {
|
||||||
|
service: v
|
||||||
|
.service
|
||||||
|
.iter()
|
||||||
|
.map(|s| ServiceResponse {
|
||||||
|
name: s.name.clone(),
|
||||||
|
})
|
||||||
|
.collect(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
v1alpha::server_reflection_response::MessageResponse::ErrorResponse(v) => {
|
||||||
|
MessageResponse::ErrorResponse(ErrorResponse {
|
||||||
|
error_code: v.error_code,
|
||||||
|
error_message: v.error_message,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_v1alpha_request(request: ServerReflectionRequest) -> v1alpha::ServerReflectionRequest {
|
||||||
|
v1alpha::ServerReflectionRequest {
|
||||||
|
host: request.host,
|
||||||
|
message_request: request.message_request.map(|m| to_v1alpha_msg_request(m)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_v1alpha_msg_request(
|
||||||
|
message: MessageRequest,
|
||||||
|
) -> v1alpha::server_reflection_request::MessageRequest {
|
||||||
|
match message {
|
||||||
|
MessageRequest::FileByFilename(v) => {
|
||||||
|
v1alpha::server_reflection_request::MessageRequest::FileByFilename(v)
|
||||||
|
}
|
||||||
|
MessageRequest::FileContainingSymbol(v) => {
|
||||||
|
v1alpha::server_reflection_request::MessageRequest::FileContainingSymbol(v)
|
||||||
|
}
|
||||||
|
MessageRequest::FileContainingExtension(ExtensionRequest {
|
||||||
|
extension_number,
|
||||||
|
containing_type,
|
||||||
|
}) => v1alpha::server_reflection_request::MessageRequest::FileContainingExtension(
|
||||||
|
v1alpha::ExtensionRequest {
|
||||||
|
extension_number,
|
||||||
|
containing_type,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
MessageRequest::AllExtensionNumbersOfType(v) => {
|
||||||
|
v1alpha::server_reflection_request::MessageRequest::AllExtensionNumbersOfType(v)
|
||||||
|
}
|
||||||
|
MessageRequest::ListServices(v) => {
|
||||||
|
v1alpha::server_reflection_request::MessageRequest::ListServices(v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -5,7 +5,9 @@ use serde_json::Deserializer;
|
|||||||
mod codec;
|
mod codec;
|
||||||
mod json_schema;
|
mod json_schema;
|
||||||
pub mod manager;
|
pub mod manager;
|
||||||
mod proto;
|
mod reflection;
|
||||||
|
mod transport;
|
||||||
|
mod client;
|
||||||
|
|
||||||
pub use tonic::metadata::*;
|
pub use tonic::metadata::*;
|
||||||
pub use tonic::Code;
|
pub use tonic::Code;
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ use std::collections::BTreeMap;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use hyper::client::HttpConnector;
|
|
||||||
use hyper::Client;
|
|
||||||
use hyper_rustls::HttpsConnector;
|
use hyper_rustls::HttpsConnector;
|
||||||
|
use hyper_util::client::legacy::connect::HttpConnector;
|
||||||
|
use hyper_util::client::legacy::Client;
|
||||||
pub use prost_reflect::DynamicMessage;
|
pub use prost_reflect::DynamicMessage;
|
||||||
use prost_reflect::{DescriptorPool, MethodDescriptor, ServiceDescriptor};
|
use prost_reflect::{DescriptorPool, MethodDescriptor, ServiceDescriptor};
|
||||||
use serde_json::Deserializer;
|
use serde_json::Deserializer;
|
||||||
@@ -16,10 +16,11 @@ use tonic::transport::Uri;
|
|||||||
use tonic::{IntoRequest, IntoStreamingRequest, Request, Response, Status, Streaming};
|
use tonic::{IntoRequest, IntoStreamingRequest, Request, Response, Status, Streaming};
|
||||||
|
|
||||||
use crate::codec::DynamicCodec;
|
use crate::codec::DynamicCodec;
|
||||||
use crate::proto::{
|
use crate::reflection::{
|
||||||
fill_pool_from_files, fill_pool_from_reflection, get_transport, method_desc_to_path,
|
fill_pool_from_files, fill_pool_from_reflection, method_desc_to_path,
|
||||||
};
|
};
|
||||||
use crate::{json_schema, MethodDefinition, ServiceDefinition};
|
use crate::{json_schema, MethodDefinition, ServiceDefinition};
|
||||||
|
use crate::transport::get_transport;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct GrpcConnection {
|
pub struct GrpcConnection {
|
||||||
|
|||||||
@@ -3,11 +3,9 @@ use std::ops::Deref;
|
|||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::client::AutoReflectionClient;
|
||||||
use anyhow::anyhow;
|
use anyhow::anyhow;
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
use hyper::client::HttpConnector;
|
|
||||||
use hyper::Client;
|
|
||||||
use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder};
|
|
||||||
use log::{debug, warn};
|
use log::{debug, warn};
|
||||||
use prost::Message;
|
use prost::Message;
|
||||||
use prost_reflect::{DescriptorPool, MethodDescriptor};
|
use prost_reflect::{DescriptorPool, MethodDescriptor};
|
||||||
@@ -16,15 +14,10 @@ use tauri::path::BaseDirectory;
|
|||||||
use tauri::{AppHandle, Manager};
|
use tauri::{AppHandle, Manager};
|
||||||
use tauri_plugin_shell::ShellExt;
|
use tauri_plugin_shell::ShellExt;
|
||||||
use tokio::fs;
|
use tokio::fs;
|
||||||
use tokio_stream::StreamExt;
|
|
||||||
use tonic::body::BoxBody;
|
|
||||||
use tonic::codegen::http::uri::PathAndQuery;
|
use tonic::codegen::http::uri::PathAndQuery;
|
||||||
use tonic::transport::Uri;
|
use tonic::transport::Uri;
|
||||||
use tonic::Request;
|
use tonic_reflection::pb::v1::server_reflection_request::MessageRequest;
|
||||||
use tonic_reflection::pb::server_reflection_client::ServerReflectionClient;
|
use tonic_reflection::pb::v1::server_reflection_response::MessageResponse;
|
||||||
use tonic_reflection::pb::server_reflection_request::MessageRequest;
|
|
||||||
use tonic_reflection::pb::server_reflection_response::MessageResponse;
|
|
||||||
use tonic_reflection::pb::ServerReflectionRequest;
|
|
||||||
|
|
||||||
pub async fn fill_pool_from_files(
|
pub async fn fill_pool_from_files(
|
||||||
app_handle: &AppHandle,
|
app_handle: &AppHandle,
|
||||||
@@ -98,7 +91,7 @@ pub async fn fill_pool_from_files(
|
|||||||
|
|
||||||
pub async fn fill_pool_from_reflection(uri: &Uri) -> Result<DescriptorPool, String> {
|
pub async fn fill_pool_from_reflection(uri: &Uri) -> Result<DescriptorPool, String> {
|
||||||
let mut pool = DescriptorPool::new();
|
let mut pool = DescriptorPool::new();
|
||||||
let mut client = ServerReflectionClient::with_origin(get_transport(), uri.clone());
|
let mut client = AutoReflectionClient::new(uri);
|
||||||
|
|
||||||
for service in list_services(&mut client).await? {
|
for service in list_services(&mut client).await? {
|
||||||
if service == "grpc.reflection.v1alpha.ServerReflection" {
|
if service == "grpc.reflection.v1alpha.ServerReflection" {
|
||||||
@@ -114,21 +107,8 @@ pub async fn fill_pool_from_reflection(uri: &Uri) -> Result<DescriptorPool, Stri
|
|||||||
Ok(pool)
|
Ok(pool)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_transport() -> Client<HttpsConnector<HttpConnector>, BoxBody> {
|
async fn list_services(client: &mut AutoReflectionClient) -> Result<Vec<String>, String> {
|
||||||
let connector = HttpsConnectorBuilder::new().with_native_roots();
|
let response = client.send_reflection_request(MessageRequest::ListServices("".into())).await?;
|
||||||
let connector = connector.https_or_http().enable_http2().wrap_connector({
|
|
||||||
let mut http_connector = HttpConnector::new();
|
|
||||||
http_connector.enforce_http(false);
|
|
||||||
http_connector
|
|
||||||
});
|
|
||||||
Client::builder().pool_max_idle_per_host(0).http2_only(true).build(connector)
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn list_services(
|
|
||||||
reflect_client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
|
|
||||||
) -> Result<Vec<String>, String> {
|
|
||||||
let response =
|
|
||||||
send_reflection_request(reflect_client, MessageRequest::ListServices("".into())).await?;
|
|
||||||
|
|
||||||
let list_services_response = match response {
|
let list_services_response = match response {
|
||||||
MessageResponse::ListServicesResponse(resp) => resp,
|
MessageResponse::ListServicesResponse(resp) => resp,
|
||||||
@@ -141,13 +121,11 @@ async fn list_services(
|
|||||||
async fn file_descriptor_set_from_service_name(
|
async fn file_descriptor_set_from_service_name(
|
||||||
service_name: &str,
|
service_name: &str,
|
||||||
pool: &mut DescriptorPool,
|
pool: &mut DescriptorPool,
|
||||||
client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
|
client: &mut AutoReflectionClient,
|
||||||
) {
|
) {
|
||||||
let response = match send_reflection_request(
|
let response = match client
|
||||||
client,
|
.send_reflection_request(MessageRequest::FileContainingSymbol(service_name.into()))
|
||||||
MessageRequest::FileContainingSymbol(service_name.into()),
|
.await
|
||||||
)
|
|
||||||
.await
|
|
||||||
{
|
{
|
||||||
Ok(resp) => resp,
|
Ok(resp) => resp,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -169,7 +147,7 @@ async fn file_descriptor_set_from_service_name(
|
|||||||
async fn add_file_descriptors_to_pool(
|
async fn add_file_descriptors_to_pool(
|
||||||
fds: Vec<Vec<u8>>,
|
fds: Vec<Vec<u8>>,
|
||||||
pool: &mut DescriptorPool,
|
pool: &mut DescriptorPool,
|
||||||
client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
|
client: &mut AutoReflectionClient,
|
||||||
) {
|
) {
|
||||||
let mut topo_sort = topology::SimpleTopoSort::new();
|
let mut topo_sort = topology::SimpleTopoSort::new();
|
||||||
let mut fd_mapping = std::collections::HashMap::with_capacity(fds.len());
|
let mut fd_mapping = std::collections::HashMap::with_capacity(fds.len());
|
||||||
@@ -198,15 +176,15 @@ async fn add_file_descriptors_to_pool(
|
|||||||
async fn file_descriptor_set_by_filename(
|
async fn file_descriptor_set_by_filename(
|
||||||
filename: &str,
|
filename: &str,
|
||||||
pool: &mut DescriptorPool,
|
pool: &mut DescriptorPool,
|
||||||
client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
|
client: &mut AutoReflectionClient,
|
||||||
) {
|
) {
|
||||||
// We already fetched this file
|
// We already fetched this file
|
||||||
if let Some(_) = pool.get_file_by_name(filename) {
|
if let Some(_) = pool.get_file_by_name(filename) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let response =
|
let msg = MessageRequest::FileByFilename(filename.into());
|
||||||
send_reflection_request(client, MessageRequest::FileByFilename(filename.into())).await;
|
let response = client.send_reflection_request(msg).await;
|
||||||
let file_descriptor_response = match response {
|
let file_descriptor_response = match response {
|
||||||
Ok(MessageResponse::FileDescriptorResponse(resp)) => resp,
|
Ok(MessageResponse::FileDescriptorResponse(resp)) => resp,
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
@@ -222,35 +200,6 @@ async fn file_descriptor_set_by_filename(
|
|||||||
.await;
|
.await;
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn send_reflection_request(
|
|
||||||
client: &mut ServerReflectionClient<Client<HttpsConnector<HttpConnector>, BoxBody>>,
|
|
||||||
message: MessageRequest,
|
|
||||||
) -> Result<MessageResponse, String> {
|
|
||||||
let reflection_request = ServerReflectionRequest {
|
|
||||||
host: "".into(), // Doesn't matter
|
|
||||||
message_request: Some(message),
|
|
||||||
};
|
|
||||||
|
|
||||||
let request = Request::new(tokio_stream::once(reflection_request));
|
|
||||||
|
|
||||||
client
|
|
||||||
.server_reflection_info(request)
|
|
||||||
.await
|
|
||||||
.map_err(|e| match e.code() {
|
|
||||||
tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(),
|
|
||||||
tonic::Code::Unauthenticated => "Authentication failed".to_string(),
|
|
||||||
tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(),
|
|
||||||
_ => e.to_string(),
|
|
||||||
})?
|
|
||||||
.into_inner()
|
|
||||||
.next()
|
|
||||||
.await
|
|
||||||
.expect("steamed response")
|
|
||||||
.map_err(|e| e.to_string())?
|
|
||||||
.message_response
|
|
||||||
.ok_or("No reflection response".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn method_desc_to_path(md: &MethodDescriptor) -> PathAndQuery {
|
pub fn method_desc_to_path(md: &MethodDescriptor) -> PathAndQuery {
|
||||||
let full_name = md.full_name();
|
let full_name = md.full_name();
|
||||||
let (namespace, method_name) = full_name
|
let (namespace, method_name) = full_name
|
||||||
@@ -292,8 +241,8 @@ mod topology {
|
|||||||
where
|
where
|
||||||
T: Eq + std::hash::Hash + Clone,
|
T: Eq + std::hash::Hash + Clone,
|
||||||
{
|
{
|
||||||
type IntoIter = SimpleTopoSortIter<T>;
|
|
||||||
type Item = <SimpleTopoSortIter<T> as Iterator>::Item;
|
type Item = <SimpleTopoSortIter<T> as Iterator>::Item;
|
||||||
|
type IntoIter = SimpleTopoSortIter<T>;
|
||||||
|
|
||||||
fn into_iter(self) -> Self::IntoIter {
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
SimpleTopoSortIter::new(self)
|
SimpleTopoSortIter::new(self)
|
||||||
19
src-tauri/yaak-grpc/src/transport.rs
Normal file
19
src-tauri/yaak-grpc/src/transport.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder};
|
||||||
|
use hyper_util::client::legacy::Client;
|
||||||
|
use hyper_util::client::legacy::connect::HttpConnector;
|
||||||
|
use hyper_util::rt::TokioExecutor;
|
||||||
|
use tonic::body::BoxBody;
|
||||||
|
|
||||||
|
pub(crate) fn get_transport() -> Client<HttpsConnector<HttpConnector>, BoxBody> {
|
||||||
|
let connector = HttpsConnectorBuilder::new().with_platform_verifier();
|
||||||
|
let connector = connector.https_or_http().enable_http2().wrap_connector({
|
||||||
|
let mut http_connector = HttpConnector::new();
|
||||||
|
http_connector.enforce_http(false);
|
||||||
|
http_connector
|
||||||
|
});
|
||||||
|
Client::builder(TokioExecutor::new())
|
||||||
|
.pool_max_idle_per_host(0)
|
||||||
|
.http2_only(true)
|
||||||
|
.build(connector)
|
||||||
|
}
|
||||||
|
|
||||||
@@ -26,13 +26,13 @@ export type GrpcEvent = { model: "grpc_event", id: string, createdAt: string, up
|
|||||||
|
|
||||||
export type GrpcEventType = "info" | "error" | "client_message" | "server_message" | "connection_start" | "connection_end";
|
export type GrpcEventType = "info" | "error" | "client_message" | "server_message" | "connection_start" | "connection_end";
|
||||||
|
|
||||||
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, };
|
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
||||||
|
|
||||||
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
||||||
|
|
||||||
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
|
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
|
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
|
||||||
|
|
||||||
@@ -40,7 +40,7 @@ export type HttpResponseHeader = { name: string, value: string, };
|
|||||||
|
|
||||||
export type HttpResponseState = "initialized" | "connected" | "closed";
|
export type HttpResponseState = "initialized" | "connected" | "closed";
|
||||||
|
|
||||||
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
|
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type KeyValue = { model: "key_value", createdAt: string, updatedAt: string, key: string, namespace: string, value: string, };
|
export type KeyValue = { model: "key_value", createdAt: string, updatedAt: string, key: string, namespace: string, value: string, };
|
||||||
|
|
||||||
|
|||||||
@@ -446,7 +446,8 @@ pub struct HttpRequestHeader {
|
|||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub id: String,
|
#[ts(optional, as = "Option<String>")]
|
||||||
|
pub id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
@@ -458,7 +459,8 @@ pub struct HttpUrlParameter {
|
|||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub id: String,
|
#[ts(optional, as = "Option<String>")]
|
||||||
|
pub id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
@@ -664,7 +666,8 @@ pub struct GrpcMetadataEntry {
|
|||||||
pub enabled: bool,
|
pub enabled: bool,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
pub id: String,
|
#[ts(optional, as = "Option<String>")]
|
||||||
|
pub id: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ serde = { version = "1.0.198", features = ["derive"] }
|
|||||||
serde_json = "1.0.113"
|
serde_json = "1.0.113"
|
||||||
tauri = { workspace = true }
|
tauri = { workspace = true }
|
||||||
tauri-plugin-shell = { workspace = true }
|
tauri-plugin-shell = { workspace = true }
|
||||||
tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "process"] }
|
tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread", "process"] }
|
||||||
tonic = "0.12.1"
|
tonic = { version = "0.12.3"}
|
||||||
ts-rs = "10.0.0"
|
ts-rs = { workspace = true }
|
||||||
thiserror = "2.0.7"
|
thiserror = "2.0.7"
|
||||||
yaak-models = { workspace = true }
|
yaak-models = { workspace = true }
|
||||||
regex = "1.10.6"
|
regex = "1.10.6"
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ export type BootRequest = { dir: string, watch: boolean, };
|
|||||||
|
|
||||||
export type BootResponse = { name: string, version: string, capabilities: Array<string>, };
|
export type BootResponse = { name: string, version: string, capabilities: Array<string>, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationRequest = { config: { [key in string]?: JsonValue }, method: string, url: string, headers: Array<HttpHeader>, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationResponse = { url: string, headers: Array<HttpHeader>, };
|
||||||
|
|
||||||
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
|
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
export type CallHttpRequestActionRequest = { key: string, pluginRefId: string, args: CallHttpRequestActionArgs, };
|
export type CallHttpRequestActionRequest = { key: string, pluginRefId: string, args: CallHttpRequestActionArgs, };
|
||||||
@@ -25,10 +29,18 @@ export type Color = "custom" | "default" | "primary" | "secondary" | "info" | "s
|
|||||||
|
|
||||||
export type CopyTextRequest = { text: string, };
|
export type CopyTextRequest = { text: string, };
|
||||||
|
|
||||||
|
export type EmptyPayload = {};
|
||||||
|
|
||||||
export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
|
export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
export type ExportHttpRequestResponse = { content: string, };
|
export type ExportHttpRequestResponse = { content: string, };
|
||||||
|
|
||||||
|
export type FileFilter = { name: string,
|
||||||
|
/**
|
||||||
|
* File extensions to require
|
||||||
|
*/
|
||||||
|
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, };
|
||||||
@@ -37,6 +49,112 @@ export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
|||||||
|
|
||||||
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
||||||
|
|
||||||
|
export type FormInput = { "type": "text" } & FormInputText | { "type": "select" } & FormInputSelect | { "type": "checkbox" } & FormInputCheckbox | { "type": "file" } & FormInputFile | { "type": "http_request" } & FormInputHttpRequest;
|
||||||
|
|
||||||
|
export type FormInputBase = { name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputCheckbox = { name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputFile = {
|
||||||
|
/**
|
||||||
|
* The title of the file selection window
|
||||||
|
*/
|
||||||
|
title: string,
|
||||||
|
/**
|
||||||
|
* Allow selecting multiple files
|
||||||
|
*/
|
||||||
|
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<FileFilter>, name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputHttpRequest = { name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputSelect = {
|
||||||
|
/**
|
||||||
|
* The options that will be available in the select input
|
||||||
|
*/
|
||||||
|
options: Array<FormInputSelectOption>, name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type FormInputSelectOption = { name: string, value: string, };
|
||||||
|
|
||||||
|
export type FormInputText = {
|
||||||
|
/**
|
||||||
|
* Placeholder for the text input
|
||||||
|
*/
|
||||||
|
placeholder?: string | null, name: string,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, };
|
||||||
|
|
||||||
|
export type GetHttpAuthenticationResponse = { name: string, pluginName: string, config: Array<FormInput>, };
|
||||||
|
|
||||||
export type GetHttpRequestActionsRequest = Record<string, never>;
|
export type GetHttpRequestActionsRequest = Record<string, never>;
|
||||||
|
|
||||||
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
|
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
|
||||||
@@ -47,6 +165,8 @@ export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, };
|
|||||||
|
|
||||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||||
|
|
||||||
|
export type HttpHeader = { name: string, value: string, };
|
||||||
|
|
||||||
export type HttpRequestAction = { key: string, label: string, icon?: Icon, };
|
export type HttpRequestAction = { key: string, label: string, icon?: Icon, };
|
||||||
|
|
||||||
export type Icon = "copy" | "info" | "check_circle" | "alert_triangle" | "_unknown";
|
export type Icon = "copy" | "info" | "check_circle" | "alert_triangle" | "_unknown";
|
||||||
@@ -59,13 +179,7 @@ export type ImportResponse = { resources: ImportResources, };
|
|||||||
|
|
||||||
export type InternalEvent = { id: string, pluginRefId: string, replyId: string | null, payload: InternalEventPayload, windowContext: WindowContext, };
|
export type InternalEvent = { id: string, pluginRefId: string, replyId: string | null, payload: InternalEventPayload, windowContext: WindowContext, };
|
||||||
|
|
||||||
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } | { "type": "reload_response" } | { "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": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "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": "empty_response" };
|
export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } & EmptyPayload | { "type": "reload_response" } & EmptyPayload | { "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": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "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_request" } & EmptyPayload | { "type": "get_http_authentication_response" } & GetHttpAuthenticationResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "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": "empty_response" } & EmptyPayload;
|
||||||
|
|
||||||
export type OpenFileFilter = { name: string,
|
|
||||||
/**
|
|
||||||
* File extensions to require
|
|
||||||
*/
|
|
||||||
extensions: Array<string>, };
|
|
||||||
|
|
||||||
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string,
|
||||||
/**
|
/**
|
||||||
@@ -100,135 +214,7 @@ export type TemplateFunction = { name: string, description?: string,
|
|||||||
* Also support alternative names. This is useful for not breaking existing
|
* Also support alternative names. This is useful for not breaking existing
|
||||||
* tags when changing the `name` property
|
* tags when changing the `name` property
|
||||||
*/
|
*/
|
||||||
aliases?: Array<string>, args: Array<TemplateFunctionArg>, };
|
aliases?: Array<string>, args: Array<FormInput>, };
|
||||||
|
|
||||||
export type TemplateFunctionArg = { "type": "text" } & TemplateFunctionTextArg | { "type": "select" } & TemplateFunctionSelectArg | { "type": "checkbox" } & TemplateFunctionCheckboxArg | { "type": "http_request" } & TemplateFunctionHttpRequestArg | { "type": "file" } & TemplateFunctionFileArg;
|
|
||||||
|
|
||||||
export type TemplateFunctionBaseArg = {
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type TemplateFunctionCheckboxArg = {
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type TemplateFunctionFileArg = {
|
|
||||||
/**
|
|
||||||
* The title of the file selection window
|
|
||||||
*/
|
|
||||||
title: string,
|
|
||||||
/**
|
|
||||||
* Allow selecting multiple files
|
|
||||||
*/
|
|
||||||
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<OpenFileFilter>,
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type TemplateFunctionHttpRequestArg = {
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type TemplateFunctionSelectArg = {
|
|
||||||
/**
|
|
||||||
* The options that will be available in the select input
|
|
||||||
*/
|
|
||||||
options: Array<TemplateFunctionSelectOption>,
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type TemplateFunctionSelectOption = { label: string, value: string, };
|
|
||||||
|
|
||||||
export type TemplateFunctionTextArg = {
|
|
||||||
/**
|
|
||||||
* Placeholder for the text input
|
|
||||||
*/
|
|
||||||
placeholder?: string,
|
|
||||||
/**
|
|
||||||
* The name of the argument. Should be `camelCase` format
|
|
||||||
*/
|
|
||||||
name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
||||||
|
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ export type EnvironmentVariable = { enabled?: boolean, name: string, value: stri
|
|||||||
|
|
||||||
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
|
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
|
||||||
|
|
||||||
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, };
|
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
||||||
|
|
||||||
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
||||||
|
|
||||||
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
|
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
|
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
|
||||||
|
|
||||||
@@ -20,6 +20,6 @@ export type HttpResponseHeader = { name: string, value: string, };
|
|||||||
|
|
||||||
export type HttpResponseState = "initialized" | "connected" | "closed";
|
export type HttpResponseState = "initialized" | "connected" | "closed";
|
||||||
|
|
||||||
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
|
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
|
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };
|
||||||
|
|||||||
@@ -39,8 +39,8 @@ pub enum InternalEventPayload {
|
|||||||
BootRequest(BootRequest),
|
BootRequest(BootRequest),
|
||||||
BootResponse(BootResponse),
|
BootResponse(BootResponse),
|
||||||
|
|
||||||
ReloadRequest,
|
ReloadRequest(EmptyPayload),
|
||||||
ReloadResponse,
|
ReloadResponse(EmptyPayload),
|
||||||
|
|
||||||
TerminateRequest,
|
TerminateRequest,
|
||||||
TerminateResponse,
|
TerminateResponse,
|
||||||
@@ -57,15 +57,22 @@ pub enum InternalEventPayload {
|
|||||||
SendHttpRequestRequest(SendHttpRequestRequest),
|
SendHttpRequestRequest(SendHttpRequestRequest),
|
||||||
SendHttpRequestResponse(SendHttpRequestResponse),
|
SendHttpRequestResponse(SendHttpRequestResponse),
|
||||||
|
|
||||||
GetHttpRequestActionsRequest(GetHttpRequestActionsRequest),
|
// Request Actions
|
||||||
|
GetHttpRequestActionsRequest(EmptyPayload),
|
||||||
GetHttpRequestActionsResponse(GetHttpRequestActionsResponse),
|
GetHttpRequestActionsResponse(GetHttpRequestActionsResponse),
|
||||||
CallHttpRequestActionRequest(CallHttpRequestActionRequest),
|
CallHttpRequestActionRequest(CallHttpRequestActionRequest),
|
||||||
|
|
||||||
|
// Template Functions
|
||||||
GetTemplateFunctionsRequest,
|
GetTemplateFunctionsRequest,
|
||||||
GetTemplateFunctionsResponse(GetTemplateFunctionsResponse),
|
GetTemplateFunctionsResponse(GetTemplateFunctionsResponse),
|
||||||
CallTemplateFunctionRequest(CallTemplateFunctionRequest),
|
CallTemplateFunctionRequest(CallTemplateFunctionRequest),
|
||||||
CallTemplateFunctionResponse(CallTemplateFunctionResponse),
|
CallTemplateFunctionResponse(CallTemplateFunctionResponse),
|
||||||
|
|
||||||
|
GetHttpAuthenticationRequest(EmptyPayload),
|
||||||
|
GetHttpAuthenticationResponse(GetHttpAuthenticationResponse),
|
||||||
|
CallHttpAuthenticationRequest(CallHttpAuthenticationRequest),
|
||||||
|
CallHttpAuthenticationResponse(CallHttpAuthenticationResponse),
|
||||||
|
|
||||||
CopyTextRequest(CopyTextRequest),
|
CopyTextRequest(CopyTextRequest),
|
||||||
|
|
||||||
RenderHttpRequestRequest(RenderHttpRequestRequest),
|
RenderHttpRequestRequest(RenderHttpRequestRequest),
|
||||||
@@ -87,9 +94,14 @@ pub enum InternalEventPayload {
|
|||||||
|
|
||||||
/// Returned when a plugin doesn't get run, just so the server
|
/// Returned when a plugin doesn't get run, just so the server
|
||||||
/// has something to listen for
|
/// has something to listen for
|
||||||
EmptyResponse,
|
EmptyResponse(EmptyPayload),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default)]
|
||||||
|
#[ts(export, type = "{}", export_to = "events.ts")]
|
||||||
|
pub struct EmptyPayload {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
@@ -281,6 +293,41 @@ pub enum Icon {
|
|||||||
_Unknown(String),
|
_Unknown(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "events.ts")]
|
||||||
|
pub struct GetHttpAuthenticationResponse {
|
||||||
|
pub name: String,
|
||||||
|
pub plugin_name: String,
|
||||||
|
pub config: Vec<FormInput>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "events.ts")]
|
||||||
|
pub struct CallHttpAuthenticationRequest {
|
||||||
|
pub config: serde_json::Map<String, serde_json::Value>,
|
||||||
|
pub method: String,
|
||||||
|
pub url: String,
|
||||||
|
pub headers: Vec<HttpHeader>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "events.ts")]
|
||||||
|
pub struct HttpHeader {
|
||||||
|
pub name: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "events.ts")]
|
||||||
|
pub struct CallHttpAuthenticationResponse {
|
||||||
|
pub url: String,
|
||||||
|
pub headers: Vec<HttpHeader>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
@@ -301,25 +348,24 @@ pub struct TemplateFunction {
|
|||||||
/// tags when changing the `name` property
|
/// tags when changing the `name` property
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub aliases: Option<Vec<String>>,
|
pub aliases: Option<Vec<String>>,
|
||||||
pub args: Vec<TemplateFunctionArg>,
|
pub args: Vec<FormInput>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case", tag = "type")]
|
#[serde(rename_all = "snake_case", tag = "type")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
pub enum TemplateFunctionArg {
|
pub enum FormInput {
|
||||||
Text(TemplateFunctionTextArg),
|
Text(FormInputText),
|
||||||
Select(TemplateFunctionSelectArg),
|
Select(FormInputSelect),
|
||||||
Checkbox(TemplateFunctionCheckboxArg),
|
Checkbox(FormInputCheckbox),
|
||||||
HttpRequest(TemplateFunctionHttpRequestArg),
|
File(FormInputFile),
|
||||||
File(TemplateFunctionFileArg),
|
HttpRequest(FormInputHttpRequest),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
pub struct TemplateFunctionBaseArg {
|
pub struct FormInputBase {
|
||||||
/// The name of the argument. Should be `camelCase` format
|
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
/// Whether the user must fill in the argument
|
/// Whether the user must fill in the argument
|
||||||
@@ -338,29 +384,29 @@ pub struct TemplateFunctionBaseArg {
|
|||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
pub struct TemplateFunctionTextArg {
|
pub struct FormInputText {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: TemplateFunctionBaseArg,
|
pub base: FormInputBase,
|
||||||
|
|
||||||
/// Placeholder for the text input
|
/// Placeholder for the text input
|
||||||
#[ts(optional)]
|
#[ts(optional = nullable)]
|
||||||
pub placeholder: Option<String>,
|
pub placeholder: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
pub struct TemplateFunctionHttpRequestArg {
|
pub struct FormInputHttpRequest {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: TemplateFunctionBaseArg,
|
pub base: FormInputBase,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
pub struct TemplateFunctionFileArg {
|
pub struct FormInputFile {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: TemplateFunctionBaseArg,
|
pub base: FormInputBase,
|
||||||
|
|
||||||
/// The title of the file selection window
|
/// The title of the file selection window
|
||||||
pub title: String,
|
pub title: String,
|
||||||
@@ -379,13 +425,13 @@ pub struct TemplateFunctionFileArg {
|
|||||||
|
|
||||||
// Specify to only allow selection of certain file extensions
|
// Specify to only allow selection of certain file extensions
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub filters: Option<Vec<OpenFileFilter>>,
|
pub filters: Option<Vec<FileFilter>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
pub struct OpenFileFilter {
|
pub struct FileFilter {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// File extensions to require
|
/// File extensions to require
|
||||||
pub extensions: Vec<String>,
|
pub extensions: Vec<String>,
|
||||||
@@ -394,27 +440,27 @@ pub struct OpenFileFilter {
|
|||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
pub struct TemplateFunctionSelectArg {
|
pub struct FormInputSelect {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: TemplateFunctionBaseArg,
|
pub base: FormInputBase,
|
||||||
|
|
||||||
/// The options that will be available in the select input
|
/// The options that will be available in the select input
|
||||||
pub options: Vec<TemplateFunctionSelectOption>,
|
pub options: Vec<FormInputSelectOption>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
pub struct TemplateFunctionCheckboxArg {
|
pub struct FormInputCheckbox {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: TemplateFunctionBaseArg,
|
pub base: FormInputBase,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "events.ts")]
|
||||||
pub struct TemplateFunctionSelectOption {
|
pub struct FormInputSelectOption {
|
||||||
pub label: String,
|
pub name: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
use crate::error::Error::{ClientNotInitializedErr, PluginErr, PluginNotFoundErr, UnknownEventErr};
|
use crate::error::Error::{ClientNotInitializedErr, PluginErr, PluginNotFoundErr, UnknownEventErr};
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::events::{
|
use crate::events::{
|
||||||
BootRequest, CallHttpRequestActionRequest, CallTemplateFunctionArgs,
|
BootRequest, CallHttpAuthenticationRequest, CallHttpAuthenticationResponse,
|
||||||
CallTemplateFunctionRequest, CallTemplateFunctionResponse, FilterRequest, FilterResponse,
|
CallHttpRequestActionRequest, CallTemplateFunctionArgs, CallTemplateFunctionRequest,
|
||||||
GetHttpRequestActionsRequest, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse,
|
CallTemplateFunctionResponse, EmptyPayload, FilterRequest, FilterResponse,
|
||||||
|
GetHttpAuthenticationResponse, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse,
|
||||||
ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, RenderPurpose,
|
ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, RenderPurpose,
|
||||||
WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
@@ -402,9 +403,7 @@ impl PluginManager {
|
|||||||
let reply_events = self
|
let reply_events = self
|
||||||
.send_and_wait(
|
.send_and_wait(
|
||||||
WindowContext::from_window(window),
|
WindowContext::from_window(window),
|
||||||
&InternalEventPayload::GetHttpRequestActionsRequest(
|
&InternalEventPayload::GetHttpRequestActionsRequest(EmptyPayload {}),
|
||||||
GetHttpRequestActionsRequest {},
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -460,6 +459,54 @@ impl PluginManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_http_authentication<R: Runtime>(
|
||||||
|
&self,
|
||||||
|
window: &WebviewWindow<R>,
|
||||||
|
) -> Result<Vec<GetHttpAuthenticationResponse>> {
|
||||||
|
let window_context = WindowContext::from_window(window);
|
||||||
|
let reply_events = self
|
||||||
|
.send_and_wait(
|
||||||
|
window_context,
|
||||||
|
&InternalEventPayload::GetHttpAuthenticationRequest(EmptyPayload {}),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let mut results = Vec::new();
|
||||||
|
for event in reply_events {
|
||||||
|
if let InternalEventPayload::GetHttpAuthenticationResponse(resp) = event.payload {
|
||||||
|
results.push(resp.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(results)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn call_http_authentication<R: Runtime>(
|
||||||
|
&self,
|
||||||
|
window: &WebviewWindow<R>,
|
||||||
|
plugin_name: &str,
|
||||||
|
req: CallHttpAuthenticationRequest,
|
||||||
|
) -> Result<CallHttpAuthenticationResponse> {
|
||||||
|
let plugin = self
|
||||||
|
.get_plugin_by_name(plugin_name)
|
||||||
|
.await
|
||||||
|
.ok_or(PluginNotFoundErr(plugin_name.to_string()))?;
|
||||||
|
let event = self
|
||||||
|
.send_to_plugin_and_wait(
|
||||||
|
WindowContext::from_window(window),
|
||||||
|
&plugin,
|
||||||
|
&InternalEventPayload::CallHttpAuthenticationRequest(req),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
match event.payload {
|
||||||
|
InternalEventPayload::CallHttpAuthenticationResponse(resp) => Ok(resp),
|
||||||
|
InternalEventPayload::EmptyResponse(_) => {
|
||||||
|
Err(PluginErr("Auth plugin returned empty".to_string()))
|
||||||
|
}
|
||||||
|
e => Err(PluginErr(format!("Auth plugin returned invalid event {:?}", e))),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn call_template_function(
|
pub async fn call_template_function(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: WindowContext,
|
||||||
@@ -552,7 +599,7 @@ impl PluginManager {
|
|||||||
|
|
||||||
match event.payload {
|
match event.payload {
|
||||||
InternalEventPayload::FilterResponse(resp) => Ok(resp),
|
InternalEventPayload::FilterResponse(resp) => Ok(resp),
|
||||||
InternalEventPayload::EmptyResponse => {
|
InternalEventPayload::EmptyResponse(_) => {
|
||||||
Err(PluginErr("Filter returned empty".to_string()))
|
Err(PluginErr("Filter returned empty".to_string()))
|
||||||
}
|
}
|
||||||
e => Err(PluginErr(format!("Export returned invalid event {:?}", e))),
|
e => Err(PluginErr(format!("Export returned invalid event {:?}", e))),
|
||||||
|
|||||||
@@ -6,15 +6,15 @@ export type EnvironmentVariable = { enabled?: boolean, name: string, value: stri
|
|||||||
|
|
||||||
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
|
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, };
|
||||||
|
|
||||||
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, };
|
export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<GrpcMetadataEntry>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
|
||||||
|
|
||||||
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
|
||||||
|
|
||||||
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, };
|
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, };
|
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, };
|
||||||
|
|
||||||
export type SyncModel = { "type": "workspace" } & Workspace | { "type": "environment" } & Environment | { "type": "folder" } & Folder | { "type": "http_request" } & HttpRequest | { "type": "grpc_request" } & GrpcRequest;
|
export type SyncModel = { "type": "workspace" } & Workspace | { "type": "environment" } & Environment | { "type": "folder" } & Folder | { "type": "http_request" } & HttpRequest | { "type": "grpc_request" } & GrpcRequest;
|
||||||
|
|
||||||
|
|||||||
251
src-web/components/DynamicForm.tsx
Normal file
251
src-web/components/DynamicForm.tsx
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
import type { Folder, HttpRequest } from '@yaakapp-internal/models';
|
||||||
|
import type {
|
||||||
|
FormInput,
|
||||||
|
FormInputCheckbox,
|
||||||
|
FormInputFile,
|
||||||
|
FormInputHttpRequest,
|
||||||
|
FormInputSelect,
|
||||||
|
FormInputText,
|
||||||
|
} from '@yaakapp-internal/plugins';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||||
|
import { useFolders } from '../hooks/useFolders';
|
||||||
|
import { useHttpRequests } from '../hooks/useHttpRequests';
|
||||||
|
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||||
|
import { Checkbox } from './core/Checkbox';
|
||||||
|
import { Input } from './core/Input';
|
||||||
|
import { Select } from './core/Select';
|
||||||
|
import { SelectFile } from './SelectFile';
|
||||||
|
|
||||||
|
// eslint-disable-next-line react-refresh/only-export-components
|
||||||
|
export const DYNAMIC_FORM_NULL_ARG = '__NULL__';
|
||||||
|
|
||||||
|
export function DynamicForm<T extends Record<string, string | boolean>>({
|
||||||
|
config,
|
||||||
|
data,
|
||||||
|
onChange,
|
||||||
|
useTemplating,
|
||||||
|
stateKey,
|
||||||
|
}: {
|
||||||
|
config: FormInput[];
|
||||||
|
onChange: (value: T) => void;
|
||||||
|
data: T;
|
||||||
|
useTemplating?: boolean;
|
||||||
|
stateKey: string;
|
||||||
|
}) {
|
||||||
|
const setDataAttr = useCallback(
|
||||||
|
(name: string, value: string | boolean | null) => {
|
||||||
|
onChange({ ...data, [name]: value == null ? '__NULL__' : value });
|
||||||
|
},
|
||||||
|
[data, onChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{config.map((a, i) => {
|
||||||
|
switch (a.type) {
|
||||||
|
case 'select':
|
||||||
|
return (
|
||||||
|
<SelectArg
|
||||||
|
key={i + stateKey}
|
||||||
|
arg={a}
|
||||||
|
onChange={(v) => setDataAttr(a.name, v)}
|
||||||
|
value={data[a.name] ? String(data[a.name]) : '__ERROR__'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'text':
|
||||||
|
return (
|
||||||
|
<TextArg
|
||||||
|
key={i}
|
||||||
|
stateKey={stateKey}
|
||||||
|
arg={a}
|
||||||
|
useTemplating={useTemplating || false}
|
||||||
|
onChange={(v) => setDataAttr(a.name, v)}
|
||||||
|
value={data[a.name] ? String(data[a.name]) : ''}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'checkbox':
|
||||||
|
return (
|
||||||
|
<CheckboxArg
|
||||||
|
key={i + stateKey}
|
||||||
|
arg={a}
|
||||||
|
onChange={(v) => setDataAttr(a.name, v)}
|
||||||
|
value={data[a.name] !== undefined ? data[a.name] === true : false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'http_request':
|
||||||
|
return (
|
||||||
|
<HttpRequestArg
|
||||||
|
key={i + stateKey}
|
||||||
|
arg={a}
|
||||||
|
onChange={(v) => setDataAttr(a.name, v)}
|
||||||
|
value={data[a.name] ? String(data[a.name]) : '__ERROR__'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
case 'file':
|
||||||
|
return (
|
||||||
|
<FileArg
|
||||||
|
key={i + stateKey}
|
||||||
|
arg={a}
|
||||||
|
onChange={(v) => setDataAttr(a.name, v)}
|
||||||
|
filePath={data[a.name] ? String(data[a.name]) : '__ERROR__'}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TextArg({
|
||||||
|
arg,
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
useTemplating,
|
||||||
|
stateKey,
|
||||||
|
}: {
|
||||||
|
arg: FormInputText;
|
||||||
|
value: string;
|
||||||
|
onChange: (v: string) => void;
|
||||||
|
useTemplating: boolean;
|
||||||
|
stateKey: string;
|
||||||
|
}) {
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(value: string) => {
|
||||||
|
onChange(value === '' ? DYNAMIC_FORM_NULL_ARG : value);
|
||||||
|
},
|
||||||
|
[onChange],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Input
|
||||||
|
name={arg.name}
|
||||||
|
onChange={handleChange}
|
||||||
|
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? '' : value}
|
||||||
|
require={!arg.optional}
|
||||||
|
label={
|
||||||
|
<>
|
||||||
|
{arg.label ?? arg.name}
|
||||||
|
{arg.optional && <span> (optional)</span>}
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
hideLabel={arg.label == null}
|
||||||
|
placeholder={arg.placeholder ?? arg.defaultValue ?? ''}
|
||||||
|
useTemplating={useTemplating}
|
||||||
|
stateKey={stateKey}
|
||||||
|
forceUpdateKey={stateKey}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SelectArg({
|
||||||
|
arg,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
arg: FormInputSelect;
|
||||||
|
value: string;
|
||||||
|
onChange: (v: string) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
label={arg.label ?? arg.name}
|
||||||
|
name={arg.name}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
options={[
|
||||||
|
...arg.options.map((a) => ({
|
||||||
|
label: a.name + (arg.defaultValue === a.value ? ' (default)' : ''),
|
||||||
|
value: a.value === arg.defaultValue ? DYNAMIC_FORM_NULL_ARG : a.value,
|
||||||
|
})),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FileArg({
|
||||||
|
arg,
|
||||||
|
filePath,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
arg: FormInputFile;
|
||||||
|
filePath: string;
|
||||||
|
onChange: (v: string | null) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<SelectFile
|
||||||
|
onChange={({ filePath }) => onChange(filePath)}
|
||||||
|
filePath={filePath === '__NULL__' ? null : filePath}
|
||||||
|
directory={!!arg.directory}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function HttpRequestArg({
|
||||||
|
arg,
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
}: {
|
||||||
|
arg: FormInputHttpRequest;
|
||||||
|
value: string;
|
||||||
|
onChange: (v: string) => void;
|
||||||
|
}) {
|
||||||
|
const folders = useFolders();
|
||||||
|
const httpRequests = useHttpRequests();
|
||||||
|
const activeRequest = useActiveRequest();
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
label={arg.label ?? arg.name}
|
||||||
|
name={arg.name}
|
||||||
|
onChange={onChange}
|
||||||
|
value={value}
|
||||||
|
options={[
|
||||||
|
...httpRequests.map((r) => {
|
||||||
|
return {
|
||||||
|
label:
|
||||||
|
buildRequestBreadcrumbs(r, folders).join(' / ') +
|
||||||
|
(r.id == activeRequest?.id ? ' (current)' : ''),
|
||||||
|
value: r.id,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRequestBreadcrumbs(request: HttpRequest, folders: Folder[]): string[] {
|
||||||
|
const ancestors: (HttpRequest | Folder)[] = [request];
|
||||||
|
|
||||||
|
const next = () => {
|
||||||
|
const latest = ancestors[0];
|
||||||
|
if (latest == null) return [];
|
||||||
|
|
||||||
|
const parent = folders.find((f) => f.id === latest.folderId);
|
||||||
|
if (parent == null) return;
|
||||||
|
|
||||||
|
ancestors.unshift(parent);
|
||||||
|
next();
|
||||||
|
};
|
||||||
|
next();
|
||||||
|
|
||||||
|
return ancestors.map((a) => (a.model === 'folder' ? a.name : fallbackRequestName(a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
function CheckboxArg({
|
||||||
|
arg,
|
||||||
|
onChange,
|
||||||
|
value,
|
||||||
|
}: {
|
||||||
|
arg: FormInputCheckbox;
|
||||||
|
value: boolean;
|
||||||
|
onChange: (v: boolean) => void;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Checkbox
|
||||||
|
onChange={onChange}
|
||||||
|
checked={value}
|
||||||
|
title={arg.label ?? arg.name}
|
||||||
|
hideLabel={arg.label == null}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import type { ShowToastRequest } from '@yaakapp/api';
|
|||||||
import { useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
import { useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||||
import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast';
|
import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast';
|
||||||
import { useGenerateThemeCss } from '../hooks/useGenerateThemeCss';
|
import { useGenerateThemeCss } from '../hooks/useGenerateThemeCss';
|
||||||
|
import { useSubscribeHttpAuthentication } from '../hooks/useHttpAuthentication';
|
||||||
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent';
|
||||||
import { useNotificationToast } from '../hooks/useNotificationToast';
|
import { useNotificationToast } from '../hooks/useNotificationToast';
|
||||||
import { useSyncFontSizeSetting } from '../hooks/useSyncFontSizeSetting';
|
import { useSyncFontSizeSetting } from '../hooks/useSyncFontSizeSetting';
|
||||||
@@ -24,6 +25,7 @@ export function GlobalHooks() {
|
|||||||
|
|
||||||
useSyncWorkspaceChildModels();
|
useSyncWorkspaceChildModels();
|
||||||
useSubscribeTemplateFunctions();
|
useSubscribeTemplateFunctions();
|
||||||
|
useSubscribeHttpAuthentication();
|
||||||
|
|
||||||
// Other useful things
|
// Other useful things
|
||||||
useNotificationToast();
|
useNotificationToast();
|
||||||
|
|||||||
@@ -6,12 +6,10 @@ import type { CSSProperties } from 'react';
|
|||||||
import React, { useCallback, useMemo, useRef } from 'react';
|
import React, { useCallback, useMemo, useRef } from 'react';
|
||||||
import { useContainerSize } from '../hooks/useContainerQuery';
|
import { useContainerSize } from '../hooks/useContainerQuery';
|
||||||
import type { ReflectResponseService } from '../hooks/useGrpc';
|
import type { ReflectResponseService } from '../hooks/useGrpc';
|
||||||
|
import { useHttpAuthentication } from '../hooks/useHttpAuthentication';
|
||||||
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
||||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||||
import { AUTH_TYPE_BASIC, AUTH_TYPE_BEARER, AUTH_TYPE_NONE } from '../lib/model_util';
|
|
||||||
import { BasicAuth } from './BasicAuth';
|
|
||||||
import { BearerAuth } from './BearerAuth';
|
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import { CountBadge } from './core/CountBadge';
|
import { CountBadge } from './core/CountBadge';
|
||||||
import { Icon } from './core/Icon';
|
import { Icon } from './core/Icon';
|
||||||
@@ -22,8 +20,8 @@ import { RadioDropdown } from './core/RadioDropdown';
|
|||||||
import { HStack, VStack } from './core/Stacks';
|
import { HStack, VStack } from './core/Stacks';
|
||||||
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';
|
||||||
import { EmptyStateText } from './EmptyStateText';
|
|
||||||
import { GrpcEditor } from './GrpcEditor';
|
import { GrpcEditor } from './GrpcEditor';
|
||||||
|
import { HttpAuthenticationEditor } from './HttpAuthenticationEditor';
|
||||||
import { MarkdownEditor } from './MarkdownEditor';
|
import { MarkdownEditor } from './MarkdownEditor';
|
||||||
import { UrlBar } from './UrlBar';
|
import { UrlBar } from './UrlBar';
|
||||||
|
|
||||||
@@ -71,6 +69,7 @@ export function GrpcConnectionSetupPane({
|
|||||||
onSend,
|
onSend,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const updateRequest = useUpdateAnyGrpcRequest();
|
const updateRequest = useUpdateAnyGrpcRequest();
|
||||||
|
const authentication = useHttpAuthentication();
|
||||||
const [activeTabs, setActiveTabs] = useAtom(tabsAtom);
|
const [activeTabs, setActiveTabs] = useAtom(tabsAtom);
|
||||||
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
|
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
|
||||||
|
|
||||||
@@ -136,11 +135,6 @@ export function GrpcConnectionSetupPane({
|
|||||||
|
|
||||||
const tabs: TabItem[] = useMemo(
|
const tabs: TabItem[] = useMemo(
|
||||||
() => [
|
() => [
|
||||||
{
|
|
||||||
value: TAB_DESCRIPTION,
|
|
||||||
label: 'Info',
|
|
||||||
rightSlot: activeRequest.description && <CountBadge count={true} />,
|
|
||||||
},
|
|
||||||
{ value: TAB_MESSAGE, label: 'Message' },
|
{ value: TAB_MESSAGE, label: 'Message' },
|
||||||
{
|
{
|
||||||
value: TAB_AUTH,
|
value: TAB_AUTH,
|
||||||
@@ -148,24 +142,21 @@ export function GrpcConnectionSetupPane({
|
|||||||
options: {
|
options: {
|
||||||
value: activeRequest.authenticationType,
|
value: activeRequest.authenticationType,
|
||||||
items: [
|
items: [
|
||||||
{ label: 'Basic Auth', shortLabel: 'Basic', value: AUTH_TYPE_BASIC },
|
...authentication.map((a) => ({
|
||||||
{ label: 'Bearer Token', shortLabel: 'Bearer', value: AUTH_TYPE_BEARER },
|
label: a.name,
|
||||||
|
value: a.pluginName,
|
||||||
|
})),
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ label: 'No Authentication', shortLabel: 'Auth', value: AUTH_TYPE_NONE },
|
{ label: 'No Authentication', shortLabel: 'Auth', value: null },
|
||||||
],
|
],
|
||||||
onChange: async (authenticationType) => {
|
onChange: (authenticationType) => {
|
||||||
let authentication: GrpcRequest['authentication'] = activeRequest.authentication;
|
let authentication: GrpcRequest['authentication'] = activeRequest.authentication;
|
||||||
if (authenticationType === AUTH_TYPE_BASIC) {
|
if (activeRequest.authenticationType !== authenticationType) {
|
||||||
authentication = {
|
authentication = {
|
||||||
username: authentication.username ?? '',
|
// Reset auth if changing types
|
||||||
password: authentication.password ?? '',
|
|
||||||
};
|
|
||||||
} else if (authenticationType === AUTH_TYPE_BEARER) {
|
|
||||||
authentication = {
|
|
||||||
token: authentication.token ?? '',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
await updateRequest.mutateAsync({
|
updateRequest.mutate({
|
||||||
id: activeRequest.id,
|
id: activeRequest.id,
|
||||||
update: { authenticationType, authentication },
|
update: { authenticationType, authentication },
|
||||||
});
|
});
|
||||||
@@ -173,12 +164,18 @@ export function GrpcConnectionSetupPane({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{ value: TAB_METADATA, label: 'Metadata' },
|
{ value: TAB_METADATA, label: 'Metadata' },
|
||||||
|
{
|
||||||
|
value: TAB_DESCRIPTION,
|
||||||
|
label: 'Info',
|
||||||
|
rightSlot: activeRequest.description && <CountBadge count={true} />,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
activeRequest.authentication,
|
activeRequest.authentication,
|
||||||
activeRequest.authenticationType,
|
activeRequest.authenticationType,
|
||||||
activeRequest.description,
|
activeRequest.description,
|
||||||
activeRequest.id,
|
activeRequest.id,
|
||||||
|
authentication,
|
||||||
updateRequest,
|
updateRequest,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@@ -213,6 +210,7 @@ export function GrpcConnectionSetupPane({
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<UrlBar
|
<UrlBar
|
||||||
|
key={forceUpdateKey}
|
||||||
url={activeRequest.url ?? ''}
|
url={activeRequest.url ?? ''}
|
||||||
method={null}
|
method={null}
|
||||||
submitIcon={null}
|
submitIcon={null}
|
||||||
@@ -321,16 +319,10 @@ export function GrpcConnectionSetupPane({
|
|||||||
protoFiles={protoFiles}
|
protoFiles={protoFiles}
|
||||||
/>
|
/>
|
||||||
</TabContent>
|
</TabContent>
|
||||||
<TabContent value="auth">
|
<TabContent value={TAB_AUTH}>
|
||||||
{activeRequest.authenticationType === AUTH_TYPE_BASIC ? (
|
<HttpAuthenticationEditor request={activeRequest} />
|
||||||
<BasicAuth key={forceUpdateKey} request={activeRequest} />
|
|
||||||
) : activeRequest.authenticationType === AUTH_TYPE_BEARER ? (
|
|
||||||
<BearerAuth key={forceUpdateKey} request={activeRequest} />
|
|
||||||
) : (
|
|
||||||
<EmptyStateText>No Authentication {activeRequest.authenticationType}</EmptyStateText>
|
|
||||||
)}
|
|
||||||
</TabContent>
|
</TabContent>
|
||||||
<TabContent value="metadata">
|
<TabContent value={TAB_METADATA}>
|
||||||
<PairOrBulkEditor
|
<PairOrBulkEditor
|
||||||
preferenceName="grpc_metadata"
|
preferenceName="grpc_metadata"
|
||||||
valueAutocompleteVariables
|
valueAutocompleteVariables
|
||||||
|
|||||||
48
src-web/components/HttpAuthenticationEditor.tsx
Normal file
48
src-web/components/HttpAuthenticationEditor.tsx
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
||||||
|
import React, { useCallback } from 'react';
|
||||||
|
import { useHttpAuthentication } from '../hooks/useHttpAuthentication';
|
||||||
|
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||||
|
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||||
|
import { DynamicForm } from './DynamicForm';
|
||||||
|
import { EmptyStateText } from './EmptyStateText';
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
request: HttpRequest | GrpcRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function HttpAuthenticationEditor({ request }: Props) {
|
||||||
|
const updateHttpRequest = useUpdateAnyHttpRequest();
|
||||||
|
const updateGrpcRequest = useUpdateAnyGrpcRequest();
|
||||||
|
const auths = useHttpAuthentication();
|
||||||
|
const auth = auths.find((a) => a.pluginName === request.authenticationType);
|
||||||
|
|
||||||
|
const handleChange = useCallback(
|
||||||
|
(authentication: Record<string, boolean>) => {
|
||||||
|
if (request.model === 'http_request') {
|
||||||
|
updateHttpRequest.mutate({
|
||||||
|
id: request.id,
|
||||||
|
update: (r) => ({ ...r, authentication }),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
updateGrpcRequest.mutate({
|
||||||
|
id: request.id,
|
||||||
|
update: (r) => ({ ...r, authentication }),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[request.id, request.model, updateGrpcRequest, updateHttpRequest],
|
||||||
|
);
|
||||||
|
|
||||||
|
if (auth == null) {
|
||||||
|
return <EmptyStateText>No Authentication {request.authenticationType}</EmptyStateText>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DynamicForm
|
||||||
|
stateKey={`auth.${request.id}.${request.authenticationType}`}
|
||||||
|
config={auth.config}
|
||||||
|
data={request.authentication}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -32,8 +32,9 @@ export function ImportCurlButton() {
|
|||||||
variant="border"
|
variant="border"
|
||||||
color="success"
|
color="success"
|
||||||
className="rounded-full"
|
className="rounded-full"
|
||||||
leftSlot={<Icon icon="paste" size="sm" />}
|
rightSlot={<Icon icon="import" size="sm" />}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
|
title="Import Curl command from clipboard"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useMemo, useRef } from 'react';
|
import { useMemo, useRef } from 'react';
|
||||||
import { useKeyPressEvent } from 'react-use';
|
|
||||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||||
import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||||
import { grpcRequestsAtom } from '../hooks/useGrpcRequests';
|
import { grpcRequestsAtom } from '../hooks/useGrpcRequests';
|
||||||
import { useHotKey } from '../hooks/useHotKey';
|
import { useHotKey } from '../hooks/useHotKey';
|
||||||
import { httpRequestsAtom } from '../hooks/useHttpRequests';
|
import { httpRequestsAtom } from '../hooks/useHttpRequests';
|
||||||
|
import { useKeyboardEvent } from '../hooks/useKeyboardEvent';
|
||||||
import { useRecentRequests } from '../hooks/useRecentRequests';
|
import { useRecentRequests } from '../hooks/useRecentRequests';
|
||||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||||
import { jotaiStore } from '../lib/jotai';
|
import { jotaiStore } from '../lib/jotai';
|
||||||
@@ -27,16 +27,16 @@ export function RecentRequestsDropdown({ className }: Props) {
|
|||||||
// Handle key-up
|
// Handle key-up
|
||||||
// TODO: Somehow make useHotKey have this functionality. Note: e.key does not work
|
// TODO: Somehow make useHotKey have this functionality. Note: e.key does not work
|
||||||
// on Linux, for example, when Control is mapped to CAPS. This will never fire.
|
// on Linux, for example, when Control is mapped to CAPS. This will never fire.
|
||||||
useKeyPressEvent('Control', undefined, () => {
|
useKeyboardEvent('keyup', 'Control', () => {
|
||||||
if (!dropdownRef.current?.isOpen) return;
|
if (dropdownRef.current?.isOpen) {
|
||||||
dropdownRef.current?.select?.();
|
dropdownRef.current?.select?.();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
useHotKey('request_switcher.prev', () => {
|
useHotKey('request_switcher.prev', () => {
|
||||||
if (!dropdownRef.current?.isOpen) {
|
if (!dropdownRef.current?.isOpen) {
|
||||||
dropdownRef.current?.open();
|
|
||||||
// Select the second because the first is the current request
|
// Select the second because the first is the current request
|
||||||
dropdownRef.current?.next?.(2);
|
dropdownRef.current?.open(1);
|
||||||
} else {
|
} else {
|
||||||
dropdownRef.current?.next?.();
|
dropdownRef.current?.next?.();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -52,7 +52,6 @@ export const RecentResponsesDropdown = function ResponsePane({
|
|||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
leftSlot: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
onSelect: deleteResponse.mutate,
|
onSelect: deleteResponse.mutate,
|
||||||
disabled: activeResponse.state !== 'closed',
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'unpin',
|
key: 'unpin',
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { activeRequestIdAtom } from '../hooks/useActiveRequestId';
|
|||||||
import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse';
|
import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse';
|
||||||
import { useContentTypeFromHeaders } from '../hooks/useContentTypeFromHeaders';
|
import { useContentTypeFromHeaders } from '../hooks/useContentTypeFromHeaders';
|
||||||
import { grpcRequestsAtom } from '../hooks/useGrpcRequests';
|
import { grpcRequestsAtom } from '../hooks/useGrpcRequests';
|
||||||
|
import { useHttpAuthentication } from '../hooks/useHttpAuthentication';
|
||||||
import { httpRequestsAtom } from '../hooks/useHttpRequests';
|
import { httpRequestsAtom } from '../hooks/useHttpRequests';
|
||||||
import { useImportCurl } from '../hooks/useImportCurl';
|
import { useImportCurl } from '../hooks/useImportCurl';
|
||||||
import { useImportQuerystring } from '../hooks/useImportQuerystring';
|
import { useImportQuerystring } from '../hooks/useImportQuerystring';
|
||||||
@@ -23,9 +24,6 @@ import { fallbackRequestName } from '../lib/fallbackRequestName';
|
|||||||
import { tryFormatJson } from '../lib/formatters';
|
import { tryFormatJson } from '../lib/formatters';
|
||||||
import { generateId } from '../lib/generateId';
|
import { generateId } from '../lib/generateId';
|
||||||
import {
|
import {
|
||||||
AUTH_TYPE_BASIC,
|
|
||||||
AUTH_TYPE_BEARER,
|
|
||||||
AUTH_TYPE_NONE,
|
|
||||||
BODY_TYPE_BINARY,
|
BODY_TYPE_BINARY,
|
||||||
BODY_TYPE_FORM_MULTIPART,
|
BODY_TYPE_FORM_MULTIPART,
|
||||||
BODY_TYPE_FORM_URLENCODED,
|
BODY_TYPE_FORM_URLENCODED,
|
||||||
@@ -36,8 +34,6 @@ import {
|
|||||||
BODY_TYPE_XML,
|
BODY_TYPE_XML,
|
||||||
} from '../lib/model_util';
|
} from '../lib/model_util';
|
||||||
import { showToast } from '../lib/toast';
|
import { showToast } from '../lib/toast';
|
||||||
import { BasicAuth } from './BasicAuth';
|
|
||||||
import { BearerAuth } from './BearerAuth';
|
|
||||||
import { BinaryFileEditor } from './BinaryFileEditor';
|
import { BinaryFileEditor } from './BinaryFileEditor';
|
||||||
import { CountBadge } from './core/CountBadge';
|
import { CountBadge } from './core/CountBadge';
|
||||||
import { Editor } from './core/Editor/Editor';
|
import { Editor } from './core/Editor/Editor';
|
||||||
@@ -55,6 +51,7 @@ import { FormMultipartEditor } from './FormMultipartEditor';
|
|||||||
import { FormUrlencodedEditor } from './FormUrlencodedEditor';
|
import { FormUrlencodedEditor } from './FormUrlencodedEditor';
|
||||||
import { GraphQLEditor } from './GraphQLEditor';
|
import { GraphQLEditor } from './GraphQLEditor';
|
||||||
import { HeadersEditor } from './HeadersEditor';
|
import { HeadersEditor } from './HeadersEditor';
|
||||||
|
import { HttpAuthenticationEditor } from './HttpAuthenticationEditor';
|
||||||
import { MarkdownEditor } from './MarkdownEditor';
|
import { MarkdownEditor } from './MarkdownEditor';
|
||||||
import { UrlBar } from './UrlBar';
|
import { UrlBar } from './UrlBar';
|
||||||
import { UrlParametersEditor } from './UrlParameterEditor';
|
import { UrlParametersEditor } from './UrlParameterEditor';
|
||||||
@@ -97,6 +94,7 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
|
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
|
||||||
const [{ urlKey }] = useRequestEditor();
|
const [{ urlKey }] = useRequestEditor();
|
||||||
const contentType = useContentTypeFromHeaders(activeRequest.headers);
|
const contentType = useContentTypeFromHeaders(activeRequest.headers);
|
||||||
|
const authentication = useHttpAuthentication();
|
||||||
|
|
||||||
const handleContentTypeChange = useCallback(
|
const handleContentTypeChange = useCallback(
|
||||||
async (contentType: string | null) => {
|
async (contentType: string | null) => {
|
||||||
@@ -236,21 +234,18 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
options: {
|
options: {
|
||||||
value: activeRequest.authenticationType,
|
value: activeRequest.authenticationType,
|
||||||
items: [
|
items: [
|
||||||
{ label: 'Basic Auth', shortLabel: 'Basic', value: AUTH_TYPE_BASIC },
|
...authentication.map((a) => ({
|
||||||
{ label: 'Bearer Token', shortLabel: 'Bearer', value: AUTH_TYPE_BEARER },
|
label: a.name,
|
||||||
|
value: a.pluginName,
|
||||||
|
})),
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{ label: 'No Authentication', shortLabel: 'Auth', value: AUTH_TYPE_NONE },
|
{ label: 'No Authentication', shortLabel: 'Auth', value: null },
|
||||||
],
|
],
|
||||||
onChange: async (authenticationType) => {
|
onChange: async (authenticationType) => {
|
||||||
let authentication: HttpRequest['authentication'] = activeRequest.authentication;
|
let authentication: HttpRequest['authentication'] = activeRequest.authentication;
|
||||||
if (authenticationType === AUTH_TYPE_BASIC) {
|
if (activeRequest.authenticationType !== authenticationType) {
|
||||||
authentication = {
|
authentication = {
|
||||||
username: authentication.username ?? '',
|
// Reset auth if changing types
|
||||||
password: authentication.password ?? '',
|
|
||||||
};
|
|
||||||
} else if (authenticationType === AUTH_TYPE_BEARER) {
|
|
||||||
authentication = {
|
|
||||||
token: authentication.token ?? '',
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
updateRequest({
|
updateRequest({
|
||||||
@@ -272,6 +267,7 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
activeRequest.headers,
|
activeRequest.headers,
|
||||||
activeRequest.method,
|
activeRequest.method,
|
||||||
activeRequestId,
|
activeRequestId,
|
||||||
|
authentication,
|
||||||
handleContentTypeChange,
|
handleContentTypeChange,
|
||||||
numParams,
|
numParams,
|
||||||
updateRequest,
|
updateRequest,
|
||||||
@@ -384,15 +380,7 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
tabListClassName="mt-2 !mb-1.5"
|
tabListClassName="mt-2 !mb-1.5"
|
||||||
>
|
>
|
||||||
<TabContent value={TAB_AUTH}>
|
<TabContent value={TAB_AUTH}>
|
||||||
{activeRequest.authenticationType === AUTH_TYPE_BASIC ? (
|
<HttpAuthenticationEditor request={activeRequest} />
|
||||||
<BasicAuth key={forceUpdateKey} request={activeRequest} />
|
|
||||||
) : activeRequest.authenticationType === AUTH_TYPE_BEARER ? (
|
|
||||||
<BearerAuth key={forceUpdateKey} request={activeRequest} />
|
|
||||||
) : (
|
|
||||||
<EmptyStateText>
|
|
||||||
No Authentication {activeRequest.authenticationType}
|
|
||||||
</EmptyStateText>
|
|
||||||
)}
|
|
||||||
</TabContent>
|
</TabContent>
|
||||||
<TabContent value={TAB_HEADERS}>
|
<TabContent value={TAB_HEADERS}>
|
||||||
<HeadersEditor
|
<HeadersEditor
|
||||||
|
|||||||
@@ -1,32 +1,14 @@
|
|||||||
import type { Folder, HttpRequest } from '@yaakapp-internal/models';
|
import type { TemplateFunction } from '@yaakapp-internal/plugins';
|
||||||
import type {
|
|
||||||
TemplateFunction,
|
|
||||||
TemplateFunctionArg,
|
|
||||||
TemplateFunctionCheckboxArg,
|
|
||||||
TemplateFunctionFileArg,
|
|
||||||
TemplateFunctionHttpRequestArg,
|
|
||||||
TemplateFunctionSelectArg,
|
|
||||||
TemplateFunctionTextArg,
|
|
||||||
} from '@yaakapp-internal/plugins';
|
|
||||||
import type { FnArg, Tokens } from '@yaakapp-internal/templates';
|
import type { FnArg, Tokens } from '@yaakapp-internal/templates';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useCallback, useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
|
||||||
import { useDebouncedValue } from '../hooks/useDebouncedValue';
|
import { useDebouncedValue } from '../hooks/useDebouncedValue';
|
||||||
import { useFolders } from '../hooks/useFolders';
|
|
||||||
import { useHttpRequests } from '../hooks/useHttpRequests';
|
|
||||||
import { useRenderTemplate } from '../hooks/useRenderTemplate';
|
import { useRenderTemplate } from '../hooks/useRenderTemplate';
|
||||||
import { useTemplateTokensToString } from '../hooks/useTemplateTokensToString';
|
import { useTemplateTokensToString } from '../hooks/useTemplateTokensToString';
|
||||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import { Checkbox } from './core/Checkbox';
|
|
||||||
import { InlineCode } from './core/InlineCode';
|
import { InlineCode } from './core/InlineCode';
|
||||||
import { PlainInput } from './core/PlainInput';
|
|
||||||
import { Select } from './core/Select';
|
|
||||||
import { VStack } from './core/Stacks';
|
import { VStack } from './core/Stacks';
|
||||||
import { SelectFile } from './SelectFile';
|
import { DYNAMIC_FORM_NULL_ARG, DynamicForm } from './DynamicForm';
|
||||||
|
|
||||||
const NULL_ARG = '__NULL__';
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
templateFunction: TemplateFunction;
|
templateFunction: TemplateFunction;
|
||||||
@@ -49,21 +31,17 @@ export function TemplateFunctionDialog({ templateFunction, hide, initialTokens,
|
|||||||
? initialArg?.value.text
|
? initialArg?.value.text
|
||||||
: // TODO: Implement variable-based args
|
: // TODO: Implement variable-based args
|
||||||
'__NULL__';
|
'__NULL__';
|
||||||
initial[arg.name] = initialArgValue ?? NULL_ARG;
|
initial[arg.name] = initialArgValue ?? DYNAMIC_FORM_NULL_ARG;
|
||||||
}
|
}
|
||||||
|
|
||||||
return initial;
|
return initial;
|
||||||
});
|
});
|
||||||
|
|
||||||
const setArgValue = useCallback((name: string, value: string | boolean | null) => {
|
|
||||||
setArgValues((v) => ({ ...v, [name]: value == null ? '__NULL__' : value }));
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const tokens: Tokens = useMemo(() => {
|
const tokens: Tokens = useMemo(() => {
|
||||||
const argTokens: FnArg[] = Object.keys(argValues).map((name) => ({
|
const argTokens: FnArg[] = Object.keys(argValues).map((name) => ({
|
||||||
name,
|
name,
|
||||||
value:
|
value:
|
||||||
argValues[name] === NULL_ARG
|
argValues[name] === DYNAMIC_FORM_NULL_ARG
|
||||||
? { type: 'null' }
|
? { type: 'null' }
|
||||||
: typeof argValues[name] === 'boolean'
|
: typeof argValues[name] === 'boolean'
|
||||||
? { type: 'bool', value: argValues[name] === true }
|
? { type: 'bool', value: argValues[name] === true }
|
||||||
@@ -100,57 +78,12 @@ export function TemplateFunctionDialog({ templateFunction, hide, initialTokens,
|
|||||||
return (
|
return (
|
||||||
<VStack className="pb-3" space={4}>
|
<VStack className="pb-3" space={4}>
|
||||||
<h1 className="font-mono !text-base">{templateFunction.name}(…)</h1>
|
<h1 className="font-mono !text-base">{templateFunction.name}(…)</h1>
|
||||||
<VStack space={2}>
|
<DynamicForm
|
||||||
{templateFunction.args.map((a: TemplateFunctionArg, i: number) => {
|
config={templateFunction.args}
|
||||||
switch (a.type) {
|
data={argValues}
|
||||||
case 'select':
|
onChange={setArgValues}
|
||||||
return (
|
stateKey={`template_function.${templateFunction.name}`}
|
||||||
<SelectArg
|
/>
|
||||||
key={i}
|
|
||||||
arg={a}
|
|
||||||
onChange={(v) => setArgValue(a.name, v)}
|
|
||||||
value={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'text':
|
|
||||||
return (
|
|
||||||
<TextArg
|
|
||||||
key={i}
|
|
||||||
arg={a}
|
|
||||||
onChange={(v) => setArgValue(a.name, v)}
|
|
||||||
value={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'checkbox':
|
|
||||||
return (
|
|
||||||
<CheckboxArg
|
|
||||||
key={i}
|
|
||||||
arg={a}
|
|
||||||
onChange={(v) => setArgValue(a.name, v)}
|
|
||||||
value={argValues[a.name] !== undefined ? argValues[a.name] === true : false}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'http_request':
|
|
||||||
return (
|
|
||||||
<HttpRequestArg
|
|
||||||
key={i}
|
|
||||||
arg={a}
|
|
||||||
onChange={(v) => setArgValue(a.name, v)}
|
|
||||||
value={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
case 'file':
|
|
||||||
return (
|
|
||||||
<FileArg
|
|
||||||
key={i}
|
|
||||||
arg={a}
|
|
||||||
onChange={(v) => setArgValue(a.name, v)}
|
|
||||||
filePath={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
})}
|
|
||||||
</VStack>
|
|
||||||
<VStack className="w-full">
|
<VStack className="w-full">
|
||||||
<div className="text-sm text-text-subtle">Preview</div>
|
<div className="text-sm text-text-subtle">Preview</div>
|
||||||
<InlineCode
|
<InlineCode
|
||||||
@@ -168,147 +101,3 @@ export function TemplateFunctionDialog({ templateFunction, hide, initialTokens,
|
|||||||
</VStack>
|
</VStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function TextArg({
|
|
||||||
arg,
|
|
||||||
onChange,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
arg: TemplateFunctionTextArg;
|
|
||||||
value: string;
|
|
||||||
onChange: (v: string) => void;
|
|
||||||
}) {
|
|
||||||
const handleChange = useCallback(
|
|
||||||
(value: string) => {
|
|
||||||
onChange(value === '' ? NULL_ARG : value);
|
|
||||||
},
|
|
||||||
[onChange],
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PlainInput
|
|
||||||
name={arg.name}
|
|
||||||
onChange={handleChange}
|
|
||||||
defaultValue={value === NULL_ARG ? '' : value}
|
|
||||||
require={!arg.optional}
|
|
||||||
label={
|
|
||||||
<>
|
|
||||||
{arg.label ?? arg.name}
|
|
||||||
{arg.optional && <span> (optional)</span>}
|
|
||||||
</>
|
|
||||||
}
|
|
||||||
hideLabel={arg.label == null}
|
|
||||||
placeholder={arg.placeholder ?? arg.defaultValue ?? ''}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function SelectArg({
|
|
||||||
arg,
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
}: {
|
|
||||||
arg: TemplateFunctionSelectArg;
|
|
||||||
value: string;
|
|
||||||
onChange: (v: string) => void;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
label={arg.label ?? arg.name}
|
|
||||||
name={arg.name}
|
|
||||||
onChange={onChange}
|
|
||||||
value={value}
|
|
||||||
options={[
|
|
||||||
...arg.options.map((a) => ({
|
|
||||||
label: a.label + (arg.defaultValue === a.value ? ' (default)' : ''),
|
|
||||||
value: a.value === arg.defaultValue ? NULL_ARG : a.value,
|
|
||||||
})),
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function FileArg({
|
|
||||||
arg,
|
|
||||||
filePath,
|
|
||||||
onChange,
|
|
||||||
}: {
|
|
||||||
arg: TemplateFunctionFileArg;
|
|
||||||
filePath: string;
|
|
||||||
onChange: (v: string | null) => void;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<SelectFile
|
|
||||||
onChange={({ filePath }) => onChange(filePath)}
|
|
||||||
filePath={filePath === '__NULL__' ? null : filePath}
|
|
||||||
directory={!!arg.directory}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function HttpRequestArg({
|
|
||||||
arg,
|
|
||||||
value,
|
|
||||||
onChange,
|
|
||||||
}: {
|
|
||||||
arg: TemplateFunctionHttpRequestArg;
|
|
||||||
value: string;
|
|
||||||
onChange: (v: string) => void;
|
|
||||||
}) {
|
|
||||||
const folders = useFolders();
|
|
||||||
const httpRequests = useHttpRequests();
|
|
||||||
const activeRequest = useActiveRequest();
|
|
||||||
return (
|
|
||||||
<Select
|
|
||||||
label={arg.label ?? arg.name}
|
|
||||||
name={arg.name}
|
|
||||||
onChange={onChange}
|
|
||||||
value={value}
|
|
||||||
options={[
|
|
||||||
...httpRequests.map((r) => {
|
|
||||||
return {
|
|
||||||
label: buildRequestBreadcrumbs(r, folders).join(' / ') + (r.id == activeRequest?.id ? ' (current)' : ''),
|
|
||||||
value: r.id,
|
|
||||||
};
|
|
||||||
}),
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function buildRequestBreadcrumbs(request: HttpRequest, folders: Folder[]): string[] {
|
|
||||||
const ancestors: (HttpRequest | Folder)[] = [request];
|
|
||||||
|
|
||||||
const next = () => {
|
|
||||||
const latest = ancestors[0];
|
|
||||||
if (latest == null) return [];
|
|
||||||
|
|
||||||
const parent = folders.find((f) => f.id === latest.folderId);
|
|
||||||
if (parent == null) return;
|
|
||||||
|
|
||||||
ancestors.unshift(parent);
|
|
||||||
next();
|
|
||||||
};
|
|
||||||
next();
|
|
||||||
|
|
||||||
return ancestors.map((a) => (a.model === 'folder' ? a.name : fallbackRequestName(a)));
|
|
||||||
}
|
|
||||||
|
|
||||||
function CheckboxArg({
|
|
||||||
arg,
|
|
||||||
onChange,
|
|
||||||
value,
|
|
||||||
}: {
|
|
||||||
arg: TemplateFunctionCheckboxArg;
|
|
||||||
value: boolean;
|
|
||||||
onChange: (v: boolean) => void;
|
|
||||||
}) {
|
|
||||||
return (
|
|
||||||
<Checkbox
|
|
||||||
onChange={onChange}
|
|
||||||
checked={value}
|
|
||||||
title={arg.label ?? arg.name}
|
|
||||||
hideLabel={arg.label == null}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useCallback, useMemo } from 'react';
|
import { useCallback, useMemo } from 'react';
|
||||||
import { generateId } from '../../lib/generateId';
|
import { generateId } from '../../lib/generateId';
|
||||||
import { Editor } from './Editor/Editor';
|
import { Editor } from './Editor/Editor';
|
||||||
import type { PairEditorProps } from './PairEditor';
|
import type { PairEditorProps, PairWithId } from './PairEditor';
|
||||||
|
|
||||||
type Props = PairEditorProps;
|
type Props = PairEditorProps;
|
||||||
|
|
||||||
@@ -45,14 +45,12 @@ export function BulkPairEditor({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function lineToPair(line: string): PairEditorProps['pairs'][0] {
|
function lineToPair(line: string): PairWithId {
|
||||||
const [, name, value] = line.match(/^(:?[^:]+):\s+(.*)$/) ?? [];
|
const [, name, value] = line.match(/^(:?[^:]+):\s+(.*)$/) ?? [];
|
||||||
|
return {
|
||||||
const pair: PairEditorProps['pairs'][0] = {
|
|
||||||
enabled: true,
|
enabled: true,
|
||||||
name: (name ?? '').trim(),
|
name: (name ?? '').trim(),
|
||||||
value: (value ?? '').trim(),
|
value: (value ?? '').trim(),
|
||||||
id: generateId(),
|
id: generateId(),
|
||||||
};
|
};
|
||||||
return pair;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { atom, useAtom } from 'jotai';
|
import { atom } from 'jotai';
|
||||||
import type {
|
import type {
|
||||||
CSSProperties,
|
CSSProperties,
|
||||||
FocusEvent as ReactFocusEvent,
|
FocusEvent as ReactFocusEvent,
|
||||||
@@ -12,11 +12,11 @@ import type {
|
|||||||
SetStateAction,
|
SetStateAction,
|
||||||
} from 'react';
|
} from 'react';
|
||||||
import React, {
|
import React, {
|
||||||
useEffect,
|
|
||||||
Children,
|
Children,
|
||||||
cloneElement,
|
cloneElement,
|
||||||
forwardRef,
|
forwardRef,
|
||||||
useCallback,
|
useCallback,
|
||||||
|
useEffect,
|
||||||
useImperativeHandle,
|
useImperativeHandle,
|
||||||
useMemo,
|
useMemo,
|
||||||
useRef,
|
useRef,
|
||||||
@@ -29,6 +29,7 @@ import { useHotKey } from '../../hooks/useHotKey';
|
|||||||
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
||||||
import { generateId } from '../../lib/generateId';
|
import { generateId } from '../../lib/generateId';
|
||||||
import { getNodeText } from '../../lib/getNodeText';
|
import { getNodeText } from '../../lib/getNodeText';
|
||||||
|
import { jotaiStore } from '../../lib/jotai';
|
||||||
import { Overlay } from '../Overlay';
|
import { Overlay } from '../Overlay';
|
||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
import { HotKey } from './HotKey';
|
import { HotKey } from './HotKey';
|
||||||
@@ -62,15 +63,13 @@ export type DropdownItem = DropdownItemDefault | DropdownItemSeparator;
|
|||||||
export interface DropdownProps {
|
export interface DropdownProps {
|
||||||
children: ReactElement<HTMLAttributes<HTMLButtonElement>>;
|
children: ReactElement<HTMLAttributes<HTMLButtonElement>>;
|
||||||
items: DropdownItem[];
|
items: DropdownItem[];
|
||||||
onOpen?: () => void;
|
|
||||||
onClose?: () => void;
|
|
||||||
fullWidth?: boolean;
|
fullWidth?: boolean;
|
||||||
hotKeyAction?: HotkeyAction;
|
hotKeyAction?: HotkeyAction;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DropdownRef {
|
export interface DropdownRef {
|
||||||
isOpen: boolean;
|
isOpen: boolean;
|
||||||
open: () => void;
|
open: (index?: number) => void;
|
||||||
toggle: () => void;
|
toggle: () => void;
|
||||||
close?: () => void;
|
close?: () => void;
|
||||||
next?: (incrBy?: number) => void;
|
next?: (incrBy?: number) => void;
|
||||||
@@ -84,46 +83,52 @@ export interface DropdownRef {
|
|||||||
const openAtom = atom<string | null>(null);
|
const openAtom = atom<string | null>(null);
|
||||||
|
|
||||||
export const Dropdown = forwardRef<DropdownRef, DropdownProps>(function Dropdown(
|
export const Dropdown = forwardRef<DropdownRef, DropdownProps>(function Dropdown(
|
||||||
{ children, items, onOpen, onClose, hotKeyAction, fullWidth }: DropdownProps,
|
{ children, items, hotKeyAction, fullWidth }: DropdownProps,
|
||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const id = useRef(generateId()).current;
|
const id = useRef(generateId());
|
||||||
const [openId, setOpenId] = useAtom(openAtom);
|
const [isOpen, _setIsOpen] = useState<boolean>(false);
|
||||||
const isOpen = openId === id;
|
|
||||||
|
useEffect(() => {
|
||||||
|
return jotaiStore.sub(openAtom, () => {
|
||||||
|
const globalOpenId = jotaiStore.get(openAtom);
|
||||||
|
const newIsOpen = globalOpenId === id.current;
|
||||||
|
if (newIsOpen !== isOpen) {
|
||||||
|
_setIsOpen(newIsOpen);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, [isOpen, _setIsOpen]);
|
||||||
|
|
||||||
// const [isOpen, _setIsOpen] = useState<boolean>(false);
|
// const [isOpen, _setIsOpen] = useState<boolean>(false);
|
||||||
const [defaultSelectedIndex, setDefaultSelectedIndex] = useState<number | null>(null);
|
const [defaultSelectedIndex, setDefaultSelectedIndex] = useState<number | null>(null);
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null);
|
const buttonRef = useRef<HTMLButtonElement>(null);
|
||||||
const menuRef = useRef<Omit<DropdownRef, 'open'>>(null);
|
const menuRef = useRef<Omit<DropdownRef, 'open'>>(null);
|
||||||
|
|
||||||
const setIsOpen = useCallback(
|
const setIsOpen = useCallback((o: SetStateAction<boolean>) => {
|
||||||
(o: SetStateAction<boolean>) => {
|
jotaiStore.set(openAtom, (prevId) => {
|
||||||
setOpenId((prevId) => {
|
const prevIsOpen = prevId === id.current;
|
||||||
const prevIsOpen = prevId === id;
|
const newIsOpen = typeof o === 'function' ? o(prevIsOpen) : o;
|
||||||
const newIsOpen = typeof o === 'function' ? o(prevIsOpen) : o;
|
|
||||||
return newIsOpen ? id : null; // Set global atom to current ID to signify open state
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[id, setOpenId],
|
|
||||||
);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (isOpen) {
|
|
||||||
// Persist background color of button until we close the dropdown
|
// Persist background color of button until we close the dropdown
|
||||||
buttonRef.current!.style.backgroundColor = window
|
if (newIsOpen) {
|
||||||
.getComputedStyle(buttonRef.current!)
|
buttonRef.current!.style.backgroundColor = window
|
||||||
.getPropertyValue('background-color');
|
.getComputedStyle(buttonRef.current!)
|
||||||
onOpen?.();
|
.getPropertyValue('background-color');
|
||||||
} else {
|
}
|
||||||
onClose?.();
|
return newIsOpen ? id.current : null; // Set global atom to current ID to signify open state
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Because a different dropdown can cause ours to close, a useEffect([isOpen]) is the only method
|
||||||
|
// we have of detecting the dropdown closed, to do cleanup.
|
||||||
|
useEffect(() => {
|
||||||
|
if (!isOpen) {
|
||||||
buttonRef.current?.focus(); // Focus button
|
buttonRef.current?.focus(); // Focus button
|
||||||
buttonRef.current!.style.backgroundColor = ''; // Clear persisted BG
|
buttonRef.current!.style.backgroundColor = ''; // Clear persisted BG
|
||||||
|
// Set to different value when opened and closed to force it to update. This is to force
|
||||||
|
// <Menu/> to reset its selected-index state, which it does when this prop changes
|
||||||
|
setDefaultSelectedIndex(null);
|
||||||
}
|
}
|
||||||
|
}, [isOpen]);
|
||||||
// Set to different value when opened and closed to force it to update. This is to force
|
|
||||||
// <Menu/> to reset its selected-index state, which it does when this prop changes
|
|
||||||
setDefaultSelectedIndex(isOpen ? -1 : null);
|
|
||||||
}, [isOpen, onClose, onOpen]);
|
|
||||||
|
|
||||||
// Pull into variable so linter forces us to add it as a hook dep to useImperativeHandle. If we don't,
|
// Pull into variable so linter forces us to add it as a hook dep to useImperativeHandle. If we don't,
|
||||||
// the ref will not update when menuRef updates, causing stale callback state to be used.
|
// the ref will not update when menuRef updates, causing stale callback state to be used.
|
||||||
@@ -138,8 +143,9 @@ export const Dropdown = forwardRef<DropdownRef, DropdownProps>(function Dropdown
|
|||||||
if (!isOpen) this.open();
|
if (!isOpen) this.open();
|
||||||
else this.close();
|
else this.close();
|
||||||
},
|
},
|
||||||
open() {
|
open(index?: number) {
|
||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
|
setDefaultSelectedIndex(index ?? -1);
|
||||||
},
|
},
|
||||||
close() {
|
close() {
|
||||||
setIsOpen(false);
|
setIsOpen(false);
|
||||||
@@ -264,11 +270,18 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle' | 'items'
|
|||||||
ref,
|
ref,
|
||||||
) {
|
) {
|
||||||
const [selectedIndex, setSelectedIndex] = useStateWithDeps<number | null>(
|
const [selectedIndex, setSelectedIndex] = useStateWithDeps<number | null>(
|
||||||
defaultSelectedIndex ?? null,
|
defaultSelectedIndex ?? -1,
|
||||||
[defaultSelectedIndex],
|
[defaultSelectedIndex],
|
||||||
);
|
);
|
||||||
const [filter, setFilter] = useState<string>('');
|
const [filter, setFilter] = useState<string>('');
|
||||||
|
|
||||||
|
// HACK: Use a ref to track selectedIndex so our closure functions (eg. select()) can
|
||||||
|
// have access to the latest value.
|
||||||
|
const selectedIndexRef = useRef(selectedIndex);
|
||||||
|
useEffect(() => {
|
||||||
|
selectedIndexRef.current = selectedIndex;
|
||||||
|
}, [selectedIndex]);
|
||||||
|
|
||||||
const handleClose = useCallback(() => {
|
const handleClose = useCallback(() => {
|
||||||
onClose();
|
onClose();
|
||||||
setFilter('');
|
setFilter('');
|
||||||
@@ -380,12 +393,12 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle' | 'items'
|
|||||||
prev: handlePrev,
|
prev: handlePrev,
|
||||||
next: handleNext,
|
next: handleNext,
|
||||||
select() {
|
select() {
|
||||||
const item = items[selectedIndex ?? -1] ?? null;
|
const item = items[selectedIndexRef.current ?? -1] ?? null;
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
handleSelect(item);
|
handleSelect(item);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}, [handleClose, handleNext, handlePrev, handleSelect, items, selectedIndex]);
|
}, [handleClose, handleNext, handlePrev, handleSelect, items]);
|
||||||
|
|
||||||
const styles = useMemo<{
|
const styles = useMemo<{
|
||||||
container: CSSProperties;
|
container: CSSProperties;
|
||||||
|
|||||||
@@ -51,14 +51,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Don't show selection on blurred input */
|
|
||||||
|
|
||||||
.cm-selectionBackground {
|
.cm-selectionBackground {
|
||||||
@apply bg-transparent;
|
@apply bg-selection !important;
|
||||||
}
|
|
||||||
|
|
||||||
&.cm-focused .cm-selectionBackground {
|
|
||||||
@apply bg-selection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Style gutters */
|
/* Style gutters */
|
||||||
|
|||||||
@@ -26,10 +26,7 @@ import { useActiveEnvironmentVariables } from '../../../hooks/useActiveEnvironme
|
|||||||
import { parseTemplate } from '../../../hooks/useParseTemplate';
|
import { parseTemplate } from '../../../hooks/useParseTemplate';
|
||||||
import { useRequestEditor } from '../../../hooks/useRequestEditor';
|
import { useRequestEditor } from '../../../hooks/useRequestEditor';
|
||||||
import { useSettings } from '../../../hooks/useSettings';
|
import { useSettings } from '../../../hooks/useSettings';
|
||||||
import {
|
import { useTemplateFunctionCompletionOptions } from '../../../hooks/useTemplateFunctions';
|
||||||
useTemplateFunctions,
|
|
||||||
useTwigCompletionOptions,
|
|
||||||
} from '../../../hooks/useTemplateFunctions';
|
|
||||||
import { showDialog } from '../../../lib/dialog';
|
import { showDialog } from '../../../lib/dialog';
|
||||||
import { TemplateFunctionDialog } from '../../TemplateFunctionDialog';
|
import { TemplateFunctionDialog } from '../../TemplateFunctionDialog';
|
||||||
import { TemplateVariableDialog } from '../../TemplateVariableDialog';
|
import { TemplateVariableDialog } from '../../TemplateVariableDialog';
|
||||||
@@ -93,6 +90,10 @@ const stateFields = { history: historyField, folds: foldState };
|
|||||||
const emptyVariables: EnvironmentVariable[] = [];
|
const emptyVariables: EnvironmentVariable[] = [];
|
||||||
const emptyExtension: Extension = [];
|
const emptyExtension: Extension = [];
|
||||||
|
|
||||||
|
// NOTE: For some reason, the cursor doesn't appear if the field is empty and there is no
|
||||||
|
// placeholder. So we set it to a space to force it to show.
|
||||||
|
const emptyPlaceholder = ' ';
|
||||||
|
|
||||||
export const Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
|
export const Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
|
||||||
{
|
{
|
||||||
readOnly,
|
readOnly,
|
||||||
@@ -126,7 +127,6 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
) {
|
) {
|
||||||
const settings = useSettings();
|
const settings = useSettings();
|
||||||
|
|
||||||
const templateFunctions = useTemplateFunctions();
|
|
||||||
const allEnvironmentVariables = useActiveEnvironmentVariables();
|
const allEnvironmentVariables = useActiveEnvironmentVariables();
|
||||||
const environmentVariables = autocompleteVariables ? allEnvironmentVariables : emptyVariables;
|
const environmentVariables = autocompleteVariables ? allEnvironmentVariables : emptyVariables;
|
||||||
|
|
||||||
@@ -178,7 +178,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
useEffect(
|
useEffect(
|
||||||
function configurePlaceholder() {
|
function configurePlaceholder() {
|
||||||
if (cm.current === null) return;
|
if (cm.current === null) return;
|
||||||
const ext = placeholderExt(placeholderElFromText(placeholder ?? ''));
|
const ext = placeholderExt(placeholderElFromText(placeholder || emptyPlaceholder));
|
||||||
const effect = placeholderCompartment.current.reconfigure(ext);
|
const effect = placeholderCompartment.current.reconfigure(ext);
|
||||||
cm.current?.view.dispatch({ effects: effect });
|
cm.current?.view.dispatch({ effects: effect });
|
||||||
},
|
},
|
||||||
@@ -300,7 +300,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
[focusParamValue],
|
[focusParamValue],
|
||||||
);
|
);
|
||||||
|
|
||||||
const completionOptions = useTwigCompletionOptions(onClickFunction);
|
const completionOptions = useTemplateFunctionCompletionOptions(onClickFunction);
|
||||||
|
|
||||||
// Update the language extension when the language changes
|
// Update the language extension when the language changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -322,7 +322,6 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
autocomplete,
|
autocomplete,
|
||||||
useTemplating,
|
useTemplating,
|
||||||
environmentVariables,
|
environmentVariables,
|
||||||
templateFunctions,
|
|
||||||
onClickFunction,
|
onClickFunction,
|
||||||
onClickVariable,
|
onClickVariable,
|
||||||
onClickMissingVariable,
|
onClickMissingVariable,
|
||||||
@@ -355,7 +354,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
const extensions = [
|
const extensions = [
|
||||||
languageCompartment.of(langExt),
|
languageCompartment.of(langExt),
|
||||||
placeholderCompartment.current.of(
|
placeholderCompartment.current.of(
|
||||||
placeholderExt(placeholderElFromText(placeholder ?? '')),
|
placeholderExt(placeholderElFromText(placeholder || emptyPlaceholder)),
|
||||||
),
|
),
|
||||||
wrapLinesCompartment.current.of(wrapLines ? EditorView.lineWrapping : []),
|
wrapLinesCompartment.current.of(wrapLines ? EditorView.lineWrapping : []),
|
||||||
keymapCompartment.current.of(
|
keymapCompartment.current.of(
|
||||||
@@ -601,13 +600,16 @@ const placeholderElFromText = (text: string) => {
|
|||||||
|
|
||||||
function saveCachedEditorState(stateKey: string | null, state: EditorState | null) {
|
function saveCachedEditorState(stateKey: string | null, state: EditorState | null) {
|
||||||
if (!stateKey || state == null) return;
|
if (!stateKey || state == null) return;
|
||||||
sessionStorage.setItem(stateKey, JSON.stringify(state.toJSON(stateFields)));
|
sessionStorage.setItem(
|
||||||
|
computeFullStateKey(stateKey),
|
||||||
|
JSON.stringify(state.toJSON(stateFields)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCachedEditorState(doc: string, stateKey: string | null) {
|
function getCachedEditorState(doc: string, stateKey: string | null) {
|
||||||
if (stateKey == null) return;
|
if (stateKey == null) return;
|
||||||
|
|
||||||
const stateStr = sessionStorage.getItem(stateKey);
|
const stateStr = sessionStorage.getItem(computeFullStateKey(stateKey));
|
||||||
if (stateStr == null) return null;
|
if (stateStr == null) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -621,3 +623,7 @@ function getCachedEditorState(doc: string, stateKey: string | null) {
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function computeFullStateKey(stateKey: string): string {
|
||||||
|
return `editor.${stateKey}`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ const icons = {
|
|||||||
help: lucide.CircleHelpIcon,
|
help: lucide.CircleHelpIcon,
|
||||||
history: lucide.HistoryIcon,
|
history: lucide.HistoryIcon,
|
||||||
house: lucide.HomeIcon,
|
house: lucide.HomeIcon,
|
||||||
|
import: lucide.ImportIcon,
|
||||||
info: lucide.InfoIcon,
|
info: lucide.InfoIcon,
|
||||||
keyboard: lucide.KeyboardIcon,
|
keyboard: lucide.KeyboardIcon,
|
||||||
left_panel_hidden: lucide.PanelLeftOpenIcon,
|
left_panel_hidden: lucide.PanelLeftOpenIcon,
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { EditorView } from 'codemirror';
|
import type { EditorView } from 'codemirror';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import { forwardRef, useCallback, useMemo, useRef, useState } from 'react';
|
import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react';
|
||||||
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
||||||
import type { EditorProps } from './Editor/Editor';
|
import type { EditorProps } from './Editor/Editor';
|
||||||
import { Editor } from './Editor/Editor';
|
import { Editor } from './Editor/Editor';
|
||||||
@@ -46,7 +46,7 @@ export type InputProps = Pick<
|
|||||||
stateKey: EditorProps['stateKey'];
|
stateKey: EditorProps['stateKey'];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Input = forwardRef<EditorView | undefined, InputProps>(function Input(
|
export const Input = forwardRef<EditorView, InputProps>(function Input(
|
||||||
{
|
{
|
||||||
className,
|
className,
|
||||||
containerClassName,
|
containerClassName,
|
||||||
@@ -79,6 +79,8 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
|
|||||||
const [obscured, setObscured] = useStateWithDeps(type === 'password', [type]);
|
const [obscured, setObscured] = useStateWithDeps(type === 'password', [type]);
|
||||||
const [currentValue, setCurrentValue] = useState(defaultValue ?? '');
|
const [currentValue, setCurrentValue] = useState(defaultValue ?? '');
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
|
const editorRef = useRef<EditorView | null>(null);
|
||||||
|
useImperativeHandle<EditorView | null, EditorView | null>(ref, () => editorRef.current);
|
||||||
|
|
||||||
const handleFocus = useCallback(() => {
|
const handleFocus = useCallback(() => {
|
||||||
if (readOnly) return;
|
if (readOnly) return;
|
||||||
@@ -88,6 +90,7 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
|
|||||||
|
|
||||||
const handleBlur = useCallback(() => {
|
const handleBlur = useCallback(() => {
|
||||||
setFocused(false);
|
setFocused(false);
|
||||||
|
editorRef.current?.dispatch({ selection: { anchor: 0 } });
|
||||||
onBlur?.();
|
onBlur?.();
|
||||||
}, [onBlur]);
|
}, [onBlur]);
|
||||||
|
|
||||||
@@ -164,7 +167,7 @@ export const Input = forwardRef<EditorView | undefined, InputProps>(function Inp
|
|||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Editor
|
<Editor
|
||||||
ref={ref}
|
ref={editorRef}
|
||||||
id={id}
|
id={id}
|
||||||
singleLine
|
singleLine
|
||||||
stateKey={stateKey}
|
stateKey={stateKey}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ export type PairEditorProps = {
|
|||||||
namePlaceholder?: string;
|
namePlaceholder?: string;
|
||||||
nameValidate?: InputProps['validate'];
|
nameValidate?: InputProps['validate'];
|
||||||
noScroll?: boolean;
|
noScroll?: boolean;
|
||||||
onChange: (pairs: Pair[]) => void;
|
onChange: (pairs: PairWithId[]) => void;
|
||||||
pairs: Pair[];
|
pairs: Pair[];
|
||||||
stateKey: InputProps['stateKey'];
|
stateKey: InputProps['stateKey'];
|
||||||
valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined;
|
valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined;
|
||||||
@@ -54,7 +54,7 @@ export type PairEditorProps = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type Pair = {
|
export type Pair = {
|
||||||
id: string;
|
id?: string;
|
||||||
enabled?: boolean;
|
enabled?: boolean;
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
@@ -63,6 +63,10 @@ export type Pair = {
|
|||||||
readOnlyName?: boolean;
|
readOnlyName?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type PairWithId = Pair & {
|
||||||
|
id: string;
|
||||||
|
};
|
||||||
|
|
||||||
/** Max number of pairs to show before prompting the user to reveal the rest */
|
/** Max number of pairs to show before prompting the user to reveal the rest */
|
||||||
const MAX_INITIAL_PAIRS = 50;
|
const MAX_INITIAL_PAIRS = 50;
|
||||||
|
|
||||||
@@ -90,7 +94,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
|||||||
const [forceFocusNamePairId, setForceFocusNamePairId] = useState<string | null>(null);
|
const [forceFocusNamePairId, setForceFocusNamePairId] = useState<string | null>(null);
|
||||||
const [forceFocusValuePairId, setForceFocusValuePairId] = useState<string | null>(null);
|
const [forceFocusValuePairId, setForceFocusValuePairId] = useState<string | null>(null);
|
||||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||||
const [pairs, setPairs] = useState<Pair[]>([]);
|
const [pairs, setPairs] = useState<PairWithId[]>([]);
|
||||||
const [showAll, toggleShowAll] = useToggle(false);
|
const [showAll, toggleShowAll] = useToggle(false);
|
||||||
|
|
||||||
useImperativeHandle(
|
useImperativeHandle(
|
||||||
@@ -105,14 +109,13 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
|||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Remove empty headers on initial render and ensure they all have valid ids (pairs didn't used to have IDs)
|
// Remove empty headers on initial render and ensure they all have valid ids (pairs didn't use to have IDs)
|
||||||
const newPairs = [];
|
const newPairs: PairWithId[] = [];
|
||||||
for (let i = 0; i < originalPairs.length; i++) {
|
for (let i = 0; i < originalPairs.length; i++) {
|
||||||
const p = originalPairs[i];
|
const p = originalPairs[i];
|
||||||
if (!p) continue; // Make TS happy
|
if (!p) continue; // Make TS happy
|
||||||
if (isPairEmpty(p)) continue;
|
if (isPairEmpty(p)) continue;
|
||||||
if (!p.id) p.id = generateId();
|
newPairs.push({ ...p, id: p.id ?? generateId() });
|
||||||
newPairs.push(p);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add empty last pair if there is none
|
// Add empty last pair if there is none
|
||||||
@@ -127,7 +130,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
|||||||
}, [forceUpdateKey]);
|
}, [forceUpdateKey]);
|
||||||
|
|
||||||
const setPairsAndSave = useCallback(
|
const setPairsAndSave = useCallback(
|
||||||
(fn: (pairs: Pair[]) => Pair[]) => {
|
(fn: (pairs: PairWithId[]) => PairWithId[]) => {
|
||||||
setPairs((oldPairs) => {
|
setPairs((oldPairs) => {
|
||||||
const pairs = fn(oldPairs);
|
const pairs = fn(oldPairs);
|
||||||
onChange(pairs);
|
onChange(pairs);
|
||||||
@@ -165,7 +168,7 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(pair: Pair) => setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))),
|
(pair: PairWithId) => setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))),
|
||||||
[setPairsAndSave],
|
[setPairsAndSave],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -267,15 +270,15 @@ enum ItemTypes {
|
|||||||
|
|
||||||
type PairEditorRowProps = {
|
type PairEditorRowProps = {
|
||||||
className?: string;
|
className?: string;
|
||||||
pair: Pair;
|
pair: PairWithId;
|
||||||
forceFocusNamePairId?: string | null;
|
forceFocusNamePairId?: string | null;
|
||||||
forceFocusValuePairId?: string | null;
|
forceFocusValuePairId?: string | null;
|
||||||
onMove: (id: string, side: 'above' | 'below') => void;
|
onMove: (id: string, side: 'above' | 'below') => void;
|
||||||
onEnd: (id: string) => void;
|
onEnd: (id: string) => void;
|
||||||
onChange: (pair: Pair) => void;
|
onChange: (pair: PairWithId) => void;
|
||||||
onDelete?: (pair: Pair, focusPrevious: boolean) => void;
|
onDelete?: (pair: PairWithId, focusPrevious: boolean) => void;
|
||||||
onFocus?: (pair: Pair) => void;
|
onFocus?: (pair: PairWithId) => void;
|
||||||
onSubmit?: (pair: Pair) => void;
|
onSubmit?: (pair: PairWithId) => void;
|
||||||
isLast?: boolean;
|
isLast?: boolean;
|
||||||
index: number;
|
index: number;
|
||||||
} & Pick<
|
} & Pick<
|
||||||
@@ -618,7 +621,7 @@ function FileActionsDropdown({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function emptyPair(): Pair {
|
function emptyPair(): PairWithId {
|
||||||
return {
|
return {
|
||||||
enabled: true,
|
enabled: true,
|
||||||
name: '',
|
name: '',
|
||||||
|
|||||||
38
src-web/hooks/useHttpAuthentication.ts
Normal file
38
src-web/hooks/useHttpAuthentication.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import type { GetHttpAuthenticationResponse } from '@yaakapp-internal/plugins';
|
||||||
|
import { useAtomValue } from 'jotai';
|
||||||
|
import { atom, useSetAtom } from 'jotai/index';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
|
||||||
|
const httpAuthenticationAtom = atom<GetHttpAuthenticationResponse[]>([]);
|
||||||
|
const orderedHttpAuthenticationAtom = atom((get) =>
|
||||||
|
get(httpAuthenticationAtom).sort((a, b) => a.name.localeCompare(b.name)),
|
||||||
|
);
|
||||||
|
|
||||||
|
export function useHttpAuthentication() {
|
||||||
|
return useAtomValue(orderedHttpAuthenticationAtom);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useSubscribeHttpAuthentication() {
|
||||||
|
const [numResults, setNumResults] = useState<number>(0);
|
||||||
|
const setAtom = useSetAtom(httpAuthenticationAtom);
|
||||||
|
|
||||||
|
useQuery({
|
||||||
|
queryKey: ['http_authentication'],
|
||||||
|
// Fetch periodically until functions are returned
|
||||||
|
// NOTE: visibilitychange (refetchOnWindowFocus) does not work on Windows, so we'll rely on this logic
|
||||||
|
// to refetch things until that's working again
|
||||||
|
// TODO: Update plugin system to wait for plugins to initialize before sending the first event to them
|
||||||
|
refetchInterval: numResults > 0 ? Infinity : 1000,
|
||||||
|
refetchOnMount: true,
|
||||||
|
queryFn: async () => {
|
||||||
|
const result = await invokeCmd<GetHttpAuthenticationResponse[]>(
|
||||||
|
'cmd_get_http_authentication',
|
||||||
|
);
|
||||||
|
setNumResults(result.length);
|
||||||
|
setAtom(result);
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
18
src-web/hooks/useKeyboardEvent.ts
Normal file
18
src-web/hooks/useKeyboardEvent.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
export function useKeyboardEvent(
|
||||||
|
event: 'keyup' | 'keydown',
|
||||||
|
key: KeyboardEvent['key'],
|
||||||
|
cb: () => void,
|
||||||
|
) {
|
||||||
|
useEffect(() => {
|
||||||
|
const fn = (e: KeyboardEvent) => {
|
||||||
|
if (e.key === key) cb();
|
||||||
|
};
|
||||||
|
document.addEventListener(event, fn);
|
||||||
|
return () => document.removeEventListener(event, fn);
|
||||||
|
|
||||||
|
// Don't have `cb` as a dep for caller convenience
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, [event]);
|
||||||
|
}
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import type { HttpResponse } from '@yaakapp-internal/models';
|
import type { HttpResponse } from '@yaakapp-internal/models';
|
||||||
import { showAlert } from '../lib/alert';
|
|
||||||
import { trackEvent } from '../lib/analytics';
|
import { trackEvent } from '../lib/analytics';
|
||||||
import { getHttpRequest } from '../lib/store';
|
import { getHttpRequest } from '../lib/store';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
@@ -23,6 +22,5 @@ export function useSendAnyHttpRequest() {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
onSettled: () => trackEvent('http_request', 'send'),
|
onSettled: () => trackEvent('http_request', 'send'),
|
||||||
onError: (err) => showAlert({ id: 'send-failed', title: 'Send Failed', body: err }),
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,10 +9,10 @@ import { usePluginsKey } from './usePlugins';
|
|||||||
|
|
||||||
const templateFunctionsAtom = atom<TemplateFunction[]>([]);
|
const templateFunctionsAtom = atom<TemplateFunction[]>([]);
|
||||||
|
|
||||||
export function useTwigCompletionOptions(
|
export function useTemplateFunctionCompletionOptions(
|
||||||
onClick: (fn: TemplateFunction, ragTag: string, pos: number) => void,
|
onClick: (fn: TemplateFunction, ragTag: string, pos: number) => void,
|
||||||
) {
|
) {
|
||||||
const templateFunctions = useTemplateFunctions();
|
const templateFunctions = useAtomValue(templateFunctionsAtom);
|
||||||
return useMemo<TwigCompletionOption[]>(() => {
|
return useMemo<TwigCompletionOption[]>(() => {
|
||||||
return (
|
return (
|
||||||
templateFunctions.map((fn) => {
|
templateFunctions.map((fn) => {
|
||||||
@@ -37,10 +37,6 @@ export function useTwigCompletionOptions(
|
|||||||
}, [onClick, templateFunctions]);
|
}, [onClick, templateFunctions]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useTemplateFunctions() {
|
|
||||||
return useAtomValue(templateFunctionsAtom);
|
|
||||||
}
|
|
||||||
|
|
||||||
export function useSubscribeTemplateFunctions() {
|
export function useSubscribeTemplateFunctions() {
|
||||||
const pluginsKey = usePluginsKey();
|
const pluginsKey = usePluginsKey();
|
||||||
const [numFns, setNumFns] = useState<number>(0);
|
const [numFns, setNumFns] = useState<number>(0);
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ export const BODY_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded';
|
|||||||
export const BODY_TYPE_FORM_MULTIPART = 'multipart/form-data';
|
export const BODY_TYPE_FORM_MULTIPART = 'multipart/form-data';
|
||||||
export const BODY_TYPE_XML = 'text/xml';
|
export const BODY_TYPE_XML = 'text/xml';
|
||||||
|
|
||||||
export const AUTH_TYPE_NONE = null;
|
|
||||||
export const AUTH_TYPE_BASIC = 'basic';
|
|
||||||
export const AUTH_TYPE_BEARER = 'bearer';
|
|
||||||
|
|
||||||
export function cookieDomain(cookie: Cookie): string {
|
export function cookieDomain(cookie: Cookie): string {
|
||||||
if (cookie.domain === 'NotPresent' || cookie.domain === 'Empty') {
|
if (cookie.domain === 'NotPresent' || cookie.domain === 'Empty') {
|
||||||
return 'n/a';
|
return 'n/a';
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ type TauriCmd =
|
|||||||
| 'cmd_get_environment'
|
| 'cmd_get_environment'
|
||||||
| 'cmd_get_folder'
|
| 'cmd_get_folder'
|
||||||
| 'cmd_get_grpc_request'
|
| 'cmd_get_grpc_request'
|
||||||
|
| 'cmd_get_http_authentication'
|
||||||
| 'cmd_get_http_request'
|
| 'cmd_get_http_request'
|
||||||
| 'cmd_get_sse_events'
|
| 'cmd_get_sse_events'
|
||||||
| 'cmd_get_key_value'
|
| 'cmd_get_key_value'
|
||||||
|
|||||||
@@ -13,9 +13,8 @@
|
|||||||
font-variant-ligatures: none;
|
font-variant-ligatures: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
::selection,
|
::selection {
|
||||||
.cm-selectionBackground {
|
@apply bg-selection;
|
||||||
@apply bg-selection !important;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disable user selection to make it more "app-like" */
|
/* Disable user selection to make it more "app-like" */
|
||||||
@@ -26,7 +25,7 @@
|
|||||||
@apply select-none cursor-default;
|
@apply select-none cursor-default;
|
||||||
}
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
textarea {
|
textarea {
|
||||||
&::placeholder {
|
&::placeholder {
|
||||||
@apply text-placeholder;
|
@apply text-placeholder;
|
||||||
|
|||||||
Reference in New Issue
Block a user