Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Multi proto file support #3006

Merged
merged 24 commits into from
Jan 25, 2021
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
64bf25b
feat: add db model and updated selector
develohpanda Jan 11, 2021
58b3828
feat: read root dir and store all proto files
develohpanda Jan 11, 2021
90935fe
feat: recursively read proto directory and store in db
develohpanda Jan 11, 2021
bc3c2ac
feat: add nesting to list
develohpanda Jan 11, 2021
2e20459
feat: spacing and layout improvements, disable editing when item is n…
develohpanda Jan 12, 2021
d75a2eb
fix: add key to component
develohpanda Jan 12, 2021
019523e
feat: add stories for indented and selectable list items
develohpanda Jan 12, 2021
0978634
feat: delete empty folders, show alert when nothing is imported, allo…
develohpanda Jan 12, 2021
ecc2181
feat: write file using the id and modified time instead of math.random
develohpanda Jan 12, 2021
c625a04
chore: comments and small refactor
develohpanda Jan 12, 2021
6f6e6cd
feat: recursively write and consume a cached proto directory! woooo
develohpanda Jan 12, 2021
934fa16
fix: proto-loader includeDirs must include every dir, not only the root
develohpanda Jan 12, 2021
93e39dd
chore: extract logic from modal to a separate class for testing
develohpanda Jan 13, 2021
857044c
chore: refactor and move a few files around
develohpanda Jan 13, 2021
925d8f6
test: repair existing tests that broke
develohpanda Jan 13, 2021
29aeaf0
test: add tests for write-proto-file
develohpanda Jan 18, 2021
cfb89b6
test: add tests for ingesting a proto file
develohpanda Jan 18, 2021
f3e1887
test: move location of proto file fixtures, add integration test and …
develohpanda Jan 19, 2021
3fbc367
fix: flaky test
develohpanda Jan 19, 2021
cd716bf
chore: remove old selector and related props
develohpanda Jan 19, 2021
f7105f8
test: add unit tests for redux proto selectors
develohpanda Jan 19, 2021
478ebb1
test: add tests for proto manager add directory
develohpanda Jan 19, 2021
19e57df
Merge branch 'develop' of github.com:Kong/insomnia into feature/ins-3…
develohpanda Jan 19, 2021
f50d1e1
Merge branch 'develop' of github.com:Kong/insomnia into feature/ins-3…
develohpanda Jan 25, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions packages/insomnia-app/app/__jest__/redux-state-for-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// @flow

import * as entities from '../ui/redux/modules/entities';

const reduxStateForTest = async (activeWorkspaceId: string): Promise<Object> => ({
entities: entities.reducer({}, entities.initializeWith(await entities.allDocs())),
global: { activeWorkspaceId },
});

export default reduxStateForTest;
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ import { difference } from 'lodash';
import {
isGrpcRequest,
isGrpcRequestId,
isProtoDirectory,
isProtoFile,
isRequest,
isRequestGroup,
isWorkspace,
} from '../is-model';
import { generateId } from '../../../common/misc';

Expand Down Expand Up @@ -89,3 +91,29 @@ describe('isProtoFile', () => {
expect(isProtoFile({ type })).toBe(false);
});
});

describe('isProtoDirectory', () => {
const supported = [models.protoDirectory.type];
const unsupported = difference(allTypes, supported);

it.each(supported)('should return true: "%s"', type => {
expect(isProtoDirectory({ type })).toBe(true);
});

it.each(unsupported)('should return false: "%s"', type => {
expect(isProtoDirectory({ type })).toBe(false);
});
});

describe('isWorkspace', () => {
const supported = [models.workspace.type];
const unsupported = difference(allTypes, supported);

it.each(supported)('should return true: "%s"', type => {
expect(isWorkspace({ type })).toBe(true);
});

it.each(unsupported)('should return false: "%s"', type => {
expect(isWorkspace({ type })).toBe(false);
});
});
10 changes: 9 additions & 1 deletion packages/insomnia-app/app/models/helpers/is-model.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// @flow
import type { BaseModel } from '../index';
import { grpcRequest, request, requestGroup, protoFile } from '../index';
import { grpcRequest, request, requestGroup, protoFile, protoDirectory, workspace } from '../index';

