Prompt for import as collection or document (#3130)

* feat: add prompt and update import logic

* feat: update usages and plugin api

* chore: WIP

* chore: convert to options

* chore: don't create workspace model until needed

* chore: add OR condition

* chore: add workspace name and don't change scope

* feat: prompt with appropriate name

* chore: rename

* chore: rename type

* chore: update signature

* chore: properly type the import functions

* feat: don't activate the workspace after importing

* chore: show loading on homepage

* fix: typo

* chore: fix tests and rename

* Update packages/insomnia-app/app/common/__tests__/import.test.js

Co-authored-by: David Marby <david@dmarby.se>

Co-authored-by: David Marby <david@dmarby.se>
This commit is contained in:
Opender Singh 2021-03-03 10:16:48 +13:00 committed by GitHub
parent 4b993a7762
commit 049964bb9b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 230 additions and 119 deletions

View File

@ -376,14 +376,26 @@ describe('export', () => {
});
});
describe('isApiSpec()', () => {
describe('isApiSpecImport()', () => {
it.each(['swagger2', 'openapi3'])('should return true if spec id is %o', (id: string) => {
expect(importUtil.isApiSpec(id)).toBe(true);
expect(importUtil.isApiSpecImport({ id })).toBe(true);
});
it('should return false if spec id is not valid', () => {
const id = 'invalid-id';
expect(importUtil.isApiSpec(id)).toBe(false);
expect(importUtil.isApiSpecImport({ id })).toBe(false);
});
});
describe('isInsomniaV4Import()', () => {
it.each(['insomnia-4'])('should return true if spec id is %o', (id: string) => {
expect(importUtil.isInsomniaV4Import({ id })).toBe(true);
});
it('should return false if spec id is not valid', () => {
const id = 'invalid-id';
expect(importUtil.isInsomniaV4Import({ id })).toBe(false);
});
});

View File

@ -20,6 +20,8 @@ import {
isRequestGroup,
isWorkspace,
} from '../models/helpers/is-model';
import type { Workspace, WorkspaceScope } from '../models/workspace';
import type { ApiSpec } from '../models/api-spec';
const WORKSPACE_ID_KEY = '__WORKSPACE_ID__';
const BASE_ENVIRONMENT_ID_KEY = '__BASE_ENVIRONMENT_ID__';
@ -61,10 +63,25 @@ export type ImportResult = {
summary: { [string]: Array<BaseModel> },
};
export async function importUri(
type ConvertResultType = {
id: string,
name: string,
description: string,
};
type ConvertResult = {
type: ConvertResultType,
data: {
resources: Array<Object>,
},
};
export type ImportRawConfig = {
getWorkspaceId: () => Promise<string | null>,
uri: string,
): Promise<ImportResult> {
getWorkspaceScope?: string => Promise<WorkspaceScope>,
};
export async function importUri(uri: string, importConfig: ImportRawConfig): Promise<ImportResult> {
let rawText;
// If GH preview, force raw
@ -86,7 +103,7 @@ export async function importUri(
rawText = decodeURIComponent(uri);
}
const result = await importRaw(getWorkspaceId, rawText);
const result = await importRaw(rawText, importConfig);
const { summary, error } = result;
if (error) {
@ -118,10 +135,10 @@ export async function importUri(
}
export async function importRaw(
getWorkspaceId: () => Promise<string | null>,
rawContent: string,
{ getWorkspaceId, getWorkspaceScope }: ImportRawConfig,
): Promise<ImportResult> {
let results;
let results: ConvertResult;
try {
results = await convert(rawContent);
} catch (err) {
@ -132,7 +149,7 @@ export async function importRaw(
};
}
const { data } = results;
const { data, type: resultsType } = results;
// Generate all the ids we may need
const generatedIds: { [string]: string | Function } = {};
@ -147,17 +164,14 @@ export async function importRaw(
const workspaceId = await getWorkspaceId();
// First try getting the workspace to overwrite
let workspace = await models.workspace.getById(workspaceId || 'n/a');
// If none provided, create a new workspace
if (workspace === null) {
workspace = await models.workspace.create({ name: 'Imported Workspace' });
}
const workspace = await models.workspace.getById(workspaceId || 'n/a');
// Update this fn so it doesn't run again
generatedIds[WORKSPACE_ID_KEY] = workspace._id;
const idToUse = workspace?._id || generateId(models.workspace.prefix);
return workspace._id;
generatedIds[WORKSPACE_ID_KEY] = idToUse;
return idToUse;
};
// Contains the ID of the base environment to be used with the import
@ -240,8 +254,15 @@ export async function importRaw(
const existingDoc = await db.get(model.type, resource._id);
let newDoc: BaseModel;
if (existingDoc) {
// If workspace, don't overwrite the existing scope
if (isWorkspace(model)) {
(resource: Workspace).scope = (existingDoc: Workspace).scope;
}
newDoc = await db.docUpdate(existingDoc, resource);
} else {
if (isWorkspace(model)) {
await updateWorkspaceScope(resource, resultsType, getWorkspaceScope);
}
newDoc = await db.docCreate(model.type, resource);
// Mark as not seen if we created a new workspace from sync
@ -256,7 +277,7 @@ export async function importRaw(
// Store spec under workspace if it's OpenAPI
for (const workspace of importedDocs[models.workspace.type]) {
if (isApiSpec(results.type.id)) {
if (isApiSpecImport(resultsType)) {
const spec = await models.apiSpec.updateOrCreateForParentId(workspace._id, {
contents: rawContent,
contentType: 'yaml',
@ -274,17 +295,46 @@ export async function importRaw(
await db.flushChanges();
trackEvent('Data', 'Import', results.type.id);
trackEvent('Data', 'Import', resultsType.id);
return {
source: results.type && typeof results.type.id === 'string' ? results.type.id : 'unknown',
source: resultsType && typeof resultsType.id === 'string' ? resultsType.id : 'unknown',
summary: importedDocs,
error: null,
};
}
export function isApiSpec(content: string): boolean {
return content === 'openapi3' || content === 'swagger2';
async function updateWorkspaceScope(
resource: Workspace,
resultType: ConvertResultType,
getWorkspaceScope?: string => Promise<WorkspaceScope>,
) {
// Set the workspace scope if creating a new workspace
// IF is creating a new workspace
// AND imported resource has no preset scope property OR scope is null
// AND we have a function to get scope
if ((!resource.hasOwnProperty('scope') || resource.scope === null) && getWorkspaceScope) {
const workspaceName = resource.name;
let specName;
// If is from insomnia v4 and the spec has contents, add to the name when prompting
if (isInsomniaV4Import(resultType)) {
const spec: ApiSpec | null = await models.apiSpec.getByParentId(resource._id);
if (spec && spec.contents.trim()) {
specName = spec.fileName;
}
}
const nameToPrompt = specName ? `${specName} / ${workspaceName}` : workspaceName;
(resource: Workspace).scope = await getWorkspaceScope(nameToPrompt);
}
}
export function isApiSpecImport({ id }: ConvertResultType): boolean {
return id === 'openapi3' || id === 'swagger2';
}
export function isInsomniaV4Import({ id }: ConvertResultType): boolean {
return id === 'insomnia-4';
}
export async function exportWorkspacesHAR(

View File

@ -29,7 +29,7 @@ export async function migrate(doc: ApiSpec): Promise<ApiSpec> {
return doc;
}
export function getByParentId(workspaceId: string): Promise<ApiSpec> {
export function getByParentId(workspaceId: string): Promise<ApiSpec | null> {
return db.getWhere(type, { parentId: workspaceId });
}

View File

@ -11,10 +11,17 @@ export const prefix = 'wrk';
export const canDuplicate = true;
export const canSync = true;
export const WorkspaceScopeKeys = {
designer: 'designer',
collection: 'collection',
};
export type WorkspaceScope = $Keys<typeof WorkspaceScopeKeys>;
type BaseWorkspace = {
name: string,
description: string,
scope: 'designer' | 'collection',
scope: WorkspaceScope,
};
export type Workspace = BaseModel & BaseWorkspace;
@ -48,7 +55,7 @@ export async function all(): Promise<Array<Workspace>> {
if (workspaces.length === 0) {
// Create default workspace
await create({ name: getAppName(), scope: 'collection' });
await create({ name: getAppName(), scope: WorkspaceScopeKeys.collection });
return all();
} else {
return workspaces;
@ -112,7 +119,10 @@ async function _migrateEnsureName(workspace: Workspace): Promise<Workspace> {
* Ensure workspace scope is set to a valid entry
*/
function _migrateScope(workspace: Workspace): Workspace {
if (workspace.scope === 'designer' || workspace.scope === 'collection') {
if (
workspace.scope === WorkspaceScopeKeys.designer ||
workspace.scope === WorkspaceScopeKeys.collection
) {
return workspace;
}
@ -120,13 +130,13 @@ function _migrateScope(workspace: Workspace): Workspace {
type OldScopeTypes = 'spec' | 'debug' | null;
switch ((workspace.scope: OldScopeTypes)) {
case 'spec': {
workspace.scope = 'designer';
workspace.scope = WorkspaceScopeKeys.designer;
break;
}
case 'debug':
case null:
default:
workspace.scope = 'collection';
workspace.scope = WorkspaceScopeKeys.collection;
break;
}

View File

@ -5,17 +5,20 @@ import {
importRaw,
importUri,
} from '../../common/import';
import type { Workspace } from '../../models/workspace';
import type { Workspace, WorkspaceScope } from '../../models/workspace';
import type { ImportRawConfig } from '../../common/import';
type PluginImportOptions = { workspaceId?: string, scope?: WorkspaceScope };
export function init(): { data: { import: Object, export: Object } } {
return {
data: {
import: {
async uri(uri: string, options: { workspaceId?: string } = {}): Promise<void> {
await importUri(() => Promise.resolve(options.workspaceId || null), uri);
async uri(uri: string, options: PluginImportOptions = {}): Promise<void> {
await importUri(uri, buildImportRawConfig(options));
},
async raw(text: string, options: { workspaceId?: string } = {}): Promise<void> {
await importRaw(() => Promise.resolve(options.workspaceId || null), text);
async raw(text: string, options: PluginImportOptions = {}): Promise<void> {
await importRaw(text, buildImportRawConfig(options));
},
},
export: {
@ -42,3 +45,9 @@ export function init(): { data: { import: Object, export: Object } } {
},
};
}
function buildImportRawConfig(options: PluginImportOptions): ImportRawConfig {
const getWorkspaceId = () => Promise.resolve(options.workspaceId || null);
const getWorkspaceScope = options.scope && (() => Promise.resolve(options.scope));
return { getWorkspaceId, getWorkspaceScope };
}

View File

@ -4,12 +4,12 @@ import Hotkey from '../hotkey';
import { hotKeyRefs } from '../../../common/hotkeys';
import * as hotkeys from '../../../common/hotkeys';
import type { Request } from '../../../models/request';
import type { ForceToWorkspace } from '../../redux/modules/helpers';
import { Pane, PaneBody, PaneHeader } from './pane';
import type { HandleImportFileCallback } from '../wrapper';
type Props = {
hotKeyRegistry: hotkeys.HotKeyRegistry,
handleImportFile: (forceToWorkspace?: ForceToWorkspace) => void,
handleImportFile: HandleImportFileCallback,
handleCreateRequest: () => Promise<Request>,
};

View File

@ -28,11 +28,11 @@ import RenderedQueryString from '../rendered-query-string';
import RequestUrlBar from '../request-url-bar.js';
import type { Settings } from '../../../models/settings';
import RequestParametersEditor from '../editors/request-parameters-editor';
import type { ForceToWorkspace } from '../../redux/modules/helpers';
import PlaceholderRequestPane from './placeholder-request-pane';
import { Pane, paneBodyClasses, PaneHeader } from './pane';
import classnames from 'classnames';
import { queryAllWorkspaceUrls } from '../../../models/helpers/query-all-workspace-urls';
import type { HandleImportFileCallback } from '../wrapper';
type Props = {
// Functions
@ -56,7 +56,7 @@ type Props = {
updateSettingsUseBulkHeaderEditor: Function,
updateSettingsUseBulkParametersEditor: Function,
handleImport: Function,
handleImportFile: (forceToWorkspace?: ForceToWorkspace) => void,
handleImportFile: HandleImportFileCallback,
// Other
workspace: Workspace,

View File

@ -2,7 +2,7 @@
import * as React from 'react';
import { autoBindMethodsForReact } from 'class-autobind-decorator';
import PageLayout from './page-layout';
import type { WrapperProps } from './wrapper';
import type { HandleImportFileCallback, WrapperProps } from './wrapper';
import RequestPane from './panes/request-pane';
import ErrorBoundary from './error-boundary';
import ResponsePane from './panes/response-pane';
@ -11,7 +11,6 @@ import SidebarFilter from './sidebar/sidebar-filter';
import EnvironmentsDropdown from './dropdowns/environments-dropdown';
import { AUTOBIND_CFG } from '../../common/constants';
import { isGrpcRequest } from '../../models/helpers/is-model';
import type { ForceToWorkspace } from '../redux/modules/helpers';
import GrpcRequestPane from './panes/grpc-request-pane';
import GrpcResponsePane from './panes/grpc-response-pane';
import WorkspacePageHeader from './workspace-page-header';
@ -31,7 +30,7 @@ type Props = {
handleForceUpdateRequest: Function,
handleForceUpdateRequestHeaders: Function,
handleImport: Function,
handleImportFile: (forceToWorkspace?: ForceToWorkspace) => void,
handleImportFile: HandleImportFileCallback,
handleRequestCreate: Function,
handleRequestGroupCreate: Function,
handleSendAndDownloadRequestWithActiveEnvironment: Function,

View File

@ -37,11 +37,15 @@ import Highlight from './base/highlight';
import type { GlobalActivity } from '../../common/constants';
import { fuzzyMatchAll } from '../../common/misc';
import type { WrapperProps } from './wrapper';
import type {
HandleImportClipboardCallback,
HandleImportFileCallback,
HandleImportUriCallback,
WrapperProps,
} from './wrapper';
import Notice from './notice';
import GitRepositorySettingsModal from '../components/modals/git-repository-settings-modal';
import PageLayout from './page-layout';
import type { ForceToWorkspace } from '../redux/modules/helpers';
import { ForceToWorkspaceKeys } from '../redux/modules/helpers';
import coreLogo from '../images/insomnia-core-logo.png';
import { MemPlugin } from '../../sync/git/mem-plugin';
@ -58,9 +62,9 @@ import AccountDropdown from './dropdowns/account-dropdown';
type Props = {|
wrapperProps: WrapperProps,
handleImportFile: (forceToWorkspace: ForceToWorkspace) => void,
handleImportUri: (uri: string, forceToWorkspace: ForceToWorkspace) => void,
handleImportClipboard: (forceToWorkspace: ForceToWorkspace) => void,
handleImportFile: HandleImportFileCallback,
handleImportUri: HandleImportUriCallback,
handleImportClipboard: HandleImportClipboardCallback,
|};
type State = {|
@ -121,11 +125,11 @@ class WrapperHome extends React.PureComponent<Props, State> {
}
_handleImportFile() {
this.props.handleImportFile(ForceToWorkspaceKeys.new);
this.props.handleImportFile({ forceToWorkspace: ForceToWorkspaceKeys.new });
}
_handleImportClipBoard() {
this.props.handleImportClipboard(ForceToWorkspaceKeys.new);
this.props.handleImportClipboard({ forceToWorkspace: ForceToWorkspaceKeys.new });
}
_handleImportUri() {
@ -135,7 +139,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
label: 'URL',
placeholder: 'https://website.com/insomnia-import.json',
onComplete: uri => {
this.props.handleImportUri(uri, ForceToWorkspaceKeys.new);
this.props.handleImportUri(uri, { forceToWorkspace: ForceToWorkspaceKeys.new });
},
});
}
@ -446,7 +450,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
<Dropdown renderButton={button}>
<DropdownDivider>New</DropdownDivider>
<DropdownItem icon={<i className="fa fa-file-o" />} onClick={this._handleDocumentCreate}>
Blank Document
Design Document
</DropdownItem>
<DropdownItem icon={<i className="fa fa-bars" />} onClick={this._handleCollectionCreate}>
Request Collection
@ -495,7 +499,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
}
render() {
const { workspaces } = this.props.wrapperProps;
const { workspaces, isLoading } = this.props.wrapperProps;
const { filter } = this.state;
// Render each card, removing all the ones that don't match the filter
@ -511,6 +515,7 @@ class WrapperHome extends React.PureComponent<Props, State> {
<React.Fragment>
<img src={coreLogo} alt="Insomnia" width="24" height="24" />
<Breadcrumb className="breadcrumb" crumbs={[getAppName()]} />
{isLoading ? <i className="fa fa-refresh fa-spin space-left" /> : null}
</React.Fragment>
}
gridRight={

View File

@ -6,17 +6,17 @@ import { showPrompt } from './modals';
import type { BaseModel } from '../../models';
import * as models from '../../models';
import { AUTOBIND_CFG, getAppLongName, getAppName } from '../../common/constants';
import type { WrapperProps } from './wrapper';
import type { HandleImportFileCallback, HandleImportUriCallback, WrapperProps } from './wrapper';
import * as db from '../../common/database';
import chartSrc from '../images/chart.svg';
import type { ForceToWorkspace } from '../redux/modules/helpers';
import { ForceToWorkspaceKeys } from '../redux/modules/helpers';
import OnboardingContainer from './onboarding-container';
import { WorkspaceScopeKeys } from '../../models/workspace';
type Props = {|
wrapperProps: WrapperProps,
handleImportFile: (forceToWorkspace: ForceToWorkspace) => any,
handleImportUri: (uri: string, forceToWorkspace: ForceToWorkspace) => any,
handleImportFile: HandleImportFileCallback,
handleImportUri: HandleImportUriCallback,
|};
type State = {|
@ -77,7 +77,10 @@ class WrapperOnboarding extends React.PureComponent<Props, State> {
_handleImportFile() {
const { handleImportFile } = this.props;
handleImportFile(ForceToWorkspaceKeys.new);
handleImportFile({
forceToWorkspace: ForceToWorkspaceKeys.new,
forceToScope: WorkspaceScopeKeys.designer,
});
}
_handleImportUri(defaultValue: string) {
@ -89,7 +92,10 @@ class WrapperOnboarding extends React.PureComponent<Props, State> {
placeholder: 'https://example.com/openapi-spec.yaml',
label: 'URI to Import',
onComplete: value => {
handleImportUri(value, ForceToWorkspaceKeys.new);
handleImportUri(value, {
forceToWorkspace: ForceToWorkspaceKeys.new,
forceToScope: WorkspaceScopeKeys.designer,
});
},
});
}

View File

@ -86,7 +86,6 @@ import WrapperDebug from './wrapper-debug';
import { importRaw } from '../../common/import';
import GitSyncDropdown from './dropdowns/git-sync-dropdown';
import { DropdownButton } from './base/dropdown';
import type { ForceToWorkspace } from '../redux/modules/helpers';
import type { UnitTest } from '../../models/unit-test';
import type { UnitTestResult } from '../../models/unit-test-result';
import type { UnitTestSuite } from '../../models/unit-test-suite';
@ -95,6 +94,7 @@ import { Spectral } from '@stoplight/spectral';
import ProtoFilesModal from './modals/proto-files-modal';
import { GrpcDispatchModalWrapper } from '../context/grpc';
import WrapperMigration from './wrapper-migration';
import type { ImportOptions } from '../redux/modules/global';
const spectral = new Spectral();
@ -103,16 +103,9 @@ export type WrapperProps = {
handleActivateRequest: Function,
handleSetSidebarFilter: Function,
handleToggleMenuBar: Function,
handleImportFileToWorkspace: (workspaceId: string, forceToWorkspace?: ForceToWorkspace) => void,
handleImportClipBoardToWorkspace: (
workspaceId: string,
forceToWorkspace?: ForceToWorkspace,
) => void,
handleImportUriToWorkspace: (
workspaceId: string,
uri: string,
forceToWorkspace?: ForceToWorkspace,
) => void,
handleImportFileToWorkspace: (workspaceId: string, options?: ImportOptions) => void,
handleImportClipBoardToWorkspace: (workspaceId: string, options?: ImportOptions) => void,
handleImportUriToWorkspace: (workspaceId: string, uri: string, options?: ImportOptions) => void,
handleInitializeEntities: () => Promise<void>,
handleExportFile: Function,
handleShowExportRequestsModal: Function,
@ -205,6 +198,10 @@ export type WrapperProps = {
activeResponse: Response | null,
};
export type HandleImportFileCallback = (options?: ImportOptions) => void;
export type HandleImportClipboardCallback = (options?: ImportOptions) => void;
export type HandleImportUriCallback = (uri: string, options?: ImportOptions) => void;
type State = {
forceRefreshKey: number,
activeGitBranch: string,
@ -348,7 +345,9 @@ class Wrapper extends React.PureComponent<WrapperProps, State> {
// Delaying generation so design to debug mode is smooth
handleSetActiveActivity(nextActivity);
setTimeout(() => {
importRaw(() => Promise.resolve(workspaceId), activeApiSpec.contents);
importRaw(activeApiSpec.contents, {
getWorkspaceId: () => Promise.resolve(workspaceId),
});
}, 1000);
}
@ -367,16 +366,16 @@ class Wrapper extends React.PureComponent<WrapperProps, State> {
return sUpdate(this.props.settings, { useBulkParametersEditor });
}
_handleImportFile(forceToWorkspace?: ForceToWorkspace): void {
this.props.handleImportFileToWorkspace(this.props.activeWorkspace._id, forceToWorkspace);
_handleImportFile(options?: ImportOptions): void {
this.props.handleImportFileToWorkspace(this.props.activeWorkspace._id, options);
}
_handleImportUri(uri: string, forceToWorkspace?: ForceToWorkspace): void {
this.props.handleImportUriToWorkspace(this.props.activeWorkspace._id, uri, forceToWorkspace);
_handleImportUri(uri: string, options?: ImportOptions): void {
this.props.handleImportUriToWorkspace(this.props.activeWorkspace._id, uri, options);
}
_handleImportClipBoard(forceToWorkspace?: ForceToWorkspace): void {
this.props.handleImportClipBoardToWorkspace(this.props.activeWorkspace._id, forceToWorkspace);
_handleImportClipBoard(options?: ImportOptions): void {
this.props.handleImportClipBoardToWorkspace(this.props.activeWorkspace._id, options);
}
_handleSetActiveResponse(responseId: string | null): void {

View File

@ -7,7 +7,7 @@ import path from 'path';
import AskModal from '../../../ui/components/modals/ask-modal';
import * as moment from 'moment';
import type { ImportResult } from '../../../common/import';
import type { ImportRawConfig, ImportResult } from '../../../common/import';
import * as importUtils from '../../../common/import';
import AlertModal from '../../components/modals/alert-modal';
import PaymentNotificationModal from '../../components/modals/payment-notification-modal';
@ -24,12 +24,12 @@ import SettingsModal, {
} from '../../components/modals/settings-modal';
import install from '../../../plugins/install';
import type { ForceToWorkspace } from './helpers';
import { askToImportIntoWorkspace } from './helpers';
import { askToImportIntoWorkspace, askToSetWorkspaceScope } from './helpers';
import { createPlugin } from '../../../plugins/create';
import { reloadPlugins } from '../../../plugins';
import { setTheme } from '../../../plugins/misc';
import type { GlobalActivity } from '../../../common/constants';
import type { Workspace } from '../../../models/workspace';
import type { Workspace, WorkspaceScope } from '../../../models/workspace';
import {
ACTIVITY_DEBUG,
ACTIVITY_HOME,
@ -293,7 +293,15 @@ export function setActiveWorkspace(workspaceId: string) {
return { type: SET_ACTIVE_WORKSPACE, workspaceId };
}
export function importFile(workspaceId: string, forceToWorkspace?: ForceToWorkspace) {
export type ImportOptions = {
forceToWorkspace?: ForceToWorkspace,
forceToScope?: WorkspaceScope,
};
export function importFile(
workspaceId: string,
{ forceToScope, forceToWorkspace }: ImportOptions = {},
) {
return async dispatch => {
dispatch(loadStart());
@ -329,28 +337,22 @@ export function importFile(workspaceId: string, forceToWorkspace?: ForceToWorksp
}
// Let's import all the paths!
let importedWorkspaces = [];
for (const p of paths) {
try {
const uri = `file://${p}`;
const result = await importUtils.importUri(
askToImportIntoWorkspace(workspaceId, forceToWorkspace),
uri,
);
importedWorkspaces = handleImportResult(
result,
'The file does not contain a valid specification.',
);
const options: ImportRawConfig = {
getWorkspaceScope: askToSetWorkspaceScope(forceToScope),
getWorkspaceId: askToImportIntoWorkspace(workspaceId, forceToWorkspace),
};
const result = await importUtils.importUri(uri, options);
handleImportResult(result, 'The file does not contain a valid specification.');
} catch (err) {
showModal(AlertModal, { title: 'Import Failed', message: err + '' });
} finally {
dispatch(loadStop());
}
}
if (importedWorkspaces.length === 1) {
dispatch(setActiveWorkspace(importedWorkspaces[0]._id));
}
};
}
@ -369,7 +371,10 @@ function handleImportResult(result: ImportResult, errorMessage: string): Array<W
return summary[models.workspace.type] || [];
}
export function importClipBoard(workspaceId: string, forceToWorkspace?: ForceToWorkspace) {
export function importClipBoard(
workspaceId: string,
{ forceToScope, forceToWorkspace }: ImportOptions = {},
) {
return async dispatch => {
dispatch(loadStart());
const schema = electron.clipboard.readText();
@ -381,16 +386,13 @@ export function importClipBoard(workspaceId: string, forceToWorkspace?: ForceToW
return;
}
// Let's import all the paths!
let importedWorkspaces = [];
try {
const result = await importUtils.importRaw(
askToImportIntoWorkspace(workspaceId, forceToWorkspace),
schema,
);
importedWorkspaces = handleImportResult(
result,
'Your clipboard does not contain a valid specification.',
);
const options: ImportRawConfig = {
getWorkspaceScope: askToSetWorkspaceScope(forceToScope),
getWorkspaceId: askToImportIntoWorkspace(workspaceId, forceToWorkspace),
};
const result = await importUtils.importRaw(schema, options);
handleImportResult(result, 'Your clipboard does not contain a valid specification.');
} catch (err) {
showModal(AlertModal, {
title: 'Import Failed',
@ -399,35 +401,29 @@ export function importClipBoard(workspaceId: string, forceToWorkspace?: ForceToW
} finally {
dispatch(loadStop());
}
if (importedWorkspaces.length === 1) {
dispatch(setActiveWorkspace(importedWorkspaces[0]._id));
}
};
}
export function importUri(workspaceId: string, uri: string, forceToWorkspace?: ForceToWorkspace) {
export function importUri(
workspaceId: string,
uri: string,
{ forceToScope, forceToWorkspace }: ImportOptions = {},
) {
return async dispatch => {
dispatch(loadStart());
let importedWorkspaces = [];
try {
const result = await importUtils.importUri(
askToImportIntoWorkspace(workspaceId, forceToWorkspace),
uri,
);
importedWorkspaces = handleImportResult(
result,
'The URI does not contain a valid specification.',
);
const options: ImportRawConfig = {
getWorkspaceScope: askToSetWorkspaceScope(forceToScope),
getWorkspaceId: askToImportIntoWorkspace(workspaceId, forceToWorkspace),
};
const result = await importUtils.importUri(uri, options);
handleImportResult(result, 'The URI does not contain a valid specification.');
} catch (err) {
showModal(AlertModal, { title: 'Import Failed', message: err + '' });
} finally {
dispatch(loadStop());
}
if (importedWorkspaces.length === 1) {
dispatch(setActiveWorkspace(importedWorkspaces[0]._id));
}
};
}

View File

@ -1,6 +1,7 @@
// @flow
import { showModal } from '../../components/modals';
import AskModal from '../../components/modals/ask-modal';
import { WorkspaceScopeKeys } from '../../../models/workspace';
export const ForceToWorkspaceKeys = {
new: 'new',
@ -31,3 +32,25 @@ export function askToImportIntoWorkspace(workspaceId: string, forceToWorkspace?:
}
};
}
export function askToSetWorkspaceScope(scope?: WorkspaceScope) {
return function(name: string) {
switch (scope) {
case WorkspaceScopeKeys.collection:
case WorkspaceScopeKeys.designer:
return scope;
default:
return new Promise(resolve => {
showModal(AskModal, {
title: 'Import As',
message: `How would you like to import "${name}"?`,
noText: 'Request Collection',
yesText: 'Design Document',
onDone: yes => {
resolve(yes ? WorkspaceScopeKeys.designer : WorkspaceScopeKeys.collection);
},
});
});
}
};
}

View File

@ -60,6 +60,7 @@ module.exports.convert = async function(rawData) {
parentId: null,
name: `${api.info.title} ${api.info.version}`,
description: api.info.description || '',
// scope is not set because it could be imported for design OR to generate requests
};
const baseEnv = {

View File

@ -41,6 +41,7 @@ module.exports.convert = async function(rawData) {
parentId: null,
name: `${api.info.title} ${api.info.version}`,
description: api.info.description || '',
// scope is not set because it could be imported for design OR to generate requests
};
const baseEnv = {