Skip to content

Commit 23692cb

Browse files
authored
Release 5.1.1 (#268)
* Skip invalid cookies and some tweaks * Fix adding context menu multiple times (#254) * Update electron and fix React key warning * OAuth password field should abide by password setting (#256) * Size app icon appropriately * Better errors inside environment editors (#257) * Start a fresh OAuth2 session every launch (#258) * Show folder name in response tag dialog (#259) * Some autocomplete tweaks and added ctrl+Enter shortcut * npm@5 in CI and package-lock.json * Always have equals signs in x-www-form-urlencoded bodies (#265) * Bump version
1 parent eef5845 commit 23692cb

29 files changed

+8329
-80
lines changed

.appveyor.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ environment:
1515
# Things to install after repo clone
1616
install:
1717
- ps: Install-Product node $env:NODEJS_VERSION $env:Platform
18-
- npm install -g npm@latest > Nul
18+
- npm install -g npm@5 > Nul
1919
- npm config set msvs_version 2013
2020
- npm install > NUL
2121
- node --version

.travis.yml

+3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,10 @@ cache:
3232
- $HOME/.cache
3333

3434
install:
35+
- npm install -g npm@5 > Nul
3536
- npm install > /dev/null
37+
- node --version
38+
- npm --version
3639

3740
script:
3841
- npm test

app/common/constants.js

+1
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ export const CHECK_FOR_UPDATES_INTERVAL = 1000 * 60 * 60 * 3; // 3 hours
6060
export const MOD_SYM = isMac() ? '⌘' : 'ctrl';
6161
export const ALT_SYM = isMac() ? '⌃' : 'alt';
6262
export const SHIFT_SYM = isMac() ? '⇧' : 'shift';
63+
export const CTRL_SYM = isMac() ? '⌃' : 'ctrl';
6364
export function joinHotKeys (keys) {
6465
return keys.join(isMac() ? '' : '+');
6566
}

app/common/render.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {setDefaultProtocol} from './misc';
44
import * as db from './database';
55
import * as templating from '../templating';
66

7-
export async function buildRenderContext (ancestors, rootEnvironment, subEnvironment) {
7+
export async function buildRenderContext (ancestors, rootEnvironment, subEnvironment, variablesOnly = true) {
88
if (!Array.isArray(ancestors)) {
99
ancestors = [];
1010
}
@@ -52,7 +52,7 @@ export async function buildRenderContext (ancestors, rootEnvironment, subEnviron
5252
* original base_url of google.com would be lost.
5353
*/
5454
if (typeof renderContext[key] === 'string') {
55-
renderContext[key] = await render(environment[key], renderContext, null, true);
55+
renderContext[key] = await render(environment[key], renderContext, null, variablesOnly);
5656
} else {
5757
renderContext[key] = environment[key];
5858
}
@@ -64,7 +64,7 @@ export async function buildRenderContext (ancestors, rootEnvironment, subEnviron
6464

6565
// Render up to 5 levels of recursive references.
6666
for (let i = 0; i < 3; i++) {
67-
finalRenderContext = await render(finalRenderContext, finalRenderContext, null, true);
67+
finalRenderContext = await render(finalRenderContext, finalRenderContext, null, variablesOnly);
6868
}
6969

7070
return finalRenderContext;
@@ -137,7 +137,7 @@ export async function render (obj, context = {}, blacklistPathRegex = null, vari
137137
return next(newObj);
138138
}
139139

140-
export async function getRenderContext (request, environmentId, ancestors = null) {
140+
export async function getRenderContext (request, environmentId, ancestors = null, variablesOnly = true) {
141141
if (!request) {
142142
return {};
143143
}
@@ -154,7 +154,7 @@ export async function getRenderContext (request, environmentId, ancestors = null
154154
const subEnvironment = await models.environment.getById(environmentId);
155155

156156
// Generate the context we need to render
157-
return buildRenderContext(ancestors, rootEnvironment, subEnvironment);
157+
return buildRenderContext(ancestors, rootEnvironment, subEnvironment, variablesOnly);
158158
}
159159

160160
export async function getRenderedRequest (request, environmentId) {

app/icons/icon.icns

64.4 KB
Binary file not shown.

app/icons/icon.ico

-2.71 KB
Binary file not shown.

app/network/__tests__/network.test.js

+49
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,55 @@ describe('actuallySend()', () => {
9090
});
9191
});
9292

93+
it('sends a urlencoded', async () => {
94+
const workspace = await models.workspace.create();
95+
const settings = await models.settings.create();
96+
const request = Object.assign(models.request.init(), {
97+
_id: 'req_123',
98+
parentId: workspace._id,
99+
headers: [{name: 'Content-Type', value: CONTENT_TYPE_FORM_URLENCODED}],
100+
method: 'POST',
101+
body: {
102+
mimeType: CONTENT_TYPE_FORM_URLENCODED,
103+
params: [
104+
{name: 'foo', value: 'bar'},
105+
{name: 'bar', value: ''},
106+
{name: '', value: 'value'}
107+
]
108+
},
109+
url: 'http://localhost'
110+
});
111+
112+
const renderedRequest = await getRenderedRequest(request);
113+
const response = await networkUtils._actuallySend(
114+
renderedRequest,
115+
workspace,
116+
settings
117+
);
118+
119+
const body = JSON.parse(Buffer.from(response.body, 'base64'));
120+
expect(body).toEqual({
121+
options: {
122+
CUSTOMREQUEST: 'POST',
123+
ACCEPT_ENCODING: '',
124+
NOBODY: 0,
125+
FOLLOWLOCATION: true,
126+
HTTPHEADER: [
127+
'Content-Type: application/x-www-form-urlencoded',
128+
'Expect: ',
129+
'Transfer-Encoding: '
130+
],
131+
NOPROGRESS: false,
132+
POSTFIELDS: 'foo=bar&bar=&=value',
133+
PROXY: '',
134+
TIMEOUT_MS: 0,
135+
URL: 'http://localhost/',
136+
USERAGENT: `insomnia/${getAppVersion()}`,
137+
VERBOSE: true
138+
}
139+
});
140+
});
141+
93142
it('skips sending and storing cookies with setting', async () => {
94143
const workspace = await models.workspace.create();
95144
const settings = await models.settings.create();

app/network/network.js

+7-3
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ export function _actuallySend (renderedRequest, workspace, settings) {
297297
let noBody = false;
298298
const expectsBody = ['POST', 'PUT', 'PATCH'].includes(renderedRequest.method.toUpperCase());
299299
if (renderedRequest.body.mimeType === CONTENT_TYPE_FORM_URLENCODED) {
300-
const d = querystring.buildFromParams(renderedRequest.body.params || [], true);
300+
const d = querystring.buildFromParams(renderedRequest.body.params || [], false);
301301
setOpt(Curl.option.POSTFIELDS, d); // Send raw data
302302
} else if (renderedRequest.body.mimeType === CONTENT_TYPE_FORM_DATA) {
303303
const data = renderedRequest.body.params.map(param => {
@@ -415,14 +415,18 @@ export function _actuallySend (renderedRequest, workspace, settings) {
415415
if (renderedRequest.settingStoreCookies && setCookieHeaders.length) {
416416
const jar = jarFromCookies(renderedRequest.cookieJar.cookies);
417417
for (const header of getSetCookieHeaders(headers)) {
418-
jar.setCookieSync(header.value, curl.getInfo(Curl.info.EFFECTIVE_URL));
418+
try {
419+
jar.setCookieSync(header.value, curl.getInfo(Curl.info.EFFECTIVE_URL));
420+
} catch (err) {
421+
timeline.push({name: 'TEXT', value: `Rejected cookie: ${err.message}`});
422+
}
419423
}
420424

421425
const cookies = await cookiesFromJar(jar);
422426

423427
// Make sure domains are prefixed with dots (Curl does this)
424428
for (const cookie of cookies) {
425-
if (cookie.domain[0] !== '.') {
429+
if (cookie.domain && cookie.domain[0] !== '.') {
426430
cookie.domain = `.${cookie.domain}`;
427431
}
428432
}

app/network/o-auth-2/grant-password.js

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as querystring from '../../common/querystring';
2-
import {getBasicAuthHeader} from '../../common/misc';
2+
import {getBasicAuthHeader, setDefaultProtocol} from '../../common/misc';
33
import * as c from './constants';
44
import {responseToObject} from './misc';
55

@@ -38,7 +38,15 @@ export default async function (accessTokenUrl,
3838
headers: headers
3939
};
4040

41-
const response = await window.fetch(accessTokenUrl, config);
41+
const url = setDefaultProtocol(accessTokenUrl);
42+
43+
let response;
44+
try {
45+
response = await window.fetch(url, config);
46+
} catch (err) {
47+
throw new Error(`Failed to fetch access token at URL "${url}"`);
48+
}
49+
4250
const body = await response.text();
4351
const results = responseToObject(body, [
4452
c.P_ACCESS_TOKEN,

app/network/o-auth-2/misc.js

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import electron from 'electron';
2+
import uuid from 'uuid';
23
import querystring from 'querystring';
34

5+
const AUTH_WINDOW_SESSION_ID = uuid.v4();
6+
47
export function responseToObject (body, keys) {
58
let data = null;
69
try {
@@ -32,7 +35,7 @@ export function authorizeUserInWindow (url, urlRegex = /.*/) {
3235
const child = new electron.remote.BrowserWindow({
3336
webPreferences: {
3437
nodeIntegration: false,
35-
partition: `persist:oauth2`
38+
partition: `oauth2_${AUTH_WINDOW_SESSION_ID}`
3639
},
3740
show: false
3841
});

app/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "insomnia",
4-
"version": "5.1.0",
4+
"version": "5.1.1",
55
"productName": "Insomnia",
66
"longName": "Insomnia REST Client",
77
"description": "Debug APIs like a human, not a robot",

app/ui/components/codemirror/extensions/autocomplete.js

+3
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,9 @@ CodeMirror.defineOption('environmentAutocomplete', null, (cm, options) => {
148148
'Ctrl-Space': completeForce, // Force autocomplete on hotkey
149149
"' '": completeIfAfterTagOrVarOpen
150150
});
151+
152+
// Close dropdown whenever something is clicked
153+
document.addEventListener('click', () => cm.closeHint());
151154
});
152155

153156
/**

app/ui/components/editors/auth/o-auth-2.js

+2
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ class OAuth2 extends PureComponent {
126126
renderInputRow (label, property, onChange, handleAutocomplete = null) {
127127
const {handleRender, handleGetRenderContext, request} = this.props;
128128
const id = label.replace(/ /g, '-');
129+
const type = !this.props.showPasswords && property === 'password' ? 'password' : 'text';
129130
return (
130131
<tr key={id}>
131132
<td className="pad-right no-wrap valign-middle">
@@ -135,6 +136,7 @@ class OAuth2 extends PureComponent {
135136
<div className="form-control form-control--underlined no-margin">
136137
<OneLineEditor
137138
id={id}
139+
type={type}
138140
onChange={onChange}
139141
defaultValue={request.authentication[property] || ''}
140142
render={handleRender}

app/ui/components/editors/environment-editor.js

+55-21
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,40 @@ import {DEBOUNCE_MILLIS} from '../../../common/constants';
55

66
@autobind
77
class EnvironmentEditor extends PureComponent {
8+
constructor (props) {
9+
super(props);
10+
this.state = {
11+
error: null,
12+
warning: null
13+
};
14+
}
15+
816
_handleChange () {
17+
let error = null;
18+
let warning = null;
19+
let value = null;
20+
21+
// Check for JSON parse errors
22+
try {
23+
value = this.getValue();
24+
} catch (err) {
25+
error = err.message;
26+
}
27+
28+
// Check for invalid key names
29+
if (value) {
30+
for (const key of Object.keys(value)) {
31+
if (!key.match(/^[a-zA-Z_$][0-9a-zA-Z_$]*$/)) {
32+
warning = `"${key}" must only contain letters, numbers, and underscores`;
33+
break;
34+
}
35+
}
36+
}
37+
38+
if (this.state.error !== error || this.state.warning !== warning) {
39+
this.setState({error, warning});
40+
}
41+
942
this.props.didChange();
1043
}
1144

@@ -18,12 +51,7 @@ class EnvironmentEditor extends PureComponent {
1851
}
1952

2053
isValid () {
21-
try {
22-
return this.getValue() !== undefined;
23-
} catch (e) {
24-
// Failed to parse JSON
25-
return false;
26-
}
54+
return !this.state.error;
2755
}
2856

2957
render () {
@@ -38,22 +66,28 @@ class EnvironmentEditor extends PureComponent {
3866
...props
3967
} = this.props;
4068

69+
const {error, warning} = this.state;
70+
4171
return (
42-
<CodeEditor
43-
ref={this._setEditorRef}
44-
autoPrettify
45-
fontSize={editorFontSize}
46-
indentSize={editorIndentSize}
47-
lineWrapping={lineWrapping}
48-
keyMap={editorKeyMap}
49-
onChange={this._handleChange}
50-
debounceMillis={DEBOUNCE_MILLIS * 6}
51-
defaultValue={JSON.stringify(environment)}
52-
render={render}
53-
getRenderContext={getRenderContext}
54-
mode="application/json"
55-
{...props}
56-
/>
72+
<div className="environment-editor">
73+
<CodeEditor
74+
ref={this._setEditorRef}
75+
autoPrettify
76+
fontSize={editorFontSize}
77+
indentSize={editorIndentSize}
78+
lineWrapping={lineWrapping}
79+
keyMap={editorKeyMap}
80+
onChange={this._handleChange}
81+
debounceMillis={DEBOUNCE_MILLIS * 6}
82+
defaultValue={JSON.stringify(environment)}
83+
render={render}
84+
getRenderContext={getRenderContext}
85+
mode="application/json"
86+
{...props}
87+
/>
88+
{error && <p className="notice error margin">{error}</p>}
89+
{(!error && warning) && <p className="notice warning margin">{warning}</p>}
90+
</div>
5791
);
5892
}
5993
}

app/ui/components/hotkey.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
import React, {PropTypes, PureComponent} from 'react';
2-
import {ALT_SYM, isMac, joinHotKeys, MOD_SYM, SHIFT_SYM} from '../../common/constants';
2+
import {ALT_SYM, CTRL_SYM, isMac, joinHotKeys, MOD_SYM, SHIFT_SYM} from '../../common/constants';
33

44
class Hotkey extends PureComponent {
55
render () {
6-
const {char, shift, alt, className} = this.props;
6+
const {char, shift, alt, ctrl, className} = this.props;
77
const chars = [ ];
88

99
alt && chars.push(ALT_SYM);
1010
shift && chars.push(SHIFT_SYM);
11-
chars.push(MOD_SYM);
11+
ctrl && chars.push(CTRL_SYM);
12+
!ctrl && chars.push(MOD_SYM);
1213
chars.push(char);
1314

1415
return (
@@ -25,6 +26,7 @@ Hotkey.propTypes = {
2526
// Optional
2627
alt: PropTypes.bool,
2728
shift: PropTypes.bool,
29+
ctrl: PropTypes.bool,
2830
className: PropTypes.string
2931
};
3032

app/ui/components/modals/request-switcher-modal.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ class RequestSwitcherModal extends PureComponent {
250250
<div className="pull-right faint italic">
251251
{requestGroup.name}
252252
&nbsp;&nbsp;
253-
<i className="fa fa-folder-o"></i>
253+
<i className="fa fa-folder-o"/>
254254
</div>
255255
)}
256256
<MethodTag method={r.method}/>
@@ -260,10 +260,10 @@ class RequestSwitcherModal extends PureComponent {
260260
);
261261
})}
262262

263-
{matchedRequests.length && matchedWorkspaces.length && (
264-
<div className="pad-left pad-right">
263+
{(matchedRequests.length > 0 && matchedWorkspaces.length > 0) && (
264+
<li className="pad-left pad-right">
265265
<hr/>
266-
</div>
266+
</li>
267267
)}
268268

269269
{matchedWorkspaces.map((w, i) => {

app/ui/components/modals/workspace-environments-edit-modal.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ class WorkspaceEnvironmentsEditModal extends PureComponent {
133133
_didChange () {
134134
const isValid = this._envEditor.isValid();
135135

136-
if (this.state.isValid === isValid) {
136+
if (this.state.isValid !== isValid) {
137137
this.setState({isValid});
138138
}
139139

0 commit comments

Comments
 (0)