mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 22:30:15 +00:00
XML and XPath support
This commit is contained in:
parent
b51fb24e53
commit
46d56804e6
@ -20,6 +20,8 @@
|
||||
"raven": "^0.12.1",
|
||||
"request": "^2.71.0",
|
||||
"tough-cookie": "^2.3.1",
|
||||
"traverse": "^0.6.6"
|
||||
"traverse": "^0.6.6",
|
||||
"xml2js": "^0.4.17",
|
||||
"xml2js-xpath": "^0.7.0"
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,10 @@
|
||||
import React, {Component, PropTypes} from 'react';
|
||||
import classnames from 'classnames';
|
||||
|
||||
import Dropdown from './base/Dropdown';
|
||||
import MethodTag from './tags/MethodTag';
|
||||
import {METHODS} from '../lib/constants';
|
||||
import {METHODS, DEBOUNCE_MILLIS} from '../lib/constants';
|
||||
import Mousetrap from '../lib/mousetrap';
|
||||
import {trackEvent} from '../lib/analytics';
|
||||
import {DEBOUNCE_MILLIS} from '../lib/constants';
|
||||
|
||||
|
||||
class RequestUrlBar extends Component {
|
||||
@ -23,7 +21,10 @@ class RequestUrlBar extends Component {
|
||||
}
|
||||
|
||||
componentDidMount () {
|
||||
Mousetrap.bindGlobal('mod+l', () => {this.input.focus(); this.input.select()});
|
||||
Mousetrap.bindGlobal('mod+l', () => {
|
||||
this.input.focus();
|
||||
this.input.select()
|
||||
});
|
||||
}
|
||||
|
||||
render () {
|
||||
@ -33,9 +34,10 @@ class RequestUrlBar extends Component {
|
||||
const hasError = !url;
|
||||
|
||||
return (
|
||||
<div className={classnames({'urlbar': true, 'urlbar--error': hasError})}>
|
||||
<form className={classnames({'urlbar': true, 'urlbar--error': hasError})}
|
||||
onSubmit={this._handleFormSubmit.bind(this)}>
|
||||
<Dropdown>
|
||||
<button>
|
||||
<button type="button">
|
||||
<div className="tall">
|
||||
<span>{method}</span>
|
||||
<i className="fa fa-caret-down"/>
|
||||
@ -54,18 +56,16 @@ class RequestUrlBar extends Component {
|
||||
))}
|
||||
</ul>
|
||||
</Dropdown>
|
||||
<form onSubmit={this._handleFormSubmit.bind(this)}>
|
||||
<div className="form-control">
|
||||
<input
|
||||
ref={n => this.input = n}
|
||||
type="text"
|
||||
placeholder="https://api.myproduct.com/v1/users"
|
||||
defaultValue={url}
|
||||
onChange={e => this._handleUrlChange(e.target.value)}/>
|
||||
</div>
|
||||
<button>Send</button>
|
||||
</form>
|
||||
</div>
|
||||
<div className="form-control">
|
||||
<input
|
||||
ref={n => this.input = n}
|
||||
type="text"
|
||||
placeholder="https://api.myproduct.com/v1/users"
|
||||
defaultValue={url}
|
||||
onChange={e => this._handleUrlChange(e.target.value)}/>
|
||||
</div>
|
||||
<button type="submit" className="urlbar__send-button">Send</button>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,8 @@ import {getDOMNode} from 'react-dom';
|
||||
import CodeMirror from 'codemirror';
|
||||
import classnames from 'classnames';
|
||||
import JSONPath from 'jsonpath-plus';
|
||||
import xml2js from 'xml2js';
|
||||
import xpath from 'xml2js-xpath';
|
||||
import {DEBOUNCE_MILLIS} from '../../lib/constants';
|
||||
import 'codemirror/mode/css/css';
|
||||
import 'codemirror/mode/htmlmixed/htmlmixed';
|
||||
@ -145,6 +147,55 @@ class Editor extends Component {
|
||||
return mode.indexOf('json') !== -1
|
||||
}
|
||||
|
||||
_isXML (mode) {
|
||||
if (!mode) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return mode.indexOf('xml') !== -1
|
||||
}
|
||||
|
||||
_formatJSON (code) {
|
||||
try {
|
||||
let obj = JSON.parse(code);
|
||||
|
||||
if (this.props.updateFilter && this.state.filter) {
|
||||
obj = JSONPath({json: obj, path: this.state.filter});
|
||||
}
|
||||
|
||||
code = JSON.stringify(obj, null, '\t');
|
||||
} catch (e) {
|
||||
// That's Ok, just leave it
|
||||
}
|
||||
|
||||
return Promise.resolve(code);
|
||||
}
|
||||
|
||||
_formatXML (code) {
|
||||
return new Promise(resolve => {
|
||||
xml2js.parseString(code, (err, obj) => {
|
||||
if (err) {
|
||||
resolve(code);
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.props.updateFilter && this.state.filter) {
|
||||
obj = xpath.find(obj, this.state.filter);
|
||||
}
|
||||
|
||||
const builder = new xml2js.Builder({
|
||||
renderOpts: {
|
||||
pretty: true,
|
||||
indent: '\t'
|
||||
}
|
||||
});
|
||||
const xml = builder.buildObject(obj);
|
||||
|
||||
resolve(xml);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets options on the CodeMirror editor while also sanitizing them
|
||||
*/
|
||||
@ -202,22 +253,18 @@ class Editor extends Component {
|
||||
this._originalCode = code;
|
||||
this._ignoreNextChange = true;
|
||||
|
||||
let promise;
|
||||
if (this.props.prettify) {
|
||||
try {
|
||||
let obj = JSON.parse(code);
|
||||
|
||||
if (this.props.updateFilter && this.state.filter) {
|
||||
obj = JSONPath({json: obj, path: this.state.filter});
|
||||
}
|
||||
|
||||
code = JSON.stringify(obj, null, '\t');
|
||||
} catch (e) {
|
||||
// That's Ok, just leave it
|
||||
// TODO: support more than just JSON prettifying
|
||||
if (this._isXML(this.props.mode)) {
|
||||
promise = this._formatXML(code);
|
||||
} else {
|
||||
promise = this._formatJSON(code);
|
||||
}
|
||||
} else {
|
||||
promise = Promise.resolve(code);
|
||||
}
|
||||
|
||||
this.codeMirror.setValue(code);
|
||||
promise.then(code => this.codeMirror.setValue(code));
|
||||
}
|
||||
|
||||
_handleFilterChange (filter) {
|
||||
@ -232,33 +279,51 @@ class Editor extends Component {
|
||||
}
|
||||
|
||||
_showFilterHelp () {
|
||||
const json = this._isJSON(this.props.mode);
|
||||
const link = json ? (
|
||||
<Link href="http://goessner.net/articles/JsonPath/">
|
||||
JSONPath
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
href="https://www.w3.org/TR/xpath/">
|
||||
XPath
|
||||
</Link>
|
||||
);
|
||||
|
||||
getModal(AlertModal).show({
|
||||
headerName: 'Response Filtering Help',
|
||||
message: (
|
||||
<div>
|
||||
<p>
|
||||
Use <Link href="http://schier.co">JSONPath</Link> to filter the
|
||||
response body. Here are some examples that you might use on a
|
||||
book store API.
|
||||
Use {link} to filter the response body. Here are some examples that
|
||||
you might use on a book store API.
|
||||
</p>
|
||||
<table className="pad-top-sm">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code className="selectable">$.store.books[*].title</code>
|
||||
<td><code className="selectable">
|
||||
{json ? '$.store.books[*].title' : '/store/books/title'}
|
||||
</code>
|
||||
</td>
|
||||
<td>Get titles of all books in the store</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code className="selectable">$.store.books[?(@.price <
|
||||
10)].title</code></td>
|
||||
<td><code className="selectable">
|
||||
{json ? '$.store.books[?(@.price < 10)].title' : '/store/books[price < 10]'}
|
||||
</code></td>
|
||||
<td>Get books costing more than $10</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code className="selectable">$.store.books[-1:]</code></td>
|
||||
<td><code className="selectable">
|
||||
{json ? '$.store.books[-1:]' : '/store/books[last()]'}
|
||||
</code></td>
|
||||
<td>Get the last book in the store</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code className="selectable">$.store.books.length</code></td>
|
||||
<td><code className="selectable">
|
||||
{json ? '$.store.books.length' : 'count(/store/books)'}
|
||||
</code></td>
|
||||
<td>Get the number of books in the store</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@ -286,14 +351,14 @@ class Editor extends Component {
|
||||
);
|
||||
|
||||
let filterElement = null;
|
||||
if (this.props.updateFilter && this._isJSON(mode)) {
|
||||
if (this.props.updateFilter && (this._isJSON(mode) || this._isXML(mode))) {
|
||||
filterElement = (
|
||||
<div className="editor__filter">
|
||||
<div className="form-control form-control--outlined">
|
||||
<input
|
||||
type="text"
|
||||
defaultValue={filter || ''}
|
||||
placeholder="$.store.book[*].author"
|
||||
placeholder={this._isJSON(mode) ? '$.store.books[*].author' : '/store/books/author'}
|
||||
onChange={e => this._handleFilterChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -3,9 +3,8 @@
|
||||
|
||||
.urlbar {
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
grid-template-rows: 1fr;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
align-self: stretch;
|
||||
|
||||
@ -51,21 +50,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
form {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
input {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
form button {
|
||||
.urlbar__send-button {
|
||||
padding-right: @padding-md;
|
||||
padding-left: @padding-md;
|
||||
}
|
||||
|
||||
form, .form-control {
|
||||
.form-control {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
form button,
|
||||
.urlbar__send-button,
|
||||
& > .dropdown {
|
||||
height: 100%;
|
||||
|
||||
|
@ -28,7 +28,7 @@
|
||||
@sidebar-width: 19rem;
|
||||
|
||||
/* Scrollbars */
|
||||
@scrollbar-width: 0.8rem;
|
||||
@scrollbar-width: 0.75rem;
|
||||
|
||||
/* Borders */
|
||||
@radius-sm: 0.15rem;
|
||||
|
@ -80,7 +80,9 @@
|
||||
"redux-thunk": "^2.0.1",
|
||||
"request": "^2.74.0",
|
||||
"tough-cookie": "^2.3.1",
|
||||
"traverse": "^0.6.6"
|
||||
"traverse": "^0.6.6",
|
||||
"xml2js": "^0.4.17",
|
||||
"xml2js-xpath": "^0.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.11.4",
|
||||
|
Loading…
Reference in New Issue
Block a user