Base Space export (#3479)

This commit is contained in:
Dimitri Mitropoulos 2021-06-30 11:33:02 -04:00 committed by GitHub
parent c24d08ad66
commit 9c0f7660dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 40 additions and 55 deletions

View File

@ -58,7 +58,7 @@ describe('exportWorkspacesHAR() and exportRequestsHAR()', () => {
});
const includePrivateDocs = true;
// Test export whole workspace.
const exportWorkspacesJson = await exportWorkspacesHAR(wrk1, includePrivateDocs);
const exportWorkspacesJson = await exportWorkspacesHAR([wrk1], includePrivateDocs);
const exportWorkspacesData = JSON.parse(exportWorkspacesJson);
expect(exportWorkspacesData).toMatchObject({
log: {
@ -171,7 +171,7 @@ describe('exportWorkspacesHAR() and exportRequestsHAR()', () => {
activeEnvironmentId: env2Private._id,
});
const includePrivateDocs = false;
const json = await exportWorkspacesHAR(null, includePrivateDocs);
const json = await exportWorkspacesHAR([], includePrivateDocs);
const data = JSON.parse(json);
expect(data).toMatchObject({
log: {
@ -259,8 +259,8 @@ describe('export', () => {
parentId: eBase._id,
});
// Test export whole workspace.
const exportedWorkspacesJson = await exportWorkspacesData(null, false, 'json');
const exportedWorkspacesYaml = await exportWorkspacesData(null, false, 'yaml');
const exportedWorkspacesJson = await exportWorkspacesData([], false, 'json');
const exportedWorkspacesYaml = await exportWorkspacesData([], false, 'yaml');
const exportWorkspacesDataJson = JSON.parse(exportedWorkspacesJson);
const exportWorkspacesDataYaml = YAML.parse(exportedWorkspacesYaml);
// Ensure JSON is the same as YAML
@ -417,7 +417,7 @@ describe('export', () => {
isPrivate: true,
parentId: eBase._id,
});
const result = await exportWorkspacesData(w, false, 'json');
const result = await exportWorkspacesData([w], false, 'json');
expect(JSON.parse(result)).toEqual({
_type: 'export',
__export_format: 4,

View File

@ -25,7 +25,7 @@ import { isRequest } from '../models/request';
import { isRequestGroup } from '../models/request-group';
import { isProtoDirectory } from '../models/proto-directory';
import { isProtoFile } from '../models/proto-file';
import { isWorkspace } from '../models/workspace';
import { isWorkspace, Workspace } from '../models/workspace';
import { isApiSpec } from '../models/api-spec';
import { isCookieJar } from '../models/cookie-jar';
import { isEnvironment } from '../models/environment';
@ -34,22 +34,22 @@ import { isUnitTest } from '../models/unit-test';
const EXPORT_FORMAT = 4;
async function getDocWithDescendants(
parentDoc: BaseModel | null = null,
includePrivateDocs = false,
) {
const getDocWithDescendants = (includePrivateDocs = false) => async (parentDoc: BaseModel | null) => {
const docs = await db.withDescendants(parentDoc);
return docs.filter(
// Don't include if private, except if we want to
doc => !doc?.isPrivate || includePrivateDocs,
);
}
};
export async function exportWorkspacesHAR(
model: BaseModel | null = null,
workspaces: Workspace[],
includePrivateDocs = false,
) {
const docs = await getDocWithDescendants(model, includePrivateDocs);
// regarding `[null]`, see the comment here in `exportWorkspacesData`
const rootDocs = workspaces.length === 0 ? [null] : workspaces;
const promises = rootDocs.map(getDocWithDescendants(includePrivateDocs));
const docs = (await Promise.all(promises)).flat();
const requests = docs.filter(isRequest);
return exportRequestsHAR(requests, includePrivateDocs);
}
@ -118,11 +118,14 @@ export async function exportRequestsHAR(
}
export async function exportWorkspacesData(
parentDoc: BaseModel | null,
workspaces: Workspace[],
includePrivateDocs: boolean,
format: 'json' | 'yaml',
) {
const docs = await getDocWithDescendants(parentDoc, includePrivateDocs);
// Semantically, if an empty array is passed, then nothing will be returned. What an empty array really signifies is "no parent", which, at the database layer is the same as "parentId === null", hence we add null in ourselves.
const rootDocs = workspaces.length === 0 ? [null] : workspaces;
const promises = rootDocs.map(getDocWithDescendants(includePrivateDocs));
const docs = (await Promise.all(promises)).flat();
const requests = docs.filter(doc => isRequest(doc) || isGrpcRequest(doc));
return exportRequestsData(requests, includePrivateDocs, format);
}
@ -141,11 +144,11 @@ export async function exportRequestsData(
resources: [],
};
const docs: BaseModel[] = [];
const workspaces: BaseModel[] = [];
const mapTypeAndIdToDoc: Record<string, any> = {};
const workspaces: Workspace[] = [];
const mapTypeAndIdToDoc: Record<string, BaseModel> = {};
for (const req of requests) {
const ancestors: BaseModel[] = clone(await db.withAncestors(req));
for (const request of requests) {
const ancestors = clone<BaseModel[]>(await db.withAncestors(request));
for (const ancestor of ancestors) {
const key = ancestor.type + '___' + ancestor._id;
@ -164,7 +167,7 @@ export async function exportRequestsData(
}
for (const workspace of workspaces) {
const descendants: BaseModel[] = (await db.withDescendants(workspace)).filter(d => {
const descendants = (await db.withDescendants(workspace)).filter(d => {
// Only interested in these additional model types.
return (
isCookieJar(d) ||

View File

@ -1,5 +1,4 @@
import * as plugin from '../data';
import * as modals from '../../../ui/components/modals';
import path from 'path';
import { globalBeforeEach } from '../../../__jest__/before-each';
import * as models from '../../../models/index';
@ -8,21 +7,13 @@ import fs from 'fs';
import { getAppVersion } from '../../../common/constants';
import { WorkspaceScopeKeys } from '../../../models/workspace';
const PLUGIN = {
name: 'my-plugin',
version: '1.0.0',
directory: '/plugins/my-plugin',
module: {},
};
jest.mock('../../../ui/components/modals');
describe('init()', () => {
beforeEach(globalBeforeEach);
it('initializes correctly', async () => {
// @ts-expect-error -- TSCONVERSION genuine, plugin.init doesn't take any arguments
const { data } = plugin.init({
name: PLUGIN,
});
const { data } = plugin.init();
expect(Object.keys(data)).toEqual(['import', 'export']);
expect(Object.keys(data.export).sort()).toEqual(['har', 'insomnia']);
expect(Object.keys(data.import).sort()).toEqual(['raw', 'uri']);
@ -40,13 +31,10 @@ describe('app.import.*', () => {
});
it('uri', async () => {
// @ts-expect-error -- TSCONVERSION mocking with jest function
modals.showModal = jest.fn();
const workspace = await models.workspace.getById('wrk_1');
expect(await db.all(models.workspace.type)).toEqual([workspace]);
expect(await db.count(models.request.type)).toBe(0);
// @ts-expect-error -- TSCONVERSION genuine, plugin.init doesn't take any arguments
const { data } = plugin.init(PLUGIN);
const { data } = plugin.init();
const filename = path.resolve(__dirname, '../__fixtures__/basic-import.json');
await data.import.uri(`file://${filename}`);
const allWorkspaces = await db.all(models.workspace.type);
@ -91,13 +79,10 @@ describe('app.import.*', () => {
});
it('importRaw', async () => {
// @ts-expect-error -- TSCONVERSION mocking with jest function
modals.showModal = jest.fn();
const workspace = await models.workspace.getById('wrk_1');
expect(await db.all(models.workspace.type)).toEqual([workspace]);
expect(await db.count(models.request.type)).toBe(0);
// @ts-expect-error -- TSCONVERSION genuine, plugin.init doesn't take any arguments
const { data } = plugin.init(PLUGIN);
const { data } = plugin.init();
const filename = path.resolve(__dirname, '../__fixtures__/basic-import.json');
await data.import.raw(fs.readFileSync(filename, 'utf8'));
const allWorkspaces = await db.all(models.workspace.type);
@ -167,10 +152,7 @@ describe('app.export.*', () => {
});
it('insomnia', async () => {
// @ts-expect-error -- TSCONVERSION mocking with jest function
modals.showModal = jest.fn();
// @ts-expect-error -- TSCONVERSION genuine, plugin.init doesn't take any arguments
const { data } = plugin.init(PLUGIN);
const { data } = plugin.init();
const exported = await data.export.insomnia();
const exportedData = JSON.parse(exported);
expect(typeof exportedData.__export_date).toBe('string');
@ -219,10 +201,7 @@ describe('app.export.*', () => {
});
it('har', async () => {
// @ts-expect-error -- TSCONVERSION mocking with jest function
modals.showModal = jest.fn();
// @ts-expect-error -- TSCONVERSION genuine, plugin.init doesn't take any arguments
const { data } = plugin.init(PLUGIN);
const { data } = plugin.init();
const exported = await data.export.har();
const exportedData = JSON.parse(exported);
exportedData.log.entries[0].startedDateTime = '2017-11-24T18:12:12.849Z';

View File

@ -9,7 +9,7 @@ interface PluginImportOptions {
}
interface InsomniaExport {
workspace?: Workspace | null;
workspace?: Workspace;
includePrivate?: boolean;
format?: 'json' | 'yaml';
}
@ -39,7 +39,7 @@ export const init = () => ({
includePrivate,
format,
}: InsomniaExport = {}) => exportWorkspacesData(
workspace || null,
workspace ? [workspace] : [],
Boolean(includePrivate),
format || 'json',
),
@ -48,7 +48,7 @@ export const init = () => ({
workspace,
includePrivate,
}: HarExport = {}) => exportWorkspacesHAR(
workspace || null,
workspace ? [workspace] : [],
Boolean(includePrivate),
),
},

View File

@ -47,7 +47,7 @@ import {
DEPRECATED_ACTIVITY_INSOMNIA,
isValidActivity,
} from '../../../common/constants';
import { selectSettings } from '../selectors';
import { selectSettings, selectWorkspacesForActiveSpace } from '../selectors';
import { getDesignerDataDir } from '../../../common/electron-helpers';
import { Settings } from '../../../models/settings';
import { GrpcRequest } from '../../../models/grpc-request';
@ -595,11 +595,12 @@ const writeExportedFileToFileSystem = (filename: string, jsonData: string, onDon
fs.writeFile(filename, jsonData, {}, onDone);
};
export const exportAllToFile = () => async (dispatch: Dispatch) => {
export const exportAllToFile = () => async (dispatch: Dispatch, getState) => {
dispatch(loadStart());
showSelectExportTypeModal({
onCancel: () => { dispatch(loadStop()); },
onDone: async selectedFormat => {
const state = getState();
// Check if we want to export private environments.
const environments = await models.environment.all();
@ -622,20 +623,22 @@ export const exportAllToFile = () => async (dispatch: Dispatch) => {
return;
}
const workspaces = selectWorkspacesForActiveSpace(state);
let stringifiedExport;
try {
switch (selectedFormat) {
case VALUE_HAR:
stringifiedExport = await exportWorkspacesHAR(null, exportPrivateEnvironments);
stringifiedExport = await exportWorkspacesHAR(workspaces, exportPrivateEnvironments);
break;
case VALUE_YAML:
stringifiedExport = await exportWorkspacesData(null, exportPrivateEnvironments, 'yaml');
stringifiedExport = await exportWorkspacesData(workspaces, exportPrivateEnvironments, 'yaml');
break;
case VALUE_JSON:
stringifiedExport = await exportWorkspacesData(null, exportPrivateEnvironments, 'json');
stringifiedExport = await exportWorkspacesData(workspaces, exportPrivateEnvironments, 'json');
break;
}
} catch (err) {