diff --git a/src-tauri/src/http_request.rs b/src-tauri/src/http_request.rs index 371a6487..489a3594 100644 --- a/src-tauri/src/http_request.rs +++ b/src-tauri/src/http_request.rs @@ -225,7 +225,22 @@ pub async fn send_http_request( let request_body = rendered_request.body; if let Some(body_type) = &rendered_request.body_type { - if request_body.contains_key("text") { + if request_body.contains_key("query") && request_body.contains_key("variables") { + let query = get_str_h(&request_body, "query"); + let variables = get_str_h(&request_body, "variables"); + let body = if variables.trim().is_empty() { + format!( + r#"{{"query":{}}}"#, + serde_json::to_string(query).unwrap_or_default() + ) + } else { + format!( + r#"{{"query":{},"variables":{variables}}}"#, + serde_json::to_string(query).unwrap_or_default() + ) + }; + request_builder = request_builder.body(body.to_owned()); + } else if request_body.contains_key("text") { let body = get_str_h(&request_body, "text"); request_builder = request_builder.body(body.to_owned()); } else if body_type == "application/x-www-form-urlencoded" @@ -498,7 +513,7 @@ fn replace_path_placeholder(p: &HttpUrlParameter, url: &str) -> String { if !p.enabled { return url.to_string(); } - + if !p.name.starts_with(":") { return url.to_string(); } diff --git a/src-web/components/GraphQLEditor.tsx b/src-web/components/GraphQLEditor.tsx index 67867894..19c4bc3b 100644 --- a/src-web/components/GraphQLEditor.tsx +++ b/src-web/components/GraphQLEditor.tsx @@ -1,77 +1,48 @@ +import type { HttpRequest } from '@yaakapp-internal/models'; +import { updateSchema } from 'cm6-graphql'; import type { EditorView } from 'codemirror'; -import { useCallback, useEffect, useMemo, useRef } from 'react'; +import { useEffect, useMemo, useRef, useState } from 'react'; import { useIntrospectGraphQL } from '../hooks/useIntrospectGraphQL'; import { tryFormatJson } from '../lib/formatters'; -import type { HttpRequest } from '@yaakapp-internal/models'; import { Button } from './core/Button'; import type { EditorProps } from './core/Editor'; import { Editor, formatGraphQL } from './core/Editor'; import { FormattedError } from './core/FormattedError'; import { Separator } from './core/Separator'; import { useDialog } from './DialogContext'; -import { updateSchema } from 'cm6-graphql'; -type Props = Pick< - EditorProps, - 'heightMode' | 'onChange' | 'defaultValue' | 'className' | 'forceUpdateKey' -> & { +type Props = Pick & { baseRequest: HttpRequest; + onChange: (body: HttpRequest['body']) => void; + body: HttpRequest['body']; }; -interface GraphQLBody { - query: string; - variables?: Record; - operationName?: string; -} - -export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEditorProps }: Props) { +export function GraphQLEditor({ body, onChange, baseRequest, ...extraEditorProps }: Props) { const editorViewRef = useRef(null); const { schema, isLoading, error, refetch } = useIntrospectGraphQL(baseRequest); - const { query, variables } = useMemo(() => { - if (defaultValue === undefined) { - return { query: '', variables: {} }; + const [currentBody, setCurrentBody] = useState<{ query: string; variables: string }>(() => { + // Migrate text bodies to GraphQL format + // NOTE: This is how GraphQL used to be stored + if ('text' in body) { + const b = tryParseJson(body.text, {}); + const variables = JSON.stringify(b.variables ?? '', null, 2); + return { query: b.query ?? '', variables }; } - try { - const p = JSON.parse(defaultValue || '{}'); - const query = p.query ?? ''; - const variables = p.variables; - const operationName = p.operationName; - return { query, variables, operationName }; - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (err) { - return { query: '' }; - } - }, [defaultValue]); - const handleChange = useCallback( - (b: GraphQLBody) => { - try { - onChange?.(JSON.stringify(b, null, 2)); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (err) { - // Meh, not much we can do here - } - }, - [onChange], - ); + return { query: body.query ?? '', variables: body.variables ?? '' }; + }); - const handleChangeQuery = useCallback( - (query: string) => handleChange({ query, variables }), - [handleChange, variables], - ); + const handleChangeQuery = (query: string) => { + const newBody = { query, variables: currentBody.variables }; + setCurrentBody(newBody); + onChange(newBody); + }; - const handleChangeVariables = useCallback( - (variables: string) => { - try { - handleChange({ query, variables: JSON.parse(variables || '{}') }); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (err) { - // Don't do anything if invalid JSON. The user probably hasn't finished - // typing yet. - } - }, - [handleChange, query], - ); + const handleChangeVariables = (variables: string) => { + const newBody = { query: currentBody.query, variables }; + setCurrentBody(newBody); + onChange(newBody); + }; // Refetch the schema when the URL changes useEffect(() => { @@ -132,9 +103,9 @@ export function GraphQLEditor({ defaultValue, onChange, baseRequest, ...extraEdi
); } + +function tryParseJson(text: string, fallback: unknown) { + try { + return JSON.parse(text); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (err) { + return fallback; + } +} diff --git a/src-web/components/RequestPane.tsx b/src-web/components/RequestPane.tsx index 45859620..0f6b1db6 100644 --- a/src-web/components/RequestPane.tsx +++ b/src-web/components/RequestPane.tsx @@ -419,8 +419,8 @@ export const RequestPane = memo(function RequestPane({ ) : activeRequest.bodyType === BODY_TYPE_FORM_URLENCODED ? (