Add a schema checking concept to the web app.

This starts the concept of a schema checker, similar to
zerver/lib/validator.py on the server.  We can use this
to validate incoming data.  Our server should filter most
of our incoming data, but it's useful to have client-side
checking to defend against things like upgrade
regressions (i.e. what if we change the name of the field
on the server side without updating all client uses).
This commit is contained in:
Steve Howell
2018-05-22 19:50:24 +00:00
committed by Tim Abbott
parent a5dee62b8f
commit 435719c65b
5 changed files with 127 additions and 0 deletions

View File

@@ -10,6 +10,7 @@
"$": false,
"_": false,
"run_test": false,
"schema": false,
"jQuery": false,
"Spinner": false,
"Handlebars": false,

View File

@@ -0,0 +1,59 @@
zrequire('schema');
run_test('basics', () => {
assert.equal(schema.check_string('x', 'fred'), undefined);
assert.equal(schema.check_string('x', [1,2]), 'x is not a string');
const fields = {
foo: schema.check_string,
bar: schema.check_string,
};
const check_rec = (val) => {
return schema.check_record('my_rec', val, fields);
};
assert.equal(
check_rec({foo: 'apple', bar: 'banana'}),
undefined
);
assert.equal(
check_rec('bogus'),
'my_rec is not a record'
);
assert.equal(
check_rec({foo: 'apple'}),
'in my_rec bar is missing'
);
assert.equal(
check_rec({}),
'in my_rec bar is missing, foo is missing'
);
assert.equal(
check_rec({foo: 'apple', bar: 42}),
'in my_rec bar is not a string'
);
const check_array = (val) => {
return schema.check_array('lst', val, schema.check_string);
};
assert.equal(
check_array(['foo', 'bar']),
undefined
);
assert.equal(
check_array('foo'),
'lst is not an array'
);
assert.equal(
check_array(['foo', 3]),
'in lst we found an item where item is not a string'
);
});

65
static/js/schema.js Normal file
View File

@@ -0,0 +1,65 @@
var schema = (function () {
var exports = {};
/*
These runtime schema validators are defensive and
should always succeed, so we don't necessarily want
to translate these. These are very similar to server
side validators in zerver/lib/validator.py.
*/
exports.check_string = function (var_name, val) {
if (!_.isString(val)) {
return var_name + ' is not a string';
}
};
exports.check_record = function (var_name, val, fields) {
if (!_.isObject(val)) {
return var_name + ' is not a record';
}
var field_results = _.map(fields, function (f, field_name) {
if (val[field_name] === undefined) {
return field_name + ' is missing';
}
return f(field_name, val[field_name]);
});
var msg = _.filter(field_results).sort().join(', ');
if (msg) {
return 'in ' + var_name + ' ' + msg;
}
};
exports.check_array = function (var_name, val, checker) {
if (!_.isArray(val)) {
return var_name + ' is not an array';
}
var msg;
_.find(val, function (item) {
var res = checker('item', item);
if (res) {
msg = res;
return msg;
}
});
if (msg) {
return 'in ' + var_name + ' we found an item where ' + msg;
}
};
return exports;
}());
if (typeof module !== 'undefined') {
module.exports = schema;
}

View File

@@ -54,6 +54,7 @@ enforce_fully_covered = {
'static/js/reactions.js',
'static/js/recent_senders.js',
'static/js/rtl.js',
'static/js/schema.js',
'static/js/scroll_util.js',
'static/js/search_suggestion.js',
# Removed because we're migrating code from uncovered other settings pages to here.

View File

@@ -929,6 +929,7 @@ JS_SPECS = {
'templates/compiled.js',
'js/feature_flags.js',
'js/loading.js',
'js/schema.js',
'js/util.js',
'js/keydown_util.js',
'js/lightbox_canvas.js',