1
0
mirror of https://github.com/matrix-org/matrix-js-sdk.git synced 2025-08-06 12:02:40 +03:00

Implement MSC3966: a push rule condition to check if an array contains a value (#3180)

* Support MSC3966 to match values in an array in push rule conditions.

* Update to stable identifiers.

* Appease the linter.
This commit is contained in:
Patrick Cloke
2023-03-07 11:36:06 -05:00
committed by GitHub
parent 54ac36d424
commit bcf3bba44e
3 changed files with 97 additions and 0 deletions

View File

@@ -576,6 +576,75 @@ describe("NotificationService", function () {
}); });
}); });
describe("Test event property contains", () => {
it.each([
// Simple string matching.
{ value: "bar", eventValue: ["bar"], expected: true },
// Matches are case-sensitive.
{ value: "bar", eventValue: ["BAR"], expected: false },
// Values should not be type-coerced.
{ value: "bar", eventValue: [true], expected: false },
{ value: "bar", eventValue: [1], expected: false },
{ value: "bar", eventValue: [false], expected: false },
// Boolean matching.
{ value: true, eventValue: [true], expected: true },
{ value: false, eventValue: [false], expected: true },
// Types should not be coerced.
{ value: true, eventValue: ["true"], expected: false },
{ value: true, eventValue: [1], expected: false },
{ value: false, eventValue: [null], expected: false },
// Null matching.
{ value: null, eventValue: [null], expected: true },
// Types should not be coerced
{ value: null, eventValue: [false], expected: false },
{ value: null, eventValue: [0], expected: false },
{ value: null, eventValue: [""], expected: false },
{ value: null, eventValue: [undefined], expected: false },
// Non-array or empty values should never be matched.
{ value: "bar", eventValue: "bar", expected: false },
{ value: "bar", eventValue: { bar: true }, expected: false },
{ value: true, eventValue: { true: true }, expected: false },
{ value: true, eventValue: true, expected: false },
{ value: null, eventValue: [], expected: false },
{ value: null, eventValue: {}, expected: false },
{ value: null, eventValue: null, expected: false },
{ value: null, eventValue: undefined, expected: false },
])("test $value against $eventValue", ({ value, eventValue, expected }) => {
matrixClient.pushRules! = {
global: {
override: [
{
actions: [PushRuleActionName.Notify],
conditions: [
{
kind: ConditionKind.EventPropertyContains,
key: "content.foo",
value: value,
},
],
default: true,
enabled: true,
rule_id: ".m.rule.test",
},
],
},
};
testEvent = utils.mkEvent({
type: "m.room.message",
room: testRoomId,
user: "@alfred:localhost",
event: true,
content: {
foo: eventValue,
},
});
const actions = pushProcessor.actionsForEvent(testEvent);
expect(actions?.notify).toBe(expected ? true : undefined);
});
});
it.each([ it.each([
// The properly escaped key works. // The properly escaped key works.
{ key: "content.m\\.test.foo", pattern: "bar", expected: true }, { key: "content.m\\.test.foo", pattern: "bar", expected: true },

View File

@@ -63,6 +63,7 @@ export function isDmMemberCountCondition(condition: AnyMemberCountCondition): bo
export enum ConditionKind { export enum ConditionKind {
EventMatch = "event_match", EventMatch = "event_match",
EventPropertyIs = "event_property_is", EventPropertyIs = "event_property_is",
EventPropertyContains = "event_property_contains",
ContainsDisplayName = "contains_display_name", ContainsDisplayName = "contains_display_name",
RoomMemberCount = "room_member_count", RoomMemberCount = "room_member_count",
SenderNotificationPermission = "sender_notification_permission", SenderNotificationPermission = "sender_notification_permission",
@@ -88,6 +89,11 @@ export interface IEventPropertyIsCondition extends IPushRuleCondition<ConditionK
value: string | boolean | null | number; value: string | boolean | null | number;
} }
export interface IEventPropertyContainsCondition extends IPushRuleCondition<ConditionKind.EventPropertyContains> {
key: string;
value: string | boolean | null | number;
}
export interface IContainsDisplayNameCondition extends IPushRuleCondition<ConditionKind.ContainsDisplayName> { export interface IContainsDisplayNameCondition extends IPushRuleCondition<ConditionKind.ContainsDisplayName> {
// no additional fields // no additional fields
} }
@@ -114,6 +120,7 @@ export interface ICallStartedPrefixCondition extends IPushRuleCondition<Conditio
export type PushRuleCondition = export type PushRuleCondition =
| IEventMatchCondition | IEventMatchCondition
| IEventPropertyIsCondition | IEventPropertyIsCondition
| IEventPropertyContainsCondition
| IContainsDisplayNameCondition | IContainsDisplayNameCondition
| IRoomMemberCountCondition | IRoomMemberCountCondition
| ISenderNotificationPermissionCondition | ISenderNotificationPermissionCondition

View File

@@ -26,6 +26,7 @@ import {
IContainsDisplayNameCondition, IContainsDisplayNameCondition,
IEventMatchCondition, IEventMatchCondition,
IEventPropertyIsCondition, IEventPropertyIsCondition,
IEventPropertyContainsCondition,
IPushRule, IPushRule,
IPushRules, IPushRules,
IRoomMemberCountCondition, IRoomMemberCountCondition,
@@ -340,6 +341,8 @@ export class PushProcessor {
return this.eventFulfillsEventMatchCondition(cond, ev); return this.eventFulfillsEventMatchCondition(cond, ev);
case ConditionKind.EventPropertyIs: case ConditionKind.EventPropertyIs:
return this.eventFulfillsEventPropertyIsCondition(cond, ev); return this.eventFulfillsEventPropertyIsCondition(cond, ev);
case ConditionKind.EventPropertyContains:
return this.eventFulfillsEventPropertyContains(cond, ev);
case ConditionKind.ContainsDisplayName: case ConditionKind.ContainsDisplayName:
return this.eventFulfillsDisplayNameCondition(cond, ev); return this.eventFulfillsDisplayNameCondition(cond, ev);
case ConditionKind.RoomMemberCount: case ConditionKind.RoomMemberCount:
@@ -488,6 +491,24 @@ export class PushProcessor {
return cond.value === this.valueForDottedKey(cond.key, ev); return cond.value === this.valueForDottedKey(cond.key, ev);
} }
/**
* Check whether the given event matches the push rule condition by fetching
* the property from the event and comparing exactly against the condition's
* value.
* @param cond - The push rule condition to check for a match.
* @param ev - The event to check for a match.
*/
private eventFulfillsEventPropertyContains(cond: IEventPropertyContainsCondition, ev: MatrixEvent): boolean {
if (!cond.key || cond.value === undefined) {
return false;
}
const val = this.valueForDottedKey(cond.key, ev);
if (!Array.isArray(val)) {
return false;
}
return val.includes(cond.value);
}
private eventFulfillsCallStartedCondition( private eventFulfillsCallStartedCondition(
_cond: ICallStartedCondition | ICallStartedPrefixCondition, _cond: ICallStartedCondition | ICallStartedPrefixCondition,
ev: MatrixEvent, ev: MatrixEvent,