mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-05-18 05:37:09 +02:00
Add cookie editing and inherited request settings
This commit is contained in:
@@ -0,0 +1,321 @@
|
||||
import type {
|
||||
Folder,
|
||||
GrpcRequest,
|
||||
HttpRequest,
|
||||
InheritedBoolSetting,
|
||||
InheritedIntSetting,
|
||||
WebsocketRequest,
|
||||
Workspace,
|
||||
} from "@yaakapp-internal/models";
|
||||
import { patchModel } from "@yaakapp-internal/models";
|
||||
import { useModelAncestors } from "../hooks/useModelAncestors";
|
||||
import { Checkbox } from "./core/Checkbox";
|
||||
import { PlainInput } from "./core/PlainInput";
|
||||
import {
|
||||
SettingOverrideRow,
|
||||
SettingRowBoolean,
|
||||
SettingRowNumber,
|
||||
SettingsList,
|
||||
SettingsSection,
|
||||
} from "./core/SettingRow";
|
||||
|
||||
interface Props {
|
||||
showSectionTitles?: boolean;
|
||||
model: ModelWithSettings;
|
||||
}
|
||||
|
||||
type ModelWithSettings = Workspace | Folder | HttpRequest | WebsocketRequest | GrpcRequest;
|
||||
type ModelWithHttpSettings = Workspace | Folder | HttpRequest;
|
||||
type ModelWithCookieSettings = Workspace | Folder | HttpRequest | WebsocketRequest | GrpcRequest;
|
||||
type BooleanSetting = boolean | InheritedBoolSetting;
|
||||
type IntegerSetting = number | InheritedIntSetting;
|
||||
type CookieSettingsPatch = {
|
||||
settingSendCookies?: ModelWithCookieSettings["settingSendCookies"];
|
||||
settingStoreCookies?: ModelWithCookieSettings["settingStoreCookies"];
|
||||
};
|
||||
type HttpSettingsPatch = {
|
||||
settingValidateCertificates?: ModelWithHttpSettings["settingValidateCertificates"];
|
||||
settingFollowRedirects?: ModelWithHttpSettings["settingFollowRedirects"];
|
||||
settingRequestTimeout?: ModelWithHttpSettings["settingRequestTimeout"];
|
||||
};
|
||||
|
||||
export function ModelSettingsEditor({ model, showSectionTitles = false }: Props) {
|
||||
const ancestors = useModelAncestors(model);
|
||||
const supportsHttpSettings =
|
||||
model.model === "workspace" || model.model === "folder" || model.model === "http_request";
|
||||
|
||||
return (
|
||||
<SettingsList className="space-y-8">
|
||||
{supportsHttpSettings && (
|
||||
<SettingsSection title={showSectionTitles ? "Requests" : null}>
|
||||
<IntegerSettingRow
|
||||
title="Request Timeout"
|
||||
description="Maximum request duration in milliseconds. Set to 0 to disable the timeout."
|
||||
name="settingRequestTimeout"
|
||||
setting={model.settingRequestTimeout}
|
||||
inheritedValue={resolveInheritedValue(
|
||||
ancestors,
|
||||
"settingRequestTimeout",
|
||||
model.settingRequestTimeout,
|
||||
)}
|
||||
onChange={(settingRequestTimeout) =>
|
||||
patchHttpSettings(model, {
|
||||
settingRequestTimeout,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<BooleanSettingRow
|
||||
title="Validate TLS certificates"
|
||||
description="When disabled, skip validation of server certificates."
|
||||
setting={model.settingValidateCertificates}
|
||||
inheritedValue={resolveInheritedValue(
|
||||
ancestors,
|
||||
"settingValidateCertificates",
|
||||
model.settingValidateCertificates,
|
||||
)}
|
||||
onChange={(settingValidateCertificates) =>
|
||||
patchHttpSettings(model, {
|
||||
settingValidateCertificates,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<BooleanSettingRow
|
||||
title="Follow redirects"
|
||||
description="Follow HTTP redirects automatically."
|
||||
setting={model.settingFollowRedirects}
|
||||
inheritedValue={resolveInheritedValue(
|
||||
ancestors,
|
||||
"settingFollowRedirects",
|
||||
model.settingFollowRedirects,
|
||||
)}
|
||||
onChange={(settingFollowRedirects) =>
|
||||
patchHttpSettings(model, {
|
||||
settingFollowRedirects,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</SettingsSection>
|
||||
)}
|
||||
<SettingsSection title={supportsHttpSettings || showSectionTitles ? "Cookies" : null}>
|
||||
<BooleanSettingRow
|
||||
title="Automatically send cookies"
|
||||
description="Attach matching cookies from the active cookie jar to outgoing requests."
|
||||
setting={model.settingSendCookies}
|
||||
inheritedValue={resolveInheritedValue(
|
||||
ancestors,
|
||||
"settingSendCookies",
|
||||
model.settingSendCookies,
|
||||
)}
|
||||
onChange={(settingSendCookies) =>
|
||||
patchCookieSettings(model, {
|
||||
settingSendCookies,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<BooleanSettingRow
|
||||
title="Automatically store cookies"
|
||||
description="Save cookies from Set-Cookie response headers to the active cookie jar."
|
||||
setting={model.settingStoreCookies}
|
||||
inheritedValue={resolveInheritedValue(
|
||||
ancestors,
|
||||
"settingStoreCookies",
|
||||
model.settingStoreCookies,
|
||||
)}
|
||||
onChange={(settingStoreCookies) =>
|
||||
patchCookieSettings(model, {
|
||||
settingStoreCookies,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</SettingsSection>
|
||||
</SettingsList>
|
||||
);
|
||||
}
|
||||
|
||||
export function countOverriddenSettings(model: ModelWithSettings) {
|
||||
const settings: (BooleanSetting | IntegerSetting)[] = [
|
||||
model.settingSendCookies,
|
||||
model.settingStoreCookies,
|
||||
];
|
||||
|
||||
if (model.model === "workspace" || model.model === "folder" || model.model === "http_request") {
|
||||
settings.push(
|
||||
model.settingValidateCertificates,
|
||||
model.settingFollowRedirects,
|
||||
model.settingRequestTimeout,
|
||||
);
|
||||
}
|
||||
|
||||
return settings.filter((setting) => isInheritedSetting(setting) && setting.enabled === true)
|
||||
.length;
|
||||
}
|
||||
|
||||
function patchCookieSettings(model: ModelWithCookieSettings, patch: Partial<CookieSettingsPatch>) {
|
||||
if (model.model === "workspace") return patchModel(model, patch as Partial<Workspace>);
|
||||
if (model.model === "folder") return patchModel(model, patch as Partial<Folder>);
|
||||
if (model.model === "http_request") return patchModel(model, patch as Partial<HttpRequest>);
|
||||
if (model.model === "websocket_request")
|
||||
return patchModel(model, patch as Partial<WebsocketRequest>);
|
||||
return patchModel(model, patch as Partial<GrpcRequest>);
|
||||
}
|
||||
|
||||
function patchHttpSettings(model: ModelWithHttpSettings, patch: Partial<HttpSettingsPatch>) {
|
||||
if (model.model === "workspace") return patchModel(model, patch as Partial<Workspace>);
|
||||
if (model.model === "folder") return patchModel(model, patch as Partial<Folder>);
|
||||
return patchModel(model, patch as Partial<HttpRequest>);
|
||||
}
|
||||
|
||||
function BooleanSettingRow({
|
||||
description,
|
||||
inheritedValue,
|
||||
setting,
|
||||
title,
|
||||
onChange,
|
||||
}: {
|
||||
description: string;
|
||||
inheritedValue: boolean;
|
||||
setting: BooleanSetting;
|
||||
title: string;
|
||||
onChange: (setting: BooleanSetting) => void;
|
||||
}) {
|
||||
const inherited = isInheritedSetting(setting);
|
||||
const overridden = inherited ? setting.enabled === true : false;
|
||||
const value = inherited ? (overridden ? setting.value : inheritedValue) : setting;
|
||||
|
||||
if (!inherited) {
|
||||
return (
|
||||
<SettingRowBoolean
|
||||
checked={value}
|
||||
title={title}
|
||||
description={description}
|
||||
onChange={(value) => onChange(value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingOverrideRow
|
||||
title={title}
|
||||
description={description}
|
||||
overridden={overridden}
|
||||
onResetOverride={() => onChange({ ...setting, enabled: false })}
|
||||
>
|
||||
<Checkbox
|
||||
hideLabel
|
||||
size="md"
|
||||
title={title}
|
||||
checked={value}
|
||||
onChange={(value) => onChange({ ...setting, enabled: true, value })}
|
||||
/>
|
||||
</SettingOverrideRow>
|
||||
);
|
||||
}
|
||||
|
||||
function IntegerSettingRow({
|
||||
description,
|
||||
inheritedValue,
|
||||
name,
|
||||
setting,
|
||||
title,
|
||||
onChange,
|
||||
}: {
|
||||
description: string;
|
||||
inheritedValue: number;
|
||||
name: string;
|
||||
setting: IntegerSetting;
|
||||
title: string;
|
||||
onChange: (setting: IntegerSetting) => void;
|
||||
}) {
|
||||
const inherited = isInheritedSetting(setting);
|
||||
const overridden = inherited ? setting.enabled === true : false;
|
||||
const value = inherited ? (overridden ? setting.value : inheritedValue) : setting;
|
||||
const showReset = overridden && value !== inheritedValue;
|
||||
|
||||
if (!inherited) {
|
||||
return (
|
||||
<SettingRowNumber
|
||||
name={name}
|
||||
title={title}
|
||||
description={description}
|
||||
value={value}
|
||||
placeholder="0"
|
||||
validate={(value) => value === "" || Number.parseInt(value, 10) >= 0}
|
||||
onChange={(value) => onChange(value)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<SettingOverrideRow
|
||||
title={title}
|
||||
description={description}
|
||||
overridden={showReset}
|
||||
onResetOverride={() => onChange({ ...setting, enabled: false })}
|
||||
>
|
||||
<PlainInput
|
||||
hideLabel
|
||||
name={name}
|
||||
label={title}
|
||||
size="sm"
|
||||
type="number"
|
||||
placeholder="0"
|
||||
defaultValue={`${value}`}
|
||||
containerClassName="!w-48"
|
||||
validate={(value) => value === "" || Number.parseInt(value, 10) >= 0}
|
||||
onChange={(value) =>
|
||||
onChange({
|
||||
...setting,
|
||||
enabled: true,
|
||||
value: Number.parseInt(value, 10) || 0,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</SettingOverrideRow>
|
||||
);
|
||||
}
|
||||
|
||||
function isInheritedSetting<T>(
|
||||
setting: T | { enabled?: boolean; value: T },
|
||||
): setting is { enabled?: boolean; value: T } {
|
||||
return typeof setting === "object" && setting != null && "value" in setting;
|
||||
}
|
||||
|
||||
function resolveInheritedValue(
|
||||
ancestors: (Folder | Workspace)[],
|
||||
key: "settingRequestTimeout",
|
||||
fallback: IntegerSetting,
|
||||
): number;
|
||||
function resolveInheritedValue(
|
||||
ancestors: (Folder | Workspace)[],
|
||||
key: BooleanWorkspaceSettingKey,
|
||||
fallback: BooleanSetting,
|
||||
): boolean;
|
||||
function resolveInheritedValue(
|
||||
ancestors: (Folder | Workspace)[],
|
||||
key: keyof WorkspaceSettings,
|
||||
fallback: BooleanSetting | IntegerSetting,
|
||||
) {
|
||||
for (const ancestor of ancestors) {
|
||||
const setting = ancestor[key] as BooleanSetting | IntegerSetting;
|
||||
if (isInheritedSetting(setting)) {
|
||||
if (setting.enabled === true) {
|
||||
return setting.value;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
return setting;
|
||||
}
|
||||
|
||||
return isInheritedSetting(fallback) ? fallback.value : fallback;
|
||||
}
|
||||
|
||||
type WorkspaceSettings = Pick<
|
||||
Workspace,
|
||||
| "settingFollowRedirects"
|
||||
| "settingRequestTimeout"
|
||||
| "settingSendCookies"
|
||||
| "settingStoreCookies"
|
||||
| "settingValidateCertificates"
|
||||
>;
|
||||
|
||||
type BooleanWorkspaceSettingKey = Exclude<keyof WorkspaceSettings, "settingRequestTimeout">;
|
||||
Reference in New Issue
Block a user