From 0406c2719dcbbe8bbd71e3866ad55f79ef98a4fb Mon Sep 17 00:00:00 2001 From: John Firebaugh Date: Sun, 26 Oct 2014 10:37:03 -0700 Subject: [PATCH] Validate v6 filter syntax (fixes #1) --- lib/validate.js | 65 +++++++++++++++++++++--- test/fixture/filters.input.json | 87 ++++++++++++++++++++++++++++++++ test/fixture/filters.output.json | 38 ++++++++++++++ test/fixture/layers.input.json | 2 +- 4 files changed, 183 insertions(+), 9 deletions(-) create mode 100644 test/fixture/filters.input.json create mode 100644 test/fixture/filters.output.json diff --git a/lib/validate.js b/lib/validate.js index 18cf39aa4d1..dcf42bfcddb 100644 --- a/lib/validate.js +++ b/lib/validate.js @@ -182,8 +182,51 @@ module.exports = function(str) { } }; - validate.filter = function(/*key, val, spec*/) { - // TODO + validate.filter = function(key, val) { + if (typeof_(val) !== 'array') { + return error(key, val, 'array expected, %s found', typeof_(val)); + } + + if (val.length < 1) { + return error(key, val, 'filter array must have at least 1 element'); + } + + validate.enum(key + '[0]', val[0], reference.filter_operator); + + switch (unbundle(val[0])) { + case '==': + case '!=': + case '<': + case '<=': + case '>': + case '>=': + if (val.length != 3) { + error(key, val, 'filter array for operator "%s" must have 3 elements', val[0]); + } + /* falls through */ + case 'in': + case '!in': + if (val.length >= 2) { + validate.string(key + '[1]', val[1]); + } + for (var i = 2; i < val.length; i++) { + var type = typeof_(val[i]); + if (val[1] == '$type') { + validate.enum(key + '[' + i + ']', val[i], reference.geometry_type); + } else if (type !== 'string' && type !== 'number' && type !== 'boolean') { + error(key + '[' + i + ']', val[i], 'string, number, or boolean expected, %s found', type); + } + } + break; + + case 'any': + case 'all': + case 'none': + for (i = 1; i < val.length; i++) { + validate.filter(key + '[' + i + ']', val[i]); + } + break; + } }; validate.function = function(key, val, spec) { @@ -220,12 +263,18 @@ module.exports = function(str) { } }; - validate.number = validate.string = validate.boolean = function(key, val, spec) { - var type = typeof_(val); - if (type !== spec.type) { - error(key, val, '%s expected, %s found', spec.type, type); - } - }; + function typeValidator(expected) { + return function(key, val) { + var actual = typeof_(val); + if (actual !== expected) { + error(key, val, '%s expected, %s found', expected, actual); + } + }; + } + + validate.number = typeValidator('number'); + validate.string = typeValidator('string'); + validate.boolean = typeValidator('boolean'); validate['*'] = function() {}; diff --git a/test/fixture/filters.input.json b/test/fixture/filters.input.json new file mode 100644 index 00000000000..13d0b136b10 --- /dev/null +++ b/test/fixture/filters.input.json @@ -0,0 +1,87 @@ +{ + "version": 6, + "sources": { + "source": { + "type": "vector", + "url": "mapbox://mapbox.mapbox-streets-v5" + } + }, + "layers": [ + { + "id": "not-array", + "type": "line", + "source": "source", + "filter": {} + }, + { + "id": "zero-elements", + "type": "line", + "source": "source", + "filter": [] + }, + { + "id": "invalid-operator", + "type": "line", + "source": "source", + "filter": [ + "=", + "key", + "value" + ] + }, + { + "id": "missing-key", + "type": "line", + "source": "source", + "filter": ["=="] + }, + { + "id": "missing-value", + "type": "line", + "source": "source", + "filter": [ + "==", + "key" + ] + }, + { + "id": "invalid-key", + "type": "line", + "source": "source", + "filter": [ + "==", + 1, + "value" + ] + }, + { + "id": "invalid-value", + "type": "line", + "source": "source", + "filter": [ + "==", + "key", + [] + ] + }, + { + "id": "invalid-type", + "type": "line", + "source": "source", + "filter": [ + "==", + "$type", + "value" + ] + }, + { + "id": "invalid-nested-filter", + "type": "line", + "source": "source", + "filter": [ + "any", + [] + ] + } + ] +} diff --git a/test/fixture/filters.output.json b/test/fixture/filters.output.json new file mode 100644 index 00000000000..35e95a26c4e --- /dev/null +++ b/test/fixture/filters.output.json @@ -0,0 +1,38 @@ +[ + { + "line": 14, + "message": "layers[0].filter: array expected, object found" + }, + { + "line": 20, + "message": "layers[1].filter: filter array must have at least 1 element" + }, + { + "line": 27, + "message": "layers[2].filter[0]: expected one of [==, !=, >, >=, <, <=, in, !in, all, any, none], = found" + }, + { + "line": 36, + "message": "layers[3].filter: filter array for operator \"==\" must have 3 elements" + }, + { + "line": 42, + "message": "layers[4].filter: filter array for operator \"==\" must have 3 elements" + }, + { + "line": 53, + "message": "layers[5].filter[1]: string expected, number found" + }, + { + "line": 64, + "message": "layers[6].filter[2]: string, number, or boolean expected, array found" + }, + { + "line": 74, + "message": "layers[7].filter[2]: expected one of [Point, LineString, Polygon], value found" + }, + { + "line": 83, + "message": "layers[8].filter[1]: filter array must have at least 1 element" + } +] \ No newline at end of file diff --git a/test/fixture/layers.input.json b/test/fixture/layers.input.json index ab0b5dd68c4..565af6ddc68 100644 --- a/test/fixture/layers.input.json +++ b/test/fixture/layers.input.json @@ -27,7 +27,7 @@ "type": "line", "source": "vector", "source-layer": "source-layer", - "filter": [], + "filter": ["any"], "render": {} }, {