@flusys/nestjs-event-manager 7.0.2 → 8.0.0-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (103) hide show
  1. package/adapters/event-manager.adapter.d.ts +2 -2
  2. package/adapters/index.d.ts +1 -1
  3. package/config/index.d.ts +3 -3
  4. package/controllers/event.controller.d.ts +3 -3
  5. package/controllers/index.d.ts +1 -1
  6. package/docs/event-manager-swagger.config.d.ts +2 -2
  7. package/docs/index.d.ts +1 -1
  8. package/dtos/calendar-query.dto.d.ts +1 -1
  9. package/dtos/event.dto.d.ts +1 -1
  10. package/dtos/index.d.ts +3 -3
  11. package/entities/event-with-company.entity.d.ts +1 -1
  12. package/entities/index.d.ts +8 -8
  13. package/fesm/477.js +1798 -0
  14. package/fesm/833.js +840 -0
  15. package/fesm/adapters/index.js +884 -1
  16. package/fesm/config/index.js +87 -3
  17. package/fesm/controllers/index.js +365 -1
  18. package/fesm/docs/index.js +155 -2
  19. package/fesm/dtos/index.js +720 -3
  20. package/fesm/entities/index.js +350 -11
  21. package/fesm/index.js +121 -9
  22. package/fesm/interfaces/index.js +16 -2
  23. package/fesm/modules/index.js +13 -1
  24. package/fesm/runtime.js +85 -0
  25. package/fesm/services/index.js +272 -4
  26. package/index.d.ts +9 -9
  27. package/interfaces/event-manager-module.interface.d.ts +3 -3
  28. package/interfaces/event.interface.d.ts +1 -1
  29. package/interfaces/index.d.ts +2 -2
  30. package/modules/event-manager.module.d.ts +2 -2
  31. package/modules/index.d.ts +1 -1
  32. package/package.json +29 -39
  33. package/services/event-manager-config.service.d.ts +3 -3
  34. package/services/event-manager-datasource.provider.d.ts +3 -3
  35. package/services/event-manager-helper.service.d.ts +3 -3
  36. package/services/event.service.d.ts +7 -7
  37. package/services/index.d.ts +4 -4
  38. package/cjs/adapters/event-manager.adapter.js +0 -65
  39. package/cjs/adapters/event-manager.adapter.spec.js +0 -104
  40. package/cjs/adapters/index.js +0 -18
  41. package/cjs/config/event-actions.js +0 -31
  42. package/cjs/config/event-manager.constants.js +0 -13
  43. package/cjs/config/index.js +0 -20
  44. package/cjs/config/message-keys.js +0 -27
  45. package/cjs/controllers/event.controller.js +0 -190
  46. package/cjs/controllers/event.controller.spec.js +0 -156
  47. package/cjs/controllers/index.js +0 -18
  48. package/cjs/docs/event-manager-swagger.config.js +0 -125
  49. package/cjs/docs/event-manager-swagger.config.spec.js +0 -65
  50. package/cjs/docs/index.js +0 -21
  51. package/cjs/dtos/calendar-query.dto.js +0 -212
  52. package/cjs/dtos/event-participant.dto.js +0 -181
  53. package/cjs/dtos/event.dto.js +0 -343
  54. package/cjs/dtos/index.js +0 -20
  55. package/cjs/entities/event-participant.entity.js +0 -89
  56. package/cjs/entities/event-with-company.entity.js +0 -56
  57. package/cjs/entities/event.entity.js +0 -156
  58. package/cjs/entities/index.js +0 -55
  59. package/cjs/entities/index.spec.js +0 -18
  60. package/cjs/index.js +0 -27
  61. package/cjs/interfaces/event-manager-module.interface.js +0 -4
  62. package/cjs/interfaces/event.interface.js +0 -4
  63. package/cjs/interfaces/index.js +0 -19
  64. package/cjs/modules/event-manager.module.js +0 -132
  65. package/cjs/modules/event-manager.module.spec.js +0 -146
  66. package/cjs/modules/index.js +0 -18
  67. package/cjs/services/event-manager-config.service.js +0 -76
  68. package/cjs/services/event-manager-config.service.spec.js +0 -111
  69. package/cjs/services/event-manager-datasource.provider.js +0 -84
  70. package/cjs/services/event-manager-datasource.provider.spec.js +0 -198
  71. package/cjs/services/event-manager-helper.service.js +0 -235
  72. package/cjs/services/event-manager-helper.service.spec.js +0 -327
  73. package/cjs/services/event.service.js +0 -481
  74. package/cjs/services/event.service.spec.js +0 -929
  75. package/cjs/services/index.js +0 -21
  76. package/fesm/adapters/event-manager.adapter.js +0 -55
  77. package/fesm/adapters/event-manager.adapter.spec.js +0 -100
  78. package/fesm/config/event-actions.js +0 -10
  79. package/fesm/config/event-manager.constants.js +0 -3
  80. package/fesm/config/message-keys.js +0 -9
  81. package/fesm/controllers/event.controller.js +0 -180
  82. package/fesm/controllers/event.controller.spec.js +0 -152
  83. package/fesm/docs/event-manager-swagger.config.js +0 -118
  84. package/fesm/docs/event-manager-swagger.config.spec.js +0 -61
  85. package/fesm/dtos/calendar-query.dto.js +0 -194
  86. package/fesm/dtos/event-participant.dto.js +0 -157
  87. package/fesm/dtos/event.dto.js +0 -322
  88. package/fesm/entities/event-participant.entity.js +0 -79
  89. package/fesm/entities/event-with-company.entity.js +0 -46
  90. package/fesm/entities/event.entity.js +0 -146
  91. package/fesm/entities/index.spec.js +0 -14
  92. package/fesm/interfaces/event-manager-module.interface.js +0 -3
  93. package/fesm/interfaces/event.interface.js +0 -1
  94. package/fesm/modules/event-manager.module.js +0 -122
  95. package/fesm/modules/event-manager.module.spec.js +0 -142
  96. package/fesm/services/event-manager-config.service.js +0 -66
  97. package/fesm/services/event-manager-config.service.spec.js +0 -107
  98. package/fesm/services/event-manager-datasource.provider.js +0 -74
  99. package/fesm/services/event-manager-datasource.provider.spec.js +0 -194
  100. package/fesm/services/event-manager-helper.service.js +0 -225
  101. package/fesm/services/event-manager-helper.service.spec.js +0 -323
  102. package/fesm/services/event.service.js +0 -471
  103. package/fesm/services/event.service.spec.js +0 -925
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", {
3
- value: true
4
- });
5
- _export_star(require("./event-manager-config.service"), exports);
6
- _export_star(require("./event-manager-datasource.provider"), exports);
7
- _export_star(require("./event-manager-helper.service"), exports);
8
- _export_star(require("./event.service"), exports);
9
- function _export_star(from, to) {
10
- Object.keys(from).forEach(function(k) {
11
- if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
12
- Object.defineProperty(to, k, {
13
- enumerable: true,
14
- get: function() {
15
- return from[k];
16
- }
17
- });
18
- }
19
- });
20
- return from;
21
- }
@@ -1,55 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- function _ts_param(paramIndex, decorator) {
24
- return function(target, key) {
25
- decorator(target, key, paramIndex);
26
- };
27
- }
28
- import { Injectable, Inject } from '@nestjs/common';
29
- import { EventManagerHelperService } from '../services/event-manager-helper.service';
30
- export class EventManagerAdapter {
31
- async createEvent(options) {
32
- return this.eventManagerHelper.createEvent(options);
33
- }
34
- async updateParticipantStatus(participantId, status) {
35
- await this.eventManagerHelper.updateParticipantStatus(participantId, status);
36
- }
37
- async getEventsForUser(userId, startDate, endDate, companyId) {
38
- return this.eventManagerHelper.getEventsForUser(userId, startDate.toISOString().split('T')[0], endDate.toISOString().split('T')[0], companyId);
39
- }
40
- async getEventById(eventId) {
41
- return this.eventManagerHelper.getEventById(eventId);
42
- }
43
- constructor(eventManagerHelper){
44
- _define_property(this, "eventManagerHelper", void 0);
45
- this.eventManagerHelper = eventManagerHelper;
46
- }
47
- }
48
- EventManagerAdapter = _ts_decorate([
49
- Injectable(),
50
- _ts_param(0, Inject(EventManagerHelperService)),
51
- _ts_metadata("design:type", Function),
52
- _ts_metadata("design:paramtypes", [
53
- typeof EventManagerHelperService === "undefined" ? Object : EventManagerHelperService
54
- ])
55
- ], EventManagerAdapter);
@@ -1,100 +0,0 @@
1
- import { ParticipantStatus } from '@flusys/nestjs-shared';
2
- import { EventManagerAdapter } from './event-manager.adapter';
3
- describe('EventManagerAdapter', ()=>{
4
- let adapter;
5
- let mockHelper;
6
- beforeEach(()=>{
7
- mockHelper = {
8
- createEvent: jest.fn(),
9
- updateParticipantStatus: jest.fn(),
10
- getEventsForUser: jest.fn(),
11
- getEventById: jest.fn()
12
- };
13
- adapter = new EventManagerAdapter(mockHelper);
14
- });
15
- describe('createEvent', ()=>{
16
- it('delegates to the helper service and returns its result', async ()=>{
17
- const options = {
18
- title: 'Sprint Planning',
19
- eventDate: '2026-04-03',
20
- startTime: '09:00',
21
- endTime: '10:00'
22
- };
23
- const expected = {
24
- id: 'event-uuid-1',
25
- title: 'Sprint Planning',
26
- description: null,
27
- eventDate: '2026-04-03',
28
- startTime: '09:00',
29
- endTime: '10:00',
30
- isAllDay: false,
31
- recurrenceType: 'none',
32
- recurrenceEndDate: null,
33
- weekDays: null,
34
- color: '#3B82F6',
35
- meetingLink: null,
36
- isActive: true,
37
- companyId: null,
38
- createdAt: new Date(),
39
- updatedAt: new Date()
40
- };
41
- mockHelper.createEvent.mockResolvedValue(expected);
42
- const result = await adapter.createEvent(options);
43
- expect(mockHelper.createEvent).toHaveBeenCalledWith(options);
44
- expect(result).toBe(expected);
45
- });
46
- it('propagates errors thrown by the helper service', async ()=>{
47
- mockHelper.createEvent.mockRejectedValue(new Error('boom'));
48
- await expect(adapter.createEvent({})).rejects.toThrow('boom');
49
- });
50
- });
51
- describe('updateParticipantStatus', ()=>{
52
- it('delegates to the helper service', async ()=>{
53
- mockHelper.updateParticipantStatus.mockResolvedValue(undefined);
54
- await adapter.updateParticipantStatus('participant-uuid-1', ParticipantStatus.ACCEPTED);
55
- expect(mockHelper.updateParticipantStatus).toHaveBeenCalledWith('participant-uuid-1', ParticipantStatus.ACCEPTED);
56
- });
57
- });
58
- describe('getEventsForUser', ()=>{
59
- it('converts Date range boundaries to YYYY-MM-DD strings before delegating', async ()=>{
60
- mockHelper.getEventsForUser.mockResolvedValue([]);
61
- const startDate = new Date('2026-04-01T00:00:00.000Z');
62
- const endDate = new Date('2026-04-30T23:59:59.000Z');
63
- await adapter.getEventsForUser('user-uuid-1', startDate, endDate, 'company-uuid-1');
64
- expect(mockHelper.getEventsForUser).toHaveBeenCalledWith('user-uuid-1', '2026-04-01', '2026-04-30', 'company-uuid-1');
65
- });
66
- it('forwards undefined companyId when not supplied', async ()=>{
67
- mockHelper.getEventsForUser.mockResolvedValue([]);
68
- const startDate = new Date('2026-04-01T00:00:00.000Z');
69
- const endDate = new Date('2026-04-02T00:00:00.000Z');
70
- await adapter.getEventsForUser('user-uuid-1', startDate, endDate);
71
- expect(mockHelper.getEventsForUser).toHaveBeenCalledWith('user-uuid-1', '2026-04-01', '2026-04-02', undefined);
72
- });
73
- it('returns the events resolved by the helper service', async ()=>{
74
- const events = [
75
- {
76
- id: 'event-uuid-1'
77
- }
78
- ];
79
- mockHelper.getEventsForUser.mockResolvedValue(events);
80
- const result = await adapter.getEventsForUser('user-uuid-1', new Date('2026-01-01T00:00:00.000Z'), new Date('2026-01-31T00:00:00.000Z'));
81
- expect(result).toBe(events);
82
- });
83
- });
84
- describe('getEventById', ()=>{
85
- it('returns the event resolved by the helper service', async ()=>{
86
- const event = {
87
- id: 'event-uuid-1'
88
- };
89
- mockHelper.getEventById.mockResolvedValue(event);
90
- const result = await adapter.getEventById('event-uuid-1');
91
- expect(mockHelper.getEventById).toHaveBeenCalledWith('event-uuid-1');
92
- expect(result).toBe(event);
93
- });
94
- it('returns null when the helper service finds no matching event', async ()=>{
95
- mockHelper.getEventById.mockResolvedValue(null);
96
- const result = await adapter.getEventById('missing-uuid');
97
- expect(result).toBeNull();
98
- });
99
- });
100
- });
@@ -1,10 +0,0 @@
1
- /**
2
- * Domain event actions published by the event manager module, alongside the CRUD
3
- * lifecycle actions every ApiService emits.
4
- */ export const EVENT_MANAGER_EVENT_ACTIONS = {
5
- PARTICIPANT_STATUS_CHANGED: 'participant-status-changed'
6
- };
7
- /** Every entity the event manager module publishes under */ export const EVENT_MANAGER_EVENT_ENTITIES = {
8
- EVENT: 'event'
9
- };
10
- export const EVENT_MANAGER_EVENT_MODULE = 'event-manager';
@@ -1,3 +0,0 @@
1
- /**
2
- * Event Manager Module Constants
3
- */ export const EVENT_MANAGER_MODULE_OPTIONS = 'EVENT_MANAGER_MODULE_OPTIONS';
@@ -1,9 +0,0 @@
1
- // ==================== EVENT MANAGER MODULE MESSAGE KEYS ====================
2
- export const EVENT_MESSAGES = {
3
- GET_ALL_SUCCESS: 'event.get.all.success',
4
- NOT_FOUND: 'event.not.found'
5
- };
6
- export const EVENT_PARTICIPANT_MESSAGES = {
7
- NOT_FOUND: 'event.participant.not.found',
8
- STATUS_UPDATE_SUCCESS: 'event.participant.status.update.success'
9
- };
@@ -1,180 +0,0 @@
1
- function _define_property(obj, key, value) {
2
- if (key in obj) {
3
- Object.defineProperty(obj, key, {
4
- value: value,
5
- enumerable: true,
6
- configurable: true,
7
- writable: true
8
- });
9
- } else {
10
- obj[key] = value;
11
- }
12
- return obj;
13
- }
14
- function _ts_decorate(decorators, target, key, desc) {
15
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
16
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
17
- else for(var i = decorators.length - 1; i >= 0; i--)if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
18
- return c > 3 && r && Object.defineProperty(target, key, r), r;
19
- }
20
- function _ts_metadata(k, v) {
21
- if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
22
- }
23
- function _ts_param(paramIndex, decorator) {
24
- return function(target, key) {
25
- decorator(target, key, paramIndex);
26
- };
27
- }
28
- import { createApiController } from '@flusys/nestjs-shared/classes';
29
- import { CurrentUser, RequirePermission } from '@flusys/nestjs-shared/decorators';
30
- import { ListResponseDto, SingleResponseDto } from '@flusys/nestjs-shared/dtos';
31
- import { JwtAuthGuard, PermissionGuard } from '@flusys/nestjs-shared/guards';
32
- import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
33
- import { Body, Controller, HttpCode, HttpStatus, Inject, Post, UseGuards } from '@nestjs/common';
34
- import { ApiBearerAuth, ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger';
35
- import { plainToInstance } from 'class-transformer';
36
- import { EVENT_MESSAGES, EVENT_PARTICIPANT_MESSAGES } from '../config';
37
- import { EVENT_PERMISSIONS } from '../docs';
38
- import { CalendarEventResponseDto, CalendarQueryDto, CreateEventDto, EventParticipantResponseDto, EventResponseDto, UpdateEventDto, UpdateParticipantStatusDto } from '../dtos';
39
- import { EventService } from '../services/event.service';
40
- export class EventController extends createApiController(CreateEventDto, UpdateEventDto, EventResponseDto, {
41
- entityName: 'event',
42
- security: {
43
- insert: {
44
- level: 'permission',
45
- permissions: [
46
- EVENT_PERMISSIONS.CREATE
47
- ]
48
- },
49
- insertMany: {
50
- level: 'permission',
51
- permissions: [
52
- EVENT_PERMISSIONS.CREATE
53
- ]
54
- },
55
- getById: {
56
- level: 'permission',
57
- permissions: [
58
- EVENT_PERMISSIONS.READ
59
- ]
60
- },
61
- getAll: {
62
- level: 'permission',
63
- permissions: [
64
- EVENT_PERMISSIONS.READ
65
- ]
66
- },
67
- update: {
68
- level: 'permission',
69
- permissions: [
70
- EVENT_PERMISSIONS.UPDATE
71
- ]
72
- },
73
- updateMany: {
74
- level: 'permission',
75
- permissions: [
76
- EVENT_PERMISSIONS.UPDATE
77
- ]
78
- },
79
- delete: {
80
- level: 'permission',
81
- permissions: [
82
- EVENT_PERMISSIONS.DELETE
83
- ]
84
- }
85
- }
86
- }) {
87
- async getCalendarEvents(dto, user) {
88
- const events = await this.eventService.getEventsForCalendarRange(dto, user);
89
- const plainOptions = {
90
- excludeExtraneousValues: true
91
- };
92
- const data = plainToInstance(CalendarEventResponseDto, events, plainOptions);
93
- return {
94
- success: true,
95
- message: 'Calendar events retrieved successfully',
96
- messageKey: EVENT_MESSAGES.GET_ALL_SUCCESS,
97
- data,
98
- meta: {
99
- total: data.length,
100
- page: 0,
101
- pageSize: data.length,
102
- count: data.length
103
- }
104
- };
105
- }
106
- async updateParticipantStatus(dto, user) {
107
- const participant = await this.eventService.updateParticipantStatus(dto, user);
108
- const plainOptions = {
109
- excludeExtraneousValues: true
110
- };
111
- return {
112
- success: true,
113
- message: 'Participant status updated successfully',
114
- messageKey: EVENT_PARTICIPANT_MESSAGES.STATUS_UPDATE_SUCCESS,
115
- data: plainToInstance(EventParticipantResponseDto, participant, plainOptions)
116
- };
117
- }
118
- constructor(eventService){
119
- super(eventService), _define_property(this, "eventService", void 0), this.eventService = eventService;
120
- }
121
- }
122
- _ts_decorate([
123
- Post('calendar'),
124
- UseGuards(JwtAuthGuard),
125
- ApiBearerAuth(),
126
- HttpCode(HttpStatus.OK),
127
- ApiOperation({
128
- summary: 'Get events for calendar range with recurrence expansion'
129
- }),
130
- ApiBody({
131
- type: CalendarQueryDto
132
- }),
133
- ApiResponse({
134
- status: 200,
135
- description: 'Calendar events retrieved successfully',
136
- type: ListResponseDto
137
- }),
138
- _ts_param(0, Body()),
139
- _ts_param(1, CurrentUser()),
140
- _ts_metadata("design:type", Function),
141
- _ts_metadata("design:paramtypes", [
142
- typeof CalendarQueryDto === "undefined" ? Object : CalendarQueryDto,
143
- typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
144
- ]),
145
- _ts_metadata("design:returntype", Promise)
146
- ], EventController.prototype, "getCalendarEvents", null);
147
- _ts_decorate([
148
- Post('update-participant-status'),
149
- UseGuards(JwtAuthGuard, PermissionGuard),
150
- ApiBearerAuth(),
151
- HttpCode(HttpStatus.OK),
152
- RequirePermission(EVENT_PERMISSIONS.UPDATE),
153
- ApiOperation({
154
- summary: 'Update participant status (accept/decline/tentative)'
155
- }),
156
- ApiBody({
157
- type: UpdateParticipantStatusDto
158
- }),
159
- ApiResponse({
160
- status: 200,
161
- type: SingleResponseDto
162
- }),
163
- _ts_param(0, Body()),
164
- _ts_param(1, CurrentUser()),
165
- _ts_metadata("design:type", Function),
166
- _ts_metadata("design:paramtypes", [
167
- typeof UpdateParticipantStatusDto === "undefined" ? Object : UpdateParticipantStatusDto,
168
- typeof ILoggedUserInfo === "undefined" ? Object : ILoggedUserInfo
169
- ]),
170
- _ts_metadata("design:returntype", Promise)
171
- ], EventController.prototype, "updateParticipantStatus", null);
172
- EventController = _ts_decorate([
173
- ApiTags('Events'),
174
- Controller('event-manager/event'),
175
- _ts_param(0, Inject(EventService)),
176
- _ts_metadata("design:type", Function),
177
- _ts_metadata("design:paramtypes", [
178
- typeof EventService === "undefined" ? Object : EventService
179
- ])
180
- ], EventController);
@@ -1,152 +0,0 @@
1
- import { NotFoundException } from '@nestjs/common';
2
- import { ParticipantStatus, RecurrenceType } from '@flusys/nestjs-shared/enums';
3
- import { buildMockUser } from '@test-utils/mocks/logged-user.mock';
4
- import { EVENT_MESSAGES, EVENT_PARTICIPANT_MESSAGES } from '../config';
5
- import { EventController } from './event.controller';
6
- function buildCalendarEvent(overrides = {}) {
7
- return {
8
- id: 'event-uuid-1',
9
- title: 'Sprint Planning',
10
- description: null,
11
- eventDate: '2026-04-03',
12
- startTime: '09:00',
13
- endTime: '10:00',
14
- isAllDay: false,
15
- recurrenceType: RecurrenceType.NONE,
16
- recurrenceEndDate: null,
17
- weekDays: null,
18
- color: '#3B82F6',
19
- meetingLink: null,
20
- isActive: true,
21
- createdAt: new Date('2026-01-01T00:00:00Z'),
22
- updatedAt: new Date('2026-01-01T00:00:00Z'),
23
- deletedAt: null,
24
- createdById: null,
25
- updatedById: null,
26
- deletedById: null,
27
- ...overrides
28
- };
29
- }
30
- describe('EventController', ()=>{
31
- let controller;
32
- let mockService;
33
- const user = buildMockUser();
34
- beforeEach(()=>{
35
- mockService = {
36
- getEventsForCalendarRange: jest.fn(),
37
- updateParticipantStatus: jest.fn()
38
- };
39
- controller = new EventController(mockService);
40
- });
41
- describe('getCalendarEvents', ()=>{
42
- it('returns a ListResponseDto shaped payload with meta reflecting the result count', async ()=>{
43
- const events = [
44
- buildCalendarEvent(),
45
- buildCalendarEvent({
46
- id: 'event-uuid-2'
47
- })
48
- ];
49
- mockService.getEventsForCalendarRange.mockResolvedValue(events);
50
- const dto = {
51
- startDate: '2026-04-01',
52
- endDate: '2026-04-30'
53
- };
54
- const result = await controller.getCalendarEvents(dto, user);
55
- expect(mockService.getEventsForCalendarRange).toHaveBeenCalledWith(dto, user);
56
- expect(result.success).toBe(true);
57
- expect(result.messageKey).toBe(EVENT_MESSAGES.GET_ALL_SUCCESS);
58
- expect(result.data).toHaveLength(2);
59
- expect(result.meta).toEqual({
60
- total: 2,
61
- page: 0,
62
- pageSize: 2,
63
- count: 2
64
- });
65
- });
66
- it('returns an empty payload with zeroed meta when no events are found', async ()=>{
67
- mockService.getEventsForCalendarRange.mockResolvedValue([]);
68
- const dto = {
69
- startDate: '2026-04-01',
70
- endDate: '2026-04-01'
71
- };
72
- const result = await controller.getCalendarEvents(dto, user);
73
- expect(result.data).toEqual([]);
74
- expect(result.meta).toEqual({
75
- total: 0,
76
- page: 0,
77
- pageSize: 0,
78
- count: 0
79
- });
80
- });
81
- it('serializes events through CalendarEventResponseDto, exposing only declared fields', async ()=>{
82
- const events = [
83
- buildCalendarEvent({
84
- originalEventId: 'event-uuid-1',
85
- isRecurrenceInstance: true,
86
- // extraneous field that must be stripped by excludeExtraneousValues
87
- ...{
88
- secretInternalField: 'should-not-leak'
89
- }
90
- })
91
- ];
92
- mockService.getEventsForCalendarRange.mockResolvedValue(events);
93
- const dto = {
94
- startDate: '2026-04-01',
95
- endDate: '2026-04-30'
96
- };
97
- const result = await controller.getCalendarEvents(dto, user);
98
- const [firstEvent] = result.data ?? [];
99
- expect(firstEvent).not.toHaveProperty('secretInternalField');
100
- expect(firstEvent).toMatchObject({
101
- id: 'event-uuid-1',
102
- originalEventId: 'event-uuid-1',
103
- isRecurrenceInstance: true
104
- });
105
- });
106
- it('propagates errors raised by the service', async ()=>{
107
- mockService.getEventsForCalendarRange.mockRejectedValue(new Error('db down'));
108
- const dto = {
109
- startDate: '2026-04-01',
110
- endDate: '2026-04-30'
111
- };
112
- await expect(controller.getCalendarEvents(dto, user)).rejects.toThrow('db down');
113
- });
114
- });
115
- describe('updateParticipantStatus', ()=>{
116
- it('returns a SingleResponseDto shaped payload on success', async ()=>{
117
- const participant = {
118
- id: 'participant-uuid-1',
119
- eventId: 'event-uuid-1',
120
- userId: user.id,
121
- status: ParticipantStatus.ACCEPTED,
122
- isOrganizer: false,
123
- createdAt: new Date('2026-01-01T00:00:00Z'),
124
- updatedAt: new Date('2026-01-02T00:00:00Z')
125
- };
126
- mockService.updateParticipantStatus.mockResolvedValue(participant);
127
- const dto = {
128
- id: 'participant-uuid-1',
129
- status: ParticipantStatus.ACCEPTED
130
- };
131
- const result = await controller.updateParticipantStatus(dto, user);
132
- expect(mockService.updateParticipantStatus).toHaveBeenCalledWith(dto, user);
133
- expect(result.success).toBe(true);
134
- expect(result.messageKey).toBe(EVENT_PARTICIPANT_MESSAGES.STATUS_UPDATE_SUCCESS);
135
- expect(result.data).toMatchObject({
136
- id: 'participant-uuid-1',
137
- status: ParticipantStatus.ACCEPTED
138
- });
139
- });
140
- it('propagates a NotFoundException raised by the service', async ()=>{
141
- mockService.updateParticipantStatus.mockRejectedValue(new NotFoundException({
142
- message: 'Event participant not found',
143
- messageKey: EVENT_PARTICIPANT_MESSAGES.NOT_FOUND
144
- }));
145
- const dto = {
146
- id: 'missing-uuid',
147
- status: ParticipantStatus.DECLINED
148
- };
149
- await expect(controller.updateParticipantStatus(dto, user)).rejects.toThrow(NotFoundException);
150
- });
151
- });
152
- });
@@ -1,118 +0,0 @@
1
- /** Schema properties to exclude when company feature is disabled */ const COMPANY_SCHEMA_EXCLUSIONS = [
2
- {
3
- schemaName: 'EventResponseDto',
4
- properties: [
5
- 'companyId'
6
- ]
7
- },
8
- {
9
- schemaName: 'CalendarEventResponseDto',
10
- properties: [
11
- 'companyId'
12
- ]
13
- }
14
- ];
15
- function buildDescription(enableCompanyFeature, isMultiTenant) {
16
- const multiTenantNote = isMultiTenant ? `\n> **Multi-Tenant Mode**: Include \`x-tenant-id\` header to target a specific tenant database.\n` : '';
17
- const companyFeatures = enableCompanyFeature ? '\n- **Company-scoped events**' : '';
18
- const companyIsolation = enableCompanyFeature ? '\n- **Company isolation**' : '';
19
- const multiTenantSection = enableCompanyFeature ? `
20
- ## Multi-Tenant Support
21
-
22
- Events are automatically isolated by:
23
- - **Company ID**: Events belong to specific companies
24
- - **User Context**: Participants are company members
25
-
26
- All queries automatically filter by current user's company context.
27
- ` : '';
28
- return `
29
- # Event Manager API
30
- ${multiTenantNote}
31
- Calendar event management with recurrence support${enableCompanyFeature ? ' and multi-tenant capabilities' : ''}.
32
-
33
- ## Features
34
-
35
- ### 📅 Event Management
36
- - Create, read, update, delete events
37
- - Color-coded events
38
- - All-day event support${companyFeatures}
39
-
40
- ### 🔄 Recurrence Support
41
- - **None**: Single occurrence
42
- - **Daily**: Every day
43
- - **Weekly**: Specific days of the week
44
- - **Biweekly**: Every two weeks
45
- - **Monthly**: Same day each month
46
-
47
- ### 👥 Participant Management
48
- - Status tracking (pending, accepted, declined, tentative)
49
- - Organizer designation
50
-
51
- ### 📆 Calendar Queries
52
- - Date range queries
53
- - Automatic recurrence expansion
54
- - Filter by user participation
55
- - Include/exclude participants in response
56
-
57
- ### 🔒 Security
58
- - JWT authentication required
59
- - Permission-based access control${companyIsolation}
60
-
61
- ## Recurrence Patterns
62
-
63
- ### Weekly Recurrence
64
- Use the \`weekDays\` array to specify which days repeat:
65
- - \`"SUN"\` = Sunday
66
- - \`"MON"\` = Monday
67
- - \`"TUE"\` = Tuesday
68
- - \`"WED"\` = Wednesday
69
- - \`"THU"\` = Thursday
70
- - \`"FRI"\` = Friday
71
- - \`"SAT"\` = Saturday
72
-
73
- Example: \`{ "weekDays": ["MON", "WED", "FRI"] }\` = Monday, Wednesday, Friday
74
-
75
- ### Recurrence Expansion
76
- Calendar queries automatically expand recurring events into individual instances.
77
- Each instance includes:
78
- - \`isRecurrenceInstance: true\`
79
- - \`originalEventId\`: Reference to parent event
80
- ${multiTenantSection}
81
- ## API Endpoints
82
-
83
- All endpoints use POST method (RPC style):
84
-
85
- ### Events
86
- - \`/event-manager/event/insert\` - Create event
87
- - \`/event-manager/event/get/:id\` - Get by ID
88
- - \`/event-manager/event/get-all\` - List with pagination
89
- - \`/event-manager/event/update\` - Update event
90
- - \`/event-manager/event/delete\` - Soft delete
91
- - \`/event-manager/event/calendar\` - Calendar query with expansion
92
-
93
- ### Participant Operations (via Event endpoints)
94
- - \`/event-manager/event/update-participant-status\` - Update status
95
-
96
- ## Getting Started
97
-
98
- 1. **Authenticate**: Obtain JWT token from /auth/login
99
- 2. **Create Event**: POST to /event-manager/event/insert
100
- 3. **Query Calendar**: POST to /event-manager/event/calendar with date range
101
- `;
102
- }
103
- /**
104
- * Event Manager Module Swagger Configuration
105
- * Use this with setupSwaggerDocs() from @flusys/nestjs-core
106
- */ export function eventManagerSwaggerConfig(bootstrapConfig) {
107
- const enableCompanyFeature = bootstrapConfig?.enableCompanyFeature ?? true;
108
- const isMultiTenant = bootstrapConfig?.databaseMode === 'multi-tenant';
109
- return {
110
- title: 'Event Manager API',
111
- description: buildDescription(enableCompanyFeature, isMultiTenant),
112
- version: '1.0',
113
- path: 'api/docs/event-manager',
114
- bearerAuth: true,
115
- excludeTags: [],
116
- excludeSchemaProperties: enableCompanyFeature ? undefined : COMPANY_SCHEMA_EXCLUSIONS
117
- };
118
- }