mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 22:30:15 +00:00
Refactor and a bunch of fixes
This commit is contained in:
parent
43214dd1d3
commit
09baf21d3a
@ -31,6 +31,7 @@ describe('Requests Actions', () => {
|
||||
modified: 1000000000000,
|
||||
name: 'Test Request',
|
||||
method: 'GET',
|
||||
url: '',
|
||||
body: '',
|
||||
headers: [],
|
||||
params: [],
|
||||
@ -61,6 +62,7 @@ describe('Requests Actions', () => {
|
||||
_mode: 'json',
|
||||
created: 1000000000000,
|
||||
modified: 1000000000000,
|
||||
url: '',
|
||||
name: 'Test Request',
|
||||
method: 'GET',
|
||||
body: '',
|
||||
@ -73,8 +75,9 @@ describe('Requests Actions', () => {
|
||||
{type: types.GLOBAL_LOAD_START},
|
||||
{
|
||||
type: types.REQUEST_UPDATE,
|
||||
request: {
|
||||
patch: {
|
||||
method: 'POST',
|
||||
id: 'rq_1000000000000',
|
||||
modified: 1000000000000
|
||||
}
|
||||
},
|
||||
@ -84,7 +87,7 @@ describe('Requests Actions', () => {
|
||||
const store = mockStore({});
|
||||
store.dispatch(addRequest('Test Request'));
|
||||
jest.runAllTimers();
|
||||
store.dispatch(updateRequest({method: 'POST'}));
|
||||
store.dispatch(updateRequest({id: 'rq_1000000000000', method: 'POST'}));
|
||||
jest.runAllTimers();
|
||||
|
||||
const actions = store.getActions();
|
||||
|
@ -9,6 +9,7 @@ function defaultRequest () {
|
||||
_mode: 'json',
|
||||
created: 0,
|
||||
modified: 0,
|
||||
url: '',
|
||||
name: '',
|
||||
method: methods.METHOD_GET,
|
||||
body: '',
|
||||
@ -30,7 +31,7 @@ function buildRequest (request) {
|
||||
const modified = request.modified || Date.now();
|
||||
|
||||
// Create the request
|
||||
return Object.assign({}, defaultRequest(), request, {
|
||||
return Object.assign(defaultRequest(), request, {
|
||||
id, created, modified
|
||||
});
|
||||
}
|
||||
@ -51,21 +52,34 @@ export function addRequest (name = 'My Request') {
|
||||
}
|
||||
|
||||
export function updateRequest (requestPatch) {
|
||||
if (!requestPatch.id) {
|
||||
throw new Error('Cannot update request without id');
|
||||
}
|
||||
|
||||
return (dispatch) => {
|
||||
dispatch(loadStart());
|
||||
|
||||
const request = Object.assign({}, requestPatch, {modified: Date.now()});
|
||||
dispatch({type: types.REQUEST_UPDATE, request});
|
||||
const modified = Date.now();
|
||||
const patch = Object.assign({}, requestPatch, {modified});
|
||||
dispatch({type: types.REQUEST_UPDATE, patch});
|
||||
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(() => {
|
||||
dispatch(loadStop());
|
||||
resolve();
|
||||
}, 500);
|
||||
}, 800);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function activateRequest (request) {
|
||||
return {type: types.REQUEST_ACTIVATE, request: request};
|
||||
export function updateRequestUrl (id, url) {
|
||||
return updateRequest({id, url});
|
||||
}
|
||||
|
||||
export function updateRequestBody (id, body) {
|
||||
return updateRequest({id, body});
|
||||
}
|
||||
|
||||
export function activateRequest (id) {
|
||||
return {type: types.REQUEST_ACTIVATE, id};
|
||||
}
|
||||
|
@ -70,6 +70,7 @@ class Editor extends Component {
|
||||
this.codeMirror.on('change', this.codemirrorValueChanged.bind(this));
|
||||
this.codeMirror.on('focus', this.focusChanged.bind(this, true));
|
||||
this.codeMirror.on('blur', this.focusChanged.bind(this, false));
|
||||
this.codeMirror.on('paste', this.codemirrorValueChanged.bind(this));
|
||||
this._currentCodemirrorValue = this.props.defaultValue || this.props.value || '';
|
||||
this.codemirrorSetValue(this._currentCodemirrorValue);
|
||||
this.codemirrorSetOptions(this.props.options);
|
51
app/components/Input.js
Normal file
51
app/components/Input.js
Normal file
@ -0,0 +1,51 @@
|
||||
import React, {Component, PropTypes} from 'react';
|
||||
|
||||
const DEFAULT_DEBOUNCE_MILLIS = 300;
|
||||
|
||||
class Input extends Component {
|
||||
valueChanged (e) {
|
||||
if (!this.props.onChange) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Surround in closure because callback may change before debounce
|
||||
clearTimeout(this._timeout);
|
||||
((value, cb, debounceMillis = DEFAULT_DEBOUNCE_MILLIS) => {
|
||||
this._timeout = setTimeout(() => cb(value), debounceMillis);
|
||||
})(e.target.value, this.props.onChange, this.props.debounceMillis);
|
||||
}
|
||||
|
||||
updateValueFromProps() {
|
||||
this.refs.input.value = this.props.initialValue || this.props.value || '';
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
this.updateValueFromProps()
|
||||
}
|
||||
|
||||
componentDidUpdate () {
|
||||
this.updateValueFromProps()
|
||||
}
|
||||
|
||||
render () {
|
||||
const {initialValue, value} = this.props;
|
||||
return (
|
||||
<input
|
||||
ref="input"
|
||||
type="text"
|
||||
initialValue={initialValue || value}
|
||||
onChange={this.valueChanged.bind(this)}
|
||||
placeholder="https://google.com"
|
||||
/>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Input.propTypes = {
|
||||
onChange: PropTypes.func.isRequired,
|
||||
initialValue: PropTypes.string,
|
||||
debounceMillis: PropTypes.number,
|
||||
value: PropTypes.string
|
||||
};
|
||||
|
||||
export default Input;
|
@ -1,5 +1,6 @@
|
||||
import React, {Component, PropTypes} from 'react'
|
||||
import Editor from '../components/Editor'
|
||||
import CodeEditor from '../components/CodeEditor'
|
||||
import UrlInput from '../components/UrlInput'
|
||||
import {Tab, Tabs, TabList, TabPanel} from 'react-tabs';
|
||||
|
||||
// Don't inject component styles (use our own)
|
||||
@ -11,21 +12,14 @@ class RequestPane extends Component {
|
||||
}
|
||||
|
||||
render () {
|
||||
const {request, updateRequest} = this.props;
|
||||
const {request, updateRequestBody, updateRequestUrl} = this.props;
|
||||
|
||||
return (
|
||||
<section id="request" className="pane col grid-v">
|
||||
<header className="pane__header bg-super-light">
|
||||
<div className="form-control url-input">
|
||||
<div className="grid">
|
||||
<button className="btn method-dropdown">
|
||||
POST <i className="fa fa-caret-down"></i>
|
||||
</button>
|
||||
<input type="text" placeholder="https://google.com"/>
|
||||
<button className="btn send-request-button">
|
||||
<i className="fa fa-repeat txt-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
<UrlInput onUrlChange={updateRequestUrl}
|
||||
urlValue={request.url}/>
|
||||
</div>
|
||||
</header>
|
||||
<div className="pane__body">
|
||||
@ -46,8 +40,8 @@ class RequestPane extends Component {
|
||||
</TabList>
|
||||
<TabPanel className="col">Params</TabPanel>
|
||||
<TabPanel className="col">
|
||||
<Editor value={request.body}
|
||||
onChange={(body) => updateRequest(Object.assign({}, request, {body}) )}
|
||||
<CodeEditor value={request.body}
|
||||
onChange={updateRequestBody}
|
||||
options={{mode: request._mode, lineNumbers: true}}/>
|
||||
</TabPanel>
|
||||
<TabPanel className="col">Basic Auth</TabPanel>
|
||||
@ -60,7 +54,8 @@ class RequestPane extends Component {
|
||||
}
|
||||
|
||||
RequestPane.propTypes = {
|
||||
updateRequest: PropTypes.func.isRequired,
|
||||
updateRequestUrl: PropTypes.func.isRequired,
|
||||
updateRequestBody: PropTypes.func.isRequired,
|
||||
request: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
import React, {PropTypes} from 'react'
|
||||
import Editor from '../components/Editor'
|
||||
import CodeEditor from '../components/CodeEditor'
|
||||
|
||||
const ResponsePane = (props) => (
|
||||
<section id="response" className="pane col grid-v">
|
||||
@ -10,7 +10,7 @@ const ResponsePane = (props) => (
|
||||
</div>
|
||||
</header>
|
||||
<div className="pane__body">
|
||||
<Editor value={'{}'} options={{mode: 'application/json'}}></Editor>
|
||||
<CodeEditor value={'{}'} options={{mode: 'application/json'}}></CodeEditor>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
|
@ -22,11 +22,11 @@ const Sidebar = (props) => (
|
||||
</li>
|
||||
</ul>
|
||||
<ul className="sidebar-items">
|
||||
{props.requests.all.map((request) => {
|
||||
const isActive = request.id === props.requests.active.id;
|
||||
{props.requests.map((request) => {
|
||||
const isActive = request.id === props.activeRequest.id;
|
||||
return (
|
||||
<li key={request.id} className={'sidebar-item ' + (isActive ? 'active': '')}>
|
||||
<a href="#" onClick={() => {props.activateRequest(request)}}>{request.name}</a>
|
||||
<a href="#" onClick={() => {props.activateRequest(request.id)}}>{request.name}</a>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
@ -37,7 +37,8 @@ const Sidebar = (props) => (
|
||||
|
||||
Sidebar.propTypes = {
|
||||
activateRequest: PropTypes.func.isRequired,
|
||||
requests: PropTypes.object.isRequired,
|
||||
requests: PropTypes.array.isRequired,
|
||||
activeRequest: PropTypes.object,
|
||||
loading: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
|
30
app/components/UrlInput.js
Normal file
30
app/components/UrlInput.js
Normal file
@ -0,0 +1,30 @@
|
||||
import React, {Component, PropTypes} from 'react'
|
||||
import Input from '../components/Input';
|
||||
|
||||
class UrlInput extends Component {
|
||||
render () {
|
||||
const {onUrlChange, urlValue} = this.props;
|
||||
|
||||
return (
|
||||
<div className="grid">
|
||||
<button className="btn method-dropdown">
|
||||
POST <i className="fa fa-caret-down"></i>
|
||||
</button>
|
||||
<Input type="text"
|
||||
placeholder="https://google.com"
|
||||
initialValue={urlValue}
|
||||
onChange={onUrlChange}/>
|
||||
<button className="btn send-request-button">
|
||||
<i className="fa fa-repeat txt-xl"></i>
|
||||
</button>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
UrlInput.propTypes = {
|
||||
onUrlChange: PropTypes.func.isRequired,
|
||||
urlValue: PropTypes.string.isRequired
|
||||
};
|
||||
|
||||
export default UrlInput;
|
@ -16,46 +16,52 @@ class App extends Component {
|
||||
}
|
||||
|
||||
renderRequestPane () {
|
||||
const {actions, requests} = this.props;
|
||||
const {actions, activeRequest} = this.props;
|
||||
return (
|
||||
<RequestPane
|
||||
updateRequest={actions.updateRequest}
|
||||
request={requests.active}/>
|
||||
updateRequestBody={actions.updateRequestBody.bind(null, activeRequest.id)}
|
||||
updateRequestUrl={actions.updateRequestUrl.bind(null, activeRequest.id)}
|
||||
request={activeRequest}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
renderResponsePane () {
|
||||
const {requests} = this.props;
|
||||
const {activeRequest} = this.props;
|
||||
return (
|
||||
<ResponsePane request={requests.active}/>
|
||||
<ResponsePane request={activeRequest}/>
|
||||
)
|
||||
}
|
||||
|
||||
render () {
|
||||
const {actions, loading, requests} = this.props;
|
||||
const {actions, loading, activeRequest, allRequests} = this.props;
|
||||
return (
|
||||
<div className="grid bg-dark">
|
||||
<Sidebar
|
||||
activateRequest={actions.activateRequest}
|
||||
addRequest={actions.addRequest}
|
||||
loading={loading}
|
||||
requests={requests}/>
|
||||
{requests.active ? this.renderRequestPane() : <div></div>}
|
||||
{requests.active ? this.renderResponsePane() : <div></div>}
|
||||
activeRequest={activeRequest}
|
||||
requests={allRequests}
|
||||
/>
|
||||
{activeRequest ? this.renderRequestPane() : <div></div>}
|
||||
{activeRequest ? this.renderResponsePane() : <div></div>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
App.propTypes = {
|
||||
requests: PropTypes.object.isRequired,
|
||||
allRequests: PropTypes.array.isRequired,
|
||||
activeRequest: PropTypes.object,
|
||||
loading: PropTypes.bool.isRequired
|
||||
};
|
||||
|
||||
function mapStateToProps (state) {
|
||||
return {
|
||||
actions: state.actions,
|
||||
requests: state.requests,
|
||||
allRequests: state.requests.all,
|
||||
activeRequest: state.requests.all.find(r => r.id === state.requests.activeId),
|
||||
loading: state.loading
|
||||
};
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ describe('Requests Reducer', () => {
|
||||
beforeEach(() => {
|
||||
initialState = {
|
||||
all: [],
|
||||
active: null
|
||||
activeId: null
|
||||
};
|
||||
|
||||
request = {
|
||||
@ -49,7 +49,7 @@ describe('Requests Reducer', () => {
|
||||
})
|
||||
).toEqual({
|
||||
all: [request],
|
||||
active: request
|
||||
activeId: request.id
|
||||
});
|
||||
});
|
||||
|
||||
@ -59,42 +59,43 @@ describe('Requests Reducer', () => {
|
||||
request: request
|
||||
});
|
||||
|
||||
const newRequest = Object.assign(
|
||||
{}, request, {name: 'New Name'}
|
||||
);
|
||||
const patch = {
|
||||
id: request.id,
|
||||
name: 'New Name'
|
||||
};
|
||||
|
||||
expect(reducer(state, {
|
||||
type: types.REQUEST_UPDATE,
|
||||
request: newRequest
|
||||
patch: patch
|
||||
})).toEqual({
|
||||
all: [newRequest],
|
||||
active: newRequest
|
||||
all: [Object.assign({}, request, patch)],
|
||||
activeId: request.id
|
||||
});
|
||||
});
|
||||
|
||||
it('should not update unknown request', () => {
|
||||
expect(reducer(initialState, {
|
||||
type: types.REQUEST_UPDATE,
|
||||
request: request
|
||||
patch: {id: 'req_1234567890123'}
|
||||
})).toEqual(initialState);
|
||||
});
|
||||
|
||||
it('should activate request', () => {
|
||||
initialState.all = [request];
|
||||
initialState.active = null;
|
||||
initialState.activeId = null;
|
||||
|
||||
expect(reducer(initialState, {
|
||||
type: types.REQUEST_ACTIVATE,
|
||||
request: request
|
||||
id: request.id
|
||||
})).toEqual({
|
||||
all: [request],
|
||||
active: request
|
||||
activeId: request.id
|
||||
});
|
||||
});
|
||||
|
||||
it('should not activate invalid request', () => {
|
||||
initialState.all = [request];
|
||||
initialState.active = null;
|
||||
initialState.activeId = null;
|
||||
|
||||
expect(reducer(initialState, {
|
||||
type: types.REQUEST_ACTIVATE
|
||||
|
@ -2,7 +2,7 @@ import * as types from "../constants/actionTypes";
|
||||
|
||||
const initialState = {
|
||||
all: [],
|
||||
active: null
|
||||
activeId: null
|
||||
};
|
||||
|
||||
function requestsReducer (state = [], action) {
|
||||
@ -11,8 +11,8 @@ function requestsReducer (state = [], action) {
|
||||
return [...state, action.request];
|
||||
case types.REQUEST_UPDATE:
|
||||
return state.map(request => {
|
||||
if (request.id === action.request.id) {
|
||||
return Object.assign({}, request, action.request);
|
||||
if (request.id === action.patch.id) {
|
||||
return Object.assign({}, request, action.patch);
|
||||
} else {
|
||||
return request;
|
||||
}});
|
||||
@ -22,20 +22,22 @@ function requestsReducer (state = [], action) {
|
||||
}
|
||||
|
||||
export default function (state = initialState, action) {
|
||||
let all, active;
|
||||
let all, activeId;
|
||||
switch (action.type) {
|
||||
case types.REQUEST_ADD:
|
||||
all = requestsReducer(state.all, action);
|
||||
active = state.active || action.request;
|
||||
return Object.assign({}, state, {all, active});
|
||||
activeId = state.activeId || action.request.id;
|
||||
return Object.assign({}, state, {all, activeId});
|
||||
case types.REQUEST_UPDATE:
|
||||
all = requestsReducer(state.all, action);
|
||||
active = state.active;
|
||||
active = active && active.id === action.request.id ? action.request : active;
|
||||
return Object.assign({}, state, {all, active});
|
||||
return Object.assign({}, state, {all});
|
||||
case types.REQUEST_ACTIVATE:
|
||||
active = action.request;
|
||||
return active ? Object.assign({}, state, {active}) : state;
|
||||
if (!state.all.find(r => r.id === action.id)) {
|
||||
// Don't set if the request doesn't exist
|
||||
return state;
|
||||
} else {
|
||||
return Object.assign({}, state, {activeId: action.id});
|
||||
}
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
@ -53,7 +53,8 @@ const requestSchema = {
|
||||
modified: {type: 'number', minimum: 1000000000000, maximum: 10000000000000},
|
||||
name: {type: 'string', minLength: 1},
|
||||
method: {enum: METHODS},
|
||||
body: {type: 'string', minLength: 0},
|
||||
url: {type: 'string'},
|
||||
body: {type: 'string'},
|
||||
authentication: {
|
||||
oneOf: [
|
||||
{$ref: '/BasicAuthentication'},
|
||||
@ -66,6 +67,7 @@ const requestSchema = {
|
||||
required: [
|
||||
'_mode',
|
||||
'id',
|
||||
'url',
|
||||
'created',
|
||||
'modified',
|
||||
'name',
|
||||
|
@ -13,6 +13,7 @@ describe('RequestSchema', () => {
|
||||
id: 'rq_1234567890123',
|
||||
created: Date.now(),
|
||||
modified: Date.now(),
|
||||
url: 'https://google.com',
|
||||
name: 'My Request',
|
||||
method: 'GET',
|
||||
body: '{"foo": "bar"}',
|
||||
|
Loading…
Reference in New Issue
Block a user