mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-20 16:01:18 +02:00
Hopefully fix weird env routing issue
This commit is contained in:
51
src-web/commands/createEnvironment.ts
Normal file
51
src-web/commands/createEnvironment.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { createWorkspaceModel, type Environment } from '@yaakapp-internal/models';
|
||||||
|
import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace';
|
||||||
|
import { createFastMutation } from '../hooks/useFastMutation';
|
||||||
|
import { jotaiStore } from '../lib/jotai';
|
||||||
|
import { showPrompt } from '../lib/prompt';
|
||||||
|
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
||||||
|
|
||||||
|
export const createEnvironmentAndActivate = createFastMutation<
|
||||||
|
string | null,
|
||||||
|
unknown,
|
||||||
|
Environment | null
|
||||||
|
>({
|
||||||
|
mutationKey: ['create_environment'],
|
||||||
|
mutationFn: async (baseEnvironment) => {
|
||||||
|
if (baseEnvironment == null) {
|
||||||
|
throw new Error('No base environment passed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const workspaceId = jotaiStore.get(activeWorkspaceIdAtom);
|
||||||
|
if (workspaceId == null) {
|
||||||
|
throw new Error('Cannot create environment when no active workspace');
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = await showPrompt({
|
||||||
|
id: 'new-environment',
|
||||||
|
title: 'New Environment',
|
||||||
|
description: 'Create multiple environments with different sets of variables',
|
||||||
|
label: 'Name',
|
||||||
|
placeholder: 'My Environment',
|
||||||
|
defaultValue: 'My Environment',
|
||||||
|
confirmText: 'Create',
|
||||||
|
});
|
||||||
|
if (name == null) return null;
|
||||||
|
|
||||||
|
return createWorkspaceModel({
|
||||||
|
model: 'environment',
|
||||||
|
name,
|
||||||
|
variables: [],
|
||||||
|
workspaceId,
|
||||||
|
base: false,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onSuccess: async (environmentId) => {
|
||||||
|
if (environmentId == null) {
|
||||||
|
return; // Was not created
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('NAVIGATING', jotaiStore.get(activeWorkspaceIdAtom), environmentId);
|
||||||
|
setWorkspaceSearchParams({ environment_id: environmentId });
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -5,6 +5,7 @@ import { useAtomValue } from 'jotai';
|
|||||||
import type { KeyboardEvent, ReactNode } from 'react';
|
import type { KeyboardEvent, ReactNode } from 'react';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
||||||
import { createFolder } from '../commands/commands';
|
import { createFolder } from '../commands/commands';
|
||||||
|
import { createEnvironmentAndActivate } from '../commands/createEnvironment';
|
||||||
import { openSettings } from '../commands/openSettings';
|
import { openSettings } from '../commands/openSettings';
|
||||||
import { switchWorkspace } from '../commands/switchWorkspace';
|
import { switchWorkspace } from '../commands/switchWorkspace';
|
||||||
import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
|
import { useActiveCookieJar } from '../hooks/useActiveCookieJar';
|
||||||
@@ -12,7 +13,6 @@ import { useActiveEnvironment } from '../hooks/useActiveEnvironment';
|
|||||||
import { useActiveRequest } from '../hooks/useActiveRequest';
|
import { useActiveRequest } from '../hooks/useActiveRequest';
|
||||||
import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace';
|
import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace';
|
||||||
import { useAllRequests } from '../hooks/useAllRequests';
|
import { useAllRequests } from '../hooks/useAllRequests';
|
||||||
import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
|
|
||||||
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
import { useCreateWorkspace } from '../hooks/useCreateWorkspace';
|
||||||
import { useDebouncedState } from '../hooks/useDebouncedState';
|
import { useDebouncedState } from '../hooks/useDebouncedState';
|
||||||
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
||||||
@@ -72,7 +72,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
const activeCookieJar = useActiveCookieJar();
|
const activeCookieJar = useActiveCookieJar();
|
||||||
const [recentRequests] = useRecentRequests();
|
const [recentRequests] = useRecentRequests();
|
||||||
const [, setSidebarHidden] = useSidebarHidden();
|
const [, setSidebarHidden] = useSidebarHidden();
|
||||||
const { mutate: createEnvironment } = useCreateEnvironment();
|
|
||||||
const { mutate: sendRequest } = useSendAnyHttpRequest();
|
const { mutate: sendRequest } = useSendAnyHttpRequest();
|
||||||
|
|
||||||
const workspaceCommands = useMemo<CommandPaletteItem[]>(() => {
|
const workspaceCommands = useMemo<CommandPaletteItem[]>(() => {
|
||||||
@@ -139,7 +138,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
{
|
{
|
||||||
key: 'environment.create',
|
key: 'environment.create',
|
||||||
label: 'Create Environment',
|
label: 'Create Environment',
|
||||||
onSelect: () => createEnvironment(baseEnvironment),
|
onSelect: () => createEnvironmentAndActivate.mutate(baseEnvironment),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'sidebar.toggle',
|
key: 'sidebar.toggle',
|
||||||
@@ -190,7 +189,6 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
activeEnvironment,
|
activeEnvironment,
|
||||||
activeRequest,
|
activeRequest,
|
||||||
baseEnvironment,
|
baseEnvironment,
|
||||||
createEnvironment,
|
|
||||||
createWorkspace,
|
createWorkspace,
|
||||||
httpRequestActions,
|
httpRequestActions,
|
||||||
sendRequest,
|
sendRequest,
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
import { useCreateEnvironment } from '../hooks/useCreateEnvironment';
|
import { createEnvironmentAndActivate } from '../commands/createEnvironment';
|
||||||
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown';
|
||||||
import { useIsEncryptionEnabled } from '../hooks/useIsEncryptionEnabled';
|
import { useIsEncryptionEnabled } from '../hooks/useIsEncryptionEnabled';
|
||||||
import { useKeyValue } from '../hooks/useKeyValue';
|
import { useKeyValue } from '../hooks/useKeyValue';
|
||||||
@@ -41,7 +41,6 @@ interface Props {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
||||||
const createEnvironment = useCreateEnvironment();
|
|
||||||
const { baseEnvironment, otherBaseEnvironments, subEnvironments, allEnvironments } =
|
const { baseEnvironment, otherBaseEnvironments, subEnvironments, allEnvironments } =
|
||||||
useEnvironmentsBreakdown();
|
useEnvironmentsBreakdown();
|
||||||
const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<string | null>(
|
const [selectedEnvironmentId, setSelectedEnvironmentId] = useState<string | null>(
|
||||||
@@ -55,7 +54,7 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
|||||||
|
|
||||||
const handleCreateEnvironment = async () => {
|
const handleCreateEnvironment = async () => {
|
||||||
if (baseEnvironment == null) return;
|
if (baseEnvironment == null) return;
|
||||||
const id = await createEnvironment.mutateAsync(baseEnvironment);
|
const id = await createEnvironmentAndActivate.mutateAsync(baseEnvironment);
|
||||||
if (id != null) setSelectedEnvironmentId(id);
|
if (id != null) setSelectedEnvironmentId(id);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -162,30 +161,30 @@ export const EnvironmentEditDialog = function ({ initialEnvironment }: Props) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const EnvironmentEditor = function ({
|
const EnvironmentEditor = function ({
|
||||||
environment: activeEnvironment,
|
environment: selectedEnvironment,
|
||||||
className,
|
className,
|
||||||
}: {
|
}: {
|
||||||
environment: Environment;
|
environment: Environment;
|
||||||
className?: string;
|
className?: string;
|
||||||
}) {
|
}) {
|
||||||
const activeWorkspaceId = activeEnvironment.workspaceId;
|
const workspaceId = selectedEnvironment.workspaceId;
|
||||||
const isEncryptionEnabled = useIsEncryptionEnabled();
|
const isEncryptionEnabled = useIsEncryptionEnabled();
|
||||||
const valueVisibility = useKeyValue<boolean>({
|
const valueVisibility = useKeyValue<boolean>({
|
||||||
namespace: 'global',
|
namespace: 'global',
|
||||||
key: ['environmentValueVisibility', activeWorkspaceId],
|
key: ['environmentValueVisibility', workspaceId],
|
||||||
fallback: false,
|
fallback: false,
|
||||||
});
|
});
|
||||||
const { allEnvironments } = useEnvironmentsBreakdown();
|
const { allEnvironments } = useEnvironmentsBreakdown();
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(variables: PairWithId[]) => patchModel(activeEnvironment, { variables }),
|
(variables: PairWithId[]) => patchModel(selectedEnvironment, { variables }),
|
||||||
[activeEnvironment],
|
[selectedEnvironment],
|
||||||
);
|
);
|
||||||
const [forceUpdateKey, regenerateForceUpdateKey] = useRandomKey();
|
const [forceUpdateKey, regenerateForceUpdateKey] = useRandomKey();
|
||||||
|
|
||||||
// Gather a list of env names from other environments to help the user get them aligned
|
// Gather a list of env names from other environments to help the user get them aligned
|
||||||
const nameAutocomplete = useMemo<GenericCompletionConfig>(() => {
|
const nameAutocomplete = useMemo<GenericCompletionConfig>(() => {
|
||||||
const options: GenericCompletionOption[] = [];
|
const options: GenericCompletionOption[] = [];
|
||||||
if (activeEnvironment.base) {
|
if (selectedEnvironment.base) {
|
||||||
return { options };
|
return { options };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +194,7 @@ const EnvironmentEditor = function ({
|
|||||||
const containingEnvs = allEnvironments.filter((e) =>
|
const containingEnvs = allEnvironments.filter((e) =>
|
||||||
e.variables.some((v) => v.name === name),
|
e.variables.some((v) => v.name === name),
|
||||||
);
|
);
|
||||||
const isAlreadyInActive = containingEnvs.find((e) => e.id === activeEnvironment.id);
|
const isAlreadyInActive = containingEnvs.find((e) => e.id === selectedEnvironment.id);
|
||||||
if (isAlreadyInActive) continue;
|
if (isAlreadyInActive) continue;
|
||||||
options.push({
|
options.push({
|
||||||
label: name,
|
label: name,
|
||||||
@@ -204,7 +203,7 @@ const EnvironmentEditor = function ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
return { options };
|
return { options };
|
||||||
}, [activeEnvironment.base, activeEnvironment.id, allEnvironments]);
|
}, [selectedEnvironment.base, selectedEnvironment.id, allEnvironments]);
|
||||||
|
|
||||||
const validateName = useCallback((name: string) => {
|
const validateName = useCallback((name: string) => {
|
||||||
// Empty just means the variable doesn't have a name yet and is unusable
|
// Empty just means the variable doesn't have a name yet and is unusable
|
||||||
@@ -217,11 +216,11 @@ const EnvironmentEditor = function ({
|
|||||||
if (!isEncryptionEnabled) {
|
if (!isEncryptionEnabled) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
return !activeEnvironment.variables.every(
|
return !selectedEnvironment.variables.every(
|
||||||
(v) => v.value === '' || analyzeTemplate(v.value) !== 'insecure',
|
(v) => v.value === '' || analyzeTemplate(v.value) !== 'insecure',
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, [activeEnvironment.variables, isEncryptionEnabled]);
|
}, [selectedEnvironment.variables, isEncryptionEnabled]);
|
||||||
|
|
||||||
const encryptEnvironment = (environment: Environment) => {
|
const encryptEnvironment = (environment: Environment) => {
|
||||||
withEncryptionEnabled(async () => {
|
withEncryptionEnabled(async () => {
|
||||||
@@ -238,10 +237,10 @@ const EnvironmentEditor = function ({
|
|||||||
return (
|
return (
|
||||||
<VStack space={4} className={classNames(className, 'pl-4')}>
|
<VStack space={4} className={classNames(className, 'pl-4')}>
|
||||||
<Heading className="w-full flex items-center gap-0.5">
|
<Heading className="w-full flex items-center gap-0.5">
|
||||||
<div className="mr-2">{activeEnvironment?.name}</div>
|
<div className="mr-2">{selectedEnvironment?.name}</div>
|
||||||
{isEncryptionEnabled ? (
|
{isEncryptionEnabled ? (
|
||||||
promptToEncrypt ? (
|
promptToEncrypt ? (
|
||||||
<BadgeButton color="notice" onClick={() => encryptEnvironment(activeEnvironment)}>
|
<BadgeButton color="notice" onClick={() => encryptEnvironment(selectedEnvironment)}>
|
||||||
Encrypt All Variables
|
Encrypt All Variables
|
||||||
</BadgeButton>
|
</BadgeButton>
|
||||||
) : (
|
) : (
|
||||||
@@ -257,9 +256,9 @@ const EnvironmentEditor = function ({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</Heading>
|
</Heading>
|
||||||
{activeEnvironment.public && promptToEncrypt && (
|
{selectedEnvironment.public && promptToEncrypt && (
|
||||||
<DismissibleBanner
|
<DismissibleBanner
|
||||||
id={`warn-unencrypted-${activeEnvironment.id}`}
|
id={`warn-unencrypted-${selectedEnvironment.id}`}
|
||||||
color="notice"
|
color="notice"
|
||||||
className="mr-3"
|
className="mr-3"
|
||||||
>
|
>
|
||||||
@@ -277,11 +276,15 @@ const EnvironmentEditor = function ({
|
|||||||
valueType={valueType}
|
valueType={valueType}
|
||||||
valueAutocompleteVariables
|
valueAutocompleteVariables
|
||||||
valueAutocompleteFunctions
|
valueAutocompleteFunctions
|
||||||
forcedEnvironmentId={activeEnvironment.id}
|
forceUpdateKey={`${selectedEnvironment.id}::${forceUpdateKey}`}
|
||||||
forceUpdateKey={`${activeEnvironment.id}::${forceUpdateKey}`}
|
pairs={selectedEnvironment.variables}
|
||||||
pairs={activeEnvironment.variables}
|
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
stateKey={`environment.${activeEnvironment.id}`}
|
stateKey={`environment.${selectedEnvironment.id}`}
|
||||||
|
forcedEnvironmentId={
|
||||||
|
// Editing the base environment should resolve variables using the active environment.
|
||||||
|
// Editing a sub environment should resolve variables as if it's the active environment
|
||||||
|
selectedEnvironment.base ? undefined : selectedEnvironment.id
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</VStack>
|
</VStack>
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
import type { Environment } from '@yaakapp-internal/models';
|
|
||||||
import { createWorkspaceModel } from '@yaakapp-internal/models';
|
|
||||||
import { useAtomValue } from 'jotai';
|
|
||||||
import { showPrompt } from '../lib/prompt';
|
|
||||||
import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams';
|
|
||||||
import { activeWorkspaceIdAtom } from './useActiveWorkspace';
|
|
||||||
import { useFastMutation } from './useFastMutation';
|
|
||||||
|
|
||||||
export function useCreateEnvironment() {
|
|
||||||
const workspaceId = useAtomValue(activeWorkspaceIdAtom);
|
|
||||||
|
|
||||||
return useFastMutation<string | null, unknown, Environment | null>({
|
|
||||||
mutationKey: ['create_environment', workspaceId],
|
|
||||||
mutationFn: async (baseEnvironment) => {
|
|
||||||
if (baseEnvironment == null) {
|
|
||||||
throw new Error('No base environment passed');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (workspaceId == null) {
|
|
||||||
throw new Error('Cannot create environment when no active workspace');
|
|
||||||
}
|
|
||||||
|
|
||||||
const name = await showPrompt({
|
|
||||||
id: 'new-environment',
|
|
||||||
title: 'New Environment',
|
|
||||||
description: 'Create multiple environments with different sets of variables',
|
|
||||||
label: 'Name',
|
|
||||||
placeholder: 'My Environment',
|
|
||||||
defaultValue: 'My Environment',
|
|
||||||
confirmText: 'Create',
|
|
||||||
});
|
|
||||||
if (name == null) return null;
|
|
||||||
|
|
||||||
return createWorkspaceModel({
|
|
||||||
model: 'environment',
|
|
||||||
name,
|
|
||||||
variables: [],
|
|
||||||
workspaceId,
|
|
||||||
base: false,
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onSuccess: async (environmentId) => {
|
|
||||||
if (environmentId == null) {
|
|
||||||
return; // Was not created
|
|
||||||
}
|
|
||||||
|
|
||||||
setWorkspaceSearchParams({ environment_id: environmentId });
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user