mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
Prompt Template Tag and Plugin arg validation (#673)
* Plugin arg validation, prompt tag, and some changes needed * Version bumps
This commit is contained in:
parent
4e0fe5d78a
commit
aba3c8ed86
@ -382,4 +382,30 @@ describe('render()', () => {
|
|||||||
expect(err.message).toBe('unknown block tag: invalid');
|
expect(err.message).toBe('unknown block tag: invalid');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('outputs correct error path', async () => {
|
||||||
|
const template = {
|
||||||
|
foo: [{bar: '{% foo %}'}]
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await renderUtils.render(template);
|
||||||
|
fail('Should have failed to render');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.path).toBe('foo[0].bar');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('outputs correct error path when private first node', async () => {
|
||||||
|
const template = {
|
||||||
|
_foo: {_bar: {baz: '{% foo %}'}}
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
await renderUtils.render(template);
|
||||||
|
fail('Should have failed to render');
|
||||||
|
} catch (err) {
|
||||||
|
expect(err.path).toBe('_bar.baz');
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -12,6 +12,8 @@ import type {Environment} from '../models/environment';
|
|||||||
|
|
||||||
export const KEEP_ON_ERROR = 'keep';
|
export const KEEP_ON_ERROR = 'keep';
|
||||||
export const THROW_ON_ERROR = 'throw';
|
export const THROW_ON_ERROR = 'throw';
|
||||||
|
export const RENDER_PURPOSE_SEND = 'send';
|
||||||
|
export const RENDER_PURPOSE_GENERAL = 'general';
|
||||||
|
|
||||||
export type RenderedRequest = Request & {
|
export type RenderedRequest = Request & {
|
||||||
cookies: Array<{name: string, value: string, disabled?: boolean}>,
|
cookies: Array<{name: string, value: string, disabled?: boolean}>,
|
||||||
@ -112,7 +114,7 @@ export async function render<T> (
|
|||||||
// Make a deep copy so no one gets mad :)
|
// Make a deep copy so no one gets mad :)
|
||||||
const newObj = clone(obj);
|
const newObj = clone(obj);
|
||||||
|
|
||||||
async function next (x: any, path: string = name): Promise<any> {
|
async function next (x: any, path: string, first: boolean = false): Promise<any> {
|
||||||
if (blacklistPathRegex && path.match(blacklistPathRegex)) {
|
if (blacklistPathRegex && path.match(blacklistPathRegex)) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
@ -158,21 +160,26 @@ export async function render<T> (
|
|||||||
|
|
||||||
const keys = Object.keys(x);
|
const keys = Object.keys(x);
|
||||||
for (const key of keys) {
|
for (const key of keys) {
|
||||||
|
if (first && key.indexOf('_') === 0) {
|
||||||
|
x[key] = await next(x[key], path);
|
||||||
|
} else {
|
||||||
const pathPrefix = path ? path + '.' : '';
|
const pathPrefix = path ? path + '.' : '';
|
||||||
x[key] = await next(x[key], `${pathPrefix}${key}`);
|
x[key] = await next(x[key], `${pathPrefix}${key}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(newObj);
|
return next(newObj, name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRenderContext (
|
export async function getRenderContext (
|
||||||
request: Request,
|
request: Request,
|
||||||
environmentId: string,
|
environmentId: string,
|
||||||
ancestors: Array<BaseModel> | null = null
|
ancestors: Array<BaseModel> | null = null,
|
||||||
|
purpose: string | null = null
|
||||||
): Promise<Object> {
|
): Promise<Object> {
|
||||||
if (!request) {
|
if (!request) {
|
||||||
return {};
|
return {};
|
||||||
@ -201,21 +208,22 @@ export async function getRenderContext (
|
|||||||
workspaceId: workspace ? workspace._id : 'n/a'
|
workspaceId: workspace ? workspace._id : 'n/a'
|
||||||
});
|
});
|
||||||
|
|
||||||
|
baseContext.getPurpose = () => purpose;
|
||||||
|
|
||||||
// Generate the context we need to render
|
// Generate the context we need to render
|
||||||
const context = await buildRenderContext(
|
return await buildRenderContext(
|
||||||
ancestors,
|
ancestors,
|
||||||
rootEnvironment,
|
rootEnvironment,
|
||||||
subEnvironment,
|
subEnvironment,
|
||||||
baseContext
|
baseContext
|
||||||
);
|
);
|
||||||
|
|
||||||
return context;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getRenderedRequest (
|
export async function getRenderedRequestAndContext (
|
||||||
request: Request,
|
request: Request,
|
||||||
environmentId: string
|
environmentId: string,
|
||||||
): Promise<RenderedRequest> {
|
purpose?: string
|
||||||
|
): Promise<{request: RenderedRequest, context: Object}> {
|
||||||
const ancestors = await db.withAncestors(request, [
|
const ancestors = await db.withAncestors(request, [
|
||||||
models.request.type,
|
models.request.type,
|
||||||
models.requestGroup.type,
|
models.requestGroup.type,
|
||||||
@ -225,20 +233,17 @@ export async function getRenderedRequest (
|
|||||||
const parentId = workspace ? workspace._id : 'n/a';
|
const parentId = workspace ? workspace._id : 'n/a';
|
||||||
const cookieJar = await models.cookieJar.getOrCreateForParentId(parentId);
|
const cookieJar = await models.cookieJar.getOrCreateForParentId(parentId);
|
||||||
|
|
||||||
const renderContext = await getRenderContext(request, environmentId, ancestors);
|
const renderContext = await getRenderContext(request, environmentId, ancestors, purpose);
|
||||||
|
|
||||||
// Render all request properties
|
// Render all request properties
|
||||||
const renderedRequest = await render(
|
const renderResult = await render(
|
||||||
request,
|
{_request: request, _cookieJar: cookieJar},
|
||||||
renderContext,
|
renderContext,
|
||||||
request.settingDisableRenderRequestBody ? /^body.*/ : null
|
request.settingDisableRenderRequestBody ? /^body.*/ : null
|
||||||
);
|
);
|
||||||
|
|
||||||
// Render cookies
|
const renderedRequest = renderResult._request;
|
||||||
const renderedCookieJar = await render(
|
const renderedCookieJar = renderResult._cookieJar;
|
||||||
cookieJar,
|
|
||||||
renderContext
|
|
||||||
);
|
|
||||||
|
|
||||||
// Remove disabled params
|
// Remove disabled params
|
||||||
renderedRequest.parameters = renderedRequest.parameters.filter(p => !p.disabled);
|
renderedRequest.parameters = renderedRequest.parameters.filter(p => !p.disabled);
|
||||||
@ -260,6 +265,8 @@ export async function getRenderedRequest (
|
|||||||
renderedRequest.url = setDefaultProtocol(renderedRequest.url);
|
renderedRequest.url = setDefaultProtocol(renderedRequest.url);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
context: renderContext,
|
||||||
|
request: {
|
||||||
// Add the yummy cookies
|
// Add the yummy cookies
|
||||||
// TODO: Eventually get rid of RenderedRequest type and put these elsewhere
|
// TODO: Eventually get rid of RenderedRequest type and put these elsewhere
|
||||||
cookieJar: renderedCookieJar,
|
cookieJar: renderedCookieJar,
|
||||||
@ -285,9 +292,19 @@ export async function getRenderedRequest (
|
|||||||
settingStoreCookies: renderedRequest.settingStoreCookies,
|
settingStoreCookies: renderedRequest.settingStoreCookies,
|
||||||
type: renderedRequest.type,
|
type: renderedRequest.type,
|
||||||
url: renderedRequest.url
|
url: renderedRequest.url
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getRenderedRequest (
|
||||||
|
request: Request,
|
||||||
|
environmentId: string,
|
||||||
|
purpose?: string
|
||||||
|
): Promise<RenderedRequest> {
|
||||||
|
const result = await getRenderedRequestAndContext(request, environmentId, purpose);
|
||||||
|
return result.request;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sort the keys that may have Nunjucks last, so that other keys get
|
* Sort the keys that may have Nunjucks last, so that other keys get
|
||||||
* defined first. Very important if env variables defined in same obj
|
* defined first. Very important if env variables defined in same obj
|
||||||
|
@ -4,7 +4,7 @@ import type {Request, RequestHeader} from '../models/request';
|
|||||||
import type {Workspace} from '../models/workspace';
|
import type {Workspace} from '../models/workspace';
|
||||||
import type {Settings} from '../models/settings';
|
import type {Settings} from '../models/settings';
|
||||||
import type {RenderedRequest} from '../common/render';
|
import type {RenderedRequest} from '../common/render';
|
||||||
import {getRenderContext, getRenderedRequest} from '../common/render';
|
import {getRenderedRequest, getRenderedRequestAndContext, RENDER_PURPOSE_SEND} from '../common/render';
|
||||||
import mkdirp from 'mkdirp';
|
import mkdirp from 'mkdirp';
|
||||||
import clone from 'clone';
|
import clone from 'clone';
|
||||||
import {parse as urlParse, resolve as urlResolve} from 'url';
|
import {parse as urlParse, resolve as urlResolve} from 'url';
|
||||||
@ -15,7 +15,7 @@ import * as electron from 'electron';
|
|||||||
import * as models from '../models';
|
import * as models from '../models';
|
||||||
import {AUTH_AWS_IAM, AUTH_BASIC, AUTH_DIGEST, AUTH_NETRC, AUTH_NTLM, CONTENT_TYPE_FORM_DATA, CONTENT_TYPE_FORM_URLENCODED, getAppVersion, getTempDir, STATUS_CODE_PLUGIN_ERROR} from '../common/constants';
|
import {AUTH_AWS_IAM, AUTH_BASIC, AUTH_DIGEST, AUTH_NETRC, AUTH_NTLM, CONTENT_TYPE_FORM_DATA, CONTENT_TYPE_FORM_URLENCODED, getAppVersion, getTempDir, STATUS_CODE_PLUGIN_ERROR} from '../common/constants';
|
||||||
import {delay, describeByteSize, getContentTypeHeader, getLocationHeader, getSetCookieHeaders, hasAcceptEncodingHeader, hasAcceptHeader, hasAuthHeader, hasContentTypeHeader, hasUserAgentHeader, waitForStreamToFinish} from '../common/misc';
|
import {delay, describeByteSize, getContentTypeHeader, getLocationHeader, getSetCookieHeaders, hasAcceptEncodingHeader, hasAcceptHeader, hasAuthHeader, hasContentTypeHeader, hasUserAgentHeader, waitForStreamToFinish} from '../common/misc';
|
||||||
import {setDefaultProtocol, smartEncodeUrl, buildQueryStringFromParams, joinUrlAndQueryString} from 'insomnia-url';
|
import {buildQueryStringFromParams, joinUrlAndQueryString, setDefaultProtocol, smartEncodeUrl} from 'insomnia-url';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import * as db from '../common/database';
|
import * as db from '../common/database';
|
||||||
import * as CACerts from './cacert';
|
import * as CACerts from './cacert';
|
||||||
@ -733,8 +733,14 @@ export async function send (
|
|||||||
throw new Error(`Failed to find request to send for ${requestId}`);
|
throw new Error(`Failed to find request to send for ${requestId}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const renderedRequestBeforePlugins = await getRenderedRequest(request, environmentId);
|
const renderResult = await getRenderedRequestAndContext(
|
||||||
const renderedContextBeforePlugins = await getRenderContext(request, environmentId, ancestors);
|
request,
|
||||||
|
environmentId,
|
||||||
|
RENDER_PURPOSE_SEND
|
||||||
|
);
|
||||||
|
|
||||||
|
const renderedRequestBeforePlugins = renderResult.request;
|
||||||
|
const renderedContextBeforePlugins = renderResult.context;
|
||||||
|
|
||||||
const workspaceDoc = ancestors.find(doc => doc.type === models.workspace.type);
|
const workspaceDoc = ancestors.find(doc => doc.type === models.workspace.type);
|
||||||
const workspace = await models.workspace.getById(workspaceDoc ? workspaceDoc._id : 'n/a');
|
const workspace = await models.workspace.getById(workspaceDoc ? workspaceDoc._id : 'n/a');
|
||||||
@ -775,8 +781,8 @@ async function _applyRequestPluginHooks (
|
|||||||
newRenderedRequest = clone(newRenderedRequest);
|
newRenderedRequest = clone(newRenderedRequest);
|
||||||
|
|
||||||
const context = {
|
const context = {
|
||||||
...pluginContexts.app.init(plugin),
|
...pluginContexts.app.init(),
|
||||||
...pluginContexts.request.init(plugin, newRenderedRequest, renderedContext)
|
...pluginContexts.request.init(newRenderedRequest, renderedContext)
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -795,8 +801,8 @@ async function _applyResponsePluginHooks (
|
|||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
for (const {plugin, hook} of await plugins.getResponseHooks()) {
|
for (const {plugin, hook} of await plugins.getResponseHooks()) {
|
||||||
const context = {
|
const context = {
|
||||||
...pluginContexts.app.init(plugin),
|
...pluginContexts.app.init(),
|
||||||
...pluginContexts.response.init(plugin, response)
|
...pluginContexts.response.init(response)
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1,22 +1,17 @@
|
|||||||
import * as plugin from '../app';
|
import * as plugin from '../app';
|
||||||
import * as modals from '../../../ui/components/modals';
|
import * as modals from '../../../ui/components/modals';
|
||||||
import {globalBeforeEach} from '../../../__jest__/before-each';
|
import {globalBeforeEach} from '../../../__jest__/before-each';
|
||||||
|
import {RENDER_PURPOSE_SEND} from '../../../common/render';
|
||||||
const PLUGIN = {
|
|
||||||
name: 'my-plugin',
|
|
||||||
version: '1.0.0',
|
|
||||||
directory: '/plugins/my-plugin',
|
|
||||||
module: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('init()', () => {
|
describe('init()', () => {
|
||||||
beforeEach(globalBeforeEach);
|
beforeEach(globalBeforeEach);
|
||||||
it('initializes correctly', async () => {
|
it('initializes correctly', async () => {
|
||||||
const result = plugin.init({name: PLUGIN});
|
const result = plugin.init();
|
||||||
expect(Object.keys(result)).toEqual(['app']);
|
expect(Object.keys(result)).toEqual(['app']);
|
||||||
expect(Object.keys(result.app).sort()).toEqual([
|
expect(Object.keys(result.app).sort()).toEqual([
|
||||||
'alert',
|
'alert',
|
||||||
'getPath',
|
'getPath',
|
||||||
|
'prompt',
|
||||||
'showSaveDialog'
|
'showSaveDialog'
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
@ -24,18 +19,56 @@ describe('init()', () => {
|
|||||||
|
|
||||||
describe('app.alert()', () => {
|
describe('app.alert()', () => {
|
||||||
beforeEach(globalBeforeEach);
|
beforeEach(globalBeforeEach);
|
||||||
it('shows alert with message', async () => {
|
it('does not show alert when not sending', async () => {
|
||||||
|
modals.showAlert = jest.fn();
|
||||||
|
const result = plugin.init();
|
||||||
|
|
||||||
|
result.app.alert();
|
||||||
|
|
||||||
|
// Make sure it passes correct arguments
|
||||||
|
expect(modals.showAlert.mock.calls).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows alert with message when sending', async () => {
|
||||||
modals.showAlert = jest.fn().mockReturnValue('dummy-return-value');
|
modals.showAlert = jest.fn().mockReturnValue('dummy-return-value');
|
||||||
const result = plugin.init(PLUGIN);
|
const result = plugin.init(RENDER_PURPOSE_SEND);
|
||||||
|
|
||||||
// Make sure it returns result of showAlert()
|
// Make sure it returns result of showAlert()
|
||||||
expect(result.app.alert()).toBe('dummy-return-value');
|
expect(result.app.alert()).toBe('dummy-return-value');
|
||||||
expect(result.app.alert('My message')).toBe('dummy-return-value');
|
expect(result.app.alert({title: 'My message'})).toBe('dummy-return-value');
|
||||||
|
|
||||||
// Make sure it passes correct arguments
|
// Make sure it passes correct arguments
|
||||||
expect(modals.showAlert.mock.calls).toEqual([
|
expect(modals.showAlert.mock.calls).toEqual([
|
||||||
[{message: '', title: 'Plugin my-plugin'}],
|
[{}],
|
||||||
[{message: 'My message', title: 'Plugin my-plugin'}]
|
[{title: 'My message'}]
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('app.prompt()', () => {
|
||||||
|
beforeEach(globalBeforeEach);
|
||||||
|
it('does not show prompt when not sending', async () => {
|
||||||
|
modals.showPrompt = jest.fn();
|
||||||
|
const result = plugin.init();
|
||||||
|
|
||||||
|
result.app.prompt();
|
||||||
|
|
||||||
|
// Make sure it passes correct arguments
|
||||||
|
expect(modals.showPrompt.mock.calls).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('shows alert with message when sending', async () => {
|
||||||
|
modals.showPrompt = jest.fn();
|
||||||
|
const result = plugin.init(RENDER_PURPOSE_SEND);
|
||||||
|
|
||||||
|
// Make sure it returns result of showAlert()
|
||||||
|
result.app.prompt();
|
||||||
|
result.app.prompt({title: 'My message'});
|
||||||
|
|
||||||
|
// Make sure it passes correct arguments
|
||||||
|
expect(modals.showPrompt.mock.calls).toEqual([
|
||||||
|
[{cancelable: false, onComplete: expect.any(Function)}],
|
||||||
|
[{cancelable: false, onComplete: expect.any(Function), title: 'My message'}]
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -2,13 +2,6 @@ import * as plugin from '../request';
|
|||||||
import * as models from '../../../models';
|
import * as models from '../../../models';
|
||||||
import {globalBeforeEach} from '../../../__jest__/before-each';
|
import {globalBeforeEach} from '../../../__jest__/before-each';
|
||||||
|
|
||||||
const PLUGIN = {
|
|
||||||
name: 'my-plugin',
|
|
||||||
version: '1.0.0',
|
|
||||||
directory: '/plugins/my-plugin',
|
|
||||||
module: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
const CONTEXT = {
|
const CONTEXT = {
|
||||||
user_key: 'my_user_key',
|
user_key: 'my_user_key',
|
||||||
hello: 'world',
|
hello: 'world',
|
||||||
@ -25,7 +18,7 @@ describe('init()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('initializes correctly', async () => {
|
it('initializes correctly', async () => {
|
||||||
const result = plugin.init(PLUGIN, await models.request.getById('req_1'), CONTEXT);
|
const result = plugin.init(await models.request.getById('req_1'), CONTEXT);
|
||||||
expect(Object.keys(result)).toEqual(['request']);
|
expect(Object.keys(result)).toEqual(['request']);
|
||||||
expect(Object.keys(result.request).sort()).toEqual([
|
expect(Object.keys(result.request).sort()).toEqual([
|
||||||
'addHeader',
|
'addHeader',
|
||||||
@ -49,7 +42,7 @@ describe('init()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fails to initialize without request', () => {
|
it('fails to initialize without request', () => {
|
||||||
expect(() => plugin.init(PLUGIN))
|
expect(() => plugin.init())
|
||||||
.toThrowError('contexts.request initialized without request');
|
.toThrowError('contexts.request initialized without request');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -70,7 +63,7 @@ describe('request.*', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('works for basic getters', async () => {
|
it('works for basic getters', async () => {
|
||||||
const result = plugin.init(PLUGIN, await models.request.getById('req_1'), CONTEXT);
|
const result = plugin.init(await models.request.getById('req_1'), CONTEXT);
|
||||||
expect(result.request.getId()).toBe('req_1');
|
expect(result.request.getId()).toBe('req_1');
|
||||||
expect(result.request.getName()).toBe('My Request');
|
expect(result.request.getName()).toBe('My Request');
|
||||||
expect(result.request.getUrl()).toBe('');
|
expect(result.request.getUrl()).toBe('');
|
||||||
@ -78,7 +71,7 @@ describe('request.*', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('works for headers', async () => {
|
it('works for headers', async () => {
|
||||||
const result = plugin.init(PLUGIN, await models.request.getById('req_1'), CONTEXT);
|
const result = plugin.init(await models.request.getById('req_1'), CONTEXT);
|
||||||
|
|
||||||
// getHeaders()
|
// getHeaders()
|
||||||
expect(result.request.getHeaders()).toEqual([
|
expect(result.request.getHeaders()).toEqual([
|
||||||
@ -112,7 +105,7 @@ describe('request.*', () => {
|
|||||||
const request = await models.request.getById('req_1');
|
const request = await models.request.getById('req_1');
|
||||||
request.cookies = []; // Because the plugin technically needs a RenderedRequest
|
request.cookies = []; // Because the plugin technically needs a RenderedRequest
|
||||||
|
|
||||||
const result = plugin.init(PLUGIN, request, CONTEXT);
|
const result = plugin.init(request, CONTEXT);
|
||||||
|
|
||||||
result.request.setCookie('foo', 'bar');
|
result.request.setCookie('foo', 'bar');
|
||||||
result.request.setCookie('foo', 'baz');
|
result.request.setCookie('foo', 'baz');
|
||||||
@ -123,7 +116,7 @@ describe('request.*', () => {
|
|||||||
const request = await models.request.getById('req_1');
|
const request = await models.request.getById('req_1');
|
||||||
request.cookies = []; // Because the plugin technically needs a RenderedRequest
|
request.cookies = []; // Because the plugin technically needs a RenderedRequest
|
||||||
|
|
||||||
const result = plugin.init(PLUGIN, request, CONTEXT);
|
const result = plugin.init(request, CONTEXT);
|
||||||
|
|
||||||
// getEnvironment
|
// getEnvironment
|
||||||
expect(result.request.getEnvironment()).toEqual({
|
expect(result.request.getEnvironment()).toEqual({
|
||||||
|
@ -5,17 +5,10 @@ import fs from 'fs';
|
|||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as models from '../../../models/index';
|
import * as models from '../../../models/index';
|
||||||
|
|
||||||
const PLUGIN = {
|
|
||||||
name: 'my-plugin',
|
|
||||||
version: '1.0.0',
|
|
||||||
directory: '/plugins/my-plugin',
|
|
||||||
module: {}
|
|
||||||
};
|
|
||||||
|
|
||||||
describe('init()', () => {
|
describe('init()', () => {
|
||||||
beforeEach(globalBeforeEach);
|
beforeEach(globalBeforeEach);
|
||||||
it('initializes correctly', async () => {
|
it('initializes correctly', async () => {
|
||||||
const result = plugin.init(PLUGIN, {});
|
const result = plugin.init({});
|
||||||
expect(Object.keys(result)).toEqual(['response']);
|
expect(Object.keys(result)).toEqual(['response']);
|
||||||
expect(Object.keys(result.response)).toEqual([
|
expect(Object.keys(result.response)).toEqual([
|
||||||
'getRequestId',
|
'getRequestId',
|
||||||
@ -31,7 +24,7 @@ describe('init()', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('fails to initialize without response', () => {
|
it('fails to initialize without response', () => {
|
||||||
expect(() => plugin.init(PLUGIN))
|
expect(() => plugin.init())
|
||||||
.toThrowError('contexts.response initialized without response');
|
.toThrowError('contexts.response initialized without response');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -51,7 +44,7 @@ describe('response.*', () => {
|
|||||||
bytesRead: 123,
|
bytesRead: 123,
|
||||||
elapsedTime: 321
|
elapsedTime: 321
|
||||||
});
|
});
|
||||||
const result = plugin.init(PLUGIN, response);
|
const result = plugin.init(response);
|
||||||
expect(result.response.getRequestId()).toBe('req_1');
|
expect(result.response.getRequestId()).toBe('req_1');
|
||||||
expect(result.response.getStatusCode()).toBe(200);
|
expect(result.response.getStatusCode()).toBe(200);
|
||||||
expect(result.response.getBytesRead()).toBe(123);
|
expect(result.response.getBytesRead()).toBe(123);
|
||||||
@ -60,7 +53,7 @@ describe('response.*', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('works for basic and empty response', async () => {
|
it('works for basic and empty response', async () => {
|
||||||
const result = plugin.init(PLUGIN, {});
|
const result = plugin.init({});
|
||||||
expect(result.response.getRequestId()).toBe('');
|
expect(result.response.getRequestId()).toBe('');
|
||||||
expect(result.response.getStatusCode()).toBe(0);
|
expect(result.response.getStatusCode()).toBe(0);
|
||||||
expect(result.response.getBytesRead()).toBe(0);
|
expect(result.response.getBytesRead()).toBe(0);
|
||||||
@ -76,7 +69,7 @@ describe('response.*', () => {
|
|||||||
{name: 'set-cookie', value: 'baz=qux'}
|
{name: 'set-cookie', value: 'baz=qux'}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
const result = plugin.init(PLUGIN, response);
|
const result = plugin.init(response);
|
||||||
expect(result.response.getHeader('Does-Not-Exist')).toBeNull();
|
expect(result.response.getHeader('Does-Not-Exist')).toBeNull();
|
||||||
expect(result.response.getHeader('CONTENT-TYPE')).toBe('application/json');
|
expect(result.response.getHeader('CONTENT-TYPE')).toBe('application/json');
|
||||||
expect(result.response.getHeader('set-cookie')).toEqual(['foo=bar', 'baz=qux']);
|
expect(result.response.getHeader('set-cookie')).toEqual(['foo=bar', 'baz=qux']);
|
||||||
|
@ -1,13 +1,35 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import type {Plugin} from '../';
|
|
||||||
import * as electron from 'electron';
|
import * as electron from 'electron';
|
||||||
import {showAlert} from '../../ui/components/modals/index';
|
import {showAlert} from '../../ui/components/modals/index';
|
||||||
|
import {showPrompt} from '../../ui/components/modals';
|
||||||
|
import {RENDER_PURPOSE_SEND} from '../../common/render';
|
||||||
|
|
||||||
export function init (plugin: Plugin): {app: Object} {
|
export function init (renderPurpose?: string): {app: Object} {
|
||||||
return {
|
return {
|
||||||
app: {
|
app: {
|
||||||
alert (message: string): Promise<void> {
|
alert (options: {title: string, message: string}): Promise<void> {
|
||||||
return showAlert({title: `Plugin ${plugin.name}`, message: message || ''});
|
if (renderPurpose !== RENDER_PURPOSE_SEND) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return showAlert(options || {});
|
||||||
|
},
|
||||||
|
prompt (options: {title: string, label?: string, defaultValue?: string, submitName?: string}): Promise<string> {
|
||||||
|
options = options || {};
|
||||||
|
|
||||||
|
if (renderPurpose !== RENDER_PURPOSE_SEND) {
|
||||||
|
return Promise.resolve(options.defaultValue || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
showPrompt({
|
||||||
|
...(options || {}),
|
||||||
|
cancelable: false,
|
||||||
|
onComplete (value: string) {
|
||||||
|
resolve(value);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
getPath (name: string): string {
|
getPath (name: string): string {
|
||||||
switch (name.toLowerCase()) {
|
switch (name.toLowerCase()) {
|
||||||
@ -18,6 +40,10 @@ export function init (plugin: Plugin): {app: Object} {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
async showSaveDialog (options: {defaultPath?: string} = {}): Promise<string | null> {
|
async showSaveDialog (options: {defaultPath?: string} = {}): Promise<string | null> {
|
||||||
|
if (renderPurpose !== RENDER_PURPOSE_SEND) {
|
||||||
|
return Promise.resolve(null);
|
||||||
|
}
|
||||||
|
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
const saveOptions = {
|
const saveOptions = {
|
||||||
title: 'Save File',
|
title: 'Save File',
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import type {Plugin} from '../';
|
|
||||||
import {exportHAR, exportJSON, importRaw, importUri} from '../../common/import';
|
import {exportHAR, exportJSON, importRaw, importUri} from '../../common/import';
|
||||||
|
|
||||||
export function init (plugin: Plugin): {'import': Object, 'export': Object} {
|
export function init (): {'import': Object, 'export': Object} {
|
||||||
return {
|
return {
|
||||||
'import': {
|
'import': {
|
||||||
async uri (uri: string, options: {workspaceId?: string} = {}): Promise<void> {
|
async uri (uri: string, options: {workspaceId?: string} = {}): Promise<void> {
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import type {Plugin} from '../';
|
|
||||||
import type {RenderedRequest} from '../../common/render';
|
import type {RenderedRequest} from '../../common/render';
|
||||||
import * as misc from '../../common/misc';
|
import * as misc from '../../common/misc';
|
||||||
|
|
||||||
export function init (
|
export function init (
|
||||||
plugin: Plugin,
|
|
||||||
renderedRequest: RenderedRequest,
|
renderedRequest: RenderedRequest,
|
||||||
renderedContext: Object
|
renderedContext: Object
|
||||||
): {request: Object} {
|
): {request: Object} {
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import type {Plugin} from '../';
|
|
||||||
import type {ResponseHeader} from '../../models/response';
|
import type {ResponseHeader} from '../../models/response';
|
||||||
import * as models from '../../models/index';
|
import * as models from '../../models/index';
|
||||||
import {Readable} from 'stream';
|
import {Readable} from 'stream';
|
||||||
@ -16,7 +15,6 @@ type MaybeResponse = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function init (
|
export function init (
|
||||||
plugin: Plugin,
|
|
||||||
response: MaybeResponse,
|
response: MaybeResponse,
|
||||||
bodyBuffer: Buffer | null = null
|
bodyBuffer: Buffer | null = null
|
||||||
): {response: Object} {
|
): {response: Object} {
|
||||||
|
@ -37,6 +37,7 @@ const CORE_PLUGINS = [
|
|||||||
'insomnia-plugin-file',
|
'insomnia-plugin-file',
|
||||||
'insomnia-plugin-now',
|
'insomnia-plugin-now',
|
||||||
'insomnia-plugin-uuid',
|
'insomnia-plugin-uuid',
|
||||||
|
'insomnia-plugin-prompt',
|
||||||
'insomnia-plugin-request',
|
'insomnia-plugin-request',
|
||||||
'insomnia-plugin-response'
|
'insomnia-plugin-response'
|
||||||
];
|
];
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import * as models from '../models/index';
|
import * as models from '../models/index';
|
||||||
import * as templating from './index';
|
import * as templating from './index';
|
||||||
|
import * as pluginContexts from '../plugins/context';
|
||||||
|
|
||||||
const EMPTY_ARG = '__EMPTY_NUNJUCKS_ARG__';
|
const EMPTY_ARG = '__EMPTY_NUNJUCKS_ARG__';
|
||||||
|
|
||||||
@ -60,6 +61,9 @@ export default class BaseExtension {
|
|||||||
// Pull out the meta helper
|
// Pull out the meta helper
|
||||||
const renderMeta = renderContext.getMeta ? renderContext.getMeta() : {};
|
const renderMeta = renderContext.getMeta ? renderContext.getMeta() : {};
|
||||||
|
|
||||||
|
// Pull out the purpose
|
||||||
|
const renderPurpose = renderContext.getPurpose ? renderContext.getPurpose() : null;
|
||||||
|
|
||||||
// Extract the rest of the args
|
// Extract the rest of the args
|
||||||
const args = runArgs
|
const args = runArgs
|
||||||
.slice(0, runArgs.length - 1)
|
.slice(0, runArgs.length - 1)
|
||||||
@ -67,6 +71,7 @@ export default class BaseExtension {
|
|||||||
|
|
||||||
// Define a helper context with utils
|
// Define a helper context with utils
|
||||||
const helperContext = {
|
const helperContext = {
|
||||||
|
...pluginContexts.app.init(renderPurpose),
|
||||||
context: renderContext,
|
context: renderContext,
|
||||||
meta: renderMeta,
|
meta: renderMeta,
|
||||||
util: {
|
util: {
|
||||||
|
@ -82,5 +82,6 @@ export type PluginTemplateTag = {
|
|||||||
description: string,
|
description: string,
|
||||||
run: (context: PluginTemplateTagContext, ...arg: Array<any>) => Promise<any> | any,
|
run: (context: PluginTemplateTagContext, ...arg: Array<any>) => Promise<any> | any,
|
||||||
deprecated?: boolean,
|
deprecated?: boolean,
|
||||||
|
validate?: (value: any) => ?string,
|
||||||
priority?: number
|
priority?: number
|
||||||
};
|
};
|
||||||
|
@ -54,7 +54,7 @@ export function render (text: string, config: Object = {}): Promise<string> {
|
|||||||
: 'error';
|
: 'error';
|
||||||
|
|
||||||
const newError = new RenderError(sanitizedMsg);
|
const newError = new RenderError(sanitizedMsg);
|
||||||
newError.path = path || null;
|
newError.path = path || '';
|
||||||
newError.message = sanitizedMsg;
|
newError.message = sanitizedMsg;
|
||||||
newError.location = {line, column};
|
newError.location = {line, column};
|
||||||
newError.type = 'render';
|
newError.type = 'render';
|
||||||
|
@ -20,6 +20,7 @@ class PromptModal extends PureComponent {
|
|||||||
upperCase: false,
|
upperCase: false,
|
||||||
hint: null,
|
hint: null,
|
||||||
inputType: 'text',
|
inputType: 'text',
|
||||||
|
cancelable: true,
|
||||||
hints: []
|
hints: []
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -66,6 +67,7 @@ class PromptModal extends PureComponent {
|
|||||||
selectText,
|
selectText,
|
||||||
upperCase,
|
upperCase,
|
||||||
hint,
|
hint,
|
||||||
|
cancelable,
|
||||||
inputType,
|
inputType,
|
||||||
placeholder,
|
placeholder,
|
||||||
label,
|
label,
|
||||||
@ -82,6 +84,7 @@ class PromptModal extends PureComponent {
|
|||||||
defaultValue,
|
defaultValue,
|
||||||
submitName,
|
submitName,
|
||||||
selectText,
|
selectText,
|
||||||
|
cancelable,
|
||||||
placeholder,
|
placeholder,
|
||||||
upperCase,
|
upperCase,
|
||||||
hint,
|
hint,
|
||||||
@ -107,7 +110,7 @@ class PromptModal extends PureComponent {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div type="button" key={hint} className={classes}>
|
<div key={hint} className={classes}>
|
||||||
<Button className="tall" onClick={this._handleSelectHint} value={hint}>
|
<Button className="tall" onClick={this._handleSelectHint} value={hint}>
|
||||||
{hint}
|
{hint}
|
||||||
</Button>
|
</Button>
|
||||||
@ -131,7 +134,8 @@ class PromptModal extends PureComponent {
|
|||||||
placeholder,
|
placeholder,
|
||||||
label,
|
label,
|
||||||
upperCase,
|
upperCase,
|
||||||
hints
|
hints,
|
||||||
|
cancelable
|
||||||
} = this.state;
|
} = this.state;
|
||||||
|
|
||||||
const input = (
|
const input = (
|
||||||
@ -152,7 +156,7 @@ class PromptModal extends PureComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal ref={this._setModalRef}>
|
<Modal ref={this._setModalRef} noEscape={!cancelable}>
|
||||||
<ModalHeader>{title}</ModalHeader>
|
<ModalHeader>{title}</ModalHeader>
|
||||||
<ModalBody className="wide">
|
<ModalBody className="wide">
|
||||||
<form onSubmit={this._handleSubmit} className="wide pad">
|
<form onSubmit={this._handleSubmit} className="wide pad">
|
||||||
|
@ -509,6 +509,12 @@ class TagEditor extends React.PureComponent<Props, State> {
|
|||||||
typeof argDefinition.displayName === 'function'
|
typeof argDefinition.displayName === 'function'
|
||||||
) ? fnOrString(argDefinition.displayName, argDatas) : '';
|
) ? fnOrString(argDefinition.displayName, argDatas) : '';
|
||||||
|
|
||||||
|
let validationError = '';
|
||||||
|
const canValidate = argDefinition.type === 'string' || argDefinition.type === 'number';
|
||||||
|
if (canValidate && typeof argDefinition.validate === 'function') {
|
||||||
|
validationError = argDefinition.validate(strValue) || '';
|
||||||
|
}
|
||||||
|
|
||||||
const formControlClasses = classnames({
|
const formControlClasses = classnames({
|
||||||
'form-control': true,
|
'form-control': true,
|
||||||
'form-control--thin': argDefinition.type === 'boolean',
|
'form-control--thin': argDefinition.type === 'boolean',
|
||||||
@ -522,6 +528,7 @@ class TagEditor extends React.PureComponent<Props, State> {
|
|||||||
{fnOrString(displayName, argDatas)}
|
{fnOrString(displayName, argDatas)}
|
||||||
{isVariable && <span className="faded space-left">(Variable)</span>}
|
{isVariable && <span className="faded space-left">(Variable)</span>}
|
||||||
{help && <HelpTooltip className="space-left">{help}</HelpTooltip>}
|
{help && <HelpTooltip className="space-left">{help}</HelpTooltip>}
|
||||||
|
{validationError && <span className="font-error space-left">{validationError}</span>}
|
||||||
{argInputVariable || argInput}
|
{argInputVariable || argInput}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "1.0.5",
|
"version": "1.0.6",
|
||||||
"name": "insomnia-app",
|
"name": "insomnia-app",
|
||||||
"app": {
|
"app": {
|
||||||
"name": "insomnia",
|
"name": "insomnia",
|
||||||
@ -108,6 +108,7 @@
|
|||||||
"insomnia-plugin-file": "^1.0.3",
|
"insomnia-plugin-file": "^1.0.3",
|
||||||
"insomnia-plugin-hash": "^1.0.3",
|
"insomnia-plugin-hash": "^1.0.3",
|
||||||
"insomnia-plugin-now": "^1.0.3",
|
"insomnia-plugin-now": "^1.0.3",
|
||||||
|
"insomnia-plugin-prompt": "^1.0.1",
|
||||||
"insomnia-plugin-request": "^1.0.4",
|
"insomnia-plugin-request": "^1.0.4",
|
||||||
"insomnia-plugin-response": "^1.0.5",
|
"insomnia-plugin-response": "^1.0.5",
|
||||||
"insomnia-plugin-uuid": "^1.0.3",
|
"insomnia-plugin-uuid": "^1.0.3",
|
||||||
|
5
plugins/insomnia-plugin-prompt/README.md
Normal file
5
plugins/insomnia-plugin-prompt/README.md
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
# Insomnia Prompt Template Tag
|
||||||
|
|
||||||
|
[![Npm Version](https://img.shields.io/npm/v/insomnia-plugin-prompt.svg)](https://www.npmjs.com/package/insomnia-plugin-prompt)
|
||||||
|
|
||||||
|
This is a core Insomnia plugin.
|
21
plugins/insomnia-plugin-prompt/index.js
Normal file
21
plugins/insomnia-plugin-prompt/index.js
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
module.exports.templateTags = [{
|
||||||
|
displayName: 'Prompt',
|
||||||
|
name: 'prompt',
|
||||||
|
description: 'prompt user for input',
|
||||||
|
args: [{
|
||||||
|
displayName: 'Title',
|
||||||
|
type: 'string'
|
||||||
|
}, {
|
||||||
|
displayName: 'Label',
|
||||||
|
type: 'string'
|
||||||
|
}, {
|
||||||
|
displayName: 'Default Value',
|
||||||
|
type: 'string',
|
||||||
|
help: 'This value is used to pre-populate the prompt dialog, but is ALSO used ' +
|
||||||
|
'when the app renders preview values (like the one below). This is to prevent the ' +
|
||||||
|
'prompt from displaying too frequently during general app use.'
|
||||||
|
}],
|
||||||
|
run (context, title, label, defaultValue) {
|
||||||
|
return context.app.prompt({title, label, defaultValue});
|
||||||
|
}
|
||||||
|
}];
|
18
plugins/insomnia-plugin-prompt/package.json
Normal file
18
plugins/insomnia-plugin-prompt/package.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"name": "insomnia-plugin-prompt",
|
||||||
|
"version": "1.0.1",
|
||||||
|
"author": "Gregory Schier <gschier1990@gmail.com>",
|
||||||
|
"description": "Insomnia prompt template tag",
|
||||||
|
"license": "MIT",
|
||||||
|
"repository": "https://github.com/getinsomnia/insomnia/tree/master/plugins/insomnia-plugin-prompt",
|
||||||
|
"bugs": {
|
||||||
|
"url": "https://github.com/getinsomnia/insomnia"
|
||||||
|
},
|
||||||
|
"main": "index.js",
|
||||||
|
"insomnia": {
|
||||||
|
"name": "prompt"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "node --version"
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user