mirror of
https://github.com/Kong/insomnia
synced 2024-11-07 14:19:58 +00:00
Better XPath handling (Fixes #492)
This commit is contained in:
parent
4b7fe5c190
commit
8eecb55515
31
app/common/xpath.js
Normal file
31
app/common/xpath.js
Normal file
@ -0,0 +1,31 @@
|
||||
// @flow
|
||||
import xpath from 'xpath';
|
||||
import {DOMParser} from 'xmldom';
|
||||
|
||||
export function query (xml: string, query: string): Array<{outer: string, inner: string}> {
|
||||
const dom = new DOMParser().parseFromString(xml);
|
||||
let rawResults = [];
|
||||
|
||||
try {
|
||||
rawResults = xpath.select(query, dom);
|
||||
} catch (err) {
|
||||
throw new Error(`Invalid XPath query: ${query}`);
|
||||
}
|
||||
|
||||
const results = [];
|
||||
for (const result of rawResults || []) {
|
||||
if (result.constructor.name === 'Attr') {
|
||||
results.push({
|
||||
outer: result.toString().trim(),
|
||||
inner: result.nodeValue
|
||||
});
|
||||
} else if (result.constructor.name === 'Element') {
|
||||
results.push({
|
||||
outer: result.toString().trim(),
|
||||
inner: result.childNodes.toString()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
@ -115,7 +115,7 @@ describe('ResponseExtension JSONPath', async () => {
|
||||
|
||||
describe('ResponseExtension XPath', async () => {
|
||||
beforeEach(globalBeforeEach);
|
||||
it('renders basic response "body", query', async () => {
|
||||
it('renders basic response "body" query', async () => {
|
||||
const request = await models.request.create({parentId: 'foo'});
|
||||
await models.response.create({
|
||||
parentId: request._id,
|
||||
@ -127,6 +127,18 @@ describe('ResponseExtension XPath', async () => {
|
||||
expect(result).toBe('Hello World!');
|
||||
});
|
||||
|
||||
it('renders basic response "body" attribute query', async () => {
|
||||
const request = await models.request.create({parentId: 'foo'});
|
||||
await models.response.create({
|
||||
parentId: request._id,
|
||||
statusCode: 200
|
||||
}, '<foo><bar hello="World!">Hello World!</bar></foo>');
|
||||
|
||||
const result = await templating.render(`{% response "body", "${request._id}", "/foo/bar/@hello" %}`);
|
||||
|
||||
expect(result).toBe('World!');
|
||||
});
|
||||
|
||||
it('no results on invalid XML', async () => {
|
||||
const request = await models.request.create({parentId: 'foo'});
|
||||
await models.response.create({
|
||||
|
@ -1,6 +1,7 @@
|
||||
// @flow
|
||||
import jq from 'jsonpath';
|
||||
import {DOMParser} from 'xmldom';
|
||||
import xpath from 'xpath';
|
||||
import * as xpath from '../../common/xpath';
|
||||
import type {ResponseHeader} from '../../models/response';
|
||||
|
||||
export default {
|
||||
name: 'response',
|
||||
@ -23,8 +24,8 @@ export default {
|
||||
},
|
||||
{
|
||||
type: 'string',
|
||||
hide: args => args[0].value === 'raw',
|
||||
displayName: args => {
|
||||
hide: (args: Array<Object>): boolean => args[0].value === 'raw',
|
||||
displayName: (args: Array<Object>): string => {
|
||||
switch (args[0].value) {
|
||||
case 'body':
|
||||
return 'Filter (JSONPath or XPath)';
|
||||
@ -37,7 +38,7 @@ export default {
|
||||
}
|
||||
],
|
||||
|
||||
async run (context, field, id, filter) {
|
||||
async run (context: Object, field: string, id: string, filter: string) {
|
||||
if (!['body', 'header', 'raw'].includes(field)) {
|
||||
throw new Error(`Invalid response field ${field}`);
|
||||
}
|
||||
@ -83,7 +84,7 @@ export default {
|
||||
}
|
||||
};
|
||||
|
||||
function matchJSONPath (bodyStr, query) {
|
||||
function matchJSONPath (bodyStr: string, query: string): string {
|
||||
let body;
|
||||
let results;
|
||||
|
||||
@ -108,17 +109,8 @@ function matchJSONPath (bodyStr, query) {
|
||||
return results[0];
|
||||
}
|
||||
|
||||
function matchXPath (bodyStr, query) {
|
||||
let results;
|
||||
|
||||
// This will never throw
|
||||
const dom = new DOMParser().parseFromString(bodyStr);
|
||||
|
||||
try {
|
||||
results = xpath.select(query, dom);
|
||||
} catch (err) {
|
||||
throw new Error(`Invalid XPath query: ${query}`);
|
||||
}
|
||||
function matchXPath (bodyStr: string, query: string): string {
|
||||
const results = xpath.query(bodyStr, query);
|
||||
|
||||
if (results.length === 0) {
|
||||
throw new Error(`Returned no results: ${query}`);
|
||||
@ -126,10 +118,10 @@ function matchXPath (bodyStr, query) {
|
||||
throw new Error(`Returned more than one result: ${query}`);
|
||||
}
|
||||
|
||||
return results[0].childNodes.toString();
|
||||
return results[0].inner;
|
||||
}
|
||||
|
||||
function matchHeader (headers, name) {
|
||||
function matchHeader (headers: Array<ResponseHeader>, name: string): string {
|
||||
const header = headers.find(
|
||||
h => h.name.toLowerCase() === name.toLowerCase()
|
||||
);
|
||||
|
@ -6,8 +6,6 @@ import classnames from 'classnames';
|
||||
import clone from 'clone';
|
||||
import jq from 'jsonpath';
|
||||
import vkBeautify from 'vkbeautify';
|
||||
import {DOMParser} from 'xmldom';
|
||||
import xpath from 'xpath';
|
||||
import {showModal} from '../modals/index';
|
||||
import FilterHelpModal from '../modals/filter-help-modal';
|
||||
import * as misc from '../../../common/misc';
|
||||
@ -19,6 +17,7 @@ import {getTagDefinitions} from '../../../templating/index';
|
||||
import Dropdown from '../base/dropdown/dropdown';
|
||||
import DropdownButton from '../base/dropdown/dropdown-button';
|
||||
import DropdownItem from '../base/dropdown/dropdown-item';
|
||||
import * as xpath2 from '../../../common/xpath';
|
||||
|
||||
const TAB_KEY = 9;
|
||||
const TAB_SIZE = 4;
|
||||
@ -322,11 +321,9 @@ class CodeEditor extends PureComponent {
|
||||
_prettifyXML (code) {
|
||||
if (this.props.updateFilter && this.state.filter) {
|
||||
try {
|
||||
const dom = new DOMParser().parseFromString(code);
|
||||
const nodes = xpath.select(this.state.filter, dom);
|
||||
const inner = nodes.map(n => n.toString()).join('\n');
|
||||
code = `<result>${inner}</result>`;
|
||||
} catch (e) {
|
||||
const results = xpath2.query(code, this.state.filter);
|
||||
code = `<result>${results.map(r => r.outer).join('\n')}</result>`;
|
||||
} catch (err) {
|
||||
// Failed to parse filter (that's ok)
|
||||
code = `<result></result>`;
|
||||
}
|
||||
|
5
flow-typed/jsonpath.js
vendored
Normal file
5
flow-typed/jsonpath.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
declare module 'jsonpath' {
|
||||
declare module.exports: {
|
||||
query: Function
|
||||
}
|
||||
}
|
5
flow-typed/xmldom.js
vendored
Normal file
5
flow-typed/xmldom.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
declare module 'xmldom' {
|
||||
declare module.exports: {
|
||||
DOMParser: Function
|
||||
}
|
||||
}
|
5
flow-typed/xpath.js
vendored
Normal file
5
flow-typed/xpath.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
declare module 'xpath' {
|
||||
declare module.exports: {
|
||||
select: Function
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user