Some stuff (#83)

This commit is contained in:
Gregory Schier 2017-02-13 00:12:02 -08:00 committed by GitHub
parent f6f148185d
commit ab6d4e03d6
12 changed files with 261 additions and 185 deletions

View File

@ -122,20 +122,29 @@ export function recursiveRender (obj, context) {
return newObj;
}
export async function getRenderedRequest (request, environmentId) {
const ancestors = await db.withAncestors(request);
const workspace = ancestors.find(doc => doc.type === models.workspace.type);
export async function getRenderContext (request, environmentId, ancestors = null) {
if (!ancestors) {
ancestors = await db.withAncestors(request);
}
const workspace = ancestors.find(doc => doc.type === models.workspace.type);
const rootEnvironment = await models.environment.getOrCreateForWorkspace(workspace);
const subEnvironment = await models.environment.getById(environmentId);
const cookieJar = await models.cookieJar.getOrCreateForWorkspace(workspace);
// Generate the context we need to render
const renderContext = buildRenderContext(
return buildRenderContext(
ancestors,
rootEnvironment,
subEnvironment
);
}
export async function getRenderedRequest (request, environmentId) {
const ancestors = await db.withAncestors(request);
const workspace = ancestors.find(doc => doc.type === models.workspace.type);
const cookieJar = await models.cookieJar.getOrCreateForWorkspace(workspace);
const renderContext = await getRenderContext(request, environmentId, ancestors);
// Render all request properties
const renderedRequest = recursiveRender(request, renderContext);

View File

@ -1,7 +1,7 @@
{
"private": true,
"name": "insomnia",
"version": "4.2.12",
"version": "4.2.13",
"productName": "Insomnia",
"longName": "Insomnia REST Client",
"description": "A simple and beautiful REST API client",

View File

@ -72,6 +72,7 @@ class RequestPane extends PureComponent {
editorLineWrapping,
handleSend,
handleSendAndDownload,
handleRender,
forceRefreshCounter,
useBulkHeaderEditor,
handleGenerateCode,
@ -190,6 +191,7 @@ class RequestPane extends PureComponent {
<TabPanel className="editor-wrapper">
<BodyEditor
handleUpdateRequestMimeType={updateRequestMimeType}
handleRender={handleRender}
key={uniqueKey}
request={request}
onChange={updateRequestBody}
@ -285,6 +287,7 @@ RequestPane.propTypes = {
handleSendAndDownload: PropTypes.func.isRequired,
handleCreateRequest: PropTypes.func.isRequired,
handleGenerateCode: PropTypes.func.isRequired,
handleRender: PropTypes.func.isRequired,
updateRequestUrl: PropTypes.func.isRequired,
updateRequestMethod: PropTypes.func.isRequired,
updateRequestBody: PropTypes.func.isRequired,

View File

@ -168,6 +168,7 @@ class Wrapper extends Component {
handleStartDragPane,
handleStartDragSidebar,
handleSetSidebarFilter,
handleRender,
handleGenerateCodeForActiveRequest,
handleGenerateCode,
isLoading,
@ -244,6 +245,7 @@ class Wrapper extends Component {
handleCreateRequest={handleCreateRequestForWorkspace}
handleGenerateCode={handleGenerateCodeForActiveRequest}
handleImport={this._handleImport}
handleRender={handleRender}
updateRequestBody={this._handleUpdateRequestBody}
updateRequestUrl={this._handleUpdateRequestUrl}
updateRequestMethod={this._handleUpdateRequestMethod}
@ -353,6 +355,7 @@ Wrapper.propTypes = {
handleSetRequestPaneRef: PropTypes.func.isRequired,
handleSetResponsePaneRef: PropTypes.func.isRequired,
handleSetResponsePreviewMode: PropTypes.func.isRequired,
handleRender: PropTypes.func.isRequired,
handleSetResponseFilter: PropTypes.func.isRequired,
handleSetActiveResponse: PropTypes.func.isRequired,
handleSetSidebarRef: PropTypes.func.isRequired,

View File

@ -132,23 +132,41 @@ class Editor extends Component {
const {value} = this.props;
// Add overlay to editor to make all links clickable
CodeMirror.defineMode('clickable', (config, parserConfig) => {
CodeMirror.defineMode('master', (config, parserConfig) => {
const baseMode = CodeMirror.getMode(config, parserConfig.baseMode || 'text/plain');
// Only add the click mode if we have links to click
if (!this.props.onClickLink) {
return baseMode;
}
const highlightLinks = !!this.props.onClickLink;
const highlightNunjucks = !this.props.readOnly;
const regexUrl = /^(https?:\/\/)?([\da-z.\-]+)\.([a-z.]{2,6})([\/\w .\-]*)*\/?/;
const regexVariable = /^{{[ |a-zA-Z0-9_\-+,'"\\()\[\]]+}}/;
const regexTag = /^{%[ |a-zA-Z0-9_\-+,'"\\()\[\]]+%}/;
const regexComment = /^{#[^#]+#}/;
const overlay = {
token: function (stream, state) {
// console.log('state', state);
if (stream.match(/^(https?:\/\/)?([\da-z.\-]+)\.([a-z.]{2,6})([\/\w .\-]*)*\/?/, true)) {
if (highlightLinks && stream.match(regexUrl, true)) {
return 'clickable';
}
while (stream.next() != null && !stream.match("http", false)) {
// Do nothing
if (highlightNunjucks && stream.match(regexVariable, true)) {
return 'variable-3';
}
if (highlightNunjucks && stream.match(regexTag, true)) {
return 'variable-3';
}
if (highlightNunjucks && stream.match(regexComment, true)) {
return 'comment';
}
while (stream.next() != null) {
if (stream.match(regexUrl, false)) break;
if (stream.match(regexVariable, false)) break;
if (stream.match(regexTag, false)) break;
if (stream.match(regexComment, false)) break;
}
return null;
@ -271,7 +289,7 @@ class Editor extends Component {
readOnly,
placeholder: this.props.placeholder || '',
mode: {
name: 'clickable',
name: 'master',
baseMode: normalizedMode,
},
lineWrapping: this.props.lineWrapping,
@ -424,16 +442,12 @@ class Editor extends Component {
}
render () {
const {readOnly, fontSize, lightTheme, mode, filter} = this.props;
const {readOnly, fontSize, mode, filter} = this.props;
const classes = classnames(
'editor',
this.props.className,
{
'editor--readonly': readOnly,
'editor--light-theme': !!lightTheme,
'editor--dark-theme': !lightTheme
}
{'editor--readonly': readOnly}
);
const toolbarChildren = [];
@ -500,6 +514,7 @@ Editor.propTypes = {
onChange: PropTypes.func,
onFocusChange: PropTypes.func,
onClickLink: PropTypes.func,
render: PropTypes.func,
keyMap: PropTypes.string,
mode: PropTypes.string,
placeholder: PropTypes.string,
@ -509,8 +524,8 @@ Editor.propTypes = {
autoPrettify: PropTypes.bool,
manualPrettify: PropTypes.bool,
className: PropTypes.any,
lightTheme: PropTypes.bool,
updateFilter: PropTypes.func,
readOnly: PropTypes.bool,
filter: PropTypes.string
};

View File

@ -7,7 +7,7 @@ import {getContentTypeFromHeaders, CONTENT_TYPE_FORM_URLENCODED, CONTENT_TYPE_FO
import {newBodyRaw, newBodyFormUrlEncoded, newBodyForm, newBodyFile} from '../../../../models/request';
class BodyEditor extends PureComponent {
_handleRawChange = (rawValue) => {
_handleRawChange = rawValue => {
const {onChange, request} = this.props;
const contentType = getContentTypeFromHeaders(request.headers);
@ -16,26 +16,26 @@ class BodyEditor extends PureComponent {
onChange(newBody);
};
_handleFormUrlEncodedChange = (parameters) => {
_handleFormUrlEncodedChange = parameters => {
const {onChange} = this.props;
const newBody = newBodyFormUrlEncoded(parameters);
onChange(newBody);
};
_handleFormChange = (parameters) => {
_handleFormChange = parameters => {
const {onChange} = this.props;
const newBody = newBodyForm(parameters);
onChange(newBody);
};
_handleFileChange = (path) => {
_handleFileChange = path => {
const {onChange} = this.props;
const newBody = newBodyFile(path);
onChange(newBody);
};
render () {
const {keyMap, fontSize, lineWrapping, request} = this.props;
const {keyMap, fontSize, lineWrapping, request, handleRender} = this.props;
const fileName = request.body.fileName;
const mimeType = request.body.mimeType;
const isBodyEmpty = typeof mimeType !== 'string' && !request.body.text;
@ -74,6 +74,7 @@ class BodyEditor extends PureComponent {
lineWrapping={lineWrapping}
contentType={contentType || 'text/plain'}
content={request.body.text || ''}
render={handleRender}
onChange={this._handleRawChange}
/>
)
@ -81,7 +82,7 @@ class BodyEditor extends PureComponent {
return (
<div className="editor pad valign-center text-center">
<p className="pad super-faint text-sm text-center">
<i className="fa fa-hand-peace-o" style={{fontSize: '8rem', opacity: 0.3}}></i>
<i className="fa fa-hand-peace-o" style={{fontSize: '8rem', opacity: 0.3}}/>
<br/><br/>
Select a body type from above
</p>
@ -95,6 +96,7 @@ BodyEditor.propTypes = {
// Required
onChange: PropTypes.func.isRequired,
handleUpdateRequestMimeType: PropTypes.func.isRequired,
handleRender: PropTypes.func.isRequired,
request: PropTypes.object.isRequired,
// Optional

View File

@ -8,6 +8,7 @@ class RawEditor extends Component {
content,
fontSize,
keyMap,
render,
lineWrapping,
onChange,
className
@ -20,6 +21,7 @@ class RawEditor extends Component {
keyMap={keyMap}
value={content}
className={className}
render={render}
onChange={onChange}
mode={contentType}
lineWrapping={lineWrapping}
@ -34,11 +36,12 @@ RawEditor.propTypes = {
onChange: PropTypes.func.isRequired,
content: PropTypes.string.isRequired,
contentType: PropTypes.string.isRequired,
fontSize: PropTypes.number.isRequired,
keyMap: PropTypes.string.isRequired,
lineWrapping: PropTypes.bool.isRequired,
// Optional
fontSize: PropTypes.number,
keyMap: PropTypes.string,
lineWrapping: PropTypes.bool
render: PropTypes.func,
};
export default RawEditor;

View File

@ -2,6 +2,7 @@ import React, {Component, PropTypes} from 'react';
import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
import {shell} from 'electron';
import Modal from '../base/Modal';
import Button from '../base/Button';
import ModalBody from '../base/ModalBody';
import ModalHeader from '../base/ModalHeader';
import SettingsShortcuts from '../settings/SettingsShortcuts';
@ -23,6 +24,29 @@ class SettingsModal extends Component {
this.state = {}
}
_setModalRef = m => this.modal = m;
_trackTab = name => trackEvent('Setting', `Tab ${name}`);
_handleTabSelect = currentTabIndex => this.setState({currentTabIndex});
_handleUpdateSetting = (key, value) => {
models.settings.update(this.props.settings, {[key]: value});
trackEvent('Setting', 'Change', key)
};
_handleExportAllToFile = () => {
this.props.handleExportAllToFile();
this.modal.hide()
};
_handleExportWorkspace = () => {
this.props.handleExportWorkspaceToFile();
this.modal.hide()
};
_handleImport = () => {
this.props.handleImportFile();
this.modal.hide()
};
_handleChangeTheme = (theme, track = true) => {
document.body.setAttribute('theme', theme);
models.settings.update(this.props.settings, {theme});
@ -52,23 +76,13 @@ class SettingsModal extends Component {
this.modal.toggle();
}
_handleTabSelect (currentTabIndex) {
this.setState({currentTabIndex});
}
render () {
const {
settings,
handleExportAllToFile,
handleExportWorkspaceToFile,
handleImportFile,
} = this.props;
const {settings} = this.props;
const {currentTabIndex} = this.state;
const email = session.isLoggedIn() ? session.getEmail() : null;
return (
<Modal ref={m => this.modal = m} tall={true} {...this.props}>
<Modal ref={this._setModalRef} tall={true} {...this.props}>
<ModalHeader>
{getAppName()} Preferences
<span className="faint txt-sm">
@ -78,57 +92,45 @@ class SettingsModal extends Component {
</span>
</ModalHeader>
<ModalBody noScroll={true}>
<Tabs onSelect={i => this._handleTabSelect(i)} selectedIndex={currentTabIndex}>
<Tabs onSelect={this._handleTabSelect} selectedIndex={currentTabIndex}>
<TabList>
<Tab selected={this._currentTabIndex === 0}>
<button onClick={e => trackEvent('Setting', 'Tab General')}>
<Button value="General" onClick={this._trackTab}>
General
</button>
</Button>
</Tab>
<Tab selected={this._currentTabIndex === 1}>
<button onClick={e => trackEvent('Setting', 'Tab Import/Export')}>
<Button value="Import/Export" onClick={this._trackTab}>
Import/Export
</button>
</Button>
</Tab>
<Tab selected={this._currentTabIndex === 2}>
<button onClick={e => trackEvent('Setting', 'Tab Themes')}>
<Button value="Themes" onClick={this._trackTab}>
Themes
</button>
</Button>
</Tab>
<Tab selected={this._currentTabIndex === 3}>
<button onClick={e => trackEvent('Setting', 'Tab Shortcuts')}>
<Button value="shortcuts" onClick={this._trackTab}>
Shortcuts
</button>
</Button>
</Tab>
<Tab selected={this._currentTabIndex === 5}>
<button onClick={e => trackEvent('Setting', 'Tab About')}>
<Tab selected={this._currentTabIndex === 4}>
<Button value="About" onClick={this._trackTab}>
About
</button>
</Button>
</Tab>
</TabList>
<TabPanel className="pad scrollable">
<SettingsGeneral
settings={settings}
updateSetting={(key, value) => {
models.settings.update(settings, {[key]: value});
trackEvent('Setting', 'Change', key)
}}
updateSetting={this._handleUpdateSetting}
/>
</TabPanel>
<TabPanel className="pad scrollable">
<SettingsImportExport
handleExportAll={() => {
handleExportAllToFile();
this.modal.hide()
}}
handleExportWorkspace={() => {
handleExportWorkspaceToFile();
this.modal.hide()
}}
handleImport={() => {
handleImportFile();
this.modal.hide()
}}
handleExportAll={this._handleExportAllToFile}
handleExportWorkspace={this._handleExportWorkspace}
handleImport={this._handleImport}
/>
</TabPanel>
<TabPanel className="pad scrollable">

View File

@ -1,52 +1,71 @@
import React, {PropTypes} from 'react';
import React, {Component, PropTypes} from 'react';
const SettingsGeneral = ({settings, updateSetting}) => (
class SettingsGeneral extends Component {
_handleUpdateSetting = e => {
let value = e.target.type === 'checkbox' ? e.target.checked : e.target.value;
if (e.target.type === 'number') {
value = parseInt(value, 10);
}
this.props.updateSetting(e.target.name, value);
};
render () {
const {settings} = this.props;
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={e => updateSetting('followRedirects', e.target.checked)}/>
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={e => updateSetting('validateSSL', e.target.checked)}/>
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={e => updateSetting('showPasswords', e.target.checked)}/>
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={e => updateSetting('useBulkHeaderEditor', e.target.checked)}/>
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={e => updateSetting('forceVerticalLayout', e.target.checked)}/>
onChange={this._handleUpdateSetting}/>
</label>
</div>
<div className="form-control form-control--thin">
<label className="inline-block">Wrap Long Lines
<input type="checkbox"
name="editorLineWrapping"
checked={settings.editorLineWrapping}
onChange={e => updateSetting('editorLineWrapping', e.target.checked)}/>
onChange={this._handleUpdateSetting}/>
</label>
</div>
@ -54,10 +73,11 @@ const SettingsGeneral = ({settings, updateSetting}) => (
<div className="form-control form-control--outlined pad-top-sm">
<label>Text Editor Font Size (px)
<input type="number"
name="editorFontSize"
min={8}
max={20}
defaultValue={settings.editorFontSize}
onChange={e => updateSetting('editorFontSize', parseInt(e.target.value, 10))}/>
onChange={this._handleUpdateSetting}/>
</label>
</div>
@ -65,7 +85,8 @@ const SettingsGeneral = ({settings, updateSetting}) => (
<label>
Text Editor Key Map
<select defaultValue={settings.editorKeyMap}
onChange={e => updateSetting('editorKeyMap', e.target.value)}>
name="editorKeyMap"
onChange={this._handleUpdateSetting}>
<option value="default">Default</option>
<option value="vim">Vim</option>
<option value="emacs">Emacs</option>
@ -78,32 +99,37 @@ const SettingsGeneral = ({settings, updateSetting}) => (
<div className="form-control form-control--outlined">
<label>Request Timeout (ms) (0 for no timeout)
<input type="number"
name="timeout"
min={0}
defaultValue={settings.timeout}
onChange={e => updateSetting('timeout', parseInt(e.target.value, 10))}/>
onChange={this._handleUpdateSetting}/>
</label>
</div>
<div className="form-row">
<div className="form-control form-control--outlined">
<label>HTTP Proxy
<input type="text"
name="httpProxy"
placeholder="localhost:8005"
defaultValue={settings.httpProxy}
onChange={e => updateSetting('httpProxy', e.target.value)}/>
onChange={this._handleUpdateSetting}/>
</label>
</div>
<div className="form-control form-control--outlined">
<label>HTTPS Proxy
<input placeholder="localhost:8005"
name="httpsProxy"
type="text"
defaultValue={settings.httpsProxy}
onChange={e => updateSetting('httpsProxy', e.target.value)}/>
onChange={this._handleUpdateSetting}/>
</label>
</div>
</div>
<br/>
</div>
);
)
}
}
SettingsGeneral.propTypes = {
settings: PropTypes.object.isRequired,

View File

@ -28,6 +28,7 @@ import * as network from '../../common/network';
import {debounce} from '../../common/misc';
import * as mime from 'mime-types';
import * as path from 'path';
import * as render from '../../common/render';
const KEY_ENTER = 13;
const KEY_COMMA = 188;
@ -143,7 +144,7 @@ class App extends Component {
models.requestGroup.create({parentId, name})
};
_requestCreate = async (parentId) => {
_requestCreate = async parentId => {
const request = await showModal(RequestCreateModal, {parentId});
this._handleSetActiveRequest(request._id)
};
@ -158,7 +159,14 @@ class App extends Component {
}
const newRequest = await models.request.duplicate(request);
this._handleSetActiveRequest(newRequest._id)
await this._handleSetActiveRequest(newRequest._id)
};
_handleRenderText = async text => {
const {activeEnvironment, activeRequest} = this.props;
const environmentId = activeEnvironment ? activeEnvironment._id : null;
const context = await render.getRenderContext(activeRequest, environmentId);
return render.render(text, context);
};
_handleGenerateCodeForActiveRequest = () => {
@ -206,12 +214,13 @@ class App extends Component {
this._savePaneWidth(paneWidth);
};
_handleSetActiveRequest = activeRequestId => {
this._updateActiveWorkspaceMeta({activeRequestId});
_handleSetActiveRequest = async activeRequestId => {
await this._updateActiveWorkspaceMeta({activeRequestId});
};
_handleSetActiveEnvironment = activeEnvironmentId => {
this._updateActiveWorkspaceMeta({activeEnvironmentId});
_handleSetActiveEnvironment = async activeEnvironmentId => {
await this._updateActiveWorkspaceMeta({activeEnvironmentId});
this._wrapper.forceRequestPaneRefresh();
};
_saveSidebarWidth = debounce(sidebarWidth => this._updateActiveWorkspaceMeta({sidebarWidth}));
@ -220,12 +229,12 @@ class App extends Component {
this._saveSidebarWidth(sidebarWidth);
};
_handleSetSidebarHidden = sidebarHidden => {
this._updateActiveWorkspaceMeta({sidebarHidden});
_handleSetSidebarHidden = async sidebarHidden => {
await this._updateActiveWorkspaceMeta({sidebarHidden});
};
_handleSetSidebarFilter = sidebarFilter => {
this._updateActiveWorkspaceMeta({sidebarFilter});
_handleSetSidebarFilter = async sidebarFilter => {
await this._updateActiveWorkspaceMeta({sidebarFilter});
};
_handleSetRequestGroupCollapsed = (requestGroupId, collapsed) => {
@ -431,7 +440,7 @@ class App extends Component {
trackEvent('General', 'Launched', getAppVersion(), {nonInteraction: true});
}
db.onChange(changes => {
db.onChange(async changes => {
for (const change of changes) {
const [event, doc, fromSync] = change;
const {activeRequest} = this.props;
@ -499,6 +508,7 @@ class App extends Component {
handleStartDragPane={this._startDragPane}
handleResetDragPane={this._resetDragPane}
handleCreateRequest={this._requestCreate}
handleRender={this._handleRenderText}
handleDuplicateRequest={this._requestDuplicate}
handleDuplicateRequestGroup={this._requestGroupDuplicate}
handleCreateRequestGroup={this._requestGroupCreate}

View File

@ -176,7 +176,6 @@
box-sizing: border-box;
}
span.cm-comment,
span.cm-meta,
span.cm-qualifier {
color: var(--color-font);
@ -215,7 +214,7 @@
}
span.cm-variable-3 {
color: var(--color-info);
color: var(--color-surprise);
}
span.cm-def {
@ -238,6 +237,10 @@
color: var(--color-surprise);
}
span.cm-comment {
color: var(--hl);
}
span.cm-error {
background: var(--color-danger);
color: var(--color-font-danger);

View File

@ -45,7 +45,7 @@ body {
--color-notice: #ead950;
--color-warning: #ff9a1f;
--color-danger: #ff5d4b;
--color-surprise: #a590ff;
--color-surprise: #a896ff;
--color-info: #22c2f0;
.tag {