@flusys/nestjs-event-manager 7.1.0 → 8.0.0
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.
- package/adapters/event-manager.adapter.d.ts +2 -2
- package/adapters/index.d.ts +1 -1
- package/config/index.d.ts +3 -3
- package/controllers/event.controller.d.ts +3 -3
- package/controllers/index.d.ts +1 -1
- package/docs/event-manager-swagger.config.d.ts +2 -2
- package/docs/index.d.ts +1 -1
- package/dtos/calendar-query.dto.d.ts +1 -1
- package/dtos/event.dto.d.ts +1 -1
- package/dtos/index.d.ts +3 -3
- package/entities/event-with-company.entity.d.ts +1 -1
- package/entities/index.d.ts +8 -8
- package/fesm/477.js +1798 -0
- package/fesm/833.js +840 -0
- package/fesm/adapters/index.js +884 -1
- package/fesm/config/index.js +87 -3
- package/fesm/controllers/index.js +365 -1
- package/fesm/docs/index.js +155 -2
- package/fesm/dtos/index.js +720 -3
- package/fesm/entities/index.js +350 -11
- package/fesm/index.js +121 -9
- package/fesm/interfaces/index.js +16 -2
- package/fesm/modules/index.js +13 -1
- package/fesm/runtime.js +85 -0
- package/fesm/services/index.js +272 -4
- package/index.d.ts +9 -9
- package/interfaces/event-manager-module.interface.d.ts +3 -3
- package/interfaces/event.interface.d.ts +1 -1
- package/interfaces/index.d.ts +2 -2
- package/modules/event-manager.module.d.ts +2 -2
- package/modules/index.d.ts +1 -1
- package/package.json +29 -39
- package/services/event-manager-config.service.d.ts +3 -3
- package/services/event-manager-datasource.provider.d.ts +3 -3
- package/services/event-manager-helper.service.d.ts +3 -3
- package/services/event.service.d.ts +7 -7
- package/services/index.d.ts +4 -4
- package/cjs/adapters/event-manager.adapter.js +0 -65
- package/cjs/adapters/event-manager.adapter.spec.js +0 -104
- package/cjs/adapters/index.js +0 -18
- package/cjs/config/event-actions.js +0 -31
- package/cjs/config/event-manager.constants.js +0 -13
- package/cjs/config/index.js +0 -20
- package/cjs/config/message-keys.js +0 -27
- package/cjs/controllers/event.controller.js +0 -190
- package/cjs/controllers/event.controller.spec.js +0 -156
- package/cjs/controllers/index.js +0 -18
- package/cjs/docs/event-manager-swagger.config.js +0 -125
- package/cjs/docs/event-manager-swagger.config.spec.js +0 -65
- package/cjs/docs/index.js +0 -21
- package/cjs/dtos/calendar-query.dto.js +0 -212
- package/cjs/dtos/event-participant.dto.js +0 -181
- package/cjs/dtos/event.dto.js +0 -343
- package/cjs/dtos/index.js +0 -20
- package/cjs/entities/event-participant.entity.js +0 -89
- package/cjs/entities/event-with-company.entity.js +0 -56
- package/cjs/entities/event.entity.js +0 -156
- package/cjs/entities/index.js +0 -55
- package/cjs/entities/index.spec.js +0 -18
- package/cjs/index.js +0 -27
- package/cjs/interfaces/event-manager-module.interface.js +0 -4
- package/cjs/interfaces/event.interface.js +0 -4
- package/cjs/interfaces/index.js +0 -19
- package/cjs/modules/event-manager.module.js +0 -132
- package/cjs/modules/event-manager.module.spec.js +0 -146
- package/cjs/modules/index.js +0 -18
- package/cjs/services/event-manager-config.service.js +0 -76
- package/cjs/services/event-manager-config.service.spec.js +0 -111
- package/cjs/services/event-manager-datasource.provider.js +0 -84
- package/cjs/services/event-manager-datasource.provider.spec.js +0 -198
- package/cjs/services/event-manager-helper.service.js +0 -235
- package/cjs/services/event-manager-helper.service.spec.js +0 -327
- package/cjs/services/event.service.js +0 -481
- package/cjs/services/event.service.spec.js +0 -929
- package/cjs/services/index.js +0 -21
- package/fesm/adapters/event-manager.adapter.js +0 -55
- package/fesm/adapters/event-manager.adapter.spec.js +0 -100
- package/fesm/config/event-actions.js +0 -10
- package/fesm/config/event-manager.constants.js +0 -3
- package/fesm/config/message-keys.js +0 -9
- package/fesm/controllers/event.controller.js +0 -180
- package/fesm/controllers/event.controller.spec.js +0 -152
- package/fesm/docs/event-manager-swagger.config.js +0 -118
- package/fesm/docs/event-manager-swagger.config.spec.js +0 -61
- package/fesm/dtos/calendar-query.dto.js +0 -194
- package/fesm/dtos/event-participant.dto.js +0 -157
- package/fesm/dtos/event.dto.js +0 -322
- package/fesm/entities/event-participant.entity.js +0 -79
- package/fesm/entities/event-with-company.entity.js +0 -46
- package/fesm/entities/event.entity.js +0 -146
- package/fesm/entities/index.spec.js +0 -14
- package/fesm/interfaces/event-manager-module.interface.js +0 -3
- package/fesm/interfaces/event.interface.js +0 -1
- package/fesm/modules/event-manager.module.js +0 -122
- package/fesm/modules/event-manager.module.spec.js +0 -142
- package/fesm/services/event-manager-config.service.js +0 -66
- package/fesm/services/event-manager-config.service.spec.js +0 -107
- package/fesm/services/event-manager-datasource.provider.js +0 -74
- package/fesm/services/event-manager-datasource.provider.spec.js +0 -194
- package/fesm/services/event-manager-helper.service.js +0 -225
- package/fesm/services/event-manager-helper.service.spec.js +0 -323
- package/fesm/services/event.service.js +0 -471
- package/fesm/services/event.service.spec.js +0 -925
package/cjs/services/index.js
DELETED
|
@@ -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,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
|
-
}
|