Merge pull request #97 from Orange-OpenSource/fix/jsonpath-parse-extra-dot

Parse extra dot in jsonpath expression
This commit is contained in:
Fabrice Reix 2020-11-28 17:46:52 +01:00 committed by GitHub
commit 6d5239a3d2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 92 additions and 35 deletions

File diff suppressed because one or more lines are too long

View File

@ -39,3 +39,10 @@ jsonpath "$.tags" includes "test"
jsonpath "$.tags" not includes "prod"
jsonpath "$.tags" not includes null
GET http://localhost:8000/assert-json/list
HTTP/1.0 200
[Asserts]
jsonpath "$" countEquals 2
jsonpath "$.[0].name" equals "Bob"
jsonpath "$[0].name" equals "Bob"

View File

@ -1 +1 @@
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"equal","value":false}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"not":true,"type":"equal","value":null}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"count","value":0}},{"query":{"type":"jsonpath","expr":"$.toto"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0].id"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.errors[0]['id']"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"equal","value":1.5}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"less-than-or-equal","value":"2.0"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"greater-than","value":"2"}},{"query":{"type":"jsonpath","expr":"$.nullable"},"predicate":{"type":"equal","value":null}}],"body":{"type":"json","value":{"success":false,"errors":[{"id":"error1"},{"id":"error2"}],"warnings":[],"duration":1.5,"tags":["test"],"nullable":null}}}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/index"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"index","query":{"type":"body"}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.errors[{{index}}].id"},"predicate":{"type":"equal","value":"error2"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"type":"include","value":"test"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":"prod"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":null}}]}}]}
{"entries":[{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"equal","value":false}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"not":true,"type":"equal","value":null}},{"query":{"type":"jsonpath","expr":"$.success"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"count","value":0}},{"query":{"type":"jsonpath","expr":"$.toto"},"predicate":{"not":true,"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.warnings"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0]"},"predicate":{"type":"exist"}},{"query":{"type":"jsonpath","expr":"$.errors[0].id"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.errors[0]['id']"},"predicate":{"type":"equal","value":"error1"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"equal","value":1.5}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"less-than-or-equal","value":"2.0"}},{"query":{"type":"jsonpath","expr":"$.duration"},"predicate":{"type":"greater-than","value":"2"}},{"query":{"type":"jsonpath","expr":"$.nullable"},"predicate":{"type":"equal","value":null}}],"body":{"type":"json","value":{"success":false,"errors":[{"id":"error1"},{"id":"error2"}],"warnings":[],"duration":1.5,"tags":["test"],"nullable":null}}}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/index"},"response":{"version":"HTTP/1.0","status":200,"captures":[{"name":"index","query":{"type":"body"}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$.errors[{{index}}].id"},"predicate":{"type":"equal","value":"error2"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"type":"include","value":"test"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":"prod"}},{"query":{"type":"jsonpath","expr":"$.tags"},"predicate":{"not":true,"type":"include","value":null}}]}},{"request":{"method":"GET","url":"http://localhost:8000/assert-json/list"},"response":{"version":"HTTP/1.0","status":200,"asserts":[{"query":{"type":"jsonpath","expr":"$"},"predicate":{"type":"count","value":2}},{"query":{"type":"jsonpath","expr":"$.[0].name"},"predicate":{"type":"equal","value":"Bob"}},{"query":{"type":"jsonpath","expr":"$[0].name"},"predicate":{"type":"equal","value":"Bob"}}]}}]}

View File

@ -1,8 +0,0 @@
{
"success": false,
"errors": [{"id":"error1"},{"id":"error2"}],
"warnings": [],
"duration": 1.5,
"tags": ["test"],
"nullable": null
}

View File

@ -16,3 +16,11 @@ def assert_json():
@app.route("/assert-json/index")
def assert_json_index():
return "1"
@app.route("/assert-json/list")
def assert_json_list():
return Response('''[
{ "id": 1, "name": "Bob"},
{ "id": 2, "name": "Bill"}
]''', mimetype='application/json')

View File

@ -57,7 +57,7 @@ fn selector(reader: &mut Reader) -> ParseResult<Selector> {
}
fn selector_array_index(reader: &mut Reader) -> Result<Selector, Error> {
try_literal("[", reader)?;
try_left_bracket(reader)?;
let i = match natural(reader) {
Err(e) => {
return Err(Error {
@ -80,7 +80,7 @@ fn selector_filter(reader: &mut Reader) -> Result<Selector, Error> {
}
fn selector_object_key_bracket(reader: &mut Reader) -> Result<Selector, Error> {
try_literal("[", reader)?;
try_left_bracket(reader)?;
match string_value(reader) {
Err(_) => Err(Error {
pos: reader.state.pos.clone(),
@ -134,6 +134,15 @@ fn selector_recursive_key(reader: &mut Reader) -> Result<Selector, Error> {
Ok(Selector::RecursiveKey(k))
}
fn try_left_bracket(reader: &mut Reader) -> Result<(), Error> {
let start = reader.state.clone();
if literal(".[", reader).is_err() {
reader.state = start;
try_literal("[", reader)?;
}
Ok(())
}
fn predicate(reader: &mut Reader) -> ParseResult<Predicate> {
// predicate always on key?
// TODO parsing key-value
@ -213,6 +222,21 @@ mod tests {
use super::super::Pos;
use super::*;
#[test]
pub fn test_try_left_bracket() {
let mut reader = Reader::init("xxx");
let error = try_left_bracket(&mut reader).err().unwrap();
assert!(error.recoverable);
let mut reader = Reader::init("[xxx");
assert!(try_left_bracket(&mut reader).is_ok());
assert_eq!(reader.state.cursor, 1);
let mut reader = Reader::init(".[xxx");
assert!(try_left_bracket(&mut reader).is_ok());
assert_eq!(reader.state.cursor, 2);
}
#[test]
pub fn test_query() {
let expected_query = Query {
@ -268,13 +292,44 @@ mod tests {
}
#[test]
pub fn test_selector() {
// Array index
pub fn test_selector_filter() {
// Filter equal on string with single quotes
let mut reader = Reader::init("[?(@.key=='value')]");
assert_eq!(
selector(&mut reader).unwrap(),
Selector::Filter(Predicate {
key: "key".to_string(),
func: PredicateFunc::EqualString("value".to_string()),
})
);
assert_eq!(reader.state.cursor, 19);
}
#[test]
pub fn test_selector_recursive() {
let mut reader = Reader::init("..book");
assert_eq!(
selector(&mut reader).unwrap(),
Selector::RecursiveKey("book".to_string())
);
assert_eq!(reader.state.cursor, 6);
}
#[test]
pub fn test_selector_array_index() {
let mut reader = Reader::init("[2]");
assert_eq!(selector(&mut reader).unwrap(), Selector::ArrayIndex(2));
assert_eq!(reader.state.cursor, 3);
// Key bracket notation
// you don't need to keep the exact string
// this is not part of the AST
let mut reader = Reader::init(".[2]");
assert_eq!(selector(&mut reader).unwrap(), Selector::ArrayIndex(2));
assert_eq!(reader.state.cursor, 4);
}
#[test]
pub fn test_key_bracket_selector() {
let mut reader = Reader::init("['key']");
assert_eq!(
selector(&mut reader).unwrap(),
@ -282,14 +337,23 @@ mod tests {
);
assert_eq!(reader.state.cursor, 7);
let mut reader = Reader::init(".['key']");
assert_eq!(
selector(&mut reader).unwrap(),
Selector::NameChild("key".to_string())
);
assert_eq!(reader.state.cursor, 8);
let mut reader = Reader::init("['key1']");
assert_eq!(
selector(&mut reader).unwrap(),
Selector::NameChild("key1".to_string())
);
assert_eq!(reader.state.cursor, 8);
}
// Key dot notation
#[test]
pub fn test_selector_key_dot_notation() {
let mut reader = Reader::init(".key");
assert_eq!(
selector(&mut reader).unwrap(),
@ -303,24 +367,6 @@ mod tests {
Selector::NameChild("key1".to_string())
);
assert_eq!(reader.state.cursor, 5);
// Filter equal on string with single quotes
let mut reader = Reader::init("[?(@.key=='value')]");
assert_eq!(
selector(&mut reader).unwrap(),
Selector::Filter(Predicate {
key: "key".to_string(),
func: PredicateFunc::EqualString("value".to_string()),
})
);
assert_eq!(reader.state.cursor, 19);
let mut reader = Reader::init("..book");
assert_eq!(
selector(&mut reader).unwrap(),
Selector::RecursiveKey("book".to_string())
);
assert_eq!(reader.state.cursor, 6);
}
#[test]

View File

@ -42,6 +42,10 @@ fn test_bookstore_path() {
test_ok("$.store.book[0].title", value.clone()),
vec![json!("Sayings of the Century")]
);
assert_eq!(
test_ok("$.store.book.[0].title", value.clone()),
vec![json!("Sayings of the Century")]
);
assert_eq!(
test_ok("$.store.book[0].title", value.clone()),
vec![json!("Sayings of the Century")]