mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
mock-extraction feedback (#7207)
* save point * both cases work * use action and hack * throw error on naming collision * fix type * fix test
This commit is contained in:
parent
39bc37bf10
commit
e38badfbf3
@ -1,22 +1,15 @@
|
||||
import { loadFixture } from '../../playwright/paths';
|
||||
import { test } from '../../playwright/test';
|
||||
|
||||
test('can make a mock route', async ({ app, page }) => {
|
||||
test('can make a mock route', async ({ page }) => {
|
||||
test.slow(process.platform === 'darwin' || process.platform === 'win32', 'Slow app start on these platforms');
|
||||
const text = await loadFixture('smoke-test-collection.yaml');
|
||||
await app.evaluate(async ({ clipboard }, text) => clipboard.writeText(text), text);
|
||||
|
||||
await page.getByRole('button', { name: 'Create in project' }).click();
|
||||
await page.getByRole('menuitemradio', { name: 'Import' }).click();
|
||||
await page.locator('[data-test-id="import-from-clipboard"]').click();
|
||||
await page.getByRole('button', { name: 'Scan' }).click();
|
||||
await page.getByRole('dialog').getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
await page.getByLabel('New Mock Server').click();
|
||||
await page.getByRole('button', { name: 'Create', exact: true }).click();
|
||||
await page.getByRole('button', { name: 'New Mock Route' }).click();
|
||||
await page.getByText('GET/').click();
|
||||
await page.getByTestId('CodeEditor').getByRole('textbox').fill('123');
|
||||
await page.getByLabel('Project Actions').click();
|
||||
await page.getByText('Rename').click();
|
||||
await page.locator('#prompt-input').fill('/123');
|
||||
await page.getByRole('button', { name: 'Rename' }).click();
|
||||
|
||||
await page.getByRole('button', { name: 'Test' }).click();
|
||||
await page.getByText('No body returned for response').click();
|
||||
|
@ -2,21 +2,32 @@ import fs from 'fs/promises';
|
||||
import React, { useState } from 'react';
|
||||
import { Button } from 'react-aria-components';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { useRouteLoaderData } from 'react-router-dom';
|
||||
import {
|
||||
useFetcher,
|
||||
useRouteLoaderData,
|
||||
} from 'react-router-dom';
|
||||
|
||||
import { invariant } from '../../../utils/invariant';
|
||||
import { useMockRoutePatcher } from '../../routes/mock-route';
|
||||
import { RequestLoaderData } from '../../routes/request';
|
||||
import { WorkspaceLoaderData } from '../../routes/workspace';
|
||||
import { HelpTooltip } from '../help-tooltip';
|
||||
import { Icon } from '../icon';
|
||||
import { showPrompt } from '../modals';
|
||||
|
||||
export const MockResponseExtractor = () => {
|
||||
const {
|
||||
activeWorkspace,
|
||||
} = useRouteLoaderData(':workspaceId') as WorkspaceLoaderData;
|
||||
const { mockServerAndRoutes, activeResponse } = useRouteLoaderData('request/:requestId') as RequestLoaderData;
|
||||
const patchMockRoute = useMockRoutePatcher();
|
||||
const navigate = useNavigate();
|
||||
const {
|
||||
organizationId,
|
||||
projectId,
|
||||
workspaceId,
|
||||
} = useParams();
|
||||
const fetcher = useFetcher();
|
||||
const [selectedMockServer, setSelectedMockServer] = useState('');
|
||||
const [selectedMockRoute, setSelectedMockRoute] = useState('');
|
||||
return (
|
||||
@ -30,21 +41,84 @@ export const MockResponseExtractor = () => {
|
||||
<form
|
||||
onSubmit={async e => {
|
||||
e.preventDefault();
|
||||
if (!selectedMockServer || !selectedMockRoute) {
|
||||
if (selectedMockServer && selectedMockRoute) {
|
||||
if (activeResponse) {
|
||||
// TODO: move this out of the renderer, and upsert mock
|
||||
const body = await fs.readFile(activeResponse.bodyPath);
|
||||
|
||||
patchMockRoute(selectedMockRoute, {
|
||||
body: body.toString(),
|
||||
mimeType: activeResponse.contentType,
|
||||
statusCode: activeResponse.statusCode,
|
||||
headers: activeResponse.headers,
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
let path = '/new-route';
|
||||
try {
|
||||
path = activeResponse ? new URL(activeResponse.url).pathname : '/new-route';
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
if (!selectedMockServer) {
|
||||
showPrompt({
|
||||
title: 'Create Mock Route',
|
||||
defaultValue: path,
|
||||
label: 'Name',
|
||||
onComplete: async name => {
|
||||
invariant(activeResponse, 'Active response must be defined');
|
||||
const body = await fs.readFile(activeResponse.bodyPath);
|
||||
// TODO: consider setting selected mock server here rather than redirecting
|
||||
fetcher.submit(
|
||||
JSON.stringify({
|
||||
name: name,
|
||||
body: body.toString(),
|
||||
mimeType: activeResponse.contentType,
|
||||
statusCode: activeResponse.statusCode,
|
||||
headers: activeResponse.headers,
|
||||
mockServerName: activeWorkspace.name,
|
||||
}),
|
||||
{
|
||||
encType: 'application/json',
|
||||
method: 'post',
|
||||
action: `/organization/${organizationId}/project/${projectId}/workspace/${workspaceId}/mock-server/mock-route/new`,
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (!selectedMockRoute) {
|
||||
showPrompt({
|
||||
title: 'Create Mock Route',
|
||||
defaultValue: path,
|
||||
label: 'Name',
|
||||
onComplete: async name => {
|
||||
invariant(activeResponse, 'Active response must be defined');
|
||||
const body = await fs.readFile(activeResponse.bodyPath);
|
||||
|
||||
if (activeResponse) {
|
||||
// TODO: move this out of the renderer, and upsert mock
|
||||
const body = await fs.readFile(activeResponse.bodyPath);
|
||||
// setSelectedMockRoute(newRoute._id);
|
||||
|
||||
patchMockRoute(selectedMockRoute, {
|
||||
body: body.toString(),
|
||||
mimeType: activeResponse.contentType,
|
||||
statusCode: activeResponse.statusCode,
|
||||
headers: activeResponse.headers,
|
||||
fetcher.submit(
|
||||
JSON.stringify({
|
||||
name: name,
|
||||
parentId: selectedMockServer,
|
||||
body: body.toString(),
|
||||
mimeType: activeResponse.contentType,
|
||||
statusCode: activeResponse.statusCode,
|
||||
headers: activeResponse.headers,
|
||||
}),
|
||||
{
|
||||
encType: 'application/json',
|
||||
method: 'post',
|
||||
action: `/organization/${organizationId}/project/${projectId}/workspace/${workspaceId}/mock-server/mock-route/new`,
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
}}
|
||||
>
|
||||
<div className="form-row">
|
||||
@ -61,7 +135,7 @@ export const MockResponseExtractor = () => {
|
||||
setSelectedMockServer(selected);
|
||||
}}
|
||||
>
|
||||
<option value="">-- Select... --</option>
|
||||
<option value="">-- Create new... --</option>
|
||||
{mockServerAndRoutes
|
||||
.map(w => (
|
||||
<option key={w._id} value={w._id}>
|
||||
@ -87,7 +161,7 @@ export const MockResponseExtractor = () => {
|
||||
setSelectedMockRoute(selected);
|
||||
}}
|
||||
>
|
||||
<option value="">-- Select... --</option>
|
||||
<option value="">-- Create new... --</option>
|
||||
{mockServerAndRoutes.find(s => s._id === selectedMockServer)?.routes
|
||||
.map(w => (
|
||||
<option key={w._id} value={w._id}>
|
||||
@ -112,10 +186,9 @@ export const MockResponseExtractor = () => {
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
isDisabled={!selectedMockServer || !selectedMockRoute}
|
||||
className="hover:no-underline bg-[--color-surprise] hover:bg-opacity-90 border border-solid border-[--hl-md] py-2 px-3 text-[--color-font-surprise] transition-colors rounded-sm"
|
||||
>
|
||||
Export
|
||||
Extract to mock route
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
@ -1211,9 +1211,26 @@ export const createMockRouteAction: ActionFunction = async ({ request, params })
|
||||
|
||||
const patch = await request.json();
|
||||
invariant(typeof patch.name === 'string', 'Name is required');
|
||||
invariant(typeof patch.parentId === 'string', 'parentId is required');
|
||||
// TODO: remove this hack
|
||||
if (patch.mockServerName) {
|
||||
const activeWorkspace = await models.workspace.getById(workspaceId);
|
||||
invariant(activeWorkspace, 'Active workspace not found');
|
||||
const workspace = await models.workspace.create({
|
||||
name: activeWorkspace.name,
|
||||
scope: 'mock-server',
|
||||
parentId: projectId,
|
||||
});
|
||||
invariant(workspace, 'Workspace not found');
|
||||
// create a mock server under the workspace with the same name
|
||||
const newServer = await models.mockServer.getOrCreateForParentId(workspace._id, { name: activeWorkspace.name });
|
||||
// TODO: filterout the mockServerName from the patch, or use an alternate method to create new workspace and server
|
||||
const mockRoute = await models.mockRoute.create({ ...patch, parentId: newServer._id });
|
||||
return redirect(`/organization/${organizationId}/project/${projectId}/workspace/${newServer.parentId}/mock-server/mock-route/${mockRoute._id}`);
|
||||
}
|
||||
const mockServer = await models.mockServer.getById(patch.parentId);
|
||||
invariant(mockServer, 'Mock server not found');
|
||||
const mockRoute = await models.mockRoute.create(patch);
|
||||
return redirect(`/organization/${organizationId}/project/${projectId}/workspace/${workspaceId}/mock-server/mock-route/${mockRoute._id}`);
|
||||
return redirect(`/organization/${organizationId}/project/${projectId}/workspace/${mockServer.parentId}/mock-server/mock-route/${mockRoute._id}`);
|
||||
};
|
||||
export const updateMockRouteAction: ActionFunction = async ({ request, params }) => {
|
||||
const { mockRouteId } = params;
|
||||
|
@ -17,10 +17,12 @@ import { CodeEditor } from '../components/codemirror/code-editor';
|
||||
import { MockResponseHeadersEditor } from '../components/editors/mock-response-headers-editor';
|
||||
import { MockResponsePane } from '../components/mocks/mock-response-pane';
|
||||
import { MockUrlBar } from '../components/mocks/mock-url-bar';
|
||||
import { showAlert } from '../components/modals';
|
||||
import { showAlert, showModal } from '../components/modals';
|
||||
import { AlertModal } from '../components/modals/alert-modal';
|
||||
import { EmptyStatePane } from '../components/panes/empty-state-pane';
|
||||
import { Pane, PaneBody, PaneHeader } from '../components/panes/pane';
|
||||
import { SvgIcon } from '../components/svg-icon';
|
||||
import { MockServerLoaderData } from './mock-server';
|
||||
import { useRootLoaderData } from './root';
|
||||
|
||||
export interface MockRouteLoaderData {
|
||||
@ -91,6 +93,8 @@ export const useMockRoutePatcher = () => {
|
||||
|
||||
export const MockRouteRoute = () => {
|
||||
const { mockServer, mockRoute } = useRouteLoaderData(':mockRouteId') as MockRouteLoaderData;
|
||||
const { mockRoutes } = useRouteLoaderData('mock-server') as MockServerLoaderData;
|
||||
|
||||
const { userSession } = useRootLoaderData();
|
||||
const patchMockRoute = useMockRoutePatcher();
|
||||
const mockbinUrl = mockServer.useInsomniaCloud ? getMockServiceURL() : mockServer.url;
|
||||
@ -142,6 +146,15 @@ export const MockRouteRoute = () => {
|
||||
});
|
||||
|
||||
const upsertMockbinHar = async (pathInput?: string) => {
|
||||
const hasRouteInServer = mockRoutes.filter(m => m._id !== mockRoute._id).find(m => m.name === pathInput);
|
||||
if (hasRouteInServer) {
|
||||
showModal(AlertModal, {
|
||||
title: 'Error',
|
||||
message: `Path "${pathInput}" must be unique. Please enter a different name.`,
|
||||
});
|
||||
|
||||
return;
|
||||
};
|
||||
const compoundId = mockRoute.parentId + pathInput;
|
||||
const error = await upsertBinOnRemoteFromResponse(compoundId);
|
||||
if (error) {
|
||||
@ -163,6 +176,15 @@ export const MockRouteRoute = () => {
|
||||
});
|
||||
};
|
||||
const onSend = async (pathInput: string) => {
|
||||
const hasRouteInServer = mockRoutes.filter(m => m._id !== mockRoute._id).find(m => m.name === pathInput);
|
||||
if (hasRouteInServer) {
|
||||
showModal(AlertModal, {
|
||||
title: 'Error',
|
||||
message: `Path "${pathInput}" must be unique. Please enter a different name.`,
|
||||
});
|
||||
|
||||
return;
|
||||
};
|
||||
await upsertMockbinHar(pathInput);
|
||||
const compoundId = mockRoute.parentId + pathInput;
|
||||
createandSendPrivateRequest({
|
||||
|
@ -11,17 +11,18 @@ import { WorkspaceSyncDropdown } from '../components/dropdowns/workspace-sync-dr
|
||||
import { EditableInput } from '../components/editable-input';
|
||||
import { Icon } from '../components/icon';
|
||||
import { showModal, showPrompt } from '../components/modals';
|
||||
import { AlertModal } from '../components/modals/alert-modal';
|
||||
import { AskModal } from '../components/modals/ask-modal';
|
||||
import { EmptyStatePane } from '../components/panes/empty-state-pane';
|
||||
import { SidebarLayout } from '../components/sidebar-layout';
|
||||
import { SvgIcon } from '../components/svg-icon';
|
||||
import { formatMethodName } from '../components/tags/method-tag';
|
||||
import { MockRouteResponse, MockRouteRoute, useMockRoutePatcher } from './mock-route';
|
||||
interface LoaderData {
|
||||
export interface MockServerLoaderData {
|
||||
mockServerId: string;
|
||||
mockRoutes: MockRoute[];
|
||||
}
|
||||
export const loader: LoaderFunction = async ({ params }): Promise<LoaderData> => {
|
||||
export const loader: LoaderFunction = async ({ params }): Promise<MockServerLoaderData> => {
|
||||
const { organizationId, projectId, workspaceId } = params;
|
||||
invariant(organizationId, 'Organization ID is required');
|
||||
invariant(projectId, 'Project ID is required');
|
||||
@ -46,7 +47,7 @@ const MockServerRoute = () => {
|
||||
workspaceId: string;
|
||||
mockRouteId: string;
|
||||
};
|
||||
const { mockServerId, mockRoutes } = useLoaderData() as LoaderData;
|
||||
const { mockServerId, mockRoutes } = useLoaderData() as MockServerLoaderData;
|
||||
const fetcher = useFetcher();
|
||||
const navigate = useNavigate();
|
||||
const patchMockRoute = useMockRoutePatcher();
|
||||
@ -66,6 +67,14 @@ const MockServerRoute = () => {
|
||||
defaultValue: mockRoutes.find(s => s._id === id)?.name,
|
||||
submitName: 'Rename',
|
||||
onComplete: name => {
|
||||
const hasRouteInServer = mockRoutes.filter(m => m._id !== id).find(m => m.name === name);
|
||||
if (hasRouteInServer) {
|
||||
showModal(AlertModal, {
|
||||
title: 'Error',
|
||||
message: `Path "${name}" must be unique. Please enter a different name.`,
|
||||
});
|
||||
return;
|
||||
};
|
||||
name && patchMockRoute(id, { name });
|
||||
},
|
||||
});
|
||||
@ -195,6 +204,14 @@ const MockServerRoute = () => {
|
||||
});
|
||||
}}
|
||||
onSubmit={name => {
|
||||
const hasRouteInServer = mockRoutes.filter(m => m._id !== item._id).find(m => m.name === name);
|
||||
if (hasRouteInServer) {
|
||||
showModal(AlertModal, {
|
||||
title: 'Error',
|
||||
message: `Path "${name}" must be unique. Please enter a different name.`,
|
||||
});
|
||||
return;
|
||||
};
|
||||
name && fetcher.submit(
|
||||
{ name },
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user