Fix #756: Add keys transform and intersection operator to filter expressions.

This commit is contained in:
Michael Kelly 2017-06-19 13:00:57 -07:00
Родитель 6d52f03e07
Коммит 3e5759f9aa
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 972176E09570E68A
3 изменённых файлов: 108 добавлений и 0 удалений

Просмотреть файл

@ -270,6 +270,27 @@ filter expressions.
object varies depending on the recipe, and use of this property is only
recommended if you are familiar with the argument schema.
Operators
---------
This section describes the special operators available to filter expressions on
top of the standard operators in JEXL. They're documented as functions, and the
parameters correspond to the operands.
.. js:function:: intersect(list1, list2)
Returns an array of all values in ``list1`` that are also present in
``list2``. Values are compared using strict equality.
:param list1:
The array to the left of the operator.
:param list2:
The array to the right of the operator
.. code-block:: javascript
// Evaluates to [2, 3]
[1, 2, 3, 4] intersect [5, 6, 2, 7, 3]
Transforms
----------
This section describes the transforms available to filter expressions, and what
@ -359,6 +380,21 @@ function is the value being transformed.
.. _ISO 8601: https://www.w3.org/TR/NOTE-datetime
.. js:function:: keys(obj)
Return an array of the given object's own keys (specifically, its enumerable
properties). Equivalent to `Object.keys`_.
:param obj:
Object to get the keys for.
.. code-block:: javascript
// Evaluates to ['foo', 'bar']
{foo: 1, bar:2}|keys
.. _Object.keys: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
Preference Filters
^^^^^^^^^^^^^^^^^^
.. js:function:: preferenceValue(prefKey, defaultValue)

Просмотреть файл

@ -31,7 +31,9 @@ XPCOMUtils.defineLazyGetter(this, "jexl", () => {
preferenceValue: PreferenceFilters.preferenceValue,
preferenceIsUserSet: PreferenceFilters.preferenceIsUserSet,
preferenceExists: PreferenceFilters.preferenceExists,
keys,
});
jexl.addBinaryOp("intersect", 40, operatorIntersect);
return jexl;
});
@ -41,3 +43,23 @@ this.FilterExpressions = {
return jexl.eval(onelineExpr, context);
},
};
/**
* Return an array of the given object's own keys (specifically, its enumerable
* properties).
* @param {Object} obj
* @return {Array[String]}
*/
function keys(obj) {
return Object.keys(obj);
}
/**
* Find all the values that are present in both lists.
* @param {Array} listA
* @param {Array} listB
* @return {Array}
*/
function operatorIntersect(listA, listB) {
return listA.filter(item => listB.includes(item));
}

Просмотреть файл

@ -91,3 +91,53 @@ add_task(async function() {
val = await FilterExpressions.eval('"normandy.test.value"|preferenceExists == true');
ok(val, "preferenceExists expression fails existence check appropriately");
});
// keys tests
add_task(async function testKeys() {
let val;
// Test an object defined in JEXL
val = await FilterExpressions.eval("{foo: 1, bar: 2}|keys");
Assert.deepEqual(
new Set(val),
new Set(["foo", "bar"]),
"keys returns the keys from an object in JEXL",
);
// Test an object in the context
const context = {ctxObject: {baz: "string", biff: NaN}};
val = await FilterExpressions.eval("ctxObject|keys", context);
Assert.deepEqual(
new Set(val),
new Set(["baz", "biff"]),
"keys returns the keys from an object in the context",
);
});
// intersect tests
add_task(async function testIntersect() {
let val;
val = await FilterExpressions.eval("[1, 2, 3] intersect [4, 2, 6, 7, 3]");
Assert.deepEqual(
new Set(val),
new Set([2, 3]),
"intersect finds the common elements between two lists in JEXL",
);
const context = {left: [5, 7], right: [4, 5, 3]};
val = await FilterExpressions.eval("left intersect right", context);
Assert.deepEqual(
new Set(val),
new Set([5]),
"intersect finds the common elements between two lists in the context",
);
val = await FilterExpressions.eval("['string', 2] intersect [4, 'string', 'other', 3]");
Assert.deepEqual(
new Set(val),
new Set(["string"]),
"intersect can compare strings",
);
});