data-matching
Version:
Matches a data object against a reference value
661 lines • 24.3 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.www_form_urlencoded_full_match = exports.www_form_urlencoded_partial_match = exports.kv_str_full_match = exports.kv_str_partial_match = exports.json_full_match = exports.json_partial_match = exports.reverse_pop_match = exports.pop_match = exports.gen_gen_matcher = exports._ = exports.MatchingError = exports.match = exports.matchify_strings = exports.string_list = exports.m = exports.matcher = exports.unordered_list = exports.any_of = exports.www_form_urlencoded = exports.kv_str = exports.xml = exports.json = exports.fm = exports.pm = exports.full_match = exports.partial_match = exports.pop = exports.push = exports.$ = exports.collect = exports.str_equal = exports.non_blank_str = exports.non_zero = exports.absent = void 0;
const _ = __importStar(require("lodash"));
const sm = __importStar(require("string-matching"));
const util = __importStar(require("util"));
const fast_xml_parser_1 = require("fast-xml-parser");
const MatchingError = sm.MatchingError;
exports.MatchingError = MatchingError;
const re_string_matching_indication = /(^|[^!])!{/;
var non_zero = (x, dict, throw_matching_error, path) => {
if (typeof x != "number") {
if (throw_matching_error)
throw new MatchingError(path, "expected to be a number");
return false;
}
if (x == 0) {
if (throw_matching_error)
throw new MatchingError(path, "expected to be non_zero");
return false;
}
return "is non_zero";
};
exports.non_zero = non_zero;
non_zero.__name__ = "non_zero";
var non_blank_str = (x, dict, throw_matching_error, path) => {
if (typeof x != "string") {
if (throw_matching_error)
throw new MatchingError(path, "expected to be a string");
return false;
}
if (x == "") {
if (throw_matching_error)
throw new MatchingError(path, "expected to be non_blank_str");
return false;
}
return "non_blank_str";
};
exports.non_blank_str = non_blank_str;
non_blank_str.__name__ = "non_blank_str";
var str_equal = (s) => {
var f = (x, dict, throw_matching_error, path) => {
if (x.toString() !== s.toString()) {
if (throw_matching_error)
throw new MatchingError(path, `not str_equal expected='${s}' received='${x}'`);
return false;
}
return "str_equal";
};
f.__name__ = `str_equal['${s}']`;
return f;
};
exports.str_equal = str_equal;
var _typeof = (v) => {
var t = typeof v;
if (t === "object") {
if (v instanceof Array)
return "array";
else if (v === undefined)
return "undefined";
else if (v === null)
return "null";
else
return "dict";
}
else {
return t;
}
};
var print_debug = (s) => {
//console.error(s) // this actually is not an error. It is just to avoid messing with STDOUT from client code.
};
var _match_arrays = (expected, received, dict, full_match, throw_matching_error, path) => {
var reason;
print_debug(`${path}: checking`);
if (expected.length != received.length) {
reason = `arrays lengths do not match: expected_len=${expected.length} received_len=${received.length}`;
if (throw_matching_error)
throw new MatchingError(path, reason);
print_debug(`${path}: ${reason}`);
return false;
}
for (var i = 0; i < expected.length; i++) {
if (!_match(expected[i], received[i], dict, full_match, throw_matching_error, path + "[" + i + "]")) {
return false;
}
}
return "array matched";
};
var _match_dicts = (expected, received, dict, full_match, throw_matching_error, path) => {
var reason;
print_debug(`${path}: checking`);
var keys_e = new Set(Object.keys(expected));
var keys_r = new Set(Object.keys(received));
for (var key of keys_e) {
print_debug(`${path}.${key}: checking`);
var val_e = expected[key];
if (val_e == absent) {
if (keys_r.has(key)) {
reason = "should be absent";
if (throw_matching_error)
throw new MatchingError(`${path}.${key}`, reason);
print_debug(`${path}.${key}: ${reason}`);
return false;
}
else {
print_debug(`${path}.${key}: absent as expected`);
}
}
else {
var rec = received[key];
if (rec === undefined) {
reason = "should be present";
if (throw_matching_error)
throw new MatchingError(`${path}.${key}`, reason);
print_debug(`${path}.${key}: ${reason}`);
return false;
}
if (!_match(expected[key], rec, dict, full_match, throw_matching_error, path + "." + key)) {
return false;
}
}
print_debug(`${path}.${key}: matched`);
keys_r.delete(key);
}
if (full_match) {
if (keys_r.size > 0) {
reason = `full match failed due extra keys [${Array.from(keys_r)}]`;
if (throw_matching_error)
throw new MatchingError(path, reason);
print_debug(`${path}: ${reason}`);
return false;
}
}
return "object matched";
};
var collect = (var_name, matcher) => {
var f = (val, dict, throw_matching_error, path) => {
if (matcher) {
if (!_match(matcher, val, dict, false, throw_matching_error, path)) {
return;
}
}
if (typeof dict[var_name] == "undefined") {
dict[var_name] = val;
print_debug(`${path}: collect OK`);
return true;
}
else {
if (dict[var_name] != val) {
var reason = `cannot set '${var_name}' to '${util.inspect(val)}' because it is already set to '${util.inspect(dict[var_name])}'`;
if (throw_matching_error)
throw new MatchingError(path, reason);
print_debug(`${path}: ${reason}`);
return false;
}
print_debug(`${path}: collect OK (found already set to same value)`);
return true;
}
};
f.__name__ = `collect['${var_name}']`;
return f;
};
exports.collect = collect;
exports.$ = collect;
var _match = (expected, received, dict, full_match, throw_matching_error, path) => {
var reason;
var type_e = _typeof(expected);
var type_r = _typeof(received);
if (type_e == "undefined") {
// this means to ignore received value
print_debug(`${path}: required to be ignored`);
return true;
}
if (type_e == type_r) {
if (type_e == "array") {
return _match_arrays(expected, received, dict, full_match, throw_matching_error, path);
}
else if (type_e == "dict") {
return _match_dicts(expected, received, dict, full_match, throw_matching_error, path);
}
if (type_r == "function" && expected.__name__.startsWith("collect[")) {
return expected(received, dict, throw_matching_error, path);
}
if (expected != received) {
//console.error("throw_matching_error:", throw_matching_error)
reason = `expected='${expected}' received='${received}'`;
if (throw_matching_error)
throw new MatchingError(path, reason);
print_debug(`${path}: ${reason}`);
return false;
}
//print_debug(`${path}: matched`)
return "matched";
}
if (type_e == "function") {
var res = expected(received, dict, throw_matching_error, path);
if (res)
print_debug(`${path}: ${res} (matched)`);
return res;
}
if (expected === received) {
return true;
}
else {
var reason2 = `expected (${JSON.stringify(expected)}) and received (${JSON.stringify(received)}) differ`;
if (throw_matching_error)
throw new MatchingError(path, reason2);
}
};
exports.match = _match;
var push = (var_name, matcher) => {
var f = (val, dict, throw_matching_error, path) => {
if (matcher) {
if (!_match(matcher, val, dict, false, throw_matching_error, path)) {
return;
}
}
if (typeof dict[var_name] == "undefined") {
dict[var_name] = [val];
return true;
}
else if (!Array.isArray(dict[var_name])) {
var reason = `'${var_name}' is not an Array`;
if (throw_matching_error)
throw new MatchingError(path, reason);
print_debug(`${path}: ${reason}`);
return false;
}
else {
dict[var_name].push(val);
return true;
}
};
f.__name__ = `push['${var_name}']`;
return f;
};
exports.push = push;
var pop = (var_name, matcher) => {
var f = (val, dict, throw_matching_error, path) => {
if (matcher) {
if (!_match(matcher, val, dict, false, throw_matching_error, path)) {
return;
}
}
if (typeof dict[var_name] == "undefined") {
var reason = `'${var_name}' is undefined`;
if (throw_matching_error)
throw new MatchingError(path, reason);
print_debug(`${path}: ${reason}`);
return false;
}
else if (!Array.isArray(dict[var_name])) {
var reason = `'${var_name}' is not an Array`;
if (throw_matching_error)
throw new MatchingError(path, reason);
print_debug(`${path}: ${reason}`);
return false;
}
else {
const idx = dict[var_name].indexOf(val);
if (idx !== -1) {
dict[var_name].splice(idx, 1);
return true;
}
else {
var reason2 = `${val} not found in the array ${var_name}`;
if (throw_matching_error)
throw new MatchingError(path, reason2);
print_debug(`${path}: ${reason2}`);
return false;
}
}
};
f.__name__ = `pop['${var_name}']`;
return f;
};
exports.pop = pop;
const absent = () => {
return "I am the absent function";
};
exports.absent = absent;
absent.__name__ = "absent";
const anything = () => {
return true;
};
exports._ = anything;
anything.__name__ = "anything";
var _deepMap = (obj, iterator, context) => {
return _.transform(obj, function (result, val, key) {
var type_val = _typeof(val);
result[key] =
type_val == "array" || type_val == "dict"
? _deepMap(val, iterator, context)
: iterator.call(context, val, key, obj);
});
};
var matchify_strings = (evt) => {
if (typeof evt == "function") {
return evt;
}
return _deepMap(evt, (x) => {
if (typeof x == "string" && x.match(re_string_matching_indication)) {
return sm.gen_matcher(x);
}
else {
return x;
}
});
};
exports.matchify_strings = matchify_strings;
var partial_match = (expected) => {
var expected2 = matchify_strings(expected);
var f = (received, dict, throw_matching_error, path) => {
return _match(expected2, received, dict, false, throw_matching_error, path);
};
f.__original_data__ = expected;
f.__name__ = "partial_match";
return f;
};
exports.partial_match = partial_match;
exports.pm = partial_match;
var full_match = (expected) => {
var expected2 = matchify_strings(expected);
var f = (received, dict, throw_matching_error, path) => {
return _match(expected2, received, dict, true, throw_matching_error, path);
};
f.__original_data__ = expected;
f.__name__ = "full_match";
return f;
};
exports.full_match = full_match;
exports.fm = full_match;
var json = (expected, full_match) => {
var expected2 = matchify_strings(expected);
var f = (s, dict, throw_matching_error, path) => {
var received = JSON.parse(s);
return _match(expected2, received, dict, full_match, throw_matching_error, path);
};
f.__original_data__ = expected;
f.__name__ = "json" + (full_match ? "_full_match" : "_partial_match");
return f;
};
exports.json = json;
var xml = (expected, full_match) => {
var expected2 = matchify_strings(expected);
var f = (s, dict, throw_matching_error, path) => {
var parser = new fast_xml_parser_1.XMLParser({
ignoreAttributes: false,
attributeNamePrefix: "",
preserveOrder: true,
//attributesGroupName: 'attrs', // cannot be use yet. See https://github.com/NaturalIntelligence/fast-xml-parser/issues/705
//trimValues: true,
});
var received = parser.parse(s);
return _match(expected2, received, dict, full_match, throw_matching_error, path);
};
f.__original_data__ = expected;
f.__name__ = "xml" + (full_match ? "_full_match" : "_partial_match");
return f;
};
exports.xml = xml;
var kv_str = (expected, param_sep, kv_sep, preparse_decoder, postparse_decoder, full_match) => {
var expected2 = matchify_strings(expected);
var f = (s, dict, throw_matching_error, path) => {
var received = s;
if (preparse_decoder) {
received = preparse_decoder(s);
}
received = _.chain(received)
.split(param_sep)
.map((s) => {
var parts = s.split(kv_sep);
var key = parts[0];
var val = parts.slice(1).join(kv_sep);
if (postparse_decoder) {
val = postparse_decoder(val);
}
return [key, val];
})
.fromPairs()
.value();
return _match(expected2, received, dict, full_match, throw_matching_error, path);
};
f.__original_data__ = expected;
f.__name__ = "kv_str" + (full_match ? "_full_match" : "_partial_match");
return f;
};
exports.kv_str = kv_str;
// Decodes a application/x-www-form-urlencoded component: '+' means space and
// the rest follows the regular percent-encoding rules used by encodeURIComponent.
var decode_www_form_urlencoded_component = (s) => {
return decodeURIComponent(s.replace(/\+/g, " "));
};
var www_form_urlencoded = (expected, postparse_decoder, full_match) => {
var expected2 = matchify_strings(expected);
var f = (s, dict, throw_matching_error, path) => {
if (typeof s !== "string") {
if (throw_matching_error) {
throw new MatchingError(path, `www_form_urlencoded expected a string but received ${typeof s}`);
}
return false;
}
var received = _.chain(s)
.split("&")
.filter((part) => part.length > 0)
.map((part) => {
var parts = part.split("=");
var key = decode_www_form_urlencoded_component(parts[0]);
var val = decode_www_form_urlencoded_component(parts.slice(1).join("="));
if (postparse_decoder) {
val = postparse_decoder(val);
}
return [key, val];
})
.fromPairs()
.value();
return _match(expected2, received, dict, full_match, throw_matching_error, path);
};
f.__original_data__ = expected;
f.__name__ =
"www_form_urlencoded" + (full_match ? "_full_match" : "_partial_match");
return f;
};
exports.www_form_urlencoded = www_form_urlencoded;
var matcher = (name, expected) => {
if (typeof expected != "function")
throw new Error("Must be a function");
var f = (received, dict, throw_matching_error, path) => {
var res = expected(received);
if (res == true)
return true;
if (throw_matching_error) {
throw new MatchingError(path, res);
}
return false;
};
f.__name__ = name;
return f;
};
exports.matcher = matcher;
exports.m = matcher;
var any_of = (matchers, name) => {
var f = (received, dict, throw_matching_error, path) => {
var res;
//we cannot use matchers.forEach() as a return doesn't interrupt iteration.
for (var i = 0; i < matchers.length; i++) {
var matcher = matchers[i];
var dict_clone = _.cloneDeep(dict);
res = _match(matcher, received, dict_clone, false, false, path);
if (res) {
_.assign(dict, dict_clone);
if (name) {
dict[name] = received;
}
return res;
}
}
if (throw_matching_error) {
throw new MatchingError(path, "any_of no match");
}
return res;
};
f.__original_data__ = matchers;
f.__name__ = "any_of";
return f;
};
exports.any_of = any_of;
var unordered_list = (expected) => {
return (received_list, dict, throw_matching_error, path) => {
if (_typeof(received_list) != "array") {
if (throw_matching_error) {
throw new MatchingError(path, "unordered_list got non list");
}
return false;
}
if (expected.length != received_list.length) {
var reason = `arrays lengths do not match: expected_len=${expected.length} received_len=${received_list.length}`;
if (throw_matching_error)
throw new MatchingError(path, reason);
print_debug(`${path}: ${reason}`);
return false;
}
var exp = _.cloneDeep(expected);
for (var i = 0; i < received_list.length; i++) {
var received = received_list[i];
for (var j = 0; j < exp.length; j++) {
var matcher = exp[j];
var res = _match(matcher, received, dict, false, false, path);
if (res) {
//remove element
exp.splice(j, 1);
break;
}
}
}
if (exp.length == 0) {
return true;
}
if (throw_matching_error) {
throw new MatchingError(path, "unordered_list no match");
}
return false;
};
};
exports.unordered_list = unordered_list;
var gen_gen_matcher = (parser, extractor, name) => {
return (expected) => {
var expected2 = matchify_strings(expected);
var f = (s, dict, throw_matching_error, path) => {
var received = parser(s);
return _.every(expected2, (val, key) => {
var item = extractor(received, key);
if (val == absent && item) {
if (throw_matching_error) {
throw Error(`key ${path}.${key} expected to be absent`);
}
return false;
}
var full_match = false;
return _match(val, item, dict, full_match, throw_matching_error, `${path}.${key}`);
});
};
f.__original_data__ = expected;
f.__name__ = name;
return f;
};
};
exports.gen_gen_matcher = gen_gen_matcher;
const pop_match = (expected, array, dict) => {
const throw_matching_error = false;
const path = "";
const full_match = false;
var expected2 = matchify_strings(expected);
var index = undefined;
for (var i = 0; i < array.length; i++) {
var item = array[i];
if (typeof expected2 == "function") {
if (expected2(item, dict, throw_matching_error, path)) {
index = i;
break;
}
}
else {
if (_match(expected2, item, dict, full_match, throw_matching_error, "")) {
index = i;
break;
}
}
}
if (index !== undefined && index >= 0) {
return array.splice(index, 1)[0];
}
return undefined;
};
exports.pop_match = pop_match;
const reverse_pop_match = (item, array, dict) => {
const throw_matching_error = false;
const path = "";
const full_match = false;
var array2 = matchify_strings(array);
var index = undefined;
for (var i = 0; i < array2.length; i++) {
var expected = array2[i];
if (typeof expected == "function") {
if (expected(item, dict, throw_matching_error, path)) {
index = i;
break;
}
}
else {
if (_match(expected, item, dict, full_match, throw_matching_error, "")) {
index = i;
break;
}
}
}
if (index !== undefined && index >= 0) {
return array.splice(index, 1)[0];
}
return undefined;
};
exports.reverse_pop_match = reverse_pop_match;
var string_list = (separator, decoder, expected) => {
var expected2 = matchify_strings(expected);
var f = (s, dict, throw_matching_error, path) => {
if (typeof s !== "string") {
if (throw_matching_error) {
throw new MatchingError(path, `string_list expected a string but received ${typeof s}`);
}
return false;
}
var parts = s.split(separator);
if (decoder) {
parts = parts.map(decoder);
}
return _match(expected2, parts, dict, false, throw_matching_error, path);
};
f.__original_data__ = expected;
f.__name__ = "string_list";
return f;
};
exports.string_list = string_list;
const json_partial_match = (expected) => {
return json(expected, false);
};
exports.json_partial_match = json_partial_match;
const json_full_match = (expected) => {
return json(expected, true);
};
exports.json_full_match = json_full_match;
const kv_str_partial_match = (expected, param_sep, kv_sep, preparse_decoder, postparse_decoder) => {
return kv_str(expected, param_sep, kv_sep, preparse_decoder, postparse_decoder, false);
};
exports.kv_str_partial_match = kv_str_partial_match;
const kv_str_full_match = (expected, param_sep, kv_sep, preparse_decoder, postparse_decoder) => {
return kv_str(expected, param_sep, kv_sep, preparse_decoder, postparse_decoder, true);
};
exports.kv_str_full_match = kv_str_full_match;
const www_form_urlencoded_partial_match = (expected, postparse_decoder) => {
return www_form_urlencoded(expected, postparse_decoder, false);
};
exports.www_form_urlencoded_partial_match = www_form_urlencoded_partial_match;
const www_form_urlencoded_full_match = (expected, postparse_decoder) => {
return www_form_urlencoded(expected, postparse_decoder, true);
};
exports.www_form_urlencoded_full_match = www_form_urlencoded_full_match;
//# sourceMappingURL=index.js.map