diff --git a/apps/meteor/app/api/server/v1/calendar.ts b/apps/meteor/app/api/server/v1/calendar.ts index 5eff639a80f5e..907c3b2c566bf 100644 --- a/apps/meteor/app/api/server/v1/calendar.ts +++ b/apps/meteor/app/api/server/v1/calendar.ts @@ -1,5 +1,6 @@ import { Calendar } from '@rocket.chat/core-services'; import { + isCalendarEventSearchProps, isCalendarEventListProps, isCalendarEventCreateProps, isCalendarEventImportProps, @@ -25,6 +26,25 @@ API.v1.addRoute( }, ); +API.v1.addRoute( + 'calendar-events.search', + { + authRequired: true, + validateParams: isCalendarEventSearchProps, + rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 1000 }, + }, + { + async get() { + const { userId } = this; + const { text } = this.queryParams; + + const data = await Calendar.searchBySubject(userId, text); + return API.v1.success({ data }); + }, + }, +); + + API.v1.addRoute( 'calendar-events.info', { authRequired: true, validateParams: isCalendarEventInfoProps, rateLimiterOptions: { numRequestsAllowed: 3, intervalTimeInMS: 1000 } }, @@ -142,4 +162,4 @@ API.v1.addRoute( return API.v1.success(); }, }, -); +); \ No newline at end of file diff --git a/apps/meteor/server/services/calendar/service.ts b/apps/meteor/server/services/calendar/service.ts index 0ddf9c9aec06f..30e687e06a187 100644 --- a/apps/meteor/server/services/calendar/service.ts +++ b/apps/meteor/server/services/calendar/service.ts @@ -99,6 +99,18 @@ export class CalendarService extends ServiceClassInternal implements ICalendarSe return event._id; } + public async searchBySubject(uid: IUser['_id'], text: string): Promise { + if (!text?.trim()) { + return []; + } + + return CalendarEvent.findBySubject(uid, text, { + limit: 50, + }).toArray(); + } + + + public async get(eventId: ICalendarEvent['_id']): Promise { return CalendarEvent.findOne({ _id: eventId }); } diff --git a/packages/core-services/src/types/ICalendarService.ts b/packages/core-services/src/types/ICalendarService.ts index a7744ddd7c12d..d29200ff31849 100644 --- a/packages/core-services/src/types/ICalendarService.ts +++ b/packages/core-services/src/types/ICalendarService.ts @@ -7,9 +7,10 @@ export interface ICalendarService { import(data: Omit, 'notificationSent'>): Promise; get(eventId: ICalendarEvent['_id']): Promise; list(uid: IUser['_id'], date: Date): Promise; + searchBySubject(uid: IUser['_id'], text: string): Promise; update(eventId: ICalendarEvent['_id'], data: Partial): Promise; delete(eventId: ICalendarEvent['_id']): Promise; setupNextNotification(): Promise; setupNextStatusChange(): Promise; cancelUpcomingStatusChanges(uid: IUser['_id'], endTime?: Date): Promise; -} +} \ No newline at end of file diff --git a/packages/models/src/models/CalendarEvent.ts b/packages/models/src/models/CalendarEvent.ts index 87a64ee7d0944..00957bbd79e9a 100644 --- a/packages/models/src/models/CalendarEvent.ts +++ b/packages/models/src/models/CalendarEvent.ts @@ -4,6 +4,10 @@ import type { FindCursor, IndexDescription, Collection, Db, UpdateResult } from import { BaseRaw } from './BaseRaw'; +function escapeRegExp(text: string): string { + return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + export class CalendarEventRaw extends BaseRaw implements ICalendarEventModel { constructor(db: Db, trash?: Collection>) { super(db, 'calendar_event', trash); @@ -48,6 +52,20 @@ export class CalendarEventRaw extends BaseRaw implements ICalend ); } + public findBySubject(uid: IUser['_id'], text: string): FindCursor { + const escapedText = escapeRegExp(text); + + return this.find( + { + uid, + subject: { $regex: escapedText, $options: 'i' }, + }, + { + sort: { startTime: 1 }, + }, + ); + } + public async updateEvent( eventId: ICalendarEvent['_id'], { @@ -257,4 +275,4 @@ export class CalendarEventRaw extends BaseRaw implements ICalend }, ); } -} +} \ No newline at end of file diff --git a/packages/rest-typings/src/v1/calendar/CalendarEventSearchProps.ts b/packages/rest-typings/src/v1/calendar/CalendarEventSearchProps.ts new file mode 100644 index 0000000000000..cc4d7e854f078 --- /dev/null +++ b/packages/rest-typings/src/v1/calendar/CalendarEventSearchProps.ts @@ -0,0 +1,20 @@ +import type { JSONSchemaType } from 'ajv'; +import { ajv } from '../../Ajv'; + +export type CalendarEventSearchProps = { + text: string; +}; + +const calendarEventSearchPropsSchema: JSONSchemaType = { + type: 'object', + properties: { + text: { + type: 'string', + nullable: false, + }, + }, + required: ['text'], + additionalProperties: false, +}; + +export const isCalendarEventSearchProps = ajv.compile(calendarEventSearchPropsSchema); \ No newline at end of file diff --git a/packages/rest-typings/src/v1/calendar/index.ts b/packages/rest-typings/src/v1/calendar/index.ts index 7ef70ecf727b9..0ddefa8577f3e 100644 --- a/packages/rest-typings/src/v1/calendar/index.ts +++ b/packages/rest-typings/src/v1/calendar/index.ts @@ -6,6 +6,7 @@ import type { CalendarEventImportProps } from './CalendarEventImportProps'; import type { CalendarEventInfoProps } from './CalendarEventInfoProps'; import type { CalendarEventListProps } from './CalendarEventListProps'; import type { CalendarEventUpdateProps } from './CalendarEventUpdateProps'; +import type { CalendarEventSearchProps } from './CalendarEventSearchProps'; export * from './CalendarEventCreateProps'; export * from './CalendarEventDeleteProps'; @@ -13,6 +14,7 @@ export * from './CalendarEventImportProps'; export * from './CalendarEventInfoProps'; export * from './CalendarEventUpdateProps'; export * from './CalendarEventListProps'; +export * from './CalendarEventSearchProps'; export type CalendarEndpoints = { '/v1/calendar-events.create': { @@ -38,4 +40,8 @@ export type CalendarEndpoints = { '/v1/calendar-events.delete': { POST: (params: CalendarEventDeleteProps) => void; }; -}; + + '/v1/calendar-events.search': { + GET: (params: CalendarEventSearchProps) => { data: ICalendarEvent[] }; + }; +}; \ No newline at end of file