mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
Add ability for plugins to add folder actions (#774)
This commit is contained in:
parent
ec1310000d
commit
d8066bc558
@ -192,7 +192,7 @@ export async function render<T>(
|
|||||||
|
|
||||||
export async function getRenderContext(
|
export async function getRenderContext(
|
||||||
request: Request,
|
request: Request,
|
||||||
environmentId: string,
|
environmentId: string | null,
|
||||||
ancestors: Array<BaseModel> | null = null,
|
ancestors: Array<BaseModel> | null = null,
|
||||||
purpose: RenderPurpose | null = null,
|
purpose: RenderPurpose | null = null,
|
||||||
): Promise<Object> {
|
): Promise<Object> {
|
||||||
@ -212,7 +212,7 @@ export async function getRenderContext(
|
|||||||
const rootEnvironment = await models.environment.getOrCreateForWorkspaceId(
|
const rootEnvironment = await models.environment.getOrCreateForWorkspaceId(
|
||||||
workspace ? workspace._id : 'n/a',
|
workspace ? workspace._id : 'n/a',
|
||||||
);
|
);
|
||||||
const subEnvironment = await models.environment.getById(environmentId);
|
const subEnvironment = await models.environment.getById(environmentId || 'n/a');
|
||||||
|
|
||||||
let keySource = {};
|
let keySource = {};
|
||||||
for (let key in (rootEnvironment || {}).data) {
|
for (let key in (rootEnvironment || {}).data) {
|
||||||
@ -261,7 +261,7 @@ export async function getRenderContext(
|
|||||||
|
|
||||||
export async function getRenderedRequestAndContext(
|
export async function getRenderedRequestAndContext(
|
||||||
request: Request,
|
request: Request,
|
||||||
environmentId: string,
|
environmentId: string | null,
|
||||||
purpose?: RenderPurpose,
|
purpose?: RenderPurpose,
|
||||||
): Promise<{ request: RenderedRequest, context: Object }> {
|
): Promise<{ request: RenderedRequest, context: Object }> {
|
||||||
const ancestors = await db.withAncestors(request, [
|
const ancestors = await db.withAncestors(request, [
|
||||||
|
@ -805,7 +805,10 @@ export async function sendWithSettings(
|
|||||||
return _actuallySend(renderResult.request, renderResult.context, workspace, settings);
|
return _actuallySend(renderResult.request, renderResult.context, workspace, settings);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function send(requestId: string, environmentId: string): Promise<ResponsePatch> {
|
export async function send(
|
||||||
|
requestId: string,
|
||||||
|
environmentId: string | null,
|
||||||
|
): Promise<ResponsePatch> {
|
||||||
// HACK: wait for all debounces to finish
|
// HACK: wait for all debounces to finish
|
||||||
/*
|
/*
|
||||||
* TODO: Do this in a more robust way
|
* TODO: Do this in a more robust way
|
||||||
|
@ -12,6 +12,7 @@ describe('init()', () => {
|
|||||||
'alert',
|
'alert',
|
||||||
'getPath',
|
'getPath',
|
||||||
'prompt',
|
'prompt',
|
||||||
|
'showGenericModalDialog',
|
||||||
'showSaveDialog',
|
'showSaveDialog',
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import * as electron from 'electron';
|
import * as electron from 'electron';
|
||||||
import { showAlert, showPrompt } from '../../ui/components/modals/index';
|
import { showAlert, showModal, showPrompt } from '../../ui/components/modals';
|
||||||
import type { RenderPurpose } from '../../common/render';
|
import type { RenderPurpose } from '../../common/render';
|
||||||
import {
|
import {
|
||||||
RENDER_PURPOSE_GENERAL,
|
RENDER_PURPOSE_GENERAL,
|
||||||
RENDER_PURPOSE_NO_RENDER,
|
RENDER_PURPOSE_NO_RENDER,
|
||||||
RENDER_PURPOSE_SEND,
|
RENDER_PURPOSE_SEND,
|
||||||
} from '../../common/render';
|
} from '../../common/render';
|
||||||
|
import WrapperModal from '../../ui/components/modals/wrapper-modal';
|
||||||
|
|
||||||
export function init(renderPurpose: RenderPurpose = RENDER_PURPOSE_GENERAL): { app: Object } {
|
export function init(renderPurpose: RenderPurpose = RENDER_PURPOSE_GENERAL): { app: Object } {
|
||||||
return {
|
return {
|
||||||
@ -18,6 +19,9 @@ export function init(renderPurpose: RenderPurpose = RENDER_PURPOSE_GENERAL): { a
|
|||||||
|
|
||||||
return showAlert({ title, message });
|
return showAlert({ title, message });
|
||||||
},
|
},
|
||||||
|
showGenericModalDialog(title: string, options?: { html: string } = {}): Promise<void> {
|
||||||
|
return showModal(WrapperModal, { title, bodyHTML: options.html });
|
||||||
|
},
|
||||||
prompt(
|
prompt(
|
||||||
title: string,
|
title: string,
|
||||||
options?: {
|
options?: {
|
||||||
|
@ -4,7 +4,7 @@ import { send } from '../../network/network';
|
|||||||
import type { Request } from '../../models/request';
|
import type { Request } from '../../models/request';
|
||||||
import * as models from '../../models';
|
import * as models from '../../models';
|
||||||
|
|
||||||
export function init(activeEnvironmentId: string): { network: Object } {
|
export function init(activeEnvironmentId: string | null): { network: Object } {
|
||||||
const network = {
|
const network = {
|
||||||
async sendRequest(request: Request): Promise<Response> {
|
async sendRequest(request: Request): Promise<Response> {
|
||||||
const responsePatch = await send(request._id, activeEnvironmentId);
|
const responsePatch = await send(request._id, activeEnvironmentId);
|
||||||
|
@ -8,6 +8,8 @@ import { resolveHomePath } from '../common/misc';
|
|||||||
import { showError } from '../ui/components/modals/index';
|
import { showError } from '../ui/components/modals/index';
|
||||||
import type { PluginTemplateTag } from '../templating/extensions/index';
|
import type { PluginTemplateTag } from '../templating/extensions/index';
|
||||||
import type { PluginTheme } from './misc';
|
import type { PluginTheme } from './misc';
|
||||||
|
import type { RequestGroup } from '../models/request-group';
|
||||||
|
import type { Request } from '../models/request';
|
||||||
|
|
||||||
export type Plugin = {
|
export type Plugin = {
|
||||||
name: string,
|
name: string,
|
||||||
@ -22,6 +24,19 @@ export type TemplateTag = {
|
|||||||
templateTag: PluginTemplateTag,
|
templateTag: PluginTemplateTag,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export type RequestGroupAction = {
|
||||||
|
plugin: Plugin,
|
||||||
|
action: (
|
||||||
|
context: Object,
|
||||||
|
models: {
|
||||||
|
requestGroup: RequestGroup,
|
||||||
|
requests: Array<Request>,
|
||||||
|
},
|
||||||
|
) => void | Promise<void>,
|
||||||
|
label: string,
|
||||||
|
icon?: string,
|
||||||
|
};
|
||||||
|
|
||||||
export type RequestHook = {
|
export type RequestHook = {
|
||||||
plugin: Plugin,
|
plugin: Plugin,
|
||||||
hook: Function,
|
hook: Function,
|
||||||
@ -156,6 +171,16 @@ export async function getPlugins(force: boolean = false): Promise<Array<Plugin>>
|
|||||||
return plugins;
|
return plugins;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getRequestGroupActions(): Promise<Array<RequestGroupAction>> {
|
||||||
|
let extensions = [];
|
||||||
|
for (const plugin of await getPlugins()) {
|
||||||
|
const actions = plugin.module.requestGroupActions || [];
|
||||||
|
extensions = [...extensions, ...actions.map(p => ({ plugin, ...p }))];
|
||||||
|
}
|
||||||
|
|
||||||
|
return extensions;
|
||||||
|
}
|
||||||
|
|
||||||
export async function getTemplateTags(): Promise<Array<TemplateTag>> {
|
export async function getTemplateTags(): Promise<Array<TemplateTag>> {
|
||||||
let extensions = [];
|
let extensions = [];
|
||||||
for (const plugin of await getPlugins()) {
|
for (const plugin of await getPlugins()) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import React, { PureComponent } from 'react';
|
// @flow
|
||||||
import PropTypes from 'prop-types';
|
import * as React from 'react';
|
||||||
import autobind from 'autobind-decorator';
|
import autobind from 'autobind-decorator';
|
||||||
|
import classnames from 'classnames';
|
||||||
import PromptButton from '../base/prompt-button';
|
import PromptButton from '../base/prompt-button';
|
||||||
import {
|
import {
|
||||||
Dropdown,
|
Dropdown,
|
||||||
@ -11,12 +12,43 @@ import {
|
|||||||
} from '../base/dropdown';
|
} from '../base/dropdown';
|
||||||
import EnvironmentEditModal from '../modals/environment-edit-modal';
|
import EnvironmentEditModal from '../modals/environment-edit-modal';
|
||||||
import * as models from '../../../models';
|
import * as models from '../../../models';
|
||||||
import { showPrompt, showModal } from '../modals/index';
|
import { showError, showModal, showPrompt } from '../modals';
|
||||||
|
import type { HotKeyRegistry } from '../../../common/hotkeys';
|
||||||
import { hotKeyRefs } from '../../../common/hotkeys';
|
import { hotKeyRefs } from '../../../common/hotkeys';
|
||||||
|
import type { RequestGroupAction } from '../../../plugins';
|
||||||
|
import { getRequestGroupActions } from '../../../plugins';
|
||||||
|
import type { RequestGroup } from '../../../models/request-group';
|
||||||
|
import type { Workspace } from '../../../models/workspace';
|
||||||
|
import * as pluginContexts from '../../../plugins/context/index';
|
||||||
|
import { RENDER_PURPOSE_NO_RENDER } from '../../../common/render';
|
||||||
|
import type { Environment } from '../../../models/environment';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
workspace: Workspace,
|
||||||
|
requestGroup: RequestGroup,
|
||||||
|
hotKeyRegistry: HotKeyRegistry,
|
||||||
|
activeEnvironment: Environment | null,
|
||||||
|
handleCreateRequest: (id: string) => any,
|
||||||
|
handleDuplicateRequestGroup: (rg: RequestGroup) => any,
|
||||||
|
handleMoveRequestGroup: (rg: RequestGroup) => any,
|
||||||
|
handleCreateRequestGroup: (id: string) => any,
|
||||||
|
};
|
||||||
|
|
||||||
|
type State = {
|
||||||
|
actionPlugins: Array<RequestGroupAction>,
|
||||||
|
loadingActions: { [string]: boolean },
|
||||||
|
};
|
||||||
|
|
||||||
@autobind
|
@autobind
|
||||||
class RequestGroupActionsDropdown extends PureComponent {
|
class RequestGroupActionsDropdown extends React.PureComponent<Props, State> {
|
||||||
_setDropdownRef(n) {
|
_dropdown: ?Dropdown;
|
||||||
|
|
||||||
|
state = {
|
||||||
|
actionPlugins: [],
|
||||||
|
loadingActions: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
_setDropdownRef(n: ?Dropdown) {
|
||||||
this._dropdown = n;
|
this._dropdown = n;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,19 +88,54 @@ class RequestGroupActionsDropdown extends PureComponent {
|
|||||||
showModal(EnvironmentEditModal, this.props.requestGroup);
|
showModal(EnvironmentEditModal, this.props.requestGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
show() {
|
async onOpen() {
|
||||||
this._dropdown.show();
|
const plugins = await getRequestGroupActions();
|
||||||
|
this.setState({ actionPlugins: plugins });
|
||||||
|
}
|
||||||
|
|
||||||
|
async show() {
|
||||||
|
this._dropdown && this._dropdown.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
async _handlePluginClick(p: RequestGroupAction) {
|
||||||
|
this.setState(state => ({ loadingActions: { ...state.loadingActions, [p.label]: true } }));
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { activeEnvironment, requestGroup } = this.props;
|
||||||
|
const activeEnvironmentId = activeEnvironment ? activeEnvironment._id : null;
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
...pluginContexts.app.init(RENDER_PURPOSE_NO_RENDER),
|
||||||
|
...pluginContexts.store.init(p.plugin),
|
||||||
|
...pluginContexts.network.init(activeEnvironmentId),
|
||||||
|
};
|
||||||
|
|
||||||
|
const requests = await models.request.findByParentId(requestGroup._id);
|
||||||
|
requests.sort((a, b) => a.metaSortKey - b.metaSortKey);
|
||||||
|
await p.action(context, { requestGroup, requests });
|
||||||
|
} catch (err) {
|
||||||
|
showError({
|
||||||
|
title: 'Plugin Action Failed',
|
||||||
|
error: err,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState(state => ({ loadingActions: { ...state.loadingActions, [p.label]: false } }));
|
||||||
|
this._dropdown && this._dropdown.hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
|
workspace, // eslint-disable-line no-unused-vars
|
||||||
requestGroup, // eslint-disable-line no-unused-vars
|
requestGroup, // eslint-disable-line no-unused-vars
|
||||||
hotKeyRegistry,
|
hotKeyRegistry,
|
||||||
...other
|
...other
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
|
const { actionPlugins, loadingActions } = this.state;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dropdown ref={this._setDropdownRef} {...other}>
|
<Dropdown ref={this._setDropdownRef} onOpen={this.onOpen} {...other}>
|
||||||
<DropdownButton>
|
<DropdownButton>
|
||||||
<i className="fa fa-caret-down" />
|
<i className="fa fa-caret-down" />
|
||||||
</DropdownButton>
|
</DropdownButton>
|
||||||
@ -96,21 +163,20 @@ class RequestGroupActionsDropdown extends PureComponent {
|
|||||||
<DropdownItem buttonClass={PromptButton} addIcon onClick={this._handleDeleteFolder}>
|
<DropdownItem buttonClass={PromptButton} addIcon onClick={this._handleDeleteFolder}>
|
||||||
<i className="fa fa-trash-o" /> Delete
|
<i className="fa fa-trash-o" /> Delete
|
||||||
</DropdownItem>
|
</DropdownItem>
|
||||||
|
{actionPlugins.length > 0 && <DropdownDivider>Plugins</DropdownDivider>}
|
||||||
|
{actionPlugins.map((p: RequestGroupAction) => (
|
||||||
|
<DropdownItem key={p.label} onClick={() => this._handlePluginClick(p)} stayOpenAfterClick>
|
||||||
|
{loadingActions[p.label] ? (
|
||||||
|
<i className="fa fa-refresh fa-spin" />
|
||||||
|
) : (
|
||||||
|
<i className={classnames('fa', p.icon || 'fa-code')} />
|
||||||
|
)}
|
||||||
|
{p.label}
|
||||||
|
</DropdownItem>
|
||||||
|
))}
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RequestGroupActionsDropdown.propTypes = {
|
|
||||||
workspace: PropTypes.object.isRequired,
|
|
||||||
hotKeyRegistry: PropTypes.object.isRequired,
|
|
||||||
handleCreateRequest: PropTypes.func.isRequired,
|
|
||||||
handleCreateRequestGroup: PropTypes.func.isRequired,
|
|
||||||
handleDuplicateRequestGroup: PropTypes.func.isRequired,
|
|
||||||
handleMoveRequestGroup: PropTypes.func.isRequired,
|
|
||||||
|
|
||||||
// Optional
|
|
||||||
requestGroup: PropTypes.object,
|
|
||||||
};
|
|
||||||
|
|
||||||
export default RequestGroupActionsDropdown;
|
export default RequestGroupActionsDropdown;
|
||||||
|
@ -9,6 +9,7 @@ type Props = {};
|
|||||||
type State = {
|
type State = {
|
||||||
title: string,
|
title: string,
|
||||||
body: React.Node,
|
body: React.Node,
|
||||||
|
bodyHTML: ?string,
|
||||||
tall: boolean,
|
tall: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -22,6 +23,7 @@ class WrapperModal extends React.PureComponent<Props, State> {
|
|||||||
this.state = {
|
this.state = {
|
||||||
title: '',
|
title: '',
|
||||||
body: null,
|
body: null,
|
||||||
|
bodyHTML: null,
|
||||||
tall: false,
|
tall: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -31,10 +33,11 @@ class WrapperModal extends React.PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
show(options: Object = {}) {
|
show(options: Object = {}) {
|
||||||
const { title, body, tall } = options;
|
const { title, body, bodyHTML, tall } = options;
|
||||||
this.setState({
|
this.setState({
|
||||||
title,
|
title,
|
||||||
body,
|
body,
|
||||||
|
bodyHTML,
|
||||||
tall: !!tall,
|
tall: !!tall,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -42,12 +45,17 @@ class WrapperModal extends React.PureComponent<Props, State> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { title, body, tall } = this.state;
|
const { title, body, bodyHTML, tall } = this.state;
|
||||||
|
|
||||||
|
let finalBody = body;
|
||||||
|
if (bodyHTML) {
|
||||||
|
finalBody = <div dangerouslySetInnerHTML={{ __html: bodyHTML }} className="tall wide pad" />;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal ref={this._setModalRef} tall={tall}>
|
<Modal ref={this._setModalRef} tall={tall}>
|
||||||
<ModalHeader>{title || 'Uh Oh!'}</ModalHeader>
|
<ModalHeader>{title || 'Uh Oh!'}</ModalHeader>
|
||||||
<ModalBody>{body}</ModalBody>
|
<ModalBody>{finalBody}</ModalBody>
|
||||||
</Modal>
|
</Modal>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import type { RequestGroup } from '../../../models/request-group';
|
|||||||
import type { Workspace } from '../../../models/workspace';
|
import type { Workspace } from '../../../models/workspace';
|
||||||
import type { Request } from '../../../models/request';
|
import type { Request } from '../../../models/request';
|
||||||
import type { HotKeyRegistry } from '../../../common/hotkeys';
|
import type { HotKeyRegistry } from '../../../common/hotkeys';
|
||||||
|
import type { Environment } from '../../../models/environment';
|
||||||
|
|
||||||
type Child = {
|
type Child = {
|
||||||
doc: Request | RequestGroup,
|
doc: Request | RequestGroup,
|
||||||
@ -31,6 +32,7 @@ type Props = {
|
|||||||
workspace: Workspace,
|
workspace: Workspace,
|
||||||
filter: string,
|
filter: string,
|
||||||
hotKeyRegistry: HotKeyRegistry,
|
hotKeyRegistry: HotKeyRegistry,
|
||||||
|
activeEnvironment: Environment | null,
|
||||||
|
|
||||||
// Optional
|
// Optional
|
||||||
activeRequest?: Request,
|
activeRequest?: Request,
|
||||||
@ -53,6 +55,7 @@ class SidebarChildren extends React.PureComponent<Props> {
|
|||||||
activeRequest,
|
activeRequest,
|
||||||
workspace,
|
workspace,
|
||||||
hotKeyRegistry,
|
hotKeyRegistry,
|
||||||
|
activeEnvironment,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const activeRequestId = activeRequest ? activeRequest._id : 'n/a';
|
const activeRequestId = activeRequest ? activeRequest._id : 'n/a';
|
||||||
@ -119,6 +122,7 @@ class SidebarChildren extends React.PureComponent<Props> {
|
|||||||
requestGroup={requestGroup}
|
requestGroup={requestGroup}
|
||||||
hotKeyRegistry={hotKeyRegistry}
|
hotKeyRegistry={hotKeyRegistry}
|
||||||
children={children}
|
children={children}
|
||||||
|
activeEnvironment={activeEnvironment}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -64,6 +64,7 @@ class SidebarRequestGroupRow extends PureComponent {
|
|||||||
isDraggingOver,
|
isDraggingOver,
|
||||||
workspace,
|
workspace,
|
||||||
hotKeyRegistry,
|
hotKeyRegistry,
|
||||||
|
activeEnvironment,
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const { dragDirection } = this.state;
|
const { dragDirection } = this.state;
|
||||||
@ -117,6 +118,7 @@ class SidebarRequestGroupRow extends PureComponent {
|
|||||||
workspace={workspace}
|
workspace={workspace}
|
||||||
requestGroup={requestGroup}
|
requestGroup={requestGroup}
|
||||||
hotKeyRegistry={hotKeyRegistry}
|
hotKeyRegistry={hotKeyRegistry}
|
||||||
|
activeEnvironment={activeEnvironment}
|
||||||
right
|
right
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -176,6 +178,7 @@ SidebarRequestGroupRow.propTypes = {
|
|||||||
|
|
||||||
// Optional
|
// Optional
|
||||||
children: PropTypes.node,
|
children: PropTypes.node,
|
||||||
|
activeEnvironment: PropTypes.object,
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -129,6 +129,7 @@ class Sidebar extends PureComponent {
|
|||||||
activeRequest={activeRequest}
|
activeRequest={activeRequest}
|
||||||
filter={filter || ''}
|
filter={filter || ''}
|
||||||
hotKeyRegistry={hotKeyRegistry}
|
hotKeyRegistry={hotKeyRegistry}
|
||||||
|
activeEnvironment={activeEnvironment}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{enableSyncBeta && vcs && isLoggedIn() && (
|
{enableSyncBeta && vcs && isLoggedIn() && (
|
||||||
|
Loading…
Reference in New Issue
Block a user