Refactor default headers to be injected dynamically (#367)

This commit is contained in:
Gregory Schier
2026-01-19 07:29:00 -08:00
committed by GitHub
parent 1893b8f8dd
commit beb47a6b6a
13 changed files with 106 additions and 33 deletions

View File

@@ -19,6 +19,7 @@ type Props = {
forceUpdateKey: string;
headers: HttpRequestHeader[];
inheritedHeaders?: HttpRequestHeader[];
inheritedHeadersLabel?: string;
stateKey: string;
onChange: (headers: HttpRequestHeader[]) => void;
label?: string;
@@ -28,11 +29,20 @@ export function HeadersEditor({
stateKey,
headers,
inheritedHeaders,
inheritedHeadersLabel = 'Inherited',
onChange,
forceUpdateKey,
}: Props) {
// Get header names defined at current level (case-insensitive)
const currentHeaderNames = new Set(
headers.filter((h) => h.name).map((h) => h.name.toLowerCase()),
);
// Filter inherited headers: must be enabled, have content, and not be overridden by current level
const validInheritedHeaders =
inheritedHeaders?.filter((pair) => pair.enabled && (pair.name || pair.value)) ?? [];
inheritedHeaders?.filter(
(pair) =>
pair.enabled && (pair.name || pair.value) && !currentHeaderNames.has(pair.name.toLowerCase()),
) ?? [];
const hasInheritedHeaders = validInheritedHeaders.length > 0;
return (
<div
@@ -48,7 +58,7 @@ export function HeadersEditor({
className="text-sm"
summary={
<HStack>
Inherited <CountBadge count={validInheritedHeaders.length} />
{inheritedHeadersLabel} <CountBadge count={validInheritedHeaders.length} />
</HStack>
}
>

View File

@@ -95,6 +95,7 @@ export function WorkspaceSettingsDialog({ workspaceId, hide, tab }: Props) {
<TabContent value={TAB_HEADERS} className="overflow-y-auto h-full px-4">
<HeadersEditor
inheritedHeaders={inheritedHeaders}
inheritedHeadersLabel="Defaults"
forceUpdateKey={workspace.id}
headers={workspace.headers}
onChange={(headers) => patchModel(workspace, { headers })}

View File

@@ -8,6 +8,7 @@ import type {
} from '@yaakapp-internal/models';
import { foldersAtom, workspacesAtom } from '@yaakapp-internal/models';
import { atom, useAtomValue } from 'jotai';
import { defaultHeaders } from '../lib/defaultHeaders';
const ancestorsAtom = atom((get) => [...get(foldersAtom), ...get(workspacesAtom)]);
@@ -17,12 +18,12 @@ export function useInheritedHeaders(baseModel: HeaderModel | null) {
const parents = useAtomValue(ancestorsAtom);
if (baseModel == null) return [];
if (baseModel.model === 'workspace') return [];
if (baseModel.model === 'workspace') return defaultHeaders;
const next = (child: HeaderModel): HttpRequestHeader[] => {
// Short-circuit
// Short-circuit at workspace level - return global defaults + workspace headers
if (child.model === 'workspace') {
return [];
return [...defaultHeaders, ...child.headers];
}
// Recurse up the tree
@@ -40,5 +41,13 @@ export function useInheritedHeaders(baseModel: HeaderModel | null) {
return [...headers, ...parent.headers];
};
return next(baseModel);
const allHeaders = next(baseModel);
// Deduplicate by header name (case-insensitive), keeping the latest (most specific) value
const headersByName = new Map<string, HttpRequestHeader>();
for (const header of allHeaders) {
headersByName.set(header.name.toLowerCase(), header);
}
return Array.from(headersByName.values());
}

View File

@@ -0,0 +1,8 @@
import type { HttpRequestHeader } from '@yaakapp-internal/models';
import { invokeCmd } from './tauri';
/**
* Global default headers fetched from the backend.
* These are static and fetched once on module load.
*/
export const defaultHeaders: HttpRequestHeader[] = await invokeCmd('cmd_default_headers');

View File

@@ -12,6 +12,7 @@ type TauriCmd =
| 'cmd_create_grpc_request'
| 'cmd_curl_to_request'
| 'cmd_decrypt_template'
| 'cmd_default_headers'
| 'cmd_delete_all_grpc_connections'
| 'cmd_delete_all_http_responses'
| 'cmd_delete_send_history'