@flusys/nestjs-notification 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.
- package/adapters/index.d.ts +1 -1
- package/adapters/notification.adapter.d.ts +2 -2
- package/config/index.d.ts +3 -3
- package/controllers/index.d.ts +1 -1
- package/controllers/notification.controller.d.ts +4 -4
- package/docs/index.d.ts +1 -1
- package/docs/notification-swagger.config.d.ts +2 -2
- package/dtos/index.d.ts +1 -1
- package/entities/index.d.ts +5 -5
- package/entities/notification-with-company.entity.d.ts +1 -1
- package/fesm/244.js +816 -0
- package/fesm/446.js +939 -0
- package/fesm/adapters/index.js +79 -1
- package/fesm/config/index.js +84 -3
- package/fesm/controllers/index.js +13 -1
- package/fesm/docs/index.js +112 -1
- package/fesm/dtos/index.js +311 -1
- package/fesm/entities/index.js +211 -8
- package/fesm/gateways/index.js +286 -1
- package/fesm/index.js +467 -10
- package/fesm/interfaces/index.js +16 -2
- package/fesm/modules/index.js +269 -1
- package/fesm/runtime.js +85 -0
- package/fesm/services/index.js +357 -4
- package/gateways/index.d.ts +1 -1
- package/gateways/notification.gateway.d.ts +2 -2
- package/index.d.ts +10 -10
- package/interfaces/index.d.ts +2 -2
- package/interfaces/notification-module.interface.d.ts +3 -3
- package/interfaces/notification.interface.d.ts +1 -1
- package/modules/index.d.ts +1 -1
- package/modules/notification.module.d.ts +2 -2
- package/package.json +33 -44
- package/services/index.d.ts +4 -4
- package/services/notification-config.service.d.ts +3 -3
- package/services/notification-datasource.provider.d.ts +3 -3
- package/services/notification-helper.service.d.ts +5 -5
- package/services/notification.service.d.ts +8 -8
- package/cjs/adapters/index.js +0 -18
- package/cjs/adapters/notification.adapter.js +0 -62
- package/cjs/adapters/notification.adapter.spec.js +0 -73
- package/cjs/config/event-actions.js +0 -33
- package/cjs/config/index.js +0 -20
- package/cjs/config/message-keys.js +0 -19
- package/cjs/config/notification.constants.js +0 -11
- package/cjs/controllers/index.js +0 -18
- package/cjs/controllers/notification.controller.js +0 -299
- package/cjs/controllers/notification.controller.spec.js +0 -214
- package/cjs/docs/index.js +0 -18
- package/cjs/docs/notification-swagger.config.js +0 -97
- package/cjs/docs/notification-swagger.config.spec.js +0 -68
- package/cjs/dtos/index.js +0 -18
- package/cjs/dtos/notification.dto.js +0 -295
- package/cjs/entities/index.js +0 -48
- package/cjs/entities/index.spec.js +0 -13
- package/cjs/entities/notification-with-company.entity.js +0 -57
- package/cjs/entities/notification.entity.js +0 -112
- package/cjs/gateways/index.js +0 -18
- package/cjs/gateways/notification.gateway.js +0 -172
- package/cjs/gateways/notification.gateway.spec.js +0 -359
- package/cjs/index.js +0 -28
- package/cjs/interfaces/index.js +0 -19
- package/cjs/interfaces/notification-module.interface.js +0 -4
- package/cjs/interfaces/notification.interface.js +0 -4
- package/cjs/modules/index.js +0 -18
- package/cjs/modules/notification.module.js +0 -139
- package/cjs/modules/notification.module.spec.js +0 -152
- package/cjs/services/index.js +0 -21
- package/cjs/services/notification-config.service.js +0 -69
- package/cjs/services/notification-config.service.spec.js +0 -113
- package/cjs/services/notification-datasource.provider.js +0 -133
- package/cjs/services/notification-datasource.provider.spec.js +0 -136
- package/cjs/services/notification-helper.service.js +0 -197
- package/cjs/services/notification-helper.service.spec.js +0 -392
- package/cjs/services/notification.service.js +0 -281
- package/cjs/services/notification.service.spec.js +0 -560
- package/fesm/adapters/notification.adapter.js +0 -52
- package/fesm/adapters/notification.adapter.spec.js +0 -69
- package/fesm/config/event-actions.js +0 -12
- package/fesm/config/message-keys.js +0 -9
- package/fesm/config/notification.constants.js +0 -1
- package/fesm/controllers/notification.controller.js +0 -289
- package/fesm/controllers/notification.controller.spec.js +0 -210
- package/fesm/docs/notification-swagger.config.js +0 -87
- package/fesm/docs/notification-swagger.config.spec.js +0 -64
- package/fesm/dtos/notification.dto.js +0 -262
- package/fesm/entities/index.spec.js +0 -9
- package/fesm/entities/notification-with-company.entity.js +0 -47
- package/fesm/entities/notification.entity.js +0 -102
- package/fesm/gateways/notification.gateway.js +0 -162
- package/fesm/gateways/notification.gateway.spec.js +0 -355
- package/fesm/interfaces/notification-module.interface.js +0 -1
- package/fesm/interfaces/notification.interface.js +0 -1
- package/fesm/modules/notification.module.js +0 -129
- package/fesm/modules/notification.module.spec.js +0 -148
- package/fesm/services/notification-config.service.js +0 -59
- package/fesm/services/notification-config.service.spec.js +0 -109
- package/fesm/services/notification-datasource.provider.js +0 -82
- package/fesm/services/notification-datasource.provider.spec.js +0 -132
- package/fesm/services/notification-helper.service.js +0 -187
- package/fesm/services/notification-helper.service.spec.js +0 -388
- package/fesm/services/notification.service.js +0 -271
- package/fesm/services/notification.service.spec.js +0 -556
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
Object.defineProperty(exports, "NotificationGateway", {
|
|
6
|
-
enumerable: true,
|
|
7
|
-
get: function() {
|
|
8
|
-
return NotificationGateway;
|
|
9
|
-
}
|
|
10
|
-
});
|
|
11
|
-
const _common = require("@nestjs/common");
|
|
12
|
-
const _jwt = require("@nestjs/jwt");
|
|
13
|
-
const _websockets = require("@nestjs/websockets");
|
|
14
|
-
const _socketio = require("socket.io");
|
|
15
|
-
const _notificationconfigservice = require("../services/notification-config.service");
|
|
16
|
-
function _define_property(obj, key, value) {
|
|
17
|
-
if (key in obj) {
|
|
18
|
-
Object.defineProperty(obj, key, {
|
|
19
|
-
value: value,
|
|
20
|
-
enumerable: true,
|
|
21
|
-
configurable: true,
|
|
22
|
-
writable: true
|
|
23
|
-
});
|
|
24
|
-
} else {
|
|
25
|
-
obj[key] = value;
|
|
26
|
-
}
|
|
27
|
-
return obj;
|
|
28
|
-
}
|
|
29
|
-
function _ts_decorate(decorators, target, key, desc) {
|
|
30
|
-
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
31
|
-
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
32
|
-
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;
|
|
33
|
-
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
34
|
-
}
|
|
35
|
-
function _ts_metadata(k, v) {
|
|
36
|
-
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
37
|
-
}
|
|
38
|
-
function _ts_param(paramIndex, decorator) {
|
|
39
|
-
return function(target, key) {
|
|
40
|
-
decorator(target, key, paramIndex);
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
let NotificationGateway = class NotificationGateway {
|
|
44
|
-
afterInit() {
|
|
45
|
-
// Gateway initialized
|
|
46
|
-
}
|
|
47
|
-
async handleConnection(client) {
|
|
48
|
-
try {
|
|
49
|
-
const token = this.extractToken(client);
|
|
50
|
-
if (!token) {
|
|
51
|
-
client.disconnect();
|
|
52
|
-
return;
|
|
53
|
-
}
|
|
54
|
-
const payload = this.verifyToken(token);
|
|
55
|
-
if (!payload) {
|
|
56
|
-
client.disconnect();
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
const userId = payload.id;
|
|
60
|
-
client.data.userId = userId;
|
|
61
|
-
client.data.companyId = payload.companyId;
|
|
62
|
-
if (!this.connectedUsers.has(userId)) {
|
|
63
|
-
this.connectedUsers.set(userId, new Set());
|
|
64
|
-
}
|
|
65
|
-
this.connectedUsers.get(userId).add(client.id);
|
|
66
|
-
client.join(`user:${userId}`);
|
|
67
|
-
if (payload.companyId) {
|
|
68
|
-
client.join(`company:${payload.companyId}`);
|
|
69
|
-
client.join(`user:${userId}:company:${payload.companyId}`);
|
|
70
|
-
}
|
|
71
|
-
} catch {
|
|
72
|
-
client.disconnect();
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
handleDisconnect(client) {
|
|
76
|
-
const userId = client.data.userId;
|
|
77
|
-
if (userId) {
|
|
78
|
-
const sockets = this.connectedUsers.get(userId);
|
|
79
|
-
sockets?.delete(client.id);
|
|
80
|
-
if (sockets?.size === 0) {
|
|
81
|
-
this.connectedUsers.delete(userId);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
emitToUser(userId, event, data) {
|
|
86
|
-
this.server?.to(`user:${userId}`).emit(event, data);
|
|
87
|
-
}
|
|
88
|
-
/**
|
|
89
|
-
* Emit to a user scoped to a specific company session.
|
|
90
|
-
* Uses the combined room `user:{id}:company:{cid}` so only sockets
|
|
91
|
-
* authenticated under that company receive the event — critical for
|
|
92
|
-
* users logged in across multiple companies on different devices.
|
|
93
|
-
* Falls back to all user sockets when companyId is absent.
|
|
94
|
-
*/ emitToUserInCompany(userId, companyId, event, data) {
|
|
95
|
-
if (!this.server) {
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (!companyId) {
|
|
99
|
-
this.emitToUser(userId, event, data);
|
|
100
|
-
return;
|
|
101
|
-
}
|
|
102
|
-
this.server.to(`user:${userId}:company:${companyId}`).emit(event, data);
|
|
103
|
-
}
|
|
104
|
-
emitToCompany(companyId, event, data) {
|
|
105
|
-
this.server?.to(`company:${companyId}`).emit(event, data);
|
|
106
|
-
}
|
|
107
|
-
broadcast(event, data) {
|
|
108
|
-
this.server?.emit(event, data);
|
|
109
|
-
}
|
|
110
|
-
isUserOnline(userId) {
|
|
111
|
-
return this.connectedUsers.has(userId);
|
|
112
|
-
}
|
|
113
|
-
getOnlineUserCount() {
|
|
114
|
-
return this.connectedUsers.size;
|
|
115
|
-
}
|
|
116
|
-
extractToken(client) {
|
|
117
|
-
const authToken = client.handshake.auth?.token;
|
|
118
|
-
if (authToken) return authToken;
|
|
119
|
-
const queryToken = client.handshake.query?.token;
|
|
120
|
-
if (typeof queryToken === 'string') return queryToken;
|
|
121
|
-
const authHeader = client.handshake.headers?.authorization;
|
|
122
|
-
if (authHeader?.startsWith('Bearer ')) {
|
|
123
|
-
return authHeader.substring(7);
|
|
124
|
-
}
|
|
125
|
-
return null;
|
|
126
|
-
}
|
|
127
|
-
verifyToken(token) {
|
|
128
|
-
try {
|
|
129
|
-
const secret = this.configService.getJwtSecret();
|
|
130
|
-
if (!secret) {
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
return this.jwtService.verify(token, {
|
|
134
|
-
secret,
|
|
135
|
-
algorithms: [
|
|
136
|
-
'HS256'
|
|
137
|
-
]
|
|
138
|
-
});
|
|
139
|
-
} catch {
|
|
140
|
-
return null;
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
constructor(configService, jwtService){
|
|
144
|
-
_define_property(this, "configService", void 0);
|
|
145
|
-
_define_property(this, "jwtService", void 0);
|
|
146
|
-
_define_property(this, "connectedUsers", void 0);
|
|
147
|
-
_define_property(this, "server", void 0);
|
|
148
|
-
this.configService = configService;
|
|
149
|
-
this.jwtService = jwtService;
|
|
150
|
-
this.connectedUsers = new Map();
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
_ts_decorate([
|
|
154
|
-
(0, _websockets.WebSocketServer)(),
|
|
155
|
-
_ts_metadata("design:type", typeof _socketio.Server === "undefined" ? Object : _socketio.Server)
|
|
156
|
-
], NotificationGateway.prototype, "server", void 0);
|
|
157
|
-
NotificationGateway = _ts_decorate([
|
|
158
|
-
(0, _websockets.WebSocketGateway)({
|
|
159
|
-
namespace: '/notifications',
|
|
160
|
-
cors: {
|
|
161
|
-
origin: '*',
|
|
162
|
-
credentials: true
|
|
163
|
-
}
|
|
164
|
-
}),
|
|
165
|
-
_ts_param(0, (0, _common.Inject)(_notificationconfigservice.NotificationConfigService)),
|
|
166
|
-
_ts_param(1, (0, _common.Inject)(_jwt.JwtService)),
|
|
167
|
-
_ts_metadata("design:type", Function),
|
|
168
|
-
_ts_metadata("design:paramtypes", [
|
|
169
|
-
typeof _notificationconfigservice.NotificationConfigService === "undefined" ? Object : _notificationconfigservice.NotificationConfigService,
|
|
170
|
-
typeof _jwt.JwtService === "undefined" ? Object : _jwt.JwtService
|
|
171
|
-
])
|
|
172
|
-
], NotificationGateway);
|
|
@@ -1,359 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
const _notificationgateway = require("./notification.gateway");
|
|
6
|
-
function createMockServer() {
|
|
7
|
-
const room = {
|
|
8
|
-
emit: jest.fn()
|
|
9
|
-
};
|
|
10
|
-
return {
|
|
11
|
-
to: jest.fn().mockReturnValue(room),
|
|
12
|
-
emit: jest.fn()
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
function createMockSocket(overrides = {}) {
|
|
16
|
-
return {
|
|
17
|
-
id: 'socket-id-1',
|
|
18
|
-
handshake: {
|
|
19
|
-
auth: {},
|
|
20
|
-
query: {},
|
|
21
|
-
headers: {}
|
|
22
|
-
},
|
|
23
|
-
data: {},
|
|
24
|
-
join: jest.fn(),
|
|
25
|
-
disconnect: jest.fn(),
|
|
26
|
-
...overrides
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
describe('NotificationGateway', ()=>{
|
|
30
|
-
let gateway;
|
|
31
|
-
let mockConfigService;
|
|
32
|
-
let mockJwtService;
|
|
33
|
-
let mockServer;
|
|
34
|
-
beforeEach(()=>{
|
|
35
|
-
mockConfigService = {
|
|
36
|
-
getJwtSecret: jest.fn().mockReturnValue('jwt-secret')
|
|
37
|
-
};
|
|
38
|
-
mockJwtService = {
|
|
39
|
-
verify: jest.fn()
|
|
40
|
-
};
|
|
41
|
-
gateway = new _notificationgateway.NotificationGateway(mockConfigService, mockJwtService);
|
|
42
|
-
mockServer = createMockServer();
|
|
43
|
-
gateway.server = mockServer;
|
|
44
|
-
});
|
|
45
|
-
describe('afterInit', ()=>{
|
|
46
|
-
it('does not throw', ()=>{
|
|
47
|
-
expect(()=>gateway.afterInit()).not.toThrow();
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
describe('handleConnection', ()=>{
|
|
51
|
-
it('disconnects the client when no token is present anywhere', async ()=>{
|
|
52
|
-
const client = createMockSocket();
|
|
53
|
-
await gateway.handleConnection(client);
|
|
54
|
-
expect(client.disconnect).toHaveBeenCalled();
|
|
55
|
-
expect(mockJwtService.verify).not.toHaveBeenCalled();
|
|
56
|
-
});
|
|
57
|
-
it('accepts a token from handshake.auth and joins the user + company rooms', async ()=>{
|
|
58
|
-
const client = createMockSocket({
|
|
59
|
-
handshake: {
|
|
60
|
-
auth: {
|
|
61
|
-
token: 'auth-token'
|
|
62
|
-
},
|
|
63
|
-
query: {},
|
|
64
|
-
headers: {}
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
mockJwtService.verify.mockReturnValue({
|
|
68
|
-
id: 'user-1',
|
|
69
|
-
companyId: 'company-1'
|
|
70
|
-
});
|
|
71
|
-
await gateway.handleConnection(client);
|
|
72
|
-
expect(mockJwtService.verify).toHaveBeenCalledWith('auth-token', {
|
|
73
|
-
secret: 'jwt-secret',
|
|
74
|
-
algorithms: [
|
|
75
|
-
'HS256'
|
|
76
|
-
]
|
|
77
|
-
});
|
|
78
|
-
expect(client.data.userId).toBe('user-1');
|
|
79
|
-
expect(client.data.companyId).toBe('company-1');
|
|
80
|
-
expect(client.join).toHaveBeenCalledWith('user:user-1');
|
|
81
|
-
expect(client.join).toHaveBeenCalledWith('company:company-1');
|
|
82
|
-
expect(client.join).toHaveBeenCalledWith('user:user-1:company:company-1');
|
|
83
|
-
expect(client.disconnect).not.toHaveBeenCalled();
|
|
84
|
-
expect(gateway.isUserOnline('user-1')).toBe(true);
|
|
85
|
-
});
|
|
86
|
-
it('accepts a token from the query string', async ()=>{
|
|
87
|
-
const client = createMockSocket({
|
|
88
|
-
handshake: {
|
|
89
|
-
auth: {},
|
|
90
|
-
query: {
|
|
91
|
-
token: 'query-token'
|
|
92
|
-
},
|
|
93
|
-
headers: {}
|
|
94
|
-
}
|
|
95
|
-
});
|
|
96
|
-
mockJwtService.verify.mockReturnValue({
|
|
97
|
-
id: 'user-2'
|
|
98
|
-
});
|
|
99
|
-
await gateway.handleConnection(client);
|
|
100
|
-
expect(mockJwtService.verify).toHaveBeenCalledWith('query-token', expect.any(Object));
|
|
101
|
-
expect(client.join).toHaveBeenCalledWith('user:user-2');
|
|
102
|
-
});
|
|
103
|
-
it('accepts a token from the Authorization header', async ()=>{
|
|
104
|
-
const client = createMockSocket({
|
|
105
|
-
handshake: {
|
|
106
|
-
auth: {},
|
|
107
|
-
query: {},
|
|
108
|
-
headers: {
|
|
109
|
-
authorization: 'Bearer header-token'
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
});
|
|
113
|
-
mockJwtService.verify.mockReturnValue({
|
|
114
|
-
id: 'user-3'
|
|
115
|
-
});
|
|
116
|
-
await gateway.handleConnection(client);
|
|
117
|
-
expect(mockJwtService.verify).toHaveBeenCalledWith('header-token', expect.any(Object));
|
|
118
|
-
expect(client.join).toHaveBeenCalledWith('user:user-3');
|
|
119
|
-
});
|
|
120
|
-
it('ignores a malformed Authorization header without the Bearer prefix', async ()=>{
|
|
121
|
-
const client = createMockSocket({
|
|
122
|
-
handshake: {
|
|
123
|
-
auth: {},
|
|
124
|
-
query: {},
|
|
125
|
-
headers: {
|
|
126
|
-
authorization: 'Basic abc123'
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
});
|
|
130
|
-
await gateway.handleConnection(client);
|
|
131
|
-
expect(client.disconnect).toHaveBeenCalled();
|
|
132
|
-
expect(mockJwtService.verify).not.toHaveBeenCalled();
|
|
133
|
-
});
|
|
134
|
-
it('joins only the personal room when the token payload has no companyId', async ()=>{
|
|
135
|
-
const client = createMockSocket({
|
|
136
|
-
handshake: {
|
|
137
|
-
auth: {
|
|
138
|
-
token: 't'
|
|
139
|
-
},
|
|
140
|
-
query: {},
|
|
141
|
-
headers: {}
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
mockJwtService.verify.mockReturnValue({
|
|
145
|
-
id: 'user-4'
|
|
146
|
-
});
|
|
147
|
-
await gateway.handleConnection(client);
|
|
148
|
-
expect(client.join).toHaveBeenCalledWith('user:user-4');
|
|
149
|
-
expect(client.join).toHaveBeenCalledTimes(1);
|
|
150
|
-
});
|
|
151
|
-
it('disconnects the client when the JWT secret is not configured', async ()=>{
|
|
152
|
-
mockConfigService.getJwtSecret.mockReturnValue(undefined);
|
|
153
|
-
const client = createMockSocket({
|
|
154
|
-
handshake: {
|
|
155
|
-
auth: {
|
|
156
|
-
token: 't'
|
|
157
|
-
},
|
|
158
|
-
query: {},
|
|
159
|
-
headers: {}
|
|
160
|
-
}
|
|
161
|
-
});
|
|
162
|
-
await gateway.handleConnection(client);
|
|
163
|
-
expect(client.disconnect).toHaveBeenCalled();
|
|
164
|
-
expect(mockJwtService.verify).not.toHaveBeenCalled();
|
|
165
|
-
});
|
|
166
|
-
it('disconnects the client when token verification throws (invalid/expired token)', async ()=>{
|
|
167
|
-
const client = createMockSocket({
|
|
168
|
-
handshake: {
|
|
169
|
-
auth: {
|
|
170
|
-
token: 'bad-token'
|
|
171
|
-
},
|
|
172
|
-
query: {},
|
|
173
|
-
headers: {}
|
|
174
|
-
}
|
|
175
|
-
});
|
|
176
|
-
mockJwtService.verify.mockImplementation(()=>{
|
|
177
|
-
throw new Error('jwt expired');
|
|
178
|
-
});
|
|
179
|
-
await gateway.handleConnection(client);
|
|
180
|
-
expect(client.disconnect).toHaveBeenCalled();
|
|
181
|
-
expect(gateway.isUserOnline('user-1')).toBe(false);
|
|
182
|
-
});
|
|
183
|
-
it('tracks multiple sockets for the same user', async ()=>{
|
|
184
|
-
mockJwtService.verify.mockReturnValue({
|
|
185
|
-
id: 'user-5'
|
|
186
|
-
});
|
|
187
|
-
const clientA = createMockSocket({
|
|
188
|
-
id: 'socket-a',
|
|
189
|
-
handshake: {
|
|
190
|
-
auth: {
|
|
191
|
-
token: 't'
|
|
192
|
-
},
|
|
193
|
-
query: {},
|
|
194
|
-
headers: {}
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
const clientB = createMockSocket({
|
|
198
|
-
id: 'socket-b',
|
|
199
|
-
handshake: {
|
|
200
|
-
auth: {
|
|
201
|
-
token: 't'
|
|
202
|
-
},
|
|
203
|
-
query: {},
|
|
204
|
-
headers: {}
|
|
205
|
-
}
|
|
206
|
-
});
|
|
207
|
-
await gateway.handleConnection(clientA);
|
|
208
|
-
await gateway.handleConnection(clientB);
|
|
209
|
-
expect(gateway.isUserOnline('user-5')).toBe(true);
|
|
210
|
-
expect(gateway.getOnlineUserCount()).toBe(1);
|
|
211
|
-
});
|
|
212
|
-
it('disconnects the client when an unexpected error occurs during connection handling', async ()=>{
|
|
213
|
-
const client = createMockSocket({
|
|
214
|
-
handshake: {
|
|
215
|
-
auth: {
|
|
216
|
-
token: 't'
|
|
217
|
-
},
|
|
218
|
-
query: {},
|
|
219
|
-
headers: {}
|
|
220
|
-
}
|
|
221
|
-
});
|
|
222
|
-
mockJwtService.verify.mockReturnValue({
|
|
223
|
-
id: 'user-6'
|
|
224
|
-
});
|
|
225
|
-
client.join = jest.fn().mockImplementation(()=>{
|
|
226
|
-
throw new Error('join failed');
|
|
227
|
-
});
|
|
228
|
-
await gateway.handleConnection(client);
|
|
229
|
-
expect(client.disconnect).toHaveBeenCalled();
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
describe('handleDisconnect', ()=>{
|
|
233
|
-
it('removes the socket and marks the user offline when it was their only connection', async ()=>{
|
|
234
|
-
mockJwtService.verify.mockReturnValue({
|
|
235
|
-
id: 'user-1'
|
|
236
|
-
});
|
|
237
|
-
const client = createMockSocket({
|
|
238
|
-
handshake: {
|
|
239
|
-
auth: {
|
|
240
|
-
token: 't'
|
|
241
|
-
},
|
|
242
|
-
query: {},
|
|
243
|
-
headers: {}
|
|
244
|
-
}
|
|
245
|
-
});
|
|
246
|
-
await gateway.handleConnection(client);
|
|
247
|
-
expect(gateway.isUserOnline('user-1')).toBe(true);
|
|
248
|
-
gateway.handleDisconnect(client);
|
|
249
|
-
expect(gateway.isUserOnline('user-1')).toBe(false);
|
|
250
|
-
expect(gateway.getOnlineUserCount()).toBe(0);
|
|
251
|
-
});
|
|
252
|
-
it('keeps the user online when they still have another connected socket', async ()=>{
|
|
253
|
-
mockJwtService.verify.mockReturnValue({
|
|
254
|
-
id: 'user-1'
|
|
255
|
-
});
|
|
256
|
-
const clientA = createMockSocket({
|
|
257
|
-
id: 'socket-a',
|
|
258
|
-
handshake: {
|
|
259
|
-
auth: {
|
|
260
|
-
token: 't'
|
|
261
|
-
},
|
|
262
|
-
query: {},
|
|
263
|
-
headers: {}
|
|
264
|
-
}
|
|
265
|
-
});
|
|
266
|
-
const clientB = createMockSocket({
|
|
267
|
-
id: 'socket-b',
|
|
268
|
-
handshake: {
|
|
269
|
-
auth: {
|
|
270
|
-
token: 't'
|
|
271
|
-
},
|
|
272
|
-
query: {},
|
|
273
|
-
headers: {}
|
|
274
|
-
}
|
|
275
|
-
});
|
|
276
|
-
await gateway.handleConnection(clientA);
|
|
277
|
-
await gateway.handleConnection(clientB);
|
|
278
|
-
gateway.handleDisconnect(clientA);
|
|
279
|
-
expect(gateway.isUserOnline('user-1')).toBe(true);
|
|
280
|
-
});
|
|
281
|
-
it('does nothing when the client never authenticated (no userId on socket data)', ()=>{
|
|
282
|
-
const client = createMockSocket();
|
|
283
|
-
expect(()=>gateway.handleDisconnect(client)).not.toThrow();
|
|
284
|
-
expect(gateway.getOnlineUserCount()).toBe(0);
|
|
285
|
-
});
|
|
286
|
-
it('is a no-op when disconnecting an unknown user id', ()=>{
|
|
287
|
-
const client = createMockSocket({
|
|
288
|
-
data: {
|
|
289
|
-
userId: 'ghost-user'
|
|
290
|
-
}
|
|
291
|
-
});
|
|
292
|
-
expect(()=>gateway.handleDisconnect(client)).not.toThrow();
|
|
293
|
-
expect(gateway.isUserOnline('ghost-user')).toBe(false);
|
|
294
|
-
});
|
|
295
|
-
});
|
|
296
|
-
describe('emitToUser', ()=>{
|
|
297
|
-
it('emits the event to the user-scoped room', ()=>{
|
|
298
|
-
gateway.emitToUser('user-1', 'notification:new', {
|
|
299
|
-
title: 'hi'
|
|
300
|
-
});
|
|
301
|
-
expect(mockServer.to).toHaveBeenCalledWith('user:user-1');
|
|
302
|
-
expect(mockServer.to('user:user-1').emit).toHaveBeenCalledWith('notification:new', {
|
|
303
|
-
title: 'hi'
|
|
304
|
-
});
|
|
305
|
-
});
|
|
306
|
-
it('does not throw when the server has not been initialized yet', ()=>{
|
|
307
|
-
const freshGateway = new _notificationgateway.NotificationGateway(mockConfigService, mockJwtService);
|
|
308
|
-
expect(()=>freshGateway.emitToUser('user-1', 'notification:new', {})).not.toThrow();
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
describe('emitToUserInCompany', ()=>{
|
|
312
|
-
it('emits to the combined user+company room when companyId is provided', ()=>{
|
|
313
|
-
gateway.emitToUserInCompany('user-1', 'company-1', 'notification:new', {
|
|
314
|
-
a: 1
|
|
315
|
-
});
|
|
316
|
-
expect(mockServer.to).toHaveBeenCalledWith('user:user-1:company:company-1');
|
|
317
|
-
});
|
|
318
|
-
it('falls back to emitToUser when companyId is null', ()=>{
|
|
319
|
-
gateway.emitToUserInCompany('user-1', null, 'notification:new', {
|
|
320
|
-
a: 1
|
|
321
|
-
});
|
|
322
|
-
expect(mockServer.to).toHaveBeenCalledWith('user:user-1');
|
|
323
|
-
});
|
|
324
|
-
it('falls back to emitToUser when companyId is undefined', ()=>{
|
|
325
|
-
gateway.emitToUserInCompany('user-1', undefined, 'notification:new', {
|
|
326
|
-
a: 1
|
|
327
|
-
});
|
|
328
|
-
expect(mockServer.to).toHaveBeenCalledWith('user:user-1');
|
|
329
|
-
});
|
|
330
|
-
it('does not throw when the server has not been initialized yet', ()=>{
|
|
331
|
-
const freshGateway = new _notificationgateway.NotificationGateway(mockConfigService, mockJwtService);
|
|
332
|
-
expect(()=>freshGateway.emitToUserInCompany('user-1', 'company-1', 'notification:new', {})).not.toThrow();
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
describe('emitToCompany', ()=>{
|
|
336
|
-
it('emits the event to the company-scoped room', ()=>{
|
|
337
|
-
gateway.emitToCompany('company-1', 'notification:new', {
|
|
338
|
-
b: 2
|
|
339
|
-
});
|
|
340
|
-
expect(mockServer.to).toHaveBeenCalledWith('company:company-1');
|
|
341
|
-
});
|
|
342
|
-
});
|
|
343
|
-
describe('broadcast', ()=>{
|
|
344
|
-
it('emits the event to all connected sockets', ()=>{
|
|
345
|
-
gateway.broadcast('server:announcement', {
|
|
346
|
-
text: 'hi all'
|
|
347
|
-
});
|
|
348
|
-
expect(mockServer.emit).toHaveBeenCalledWith('server:announcement', {
|
|
349
|
-
text: 'hi all'
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
});
|
|
353
|
-
describe('isUserOnline / getOnlineUserCount', ()=>{
|
|
354
|
-
it('reports zero online users when nobody has connected', ()=>{
|
|
355
|
-
expect(gateway.isUserOnline('nobody')).toBe(false);
|
|
356
|
-
expect(gateway.getOnlineUserCount()).toBe(0);
|
|
357
|
-
});
|
|
358
|
-
});
|
|
359
|
-
});
|
package/cjs/index.js
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
// Adapters
|
|
2
|
-
"use strict";
|
|
3
|
-
Object.defineProperty(exports, "__esModule", {
|
|
4
|
-
value: true
|
|
5
|
-
});
|
|
6
|
-
_export_star(require("./adapters"), exports);
|
|
7
|
-
_export_star(require("./config"), exports);
|
|
8
|
-
_export_star(require("./controllers"), exports);
|
|
9
|
-
_export_star(require("./docs"), exports);
|
|
10
|
-
_export_star(require("./dtos"), exports);
|
|
11
|
-
_export_star(require("./entities"), exports);
|
|
12
|
-
_export_star(require("./gateways"), exports);
|
|
13
|
-
_export_star(require("./interfaces"), exports);
|
|
14
|
-
_export_star(require("./modules"), exports);
|
|
15
|
-
_export_star(require("./services"), exports);
|
|
16
|
-
function _export_star(from, to) {
|
|
17
|
-
Object.keys(from).forEach(function(k) {
|
|
18
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
19
|
-
Object.defineProperty(to, k, {
|
|
20
|
-
enumerable: true,
|
|
21
|
-
get: function() {
|
|
22
|
-
return from[k];
|
|
23
|
-
}
|
|
24
|
-
});
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
return from;
|
|
28
|
-
}
|
package/cjs/interfaces/index.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./notification.interface"), exports);
|
|
6
|
-
_export_star(require("./notification-module.interface"), exports);
|
|
7
|
-
function _export_star(from, to) {
|
|
8
|
-
Object.keys(from).forEach(function(k) {
|
|
9
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
10
|
-
Object.defineProperty(to, k, {
|
|
11
|
-
enumerable: true,
|
|
12
|
-
get: function() {
|
|
13
|
-
return from[k];
|
|
14
|
-
}
|
|
15
|
-
});
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
return from;
|
|
19
|
-
}
|
package/cjs/modules/index.js
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", {
|
|
3
|
-
value: true
|
|
4
|
-
});
|
|
5
|
-
_export_star(require("./notification.module"), exports);
|
|
6
|
-
function _export_star(from, to) {
|
|
7
|
-
Object.keys(from).forEach(function(k) {
|
|
8
|
-
if (k !== "default" && !Object.prototype.hasOwnProperty.call(to, k)) {
|
|
9
|
-
Object.defineProperty(to, k, {
|
|
10
|
-
enumerable: true,
|
|
11
|
-
get: function() {
|
|
12
|
-
return from[k];
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}
|
|
16
|
-
});
|
|
17
|
-
return from;
|
|
18
|
-
}
|