Refactor settings and store timeline separately

This commit is contained in:
Gregory Schier committed 2019-04-27 01:46:10 -07:00
1 parent 0601fc9582
commit dce5f320c3
16 files changed
+424 -657

No files matched your search

@@ -85,7 +85,6 @@ type BaseRequest = {
settingDisableRenderRequestBody: boolean,
settingEncodeUrl: boolean,
settingRebuildPath: boolean,
settingMaxTimelineDataSize: number,
};
export type Request = BaseModel & BaseRequest;
@@ -109,7 +108,6 @@ export function init(): BaseRequest {
settingDisableRenderRequestBody: false,
settingEncodeUrl: true,
settingRebuildPath: true,
settingMaxTimelineDataSize: 1000,
};
}
+60 -17
View File
@@ -37,8 +37,8 @@ type BaseResponse = {
bytesContent: number,
elapsedTime: number,
headers: Array<ResponseHeader>,
timeline: Array<ResponseTimelineEntry>,
bodyPath: string, // Actual bodies are stored on the filesystem
timelinePath: string, // Actual timelines are stored on the filesystem
bodyCompression: 'zip' | null | '__NEEDS_MIGRATION__',
error: string,
requestVersionId: string | null,
@@ -61,7 +61,7 @@ export function init(): BaseResponse {
bytesContent: -1, // -1 means that it was legacy and this property didn't exist yet
elapsedTime: 0,
headers: [],
timeline: [],
timelinePath: '', // Actual timelines are stored on the filesystem
bodyPath: '', // Actual bodies are stored on the filesystem
bodyCompression: '__NEEDS_MIGRATION__', // For legacy bodies
error: '',
@@ -76,6 +76,7 @@ export function init(): BaseResponse {
export async function migrate(doc: Object) {
doc = await migrateBodyToFileSystem(doc);
doc = await migrateBodyCompression(doc);
doc = await migrateTimelineToFileSystem(doc);
return doc;
}
@@ -86,11 +87,12 @@ export async function hookDatabaseInit() {
}
export function hookRemove(doc: Response) {
if (!doc.bodyPath) {
return;
}
fs.unlinkSync(doc.bodyPath);
fs.unlink(doc.bodyPath, () => {
console.log(`[response] Delete body ${doc.bodyPath}`);
});
fs.unlink(doc.timelinePath, () => {
console.log(`[response] Delete timeline ${doc.timelinePath}`);
});
}
export function getById(id: string) {
@@ -160,6 +162,10 @@ export function getBodyBuffer<T>(response: Object, readFailureValue: ?T): Buffer
return getBodyBufferFromPath(response.bodyPath || '', response.bodyCompression, readFailureValue);
}
export function getTimeline(response: Object): Array<ResponseTimelineEntry> {
return getTimelineFromPath(response.timelinePath || '');
}
function getBodyStreamFromPath<T>(
bodyPath: string,
compression: string | null,
@@ -208,11 +214,25 @@ function getBodyBufferFromPath<T>(
}
}
function getTimelineFromPath(timelinePath: string): Array<ResponseTimelineEntry> {
// No body, so return empty Buffer
if (!timelinePath) {
return [];
}
try {
const rawBuffer = fs.readFileSync(timelinePath);
return JSON.parse(rawBuffer.toString());
} catch (err) {
console.warn('Failed to read response body', err.message);
return [];
}
}
async function migrateBodyToFileSystem(doc: Object) {
if (doc.hasOwnProperty('body') && doc._id && !doc.bodyPath) {
const bodyBuffer = Buffer.from(doc.body, doc.encoding || 'utf8');
const root = getDataDirectory();
const dir = path.join(root, 'responses');
const dir = path.join(getDataDirectory(), 'responses');
mkdirp.sync(dir);
@@ -243,23 +263,46 @@ function migrateBodyCompression(doc: Object) {
return doc;
}
async function migrateTimelineToFileSystem(doc: Object) {
if (doc.hasOwnProperty('timeline') && doc._id && !doc.timelinePath) {
const dir = path.join(getDataDirectory(), 'responses');
mkdirp.sync(dir);
const timelineStr = JSON.stringify(doc.timeline, null, '\t');
const fsPath = doc.bodyPath + '.timeline';
try {
fs.writeFileSync(fsPath, timelineStr);
} catch (err) {
console.warn('Failed to write response body to file', err.message);
}
return db.docUpdate(doc, { timelinePath: fsPath });
} else {
return doc;
}
}
export async function cleanDeletedResponses() {
const responsesDir = path.join(getDataDirectory(), 'responses');
mkdirp.sync(responsesDir);
let files = fs.readdirSync(responsesDir);
const files = fs.readdirSync(responsesDir);
if (files.length === 0) {
return;
}
let whitelistFiles = (await db.all(type)).map(res => {
return res.bodyPath.slice(responsesDir.length + 1);
});
const whitelistFiles = [];
for (const r of await db.all(type)) {
whitelistFiles.push(r.bodyPath.slice(responsesDir.length + 1));
whitelistFiles.push(r.timelinePath.slice(responsesDir.length + 1));
}
for (let index = 0; index < files.length; index++) {
if (whitelistFiles.indexOf(files[index]) === -1) {
const bodyPath = path.join(responsesDir, files[index]);
fs.unlinkSync(bodyPath);
for (const filePath of files) {
if (whitelistFiles.indexOf(filePath) >= 0) {
continue;
}
fs.unlinkSync(path.join(responsesDir, filePath));
}
}
@@ -36,6 +36,7 @@ type BaseSettings = {
fontInterface: string | null,
fontSize: number,
fontVariantLigatures: boolean,
maxTimelineDataSizeKB: number,
// Feature flags
enableSyncBeta: boolean,
@@ -83,6 +84,7 @@ export function init(): BaseSettings {
fontInterface: null,
fontSize: 13,
fontVariantLigatures: false,
maxTimelineDataSizeKB: 10,
enableSyncBeta: false,
hotKeyRegistry: hotkeys.newDefaultRegistry(),
};
+28 -5
View File
@@ -6,6 +6,7 @@ import type { Settings } from '../models/settings';
import type { RenderedRequest } from '../common/render';
import { getRenderedRequestAndContext, RENDER_PURPOSE_SEND } from '../common/render';
import mkdirp from 'mkdirp';
import crypto from 'crypto';
import clone from 'clone';
import { parse as urlParse, resolve as urlResolve } from 'url';
import { Curl } from 'insomnia-libcurl';
@@ -56,7 +57,7 @@ import { urlMatchesCertHost } from './url-matches-cert-host';
import aws4 from 'aws4';
import { buildMultipart } from './multipart';
export type ResponsePatch = {
export type ResponsePatch = {|
statusMessage?: string,
error?: string,
url?: string,
@@ -73,8 +74,8 @@ export type ResponsePatch = {
parentId?: string,
settingStoreCookies?: boolean,
settingSendCookies?: boolean,
timeline?: Array<ResponseTimelineEntry>,
};
timelinePath?: string,
|};
// Time since user's last keypress to wait before making the request
const MAX_DELAY_TIME = 1000;
@@ -117,11 +118,13 @@ export async function _actuallySend(
bodyPath: string | null,
noPlugins: boolean = false,
): Promise<void> {
const timelinePath = await storeTimeline(timeline);
const responsePatchBeforeHooks = Object.assign(
({
timelinePath,
parentId: renderedRequest._id,
bodyCompression: null, // Will default to .zip otherwise
timeline: timeline,
bodyPath: bodyPath || '',
settingSendCookies: renderedRequest.settingSendCookies,
settingStoreCookies: renderedRequest.settingStoreCookies,
@@ -253,7 +256,7 @@ export async function _actuallySend(
if (infoType === Curl.info.debug.DATA_OUT) {
if (content.length === 0) {
// Sometimes this happens, but I'm not sure why. Just ignore it.
} else if (content.length < renderedRequest.settingMaxTimelineDataSize) {
} else if (content.length / 1024 < settings.maxTimelineDataSizeKB) {
addTimeline(name, content);
} else {
addTimeline(name, `(${describeByteSize(content.length)} hidden)`);
@@ -991,6 +994,26 @@ export function _getAwsAuthHeaders(
.map(name => ({ name, value: signature.headers[name] }));
}
function storeTimeline(timeline: Array<ResponseTimelineEntry>): Promise<string> {
return new Promise((resolve, reject) => {
const timelineStr = JSON.stringify(timeline, null, '\t');
const timelineHash = crypto
.createHash('sha1')
.update(timelineStr)
.digest('hex');
const responsesDir = pathJoin(getDataDirectory(), 'responses');
mkdirp.sync(responsesDir);
const timelinePath = pathJoin(responsesDir, timelineHash + '.timeline');
fs.writeFile(timelinePath, timelineStr, err => {
if (err != null) {
reject(err);
} else {
resolve(timelinePath);
}
});
});
}
document.addEventListener('keydown', (e: KeyboardEvent) => {
if (e.ctrlKey || e.metaKey || e.altKey) {
return;
@@ -313,67 +313,34 @@ class OneLineEditor extends PureComponent {
if (showEditor) {
return (
<React.Fragment>
{isVariableUncovered && (
<CodeEditor
ref={this._setEditorRef}
defaultTabBehavior
hideLineNumbers
hideScrollbars
noMatchBrackets
noStyleActiveLine
noLint
singleLine
tabIndex={0}
id={id}
type={type}
mode={syntaxMode}
placeholder={placeholder}
onPaste={onPaste}
onBlur={this._handleEditorBlur}
onKeyDown={this._handleKeyDown}
onFocus={this._handleEditorFocus}
onMouseLeave={this._handleEditorMouseLeave}
onChange={onChange}
render={render}
getRenderContext={getRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
getAutocompleteConstants={getAutocompleteConstants}
className={classnames('editor--single-line', className)}
defaultValue={defaultValue}
isVariableUncovered={isVariableUncovered}
/>
)}
{!isVariableUncovered && (
<CodeEditor
ref={this._setEditorRef}
defaultTabBehavior
hideLineNumbers
hideScrollbars
noMatchBrackets
noStyleActiveLine
noLint
singleLine
tabIndex={0}
id={id}
type={type}
mode={syntaxMode}
placeholder={placeholder}
onPaste={onPaste}
onBlur={this._handleEditorBlur}
onKeyDown={this._handleKeyDown}
onFocus={this._handleEditorFocus}
onMouseLeave={this._handleEditorMouseLeave}
onChange={onChange}
render={render}
getRenderContext={getRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
getAutocompleteConstants={getAutocompleteConstants}
className={classnames('editor--single-line', className)}
defaultValue={defaultValue}
isVariableUncovered={isVariableUncovered}
/>
)}
<CodeEditor
ref={this._setEditorRef}
defaultTabBehavior
hideLineNumbers
hideScrollbars
noMatchBrackets
noStyleActiveLine
noLint
singleLine
tabIndex={0}
id={id}
type={type}
mode={syntaxMode}
placeholder={placeholder}
onPaste={onPaste}
onBlur={this._handleEditorBlur}
onKeyDown={this._handleKeyDown}
onFocus={this._handleEditorFocus}
onMouseLeave={this._handleEditorMouseLeave}
onChange={onChange}
render={render}
getRenderContext={getRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
getAutocompleteConstants={getAutocompleteConstants}
className={classnames('editor--single-line', className)}
defaultValue={defaultValue}
isVariableUncovered={isVariableUncovered}
/>
</React.Fragment>
);
} else {
@@ -590,56 +590,29 @@ class GraphQLEditor extends React.PureComponent<Props, State> {
)}
</h2>
<div className="graphql-editor__variables">
{isVariableUncovered && (
<CodeEditor
dynamicHeight
uniquenessKey={uniquenessKey ? uniquenessKey + '::variables' : undefined}
debounceMillis={DEBOUNCE_MILLIS * 4}
manualPrettify={false}
fontSize={settings.editorFontSize}
indentSize={settings.editorIndentSize}
keyMap={settings.editorKeyMap}
defaultValue={variables}
className={className}
render={render}
getRenderContext={getRenderContext}
getAutocompleteConstants={() => Object.keys(variableTypes || {})}
lintOptions={{
variableToType: variableTypes,
}}
nunjucksPowerUserMode={settings.nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
onChange={this._handleVariablesChange}
mode="graphql-variables"
lineWrapping={settings.editorLineWrapping}
placeholder=""
/>
)}
{!isVariableUncovered && (
<CodeEditor
dynamicHeight
uniquenessKey={uniquenessKey ? uniquenessKey + '::variables' : undefined}
debounceMillis={DEBOUNCE_MILLIS * 4}
manualPrettify={false}
fontSize={settings.editorFontSize}
indentSize={settings.editorIndentSize}
keyMap={settings.editorKeyMap}
defaultValue={variables}
className={className}
render={render}
getRenderContext={getRenderContext}
getAutocompleteConstants={() => Object.keys(variableTypes || {})}
lintOptions={{
variableToType: variableTypes,
}}
nunjucksPowerUserMode={settings.nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
onChange={this._handleVariablesChange}
mode="graphql-variables"
lineWrapping={settings.editorLineWrapping}
placeholder=""
/>
)}
<CodeEditor
dynamicHeight
uniquenessKey={uniquenessKey ? uniquenessKey + '::variables' : undefined}
debounceMillis={DEBOUNCE_MILLIS * 4}
manualPrettify={false}
fontSize={settings.editorFontSize}
indentSize={settings.editorIndentSize}
keyMap={settings.editorKeyMap}
defaultValue={variables}
className={className}
render={render}
getRenderContext={getRenderContext}
getAutocompleteConstants={() => Object.keys(variableTypes || {})}
lintOptions={{
variableToType: variableTypes,
}}
nunjucksPowerUserMode={settings.nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
onChange={this._handleVariablesChange}
mode="graphql-variables"
lineWrapping={settings.editorLineWrapping}
placeholder=""
/>
</div>
<div className="pane__footer">
<button className="pull-right btn btn--compact" onClick={this._handlePrettify}>
@@ -25,46 +25,24 @@ class RawEditor extends PureComponent {
return (
<React.Fragment>
{isVariableUncovered && (
<CodeEditor
manualPrettify
uniquenessKey={uniquenessKey}
fontSize={fontSize}
indentSize={indentSize}
indentWithTabs={indentWithTabs}
keyMap={keyMap}
defaultValue={content}
className={className}
render={render}
getRenderContext={getRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
onChange={onChange}
mode={contentType}
lineWrapping={lineWrapping}
placeholder="..."
/>
)}
{!isVariableUncovered && (
<CodeEditor
manualPrettify
uniquenessKey={uniquenessKey}
fontSize={fontSize}
indentSize={indentSize}
indentWithTabs={indentWithTabs}
keyMap={keyMap}
defaultValue={content}
className={className}
render={render}
getRenderContext={getRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
onChange={onChange}
mode={contentType}
lineWrapping={lineWrapping}
placeholder="..."
/>
)}
<CodeEditor
manualPrettify
uniquenessKey={uniquenessKey}
fontSize={fontSize}
indentSize={indentSize}
indentWithTabs={indentWithTabs}
keyMap={keyMap}
defaultValue={content}
className={className}
render={render}
getRenderContext={getRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
onChange={onChange}
mode={contentType}
lineWrapping={lineWrapping}
placeholder="..."
/>
</React.Fragment>
);
}
@@ -99,42 +99,22 @@ class EnvironmentEditor extends React.PureComponent<Props, State> {
return (
<div className="environment-editor">
{isVariableUncovered && (
<CodeEditor
ref={this._setEditorRef}
autoPrettify
fontSize={editorFontSize}
indentSize={editorIndentSize}
lineWrapping={lineWrapping}
keyMap={editorKeyMap}
onChange={this._handleChange}
defaultValue={JSON.stringify(environment)}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
render={render}
getRenderContext={getRenderContext}
mode="application/json"
{...props}
/>
)}
{!isVariableUncovered && (
<CodeEditor
ref={this._setEditorRef}
autoPrettify
fontSize={editorFontSize}
indentSize={editorIndentSize}
lineWrapping={lineWrapping}
keyMap={editorKeyMap}
onChange={this._handleChange}
defaultValue={JSON.stringify(environment)}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
render={render}
getRenderContext={getRenderContext}
mode="application/json"
{...props}
/>
)}
<CodeEditor
ref={this._setEditorRef}
autoPrettify
fontSize={editorFontSize}
indentSize={editorIndentSize}
lineWrapping={lineWrapping}
keyMap={editorKeyMap}
onChange={this._handleChange}
defaultValue={JSON.stringify(environment)}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
render={render}
getRenderContext={getRenderContext}
mode="application/json"
{...props}
/>
{error && <p className="notice error margin">{error}</p>}
{!error && warning && <p className="notice warning margin">{warning}</p>}
</div>
@@ -112,32 +112,17 @@ class RequestHeadersEditor extends React.PureComponent<Props> {
return bulk ? (
<div className="tall">
{isVariableUncovered && (
<CodeEditor
getRenderContext={handleGetRenderContext}
render={handleRender}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
fontSize={editorFontSize}
indentSize={editorIndentSize}
lineWrapping={editorLineWrapping}
onChange={this._handleBulkUpdate}
defaultValue={this._getHeadersString()}
/>
)}
{!isVariableUncovered && (
<CodeEditor
getRenderContext={handleGetRenderContext}
render={handleRender}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
fontSize={editorFontSize}
indentSize={editorIndentSize}
lineWrapping={editorLineWrapping}
onChange={this._handleBulkUpdate}
defaultValue={this._getHeadersString()}
/>
)}
<CodeEditor
getRenderContext={handleGetRenderContext}
render={handleRender}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
fontSize={editorFontSize}
indentSize={editorIndentSize}
lineWrapping={editorLineWrapping}
onChange={this._handleBulkUpdate}
defaultValue={this._getHeadersString()}
/>
</div>
) : (
<div className="pad-bottom scrollable-container">
@@ -68,52 +68,27 @@ class MarkdownEditor extends PureComponent {
</TabList>
<TabPanel className="react-tabs__tab-panel markdown-editor__edit">
<div className="form-control form-control--outlined">
{isVariableUncovered && (
<CodeEditor
ref={this._setEditorRef}
hideGutters
hideLineNumbers
dynamicHeight={!tall}
manualPrettify
noStyleActiveLine
mode={mode || 'text/x-markdown'}
placeholder={placeholder}
debounceMillis={300}
keyMap={keyMap}
fontSize={fontSize}
lineWrapping={lineWrapping}
indentSize={indentSize}
defaultValue={markdown}
render={handleRender}
getRenderContext={handleGetRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
onChange={this._handleChange}
/>
)}
{!isVariableUncovered && (
<CodeEditor
ref={this._setEditorRef}
hideGutters
hideLineNumbers
dynamicHeight={!tall}
manualPrettify
noStyleActiveLine
mode={mode || 'text/x-markdown'}
placeholder={placeholder}
debounceMillis={300}
keyMap={keyMap}
fontSize={fontSize}
lineWrapping={lineWrapping}
indentSize={indentSize}
defaultValue={markdown}
render={handleRender}
getRenderContext={handleGetRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
onChange={this._handleChange}
/>
)}
<CodeEditor
ref={this._setEditorRef}
hideGutters
hideLineNumbers
dynamicHeight={!tall}
manualPrettify
noStyleActiveLine
mode={mode || 'text/x-markdown'}
placeholder={placeholder}
debounceMillis={300}
keyMap={keyMap}
fontSize={fontSize}
lineWrapping={lineWrapping}
indentSize={indentSize}
defaultValue={markdown}
render={handleRender}
getRenderContext={handleGetRenderContext}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
onChange={this._handleChange}
/>
</div>
<div className="txt-sm italic faint">Styling with Markdown is supported</div>
</TabPanel>
@@ -135,42 +135,22 @@ class CodePromptModal extends PureComponent {
) : (
<div className="pad-sm pad-bottom tall">
<div className="form-control form-control--outlined form-control--tall tall">
{isVariableUncovered && (
<CodeEditor
hideLineNumbers
className="tall"
defaultValue={defaultValue}
placeholder={placeholder}
onChange={this._handleChange}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
getRenderContext={enableRender ? handleGetRenderContext : null}
render={enableRender ? handleRender : null}
mode={mode}
keyMap={editorKeyMap}
indentSize={editorIndentSize}
fontSize={editorFontSize}
lineWrapping={editorLineWrapping}
/>
)}
{!isVariableUncovered && (
<CodeEditor
hideLineNumbers
className="tall"
defaultValue={defaultValue}
placeholder={placeholder}
onChange={this._handleChange}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
getRenderContext={enableRender ? handleGetRenderContext : null}
render={enableRender ? handleRender : null}
mode={mode}
keyMap={editorKeyMap}
indentSize={editorIndentSize}
fontSize={editorFontSize}
lineWrapping={editorLineWrapping}
/>
)}
<CodeEditor
hideLineNumbers
className="tall"
defaultValue={defaultValue}
placeholder={placeholder}
onChange={this._handleChange}
nunjucksPowerUserMode={nunjucksPowerUserMode}
isVariableUncovered={isVariableUncovered}
getRenderContext={enableRender ? handleGetRenderContext : null}
render={enableRender ? handleRender : null}
mode={mode}
keyMap={editorKeyMap}
indentSize={editorIndentSize}
fontSize={editorFontSize}
lineWrapping={editorLineWrapping}
/>
</div>
</div>
)}
@@ -58,7 +58,7 @@ class ResponseDebugModal extends React.PureComponent<Props, State> {
editorFontSize={settings.editorFontSize}
editorIndentSize={settings.editorIndentSize}
editorLineWrapping={settings.editorLineWrapping}
timeline={response.timeline}
response={response}
/>
) : (
<div>No response found</div>
@@ -352,7 +352,7 @@ class ResponsePane extends React.PureComponent<Props> {
<TabPanel className="react-tabs__tab-panel">
<ErrorBoundary key={response._id} errorClassName="font-error pad text-center">
<ResponseTimelineViewer
timeline={response.timeline || []}
response={response}
editorLineWrapping={editorLineWrapping}
editorFontSize={editorFontSize}
editorIndentSize={editorIndentSize}
@@ -19,6 +19,7 @@ import type { Settings } from '../../../models/settings';
import CheckForUpdatesButton from '../check-for-updates-button';
import { setFont } from '../../../plugins/misc';
import * as session from '../../../account/session';
import Tooltip from '../tooltip';
type Props = {
settings: Settings,
@@ -69,7 +70,7 @@ class General extends React.PureComponent<Props, State> {
this.props.handleToggleMenuBar(settings.autoHideMenuBar);
}
async _handleToggleSyncBeta(e: SyntheticEvent<HTMLInputElement>) {
async _handleUpdateSettingAndRestart(e: SyntheticEvent<HTMLInputElement>) {
await this._handleUpdateSetting(e);
const { app } = electron.remote || electron;
app.relaunch();
@@ -91,140 +92,91 @@ class General extends React.PureComponent<Props, State> {
setFont(settings);
}
renderBooleanSetting(label: string, name: string, help: string, forceRestart?: boolean) {
const { settings } = this.props;
if (!settings.hasOwnProperty(name)) {
throw new Error(`Invalid boolean setting name ${name}`);
}
const onChange = forceRestart ? this._handleUpdateSettingAndRestart : this._handleUpdateSetting;
return (
<div className="form-control form-control--thin">
<label className="inline-block">
{label}
{help && (
<HelpTooltip className="space-left">
Configure the autocomplete popup delay in milliseconds (0 to disable)
</HelpTooltip>
)}
{forceRestart && (
<Tooltip message="Will restart app" className="space-left">
<i className="fa fa-refresh super-duper-faint" />
</Tooltip>
)}
<input type="checkbox" name={name} checked={settings[name]} onChange={onChange} />
</label>
</div>
);
}
renderTextSetting(label: string, name: string, help: string, props: Object) {
const { settings } = this.props;
if (!settings.hasOwnProperty(name)) {
throw new Error(`Invalid number setting name ${name}`);
}
return (
<div className="form-control form-control--outlined">
<label>
{label}
{help && (
<HelpTooltip className="space-left">
Configure the autocomplete popup delay in milliseconds (0 to disable)
</HelpTooltip>
)}
<input
type={props.type || 'text'}
name={name}
defaultValue={settings[name]}
{...props}
onChange={props.onChange || this._handleUpdateSetting}
/>
</label>
</div>
);
}
renderNumberSetting(label: string, name: string, help: string, props: Object) {
return this.renderTextSetting(label, name, help, { ...props, type: 'number' });
}
render() {
const { settings } = this.props;
const { fonts } = this.state;
return (
<div>
<div className="form-control form-control--thin">
<label className="inline-block">
Follow redirects automatically
<input
type="checkbox"
name="followRedirects"
checked={settings.followRedirects}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-control form-control--thin">
<label className="inline-block">
Validate SSL Certificates
<input
type="checkbox"
name="validateSSL"
checked={settings.validateSSL}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-control form-control--thin">
<label className="inline-block">
Show passwords in plain-text
<input
type="checkbox"
name="showPasswords"
checked={settings.showPasswords}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-control form-control--thin">
<label className="inline-block">
Use bulk header editor by default
<input
type="checkbox"
name="useBulkHeaderEditor"
checked={settings.useBulkHeaderEditor}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-control form-control--thin">
<label className="inline-block">
Always use vertical layout
<input
type="checkbox"
name="forceVerticalLayout"
checked={settings.forceVerticalLayout}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{!isMac() && (
<div className="form-control form-control--thin">
<label className="inline-block">
Hide Menu Bar
<input
type="checkbox"
name="autoHideMenuBar"
checked={settings.autoHideMenuBar}
onChange={this._handleToggleMenuBar}
/>
</label>
<div className="row-fill row-fill--top">
<div>
{this.renderBooleanSetting('Force bulk header editor', 'useBulkHeaderEditor', '')}
{this.renderBooleanSetting(
'Vertical request/response layout',
'forceVerticalLayout',
'',
)}
</div>
<div>
{this.renderBooleanSetting('Reveal passwords', 'showPasswords', '')}
{!isMac() && this.renderBooleanSetting('Hide menu bar', 'autoHideMenuBar', '')}
{this.renderBooleanSetting('Raw template syntax', 'nunjucksPowerUserMode', '', true)}
</div>
)}
<div className="form-control form-control--thin">
<label className="inline-block">
Wrap Long Lines
<input
type="checkbox"
name="editorLineWrapping"
checked={settings.editorLineWrapping}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-control form-control--thin">
<label className="inline-block">
Nunjucks Power User Mode{' '}
<HelpTooltip>
Disable tag editing interface in favor of raw Nunjucks syntax (requires restart)
</HelpTooltip>
<input
type="checkbox"
name="nunjucksPowerUserMode"
checked={settings.nunjucksPowerUserMode}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-control form-control--thin">
<label className="inline-block">
Indent With Tabs
<input
type="checkbox"
name="editorIndentWithTabs"
checked={settings.editorIndentWithTabs}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-control form-control--thin">
<label className="inline-block">
Font Ligatures
<input
type="checkbox"
name="fontVariantLigatures"
checked={settings.fontVariantLigatures}
onChange={this._handleFontLigatureChange}
/>
</label>
</div>
<div className="form-row">
<div className="form-control form-control--outlined pad-top-sm">
<div className="row-fill row-fill--top pad-top-sm">
<div className="form-control form-control--outlined">
<label>
Environment Highlight Color Style{' '}
Environment Highlight Style{' '}
<HelpTooltip>Configures the appearance of environment's color indicator</HelpTooltip>
<select
defaultValue={settings.environmentHighlightColorStyle}
@@ -239,25 +191,30 @@ class General extends React.PureComponent<Props, State> {
</select>
</label>
</div>
<div className="form-control form-control--outlined pad-top-sm">
<label>
Autocomplete popup delay
<HelpTooltip className="space-left">
Configure the autocomplete popup delay in milliseconds (0 to disable)
</HelpTooltip>
<input
type="number"
name="autocompleteDelay"
min={0}
max={3000}
defaultValue={settings.autocompleteDelay}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderNumberSetting(
'Autocomplete popup delay',
'autocompleteDelay',
'Configure the autocomplete popup delay in milliseconds (0 to disable)',
{
min: 0,
max: 3000,
},
)}
</div>
<div className="form-row">
<hr className="pad-top" />
<h2>Font</h2>
<div className="row-fill row-fill--top">
<div>
{this.renderBooleanSetting('Indent with tabs', 'editorIndentWithTabs', '')}
{this.renderBooleanSetting('Wrap text editor lines', 'editorLineWrapping', '')}
</div>
<div>{this.renderBooleanSetting('Font ligatures', 'fontVariantLigatures', '')}</div>
</div>
<div className="form-row pad-top-sm">
<div className="form-control form-control--outlined">
<label>
Interface Font
@@ -280,19 +237,11 @@ class General extends React.PureComponent<Props, State> {
)}
</label>
</div>
<div className="form-control form-control--outlined">
<label>
Interface Font Size (px)
<input
type="number"
name="fontSize"
min={8}
max={20}
defaultValue={settings.fontSize}
onChange={this._handleFontSizeChange}
/>
</label>
</div>
{this.renderNumberSetting('Interface Font Size (px)', 'fontSize', '', {
min: 8,
max: 20,
onChange: this._handleFontSizeChange,
})}
</div>
<div className="form-row">
@@ -320,36 +269,17 @@ class General extends React.PureComponent<Props, State> {
)}
</label>
</div>
<div className="form-control form-control--outlined">
<label>
Text Editor Font Size (px)
<input
type="number"
name="editorFontSize"
min={8}
max={20}
defaultValue={settings.editorFontSize}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderNumberSetting('Editor Font Size (px)', 'editorFontSize', '', {
min: 8,
max: 20,
})}
</div>
<div className="form-row">
<div className="form-control form-control--outlined">
<label>
Text Editor Indent Size
<input
type="number"
name="editorIndentSize"
min={1}
max={16}
defaultValue={settings.editorIndentSize}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderNumberSetting('Editor Indent Size', 'editorIndentSize', '', {
min: 1,
max: 16,
})}
<div className="form-control form-control--outlined">
<label>
Text Editor Key Map
@@ -368,55 +298,24 @@ class General extends React.PureComponent<Props, State> {
<hr className="pad-top" />
<h2>Request Settings</h2>
<h2>Network</h2>
<div className="form-row">
<div className="form-control form-control--outlined">
<label>
Maximum Redirects
<HelpTooltip className="space-left">(-1 for unlimited)</HelpTooltip>
<input
type="number"
name="maxRedirects"
min={-1}
defaultValue={settings.maxRedirects}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderBooleanSetting('Validate certificates', 'validateSSL', '')}
{this.renderBooleanSetting('Follow redirects', 'followRedirects', '')}
<div className="form-control form-control--outlined">
<label>
Network Request Timeout
<HelpTooltip className="space-left">
Request timeout in milliseconds (0 for no timeout)
</HelpTooltip>
<input
type="number"
name="timeout"
min={-1}
defaultValue={settings.timeout}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-row pad-top-sm">
{this.renderNumberSetting('Maximum Redirects', 'maxRedirects', '-1 for unlimited', {
min: -1,
})}
{this.renderNumberSetting('Request Timeout', 'timeout', '-1 for infinite', { min: -1 })}
</div>
<div className="form-control form-control--outlined">
<label>
Response History Limit
<HelpTooltip className="space-left">
Number of responses to keep for each request
</HelpTooltip>
<input
type="number"
name="maxHistoryResponses"
min={1}
defaultValue={settings.maxHistoryResponses}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderNumberSetting(
'Response History Limit',
'maxHistoryResponses',
'Number of responses to keep for each request (-1 for infinity)',
{ min: -1 },
)}
<hr className="pad-top" />
@@ -429,61 +328,26 @@ class General extends React.PureComponent<Props, State> {
</HelpTooltip>
</h2>
<div className="form-control form-control--thin">
<label className="inline-block">
Enable Proxy
<input
type="checkbox"
name="proxyEnabled"
checked={settings.proxyEnabled}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderBooleanSetting('Enable proxy', 'proxyEnabled', '')}
<div className="form-row pad-top-sm">
<div className="form-control form-control--outlined">
<label>
HTTP Proxy
<input
type="text"
name="httpProxy"
disabled={!settings.proxyEnabled}
placeholder="localhost:8005"
defaultValue={settings.httpProxy}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-control form-control--outlined">
<label>
HTTPS Proxy
<input
placeholder="localhost:8005"
disabled={!settings.proxyEnabled}
name="httpsProxy"
type="text"
defaultValue={settings.httpsProxy}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
<div className="form-control form-control--outlined">
<label>
No Proxy{' '}
<HelpTooltip>
Comma-separated list of hostnames that do not require a proxy to be contacted
</HelpTooltip>
<input
placeholder="localhost,127.0.0.1"
disabled={!settings.proxyEnabled}
name="noProxy"
type="text"
defaultValue={settings.noProxy}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderTextSetting('HTTP Proxy', 'httpProxy', '', {
placeholder: 'localhost:8005',
disabled: !settings.proxyEnabled,
})}
{this.renderTextSetting('HTTPS Proxy', 'httpsProxy', '', {
placeholder: 'localhost:8005',
disabled: !settings.proxyEnabled,
})}
{this.renderTextSetting(
'No Proxy',
'noProxy',
'Comma-separated list of hostnames that do not require a proxy to be contacted',
{
placeholder: 'localhost,127.0.0.1',
disabled: !settings.proxyEnabled,
},
)}
</div>
{(isWindows() || isMac()) && (
@@ -497,20 +361,11 @@ class General extends React.PureComponent<Props, State> {
</div>
<h2>Software Updates</h2>
</div>
<div className="form-control form-control--thin">
<label className="inline-block">
Automatically download and install updates
<HelpTooltip className="space-left">
If disabled, you will receive a notification when a new update is available
</HelpTooltip>
<input
type="checkbox"
name="updateAutomatically"
checked={settings.updateAutomatically}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderBooleanSetting(
'Automatically download and install updates',
'updateAutomatically',
'If disabled, you will receive a notification when a new update is available',
)}
<div className="form-control form-control--outlined pad-top-sm">
<label>
Update Channel
@@ -530,54 +385,30 @@ class General extends React.PureComponent<Props, State> {
<React.Fragment>
<hr className="pad-top" />
<h2>Software Updates</h2>
<div className="form-control form-control--thin">
<label className="inline-block">
Do not notify of new releases
<input
type="checkbox"
name="disableUpdateNotification"
checked={settings.disableUpdateNotification}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderBooleanSetting(
'Do not notify of new releases',
'disableUpdateNotification',
'',
)}
</React.Fragment>
)}
<hr className="pad-top" />
<h2>Plugins</h2>
<div className="form-control form-control--outlined">
<label>
Additional Plugin Path{' '}
<HelpTooltip>Tell Insomnia to look for plugins in a different directory</HelpTooltip>
<input
placeholder="~/.insomnia:/other/path"
name="pluginPath"
type="text"
defaultValue={settings.pluginPath}
onChange={this._handleUpdateSetting}
/>
</label>
</div>
{this.renderTextSetting(
'Additional Plugin Path',
'pluginPath',
'Tell Insomnia to look for plugins in a different directory',
{ placeholder: '~/.insomnia:/other/path' },
)}
<br />
{session.isLoggedIn() && (
<React.Fragment>
<hr />
<div className="form-control form-control--thin">
<label className="inline-block">
Enable sync beta (will restart app){' '}
<HelpTooltip>Enable the new sync beta features</HelpTooltip>
<input
type="checkbox"
name="enableSyncBeta"
checked={settings.enableSyncBeta}
onChange={this._handleToggleSyncBeta}
/>
</label>
</div>
{this.renderBooleanSetting('Enable version control beta', 'enableSyncBeta', '', true)}
</React.Fragment>
)}
</div>
@@ -2,12 +2,38 @@ import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';
import { shell } from 'electron';
import CodeEditor from '../codemirror/code-editor';
import * as models from '../../../models';
class ResponseTimelineViewer extends PureComponent {
_handleClickLink(link) {
state = {
timeline: [],
timelineKey: '',
};
static _handleClickLink(link) {
shell.openExternal(link);
}
componentDidMount() {
this.refreshTimeline();
}
componentDidUpdate(prevProps) {
const { response } = this.props;
if (response._id !== prevProps.response._id) {
this.refreshTimeline();
}
}
async refreshTimeline() {
const { response } = this.props;
const timeline = await models.response.getTimeline(response);
this.setState({
timeline,
timelineKey: response._id,
});
}
renderRow(row, i, all) {
const { name, value } = row;
const previousName = i > 0 ? all[i - 1].name : '';
@@ -56,16 +82,18 @@ class ResponseTimelineViewer extends PureComponent {
}
render() {
const { timeline, editorFontSize, editorIndentSize, editorLineWrapping } = this.props;
const { editorFontSize, editorIndentSize, editorLineWrapping } = this.props;
const { timeline, timelineKey } = this.state;
const rows = timeline
.map(this.renderRow)
.filter(r => r !== null)
.join('\n');
return (
<CodeEditor
key={timelineKey}
hideLineNumbers
readOnly
onClickLink={this._handleClickLink}
onClickLink={ResponseTimelineViewer._handleClickLink}
defaultValue={rows}
fontSize={editorFontSize}
indentSize={editorIndentSize}
@@ -78,7 +106,7 @@ class ResponseTimelineViewer extends PureComponent {
}
ResponseTimelineViewer.propTypes = {
timeline: PropTypes.array.isRequired,
response: PropTypes.object.isRequired,
editorFontSize: PropTypes.number.isRequired,
editorIndentSize: PropTypes.number.isRequired,
editorLineWrapping: PropTypes.bool.isRequired,
@@ -367,6 +367,10 @@ blockquote {
align-content: stretch;
width: 100%;
&.row-fill--top {
align-items: start;
}
& > * {
width: 100%;
}