export function isGrpcRequest(obj: BaseModel): boolean {
return obj.type === grpcRequest.type;
Expand All @@ -26,3 +26,11 @@ export function isRequestGroup(obj: BaseModel): boolean {
export function isProtoFile(obj: BaseModel): boolean {
return obj.type === protoFile.type;
}

export function isProtoDirectory(obj: BaseModel): boolean {
return obj.type === protoDirectory.type;
}

export function isWorkspace(obj: BaseModel): boolean {
return obj.type === workspace.type;
}
3 changes: 3 additions & 0 deletions packages/insomnia-app/app/models/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import * as _unitTest from './unit-test';
import * as _unitTestResult from './unit-test-result';
import * as _unitTestSuite from './unit-test-suite';
import * as _protoFile from './proto-file';
import * as _protoDirectory from './proto-directory';
import * as _grpcRequest from './grpc-request';
import * as _grpcRequestMeta from './grpc-request-meta';
import * as _workspace from './workspace';
Expand Down Expand Up @@ -52,6 +53,7 @@ export const unitTest = _unitTest;
export const unitTestSuite = _unitTestSuite;
export const unitTestResult = _unitTestResult;
export const protoFile = _protoFile;
export const protoDirectory = _protoDirectory;
export const grpcRequest = _grpcRequest;
export const grpcRequestMeta = _grpcRequestMeta;
export const workspace = _workspace;
Expand Down Expand Up @@ -80,6 +82,7 @@ export function all() {
unitTestResult,
unitTest,
protoFile,
protoDirectory,
grpcRequest,
grpcRequestMeta,
];
Expand Down
49 changes: 49 additions & 0 deletions packages/insomnia-app/app/models/proto-directory.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// @flow
import * as db from '../common/database';
import type { BaseModel } from './index';

export const name = 'Proto Directory';
export const type = 'ProtoDirectory';
export const prefix = 'pd';
export const canDuplicate = true;
export const canSync = true;

type BaseProtoDirectory = {
name: string,
};

export type ProtoDirectory = BaseModel & BaseProtoDirectory;

export function init(): BaseProtoDirectory {
return {
name: 'New Proto Directory',
};
}

export function migrate(doc: ProtoDirectory): ProtoDirectory {
return doc;
}

export function create(patch: $Shape<ProtoDirectory> = {}): Promise<ProtoDirectory> {
if (!patch.parentId) {
throw new Error('New ProtoDirectory missing `parentId`');
}

return db.docCreate(type, patch);
}

export function getById(_id: string): Promise<ProtoDirectory | null> {
return db.getWhere(type, { _id });
}

export function getByParentId(parentId: string): Promise<ProtoDirectory | null> {
return db.getWhere(type, { parentId });
}

export function remove(obj: ProtoDirectory): Promise<void> {
return db.remove(obj);
}

export function all(): Promise<Array<ProtoDirectory>> {
return db.all(type);
}
4 changes: 4 additions & 0 deletions packages/insomnia-app/app/models/proto-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ export function getById(_id: string): Promise<ProtoFile | null> {
return db.getWhere(type, { _id });
}

export function getByParentId(parentId: string): Promise<ProtoFile | null> {
return db.getWhere(type, { parentId });
}

export function findByParentId(parentId: string): Promise<Array<ProtoFile>> {
return db.find(type, { parentId });
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
syntax = "proto3";
import "google/protobuf/timestamp.proto";
import "google/protobuf/empty.proto";

package time;

service TimeService {
rpc GetTime(google.protobuf.Empty) returns (TimeResponse);
}

message TimeResponse {
google.protobuf.Timestamp timestamp_value = 27;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
syntax = "proto3";

package lib;

import public 'hello.proto';
import public 'nested/time/time.proto';
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// @flow
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

import path from 'path';
import fs from 'fs';
import os from 'os';
import * as protoManager from '../proto-manager';
import * as protoLoader from '../proto-loader';
import * as models from '../../../models';
import { globalBeforeEach } from '../../../__jest__/before-each';
import selectFileOrFolder from '../../../common/select-file-or-folder';

jest.mock('../../../common/select-file-or-folder', () => ({
__esModule: true,
default: jest.fn(),
}));

describe('proto management integration test', () => {
beforeEach(globalBeforeEach);

it('can ingest proto file and load methods from it', async () => {
const w = await models.workspace.create();

// Mock folder selection
const protoFilePath = path.join(__dirname, '../__fixtures__/library/hello.proto');
selectFileOrFolder.mockResolvedValue({ filePath: protoFilePath });

// Ingest into database
let createdProtoFileId;
await protoManager.addFile(w._id, id => {
createdProtoFileId = id;
});

expect(selectFileOrFolder).toHaveBeenCalledWith({
itemTypes: ['file'],
extensions: ['proto'],
});

// Find proto file entries
const helloProto = await models.protoFile.getById(createdProtoFileId);

// Load protoMethods
const helloMethods = await protoLoader.loadMethods(helloProto);

expect(helloMethods.length).toBe(4);
});

it('can ingest proto directory tree and load methods from any file', async () => {
const w = await models.workspace.create();

// Mock folder selection
const libraryDirPath = path.join(__dirname, '../__fixtures__/library');
selectFileOrFolder.mockResolvedValue({ filePath: libraryDirPath });

// Ingest into database
await protoManager.addDirectory(w._id);

expect(selectFileOrFolder).toHaveBeenCalledWith({
itemTypes: ['directory'],
extensions: ['proto'],
});

// Find proto file entries
const protoFiles = await models.protoFile.all();
const rootProto = protoFiles.find(pf => pf.name === 'root.proto');
const helloProto = protoFiles.find(pf => pf.name === 'hello.proto');
const timeProto = protoFiles.find(pf => pf.name === 'time.proto');

// Load protoMethods
const rootMethods = await protoLoader.loadMethods(rootProto);
const helloMethods = await protoLoader.loadMethods(helloProto);
const timeMethods = await protoLoader.loadMethods(timeProto);

expect(rootMethods.length).toBe(helloMethods.length + timeMethods.length);
expect(helloMethods.length).toBe(4);
expect(timeMethods.length).toBe(1);

// Create request
const gr = await models.grpcRequest.create({
parentId: w._id,
protoFileId: rootProto._id,
protoMethodName: rootMethods[0].path,
});

// Load selected method
const selectedMethod = await protoLoader.getSelectedMethod(gr);

expect(JSON.stringify(selectedMethod)).toEqual(JSON.stringify(rootMethods[0]));
});

afterEach(async () => {
const tempDirPath = path.join(os.tmpdir(), 'insomnia-grpc');
await fs.promises.rmdir(tempDirPath, { recursive: true });
});
});

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
module.exports = {
loadMethods: jest.fn(),
loadMethodsFromPath: jest.fn(),
getSelectedMethod: jest.fn(),
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// @flow
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

image

import * as protoLoader from '../index';
import writeProtoFile from '../write-proto-file';
import path from 'path';
import { globalBeforeEach } from '../../../../__jest__/before-each';
import * as models from '../../../../models';

jest.mock('../write-proto-file', () => ({
__esModule: true,
default: jest.fn(),
}));

describe('loadMethods', () => {
const protoFilePath = path.join(__dirname, '../../__fixtures__/library/hello.proto');

beforeEach(() => {
globalBeforeEach();
jest.resetAllMocks();
});

it('should load methods', async () => {
const w = await models.workspace.create();
const pf = await models.protoFile.create({
parentId: w._id,
protoText: 'this is just a placeholder because writing to a file is mocked',
});
writeProtoFile.mockResolvedValue({ filePath: protoFilePath, dirs: [] });

const methods = await protoLoader.loadMethods(pf);

expect(writeProtoFile).toHaveBeenCalledWith(pf);

expect(methods).toHaveLength(4);
expect(methods.map(c => c.path)).toStrictEqual(
expect.arrayContaining([
'/hello.HelloService/SayHello',
'/hello.HelloService/LotsOfReplies',
'/hello.HelloService/LotsOfGreetings',
'/hello.HelloService/BidiHello',
]),
);
});

it('should load no methods if protofile does not exist or is empty', async () => {
const w = await models.workspace.create();
const pf = await models.protoFile.create({
parentId: w._id,
protoText: '',
});

await expect(protoLoader.loadMethods(undefined)).resolves.toHaveLength(0);
await expect(protoLoader.loadMethods(pf)).resolves.toHaveLength(0);
});
});
Loading