mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-03-30 14:12:07 +02:00
Request Inheritance (#209)
This commit is contained in:
64
src-web/hooks/useAuthTab.tsx
Normal file
64
src-web/hooks/useAuthTab.tsx
Normal file
@@ -0,0 +1,64 @@
|
||||
import type { Folder } from '@yaakapp-internal/models';
|
||||
import { patchModel } from '@yaakapp-internal/models';
|
||||
import { useMemo } from 'react';
|
||||
import { IconTooltip } from '../components/core/IconTooltip';
|
||||
import { HStack } from '../components/core/Stacks';
|
||||
import type { TabItem } from '../components/core/Tabs/Tabs';
|
||||
import { useHttpAuthenticationSummaries } from './useHttpAuthentication';
|
||||
import type { AuthenticatedModel} from './useInheritedAuthentication';
|
||||
import { useInheritedAuthentication } from './useInheritedAuthentication';
|
||||
|
||||
export function useAuthTab<T extends string>(tabValue: T, model: AuthenticatedModel | null) {
|
||||
const authentication = useHttpAuthenticationSummaries();
|
||||
const inheritedAuth = useInheritedAuthentication(model);
|
||||
|
||||
return useMemo<TabItem[]>(() => {
|
||||
if (model == null) return [];
|
||||
|
||||
const tab: TabItem = {
|
||||
value: tabValue,
|
||||
label: 'Auth',
|
||||
options: {
|
||||
value: model.authenticationType,
|
||||
items: [
|
||||
...authentication.map((a) => ({
|
||||
label: a.label || 'UNKNOWN',
|
||||
shortLabel: a.shortLabel,
|
||||
value: a.name,
|
||||
})),
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: 'Inherit from Parent',
|
||||
shortLabel:
|
||||
inheritedAuth != null && inheritedAuth.authenticationType != 'none' ? (
|
||||
<HStack space={1.5}>
|
||||
{authentication.find((a) => a.name === inheritedAuth.authenticationType)
|
||||
?.shortLabel ?? 'UNKNOWN'}
|
||||
<IconTooltip
|
||||
icon="magic_wand"
|
||||
iconSize="xs"
|
||||
content="Authenticatin was inherited from an ancestor"
|
||||
/>
|
||||
</HStack>
|
||||
) : (
|
||||
'Auth'
|
||||
),
|
||||
value: null,
|
||||
},
|
||||
{ label: 'No Auth', shortLabel: 'No Auth', value: 'none' },
|
||||
],
|
||||
onChange: async (authenticationType) => {
|
||||
let authentication: Folder['authentication'] = model.authentication;
|
||||
if (model.authenticationType !== authenticationType) {
|
||||
authentication = {
|
||||
// Reset auth if changing types
|
||||
};
|
||||
}
|
||||
await patchModel(model, { authentication, authenticationType });
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
return [tab];
|
||||
}, [authentication, inheritedAuth, model, tabValue]);
|
||||
}
|
||||
31
src-web/hooks/useHeadersTab.tsx
Normal file
31
src-web/hooks/useHeadersTab.tsx
Normal file
@@ -0,0 +1,31 @@
|
||||
import React, { useMemo } from 'react';
|
||||
import { CountBadge } from '../components/core/CountBadge';
|
||||
import type { TabItem } from '../components/core/Tabs/Tabs';
|
||||
import type { HeaderModel } from './useInheritedHeaders';
|
||||
import { useInheritedHeaders } from './useInheritedHeaders';
|
||||
|
||||
export function useHeadersTab<T extends string>(
|
||||
tabValue: T,
|
||||
model: HeaderModel | null,
|
||||
label?: string,
|
||||
) {
|
||||
const inheritedHeaders = useInheritedHeaders(model);
|
||||
|
||||
return useMemo<TabItem[]>(() => {
|
||||
if (model == null) return [];
|
||||
|
||||
const allHeaders = [
|
||||
...inheritedHeaders,
|
||||
...(model.model === 'grpc_request' ? model.metadata : model.headers),
|
||||
];
|
||||
const numHeaders = allHeaders.filter((h) => h.name).length;
|
||||
|
||||
const tab: TabItem = {
|
||||
value: tabValue,
|
||||
label: label ?? 'Headers',
|
||||
rightSlot: <CountBadge count={numHeaders} />,
|
||||
};
|
||||
|
||||
return [tab];
|
||||
}, [inheritedHeaders, label, model, tabValue]);
|
||||
}
|
||||
@@ -1,5 +1,11 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models';
|
||||
import type {
|
||||
Folder,
|
||||
GrpcRequest,
|
||||
HttpRequest,
|
||||
WebsocketRequest,
|
||||
Workspace,
|
||||
} from '@yaakapp-internal/models';
|
||||
import { httpResponsesAtom } from '@yaakapp-internal/models';
|
||||
import type { GetHttpAuthenticationConfigResponse, JsonPrimitive } from '@yaakapp-internal/plugins';
|
||||
import { useAtomValue } from 'jotai';
|
||||
@@ -49,13 +55,15 @@ export function useHttpAuthenticationConfig(
|
||||
...config,
|
||||
actions: config.actions?.map((a, i) => ({
|
||||
...a,
|
||||
call: async ({ id: requestId }: HttpRequest | GrpcRequest | WebsocketRequest) => {
|
||||
call: async ({
|
||||
id: modelId,
|
||||
}: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace) => {
|
||||
await invokeCmd('cmd_call_http_authentication_action', {
|
||||
pluginRefId: config.pluginRefId,
|
||||
actionIndex: i,
|
||||
authName,
|
||||
values,
|
||||
requestId,
|
||||
modelId,
|
||||
});
|
||||
|
||||
// Ensure the config is refreshed after the action is done
|
||||
|
||||
50
src-web/hooks/useInheritedAuthentication.ts
Normal file
50
src-web/hooks/useInheritedAuthentication.ts
Normal file
@@ -0,0 +1,50 @@
|
||||
import type {
|
||||
Folder,
|
||||
GrpcRequest,
|
||||
HttpRequest,
|
||||
WebsocketRequest,
|
||||
Workspace,
|
||||
} from '@yaakapp-internal/models';
|
||||
import { foldersAtom, workspacesAtom } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
|
||||
const ancestorsAtom = atom(function (get) {
|
||||
return [...get(foldersAtom), ...get(workspacesAtom)];
|
||||
});
|
||||
|
||||
export type AuthenticatedModel = HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace;
|
||||
|
||||
export function useInheritedAuthentication(
|
||||
baseModel: AuthenticatedModel | null,
|
||||
) {
|
||||
const parents = useAtomValue(ancestorsAtom);
|
||||
|
||||
if (baseModel == null) return null;
|
||||
|
||||
const next = (child: AuthenticatedModel) => {
|
||||
// We hit the top
|
||||
if (child.model === 'workspace') {
|
||||
return child.authenticationType == null ? null : child;
|
||||
}
|
||||
|
||||
// Has valid auth
|
||||
if (child.authenticationType !== null) {
|
||||
return child;
|
||||
}
|
||||
|
||||
// Recurse up the tree
|
||||
const parent = parents.find((p) => {
|
||||
if (child.folderId) return p.id === child.folderId;
|
||||
else return p.id === child.workspaceId;
|
||||
});
|
||||
|
||||
// Failed to find parent (should never happen)
|
||||
if (parent == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return next(parent);
|
||||
};
|
||||
|
||||
return next(baseModel);
|
||||
}
|
||||
46
src-web/hooks/useInheritedHeaders.ts
Normal file
46
src-web/hooks/useInheritedHeaders.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import type {
|
||||
Folder,
|
||||
GrpcRequest,
|
||||
HttpRequest,
|
||||
HttpRequestHeader,
|
||||
WebsocketRequest,
|
||||
Workspace,
|
||||
} from '@yaakapp-internal/models';
|
||||
import { foldersAtom, workspacesAtom } from '@yaakapp-internal/models';
|
||||
import { atom, useAtomValue } from 'jotai';
|
||||
|
||||
const ancestorsAtom = atom(function (get) {
|
||||
return [...get(foldersAtom), ...get(workspacesAtom)];
|
||||
});
|
||||
|
||||
export type HeaderModel = HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace;
|
||||
|
||||
export function useInheritedHeaders(baseModel: HeaderModel | null) {
|
||||
const parents = useAtomValue(ancestorsAtom);
|
||||
|
||||
if (baseModel == null) return [];
|
||||
if (baseModel.model === 'workspace') return [];
|
||||
|
||||
const next = (child: HeaderModel): HttpRequestHeader[] => {
|
||||
// Short-circuit
|
||||
if (child.model === 'workspace') {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Recurse up the tree
|
||||
const parent = parents.find((p) => {
|
||||
if (child.folderId) return p.id === child.folderId;
|
||||
else return p.id === child.workspaceId;
|
||||
});
|
||||
|
||||
// Failed to find parent (should never happen)
|
||||
if (parent == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const headers = next(parent);
|
||||
return [...headers, ...parent.headers];
|
||||
};
|
||||
|
||||
return next(baseModel);
|
||||
}
|
||||
Reference in New Issue
Block a user