From 38ad83277945e9395e4dff621e42c90ef986b518 Mon Sep 17 00:00:00 2001 From: Cyril Beslay Date: Tue, 17 Dec 2024 09:35:08 +0100 Subject: [PATCH] feat(scenes): Add does not contain for Calendar action --- front/src/config/i18n/de.json | 1 + front/src/config/i18n/en.json | 1 + front/src/config/i18n/fr.json | 1 + .../actions/CalendarIsEventRunning.jsx | 3 + .../calendar.findCurrentlyRunningEvent.js | 3 +- server/lib/scene/scene.actions.js | 15 ++- server/models/scene.js | 1 + .../test/lib/calendar/calendar.event.test.js | 11 ++ .../scene.action.isEventRunnning.test.js | 126 +++++++++++++++++- 9 files changed, 159 insertions(+), 3 deletions(-) diff --git a/front/src/config/i18n/de.json b/front/src/config/i18n/de.json index e5d4902a58..e23106481e 100644 --- a/front/src/config/i18n/de.json +++ b/front/src/config/i18n/de.json @@ -2223,6 +2223,7 @@ "nameLabel": "Wenn der Name", "isExactly": "genau ist", "contains": "enthält", + "doesNotContain": "enthält nicht", "startsWith": "beginnt mit", "endsWith": "endet mit", "hasAnyName": "einen beliebigen Namen hat", diff --git a/front/src/config/i18n/en.json b/front/src/config/i18n/en.json index 7de43c0f2a..45597d520e 100644 --- a/front/src/config/i18n/en.json +++ b/front/src/config/i18n/en.json @@ -2223,6 +2223,7 @@ "nameLabel": "If the name", "isExactly": "is exactly", "contains": "contains", + "doesNotContain": "does not contain", "startsWith": "starts with", "endsWith": "ends with", "hasAnyName": "has any name", diff --git a/front/src/config/i18n/fr.json b/front/src/config/i18n/fr.json index 6674e794d6..ab36d39457 100644 --- a/front/src/config/i18n/fr.json +++ b/front/src/config/i18n/fr.json @@ -2223,6 +2223,7 @@ "nameLabel": "Si le nom", "isExactly": "est exactement", "contains": "contient", + "doesNotContain": "ne contient pas", "startsWith": "commence par", "endsWith": "finit par", "hasAnyName": "a n'importe quel nom", diff --git a/front/src/routes/scene/edit-scene/actions/CalendarIsEventRunning.jsx b/front/src/routes/scene/edit-scene/actions/CalendarIsEventRunning.jsx index d4d662165e..44c6f15ca9 100644 --- a/front/src/routes/scene/edit-scene/actions/CalendarIsEventRunning.jsx +++ b/front/src/routes/scene/edit-scene/actions/CalendarIsEventRunning.jsx @@ -217,6 +217,9 @@ class CheckTime extends Component { + diff --git a/server/lib/calendar/calendar.findCurrentlyRunningEvent.js b/server/lib/calendar/calendar.findCurrentlyRunningEvent.js index 52f8ef3aa3..fcf701fb3f 100644 --- a/server/lib/calendar/calendar.findCurrentlyRunningEvent.js +++ b/server/lib/calendar/calendar.findCurrentlyRunningEvent.js @@ -41,8 +41,9 @@ async function findCurrentlyRunningEvent(calendars, calendarEventNameComparator, }; // eslint-disable-next-line default-case switch (calendarEventNameComparator) { - // do nothing in that case + // do nothing in that cases case 'has-any-name': + case 'does-not-contains': break; case 'is-exactly': // @ts-ignore diff --git a/server/lib/scene/scene.actions.js b/server/lib/scene/scene.actions.js index 6f214b803c..d6c678fbcb 100644 --- a/server/lib/scene/scene.actions.js +++ b/server/lib/scene/scene.actions.js @@ -441,12 +441,25 @@ const actionsFunc = { }, [ACTIONS.CALENDAR.IS_EVENT_RUNNING]: async (self, action, scope, columnIndex, rowIndex) => { // find if one event match the condition - const events = await self.calendar.findCurrentlyRunningEvent( + let events = await self.calendar.findCurrentlyRunningEvent( action.calendars, action.calendar_event_name_comparator, action.calendar_event_name, ); + // manage special does not contain condition + if (action.calendar_event_name_comparator === 'does-not-contain') { + const eventMatchingDoesNotContain = events.filter((event) => event.name.includes(action.calendar_event_name)); + const eventNotMatchingDoesNotContain = events.filter((event) => !event.name.includes(action.calendar_event_name)); + // if at least an event is found not containing the word, we want to stop the scene + if (eventMatchingDoesNotContain.length > 0) { + events = []; + } else { + // else we use the other events + events = eventNotMatchingDoesNotContain; + } + } + const atLeastOneEventFound = events.length > 0; // If one event was found, and the scene should be stopped in that case if (atLeastOneEventFound && action.stop_scene_if_event_found === true) { diff --git a/server/models/scene.js b/server/models/scene.js index 7c91a90c7b..b05267d90b 100644 --- a/server/models/scene.js +++ b/server/models/scene.js @@ -33,6 +33,7 @@ const actionSchema = Joi.array().items( calendar_event_name_comparator: Joi.string().valid( 'is-exactly', 'contains', + 'does-not-contain', 'starts-with', 'ends-with', 'has-any-name', diff --git a/server/test/lib/calendar/calendar.event.test.js b/server/test/lib/calendar/calendar.event.test.js index a4d1bc40a3..7a9e3e73c4 100644 --- a/server/test/lib/calendar/calendar.event.test.js +++ b/server/test/lib/calendar/calendar.event.test.js @@ -302,6 +302,17 @@ describe('calendar.findCurrentlyRunningEvent', () => { const eventsId = events.map((e) => e.id); expect(eventsId).deep.equal(['a2b57b0a-7148-4961-8540-e493104bfd7c']); }); + it('should find event in calendar - does not contain (rule is inside scene code)', async () => { + await calendar.createEvent('test-calendar', { + id: 'a2b57b0a-7148-4961-8540-e493104bfd7c', + name: 'my test event', + start: startDate, + end: endDate, + }); + const events = await calendar.findCurrentlyRunningEvent(['test-calendar'], 'does-not-contain', 'random'); + const eventsId = events.map((e) => e.id); + expect(eventsId).deep.equal(['a2b57b0a-7148-4961-8540-e493104bfd7c']); + }); it('should find event in calendar - starts-with', async () => { await calendar.createEvent('test-calendar', { id: 'a2b57b0a-7148-4961-8540-e493104bfd7c', diff --git a/server/test/lib/scene/actions/scene.action.isEventRunnning.test.js b/server/test/lib/scene/actions/scene.action.isEventRunnning.test.js index 45f5c12474..a85c326e6d 100644 --- a/server/test/lib/scene/actions/scene.action.isEventRunnning.test.js +++ b/server/test/lib/scene/actions/scene.action.isEventRunnning.test.js @@ -14,7 +14,7 @@ const Calendar = require('../../../../lib/calendar'); const event = new EventEmitter(); describe('scene.action.isEventRunning', () => { - const calendar = new Calendar(); + let calendar = new Calendar(); let clock; const now = new Date(); const startDate = dayjs(now) @@ -24,6 +24,7 @@ describe('scene.action.isEventRunning', () => { .add(45, 'minute') .toDate(); beforeEach(async () => { + calendar = new Calendar(); clock = useFakeTimers(now); }); afterEach(() => { @@ -236,4 +237,127 @@ describe('scene.action.isEventRunning', () => { await chaiAssert.isRejected(promise, AbortScene); assert.notCalled(message.sendToUser); }); + it('should execute condition is-event-running (does-not-contain), and send message because condition is true', async () => { + const stateManager = new StateManager(event); + const message = { + sendToUser: fake.resolves(null), + }; + const scope = {}; + await calendar.createEvent('test-calendar', { + id: 'a2b57b0a-7148-4961-8540-e493104bfd7c', + name: 'my test event', + description: 'my event description', + start: startDate, + end: endDate, + }); + await executeActions( + { stateManager, event, message, calendar }, + [ + [ + { + type: ACTIONS.CALENDAR.IS_EVENT_RUNNING, + calendars: ['test-calendar'], + calendar_event_name_comparator: 'does-not-contain', + calendar_event_name: 'rouge', + stop_scene_if_event_not_found: true, + stop_scene_if_event_found: false, + }, + ], + [ + { + type: ACTIONS.MESSAGE.SEND, + user: 'pepper', + text: 'hello', + }, + ], + ], + scope, + ); + assert.calledWith(message.sendToUser, 'pepper', 'hello'); + }); + it('should execute condition is-event-running (does-not-contain), and stop because condition is false', async () => { + const stateManager = new StateManager(event); + const message = { + sendToUser: fake.resolves(null), + }; + const scope = {}; + await calendar.createEvent('test-calendar', { + id: 'a2b57b0a-7148-4961-8540-e493104bfd7c', + name: 'my rouge event', + description: 'my event description', + start: startDate, + end: endDate, + }); + const promise = executeActions( + { stateManager, event, message, calendar }, + [ + [ + { + type: ACTIONS.CALENDAR.IS_EVENT_RUNNING, + calendars: ['test-calendar'], + calendar_event_name_comparator: 'does-not-contain', + calendar_event_name: 'rouge', + stop_scene_if_event_not_found: true, + stop_scene_if_event_found: false, + }, + ], + [ + { + type: ACTIONS.MESSAGE.SEND, + user: 'pepper', + text: 'hello', + }, + ], + ], + scope, + ); + await chaiAssert.isRejected(promise, AbortScene); + assert.notCalled(message.sendToUser); + }); + it('should execute condition is-event-running (does-not-contain), and stop because condition is false', async () => { + const stateManager = new StateManager(event); + const message = { + sendToUser: fake.resolves(null), + }; + const scope = {}; + await calendar.createEvent('test-calendar', { + id: 'a2b57b0a-7148-4961-8540-e493104bfd7c', + name: 'my rouge event', + description: 'my event description', + start: startDate, + end: endDate, + }); + await calendar.createEvent('test-calendar', { + id: '3cf3f147-96f6-484e-9655-e676fda4d578', + name: 'my second event', + description: 'my seconde event description', + start: startDate, + end: endDate, + }); + const promise = executeActions( + { stateManager, event, message, calendar }, + [ + [ + { + type: ACTIONS.CALENDAR.IS_EVENT_RUNNING, + calendars: ['test-calendar'], + calendar_event_name_comparator: 'does-not-contain', + calendar_event_name: 'rouge', + stop_scene_if_event_not_found: true, + stop_scene_if_event_found: false, + }, + ], + [ + { + type: ACTIONS.MESSAGE.SEND, + user: 'pepper', + text: 'hello', + }, + ], + ], + scope, + ); + await chaiAssert.isRejected(promise, AbortScene); + assert.notCalled(message.sendToUser); + }); });