@zeeshan60/event-processor 1.0.4 → 1.0.5
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/dist/ActivityEventHandler.d.ts +12 -0
- package/dist/ActivityEventHandler.d.ts.map +1 -0
- package/dist/ActivityEventHandler.js +35 -0
- package/dist/FriendEventHandler.d.ts +21 -0
- package/dist/FriendEventHandler.d.ts.map +1 -0
- package/dist/FriendEventHandler.js +175 -0
- package/dist/GroupEventHandler.d.ts +25 -0
- package/dist/GroupEventHandler.d.ts.map +1 -0
- package/dist/GroupEventHandler.js +270 -0
- package/dist/GroupTransactionEventHandler.d.ts +25 -0
- package/dist/GroupTransactionEventHandler.d.ts.map +1 -0
- package/dist/GroupTransactionEventHandler.js +296 -0
- package/dist/TransactionEventHandler.d.ts +11 -7
- package/dist/TransactionEventHandler.d.ts.map +1 -1
- package/dist/TransactionEventHandler.js +139 -10
- package/dist/UserEventHandler.d.ts +17 -0
- package/dist/UserEventHandler.d.ts.map +1 -0
- package/dist/UserEventHandler.js +82 -0
- package/dist/events.d.ts +30 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +60 -0
- package/dist/index.d.ts +1 -21
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -86
- package/dist/utils/eventConverter.d.ts +23 -0
- package/dist/utils/eventConverter.d.ts.map +1 -0
- package/dist/utils/eventConverter.js +370 -0
- package/dist/utils/splitTypeUtils.d.ts +7 -0
- package/dist/utils/splitTypeUtils.d.ts.map +1 -0
- package/dist/utils/splitTypeUtils.js +45 -0
- package/dist/utils/userPathUtils.d.ts +6 -0
- package/dist/utils/userPathUtils.d.ts.map +1 -0
- package/dist/utils/userPathUtils.js +16 -0
- package/package.json +1 -1
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GroupTransactionEventHandler = void 0;
|
|
4
|
+
const _1 = require(".");
|
|
5
|
+
const splitTypeUtils_1 = require("./utils/splitTypeUtils");
|
|
6
|
+
const uuid_1 = require("uuid");
|
|
7
|
+
class GroupTransactionEventHandler {
|
|
8
|
+
constructor(modelStore, eventStore, groupModelStore, transactionEventStore, transactionModelStore, activityEventHandler) {
|
|
9
|
+
this.modelStore = modelStore;
|
|
10
|
+
this.eventStore = eventStore;
|
|
11
|
+
this.groupModelStore = groupModelStore;
|
|
12
|
+
this.transactionEventStore = transactionEventStore;
|
|
13
|
+
this.transactionModelStore = transactionModelStore;
|
|
14
|
+
this.activityEventHandler = activityEventHandler;
|
|
15
|
+
}
|
|
16
|
+
async handleEvent(event) {
|
|
17
|
+
const validEventTypes = [
|
|
18
|
+
'GroupTransactionCreated',
|
|
19
|
+
'GroupTransactionSplitDetailsChanged',
|
|
20
|
+
];
|
|
21
|
+
const eventType = event.constructor.name;
|
|
22
|
+
if (!validEventTypes.includes(eventType)) {
|
|
23
|
+
throw new Error(`Unknown group transaction event type: ${eventType}`);
|
|
24
|
+
}
|
|
25
|
+
switch (eventType) {
|
|
26
|
+
case 'GroupTransactionCreated':
|
|
27
|
+
return await this.handleGroupTransactionCreated(event);
|
|
28
|
+
case 'GroupTransactionSplitDetailsChanged':
|
|
29
|
+
return await this.handleGroupTransactionSplitDetailsChanged(event);
|
|
30
|
+
default:
|
|
31
|
+
throw new Error(`Unknown group transaction event type: ${eventType}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
calculateNetBalances(members) {
|
|
35
|
+
const netBalances = new Map();
|
|
36
|
+
for (const member of members) {
|
|
37
|
+
const net = member.paid - member.owed;
|
|
38
|
+
netBalances.set(member.memberId, net);
|
|
39
|
+
}
|
|
40
|
+
return netBalances;
|
|
41
|
+
}
|
|
42
|
+
async createFriendTransactions(groupTransactionEvent) {
|
|
43
|
+
const netBalances = this.calculateNetBalances(groupTransactionEvent.originalTransaction.members);
|
|
44
|
+
const creditors = [];
|
|
45
|
+
const debtors = [];
|
|
46
|
+
for (const [memberId, net] of netBalances.entries()) {
|
|
47
|
+
if (net > 0) {
|
|
48
|
+
creditors.push({ memberId, amount: net });
|
|
49
|
+
}
|
|
50
|
+
else if (net < 0) {
|
|
51
|
+
debtors.push({ memberId, amount: Math.abs(net) });
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const creditor of creditors) {
|
|
55
|
+
for (const debtor of debtors) {
|
|
56
|
+
if (creditor.amount > 0 && debtor.amount > 0) {
|
|
57
|
+
const transactionAmount = Math.min(creditor.amount, debtor.amount);
|
|
58
|
+
const creditorStreamId = `stream-${(0, uuid_1.v4)()}`;
|
|
59
|
+
const creditorTransactionEvent = new _1.TransactionCreated({
|
|
60
|
+
userId: creditor.memberId,
|
|
61
|
+
recipientUserId: debtor.memberId,
|
|
62
|
+
logicalTransactionId: groupTransactionEvent.transactionId,
|
|
63
|
+
description: groupTransactionEvent.description ?? '',
|
|
64
|
+
currency: groupTransactionEvent.currency,
|
|
65
|
+
splitType: _1.SplitType.SpecificAmounts,
|
|
66
|
+
totalAmount: transactionAmount,
|
|
67
|
+
amount: transactionAmount,
|
|
68
|
+
isOwed: false,
|
|
69
|
+
transactionDate: groupTransactionEvent.transactionDate,
|
|
70
|
+
groupId: groupTransactionEvent.groupId,
|
|
71
|
+
groupTransactionId: groupTransactionEvent.transactionId,
|
|
72
|
+
createdAt: new Date(),
|
|
73
|
+
createdBy: groupTransactionEvent.createdBy,
|
|
74
|
+
streamId: creditorStreamId,
|
|
75
|
+
version: 1,
|
|
76
|
+
});
|
|
77
|
+
await this.transactionEventStore.addEvent(creditor.memberId, creditorTransactionEvent);
|
|
78
|
+
const creditorModel = creditorTransactionEvent.apply(undefined);
|
|
79
|
+
await this.transactionModelStore.save(creditorModel);
|
|
80
|
+
const debtorStreamId = `stream-${(0, uuid_1.v4)()}`;
|
|
81
|
+
const debtorTransactionEvent = new _1.TransactionCreated({
|
|
82
|
+
userId: debtor.memberId,
|
|
83
|
+
recipientUserId: creditor.memberId,
|
|
84
|
+
logicalTransactionId: groupTransactionEvent.transactionId,
|
|
85
|
+
description: groupTransactionEvent.description ?? '',
|
|
86
|
+
currency: groupTransactionEvent.currency,
|
|
87
|
+
splitType: _1.SplitType.SpecificAmounts,
|
|
88
|
+
totalAmount: transactionAmount,
|
|
89
|
+
amount: transactionAmount,
|
|
90
|
+
isOwed: true,
|
|
91
|
+
transactionDate: groupTransactionEvent.transactionDate,
|
|
92
|
+
groupId: groupTransactionEvent.groupId,
|
|
93
|
+
groupTransactionId: groupTransactionEvent.transactionId,
|
|
94
|
+
createdAt: new Date(),
|
|
95
|
+
createdBy: groupTransactionEvent.createdBy,
|
|
96
|
+
streamId: debtorStreamId,
|
|
97
|
+
version: 1,
|
|
98
|
+
});
|
|
99
|
+
await this.transactionEventStore.addEvent(debtor.memberId, debtorTransactionEvent);
|
|
100
|
+
const debtorModel = debtorTransactionEvent.apply(undefined);
|
|
101
|
+
await this.transactionModelStore.save(debtorModel);
|
|
102
|
+
creditor.amount -= transactionAmount;
|
|
103
|
+
debtor.amount -= transactionAmount;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async updateFriendTransactions(event, existingModel, _oldMembers, newMembers) {
|
|
109
|
+
await this.updateExistingFriendTransactions(event, existingModel, newMembers);
|
|
110
|
+
}
|
|
111
|
+
calculateRequiredTransactions(newMembers) {
|
|
112
|
+
const newNetBalances = this.calculateNetBalances(newMembers);
|
|
113
|
+
const requiredTransactions = new Map();
|
|
114
|
+
const creditors = [];
|
|
115
|
+
const debtors = [];
|
|
116
|
+
for (const [memberId, net] of newNetBalances.entries()) {
|
|
117
|
+
if (net > 0) {
|
|
118
|
+
creditors.push({ memberId, amount: net });
|
|
119
|
+
}
|
|
120
|
+
else if (net < 0) {
|
|
121
|
+
debtors.push({ memberId, amount: Math.abs(net) });
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
for (const creditor of creditors) {
|
|
125
|
+
for (const debtor of debtors) {
|
|
126
|
+
if (creditor.amount > 0 && debtor.amount > 0) {
|
|
127
|
+
const transactionAmount = Math.min(creditor.amount, debtor.amount);
|
|
128
|
+
const creditorKey = `${creditor.memberId}-${debtor.memberId}`;
|
|
129
|
+
requiredTransactions.set(creditorKey, {
|
|
130
|
+
recipientUserId: debtor.memberId,
|
|
131
|
+
amount: transactionAmount,
|
|
132
|
+
isOwed: false
|
|
133
|
+
});
|
|
134
|
+
const debtorKey = `${debtor.memberId}-${creditor.memberId}`;
|
|
135
|
+
requiredTransactions.set(debtorKey, {
|
|
136
|
+
recipientUserId: creditor.memberId,
|
|
137
|
+
amount: transactionAmount,
|
|
138
|
+
isOwed: true
|
|
139
|
+
});
|
|
140
|
+
creditor.amount -= transactionAmount;
|
|
141
|
+
debtor.amount -= transactionAmount;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return requiredTransactions;
|
|
146
|
+
}
|
|
147
|
+
async updateExistingFriendTransactions(event, existingModel, newMembers) {
|
|
148
|
+
const requiredTransactions = this.calculateRequiredTransactions(newMembers);
|
|
149
|
+
const oldMemberIds = new Set(existingModel.originalTransaction.members.map(m => m.memberId));
|
|
150
|
+
const newMemberIds = new Set(newMembers.map(m => m.memberId));
|
|
151
|
+
const allMemberIds = new Set([...oldMemberIds, ...newMemberIds]);
|
|
152
|
+
for (const memberId of allMemberIds) {
|
|
153
|
+
const existingTransactions = await this.transactionModelStore.findByGroupTransactionId(memberId, existingModel.transactionId);
|
|
154
|
+
for (const existingTx of existingTransactions) {
|
|
155
|
+
const key = `${existingTx.userId}-${existingTx.recipientUserId}`;
|
|
156
|
+
const requiredTx = requiredTransactions.get(key);
|
|
157
|
+
if (requiredTx) {
|
|
158
|
+
if (existingTx.amount !== requiredTx.amount ||
|
|
159
|
+
existingTx.totalAmount !== requiredTx.amount ||
|
|
160
|
+
existingTx.isOwed !== requiredTx.isOwed) {
|
|
161
|
+
const updateEvent = new _1.TransactionDetailsChanged({
|
|
162
|
+
totalAmount: requiredTx.amount,
|
|
163
|
+
amount: requiredTx.amount,
|
|
164
|
+
isOwed: requiredTx.isOwed,
|
|
165
|
+
streamId: existingTx.streamId,
|
|
166
|
+
version: existingTx.version + 1,
|
|
167
|
+
createdAt: new Date(),
|
|
168
|
+
createdBy: event.createdBy,
|
|
169
|
+
});
|
|
170
|
+
await this.transactionEventStore.addEvent(memberId, updateEvent);
|
|
171
|
+
const updatedModel = updateEvent.apply(existingTx);
|
|
172
|
+
await this.transactionModelStore.save(updatedModel);
|
|
173
|
+
}
|
|
174
|
+
requiredTransactions.delete(key);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
const deleteEvent = new _1.TransactionDeleted({
|
|
178
|
+
createdAt: new Date(),
|
|
179
|
+
createdBy: event.createdBy,
|
|
180
|
+
streamId: existingTx.streamId,
|
|
181
|
+
version: existingTx.version + 1,
|
|
182
|
+
});
|
|
183
|
+
await this.transactionEventStore.addEvent(memberId, deleteEvent);
|
|
184
|
+
const deletedModel = deleteEvent.apply(existingTx);
|
|
185
|
+
await this.transactionModelStore.save(deletedModel);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
for (const [key, txDetails] of requiredTransactions.entries()) {
|
|
190
|
+
const [userId, recipientUserId] = key.split('-');
|
|
191
|
+
const streamId = `stream-${(0, uuid_1.v4)()}`;
|
|
192
|
+
const createEvent = new _1.TransactionCreated({
|
|
193
|
+
userId: userId,
|
|
194
|
+
recipientUserId: recipientUserId,
|
|
195
|
+
logicalTransactionId: existingModel.transactionId,
|
|
196
|
+
description: existingModel.description ?? '',
|
|
197
|
+
currency: existingModel.currency,
|
|
198
|
+
splitType: _1.SplitType.SpecificAmounts,
|
|
199
|
+
totalAmount: txDetails.amount,
|
|
200
|
+
amount: txDetails.amount,
|
|
201
|
+
isOwed: txDetails.isOwed,
|
|
202
|
+
transactionDate: existingModel.transactionDate,
|
|
203
|
+
groupId: existingModel.groupId,
|
|
204
|
+
groupTransactionId: existingModel.transactionId,
|
|
205
|
+
createdAt: new Date(),
|
|
206
|
+
createdBy: event.createdBy,
|
|
207
|
+
streamId: streamId,
|
|
208
|
+
version: 1,
|
|
209
|
+
});
|
|
210
|
+
await this.transactionEventStore.addEvent(userId, createEvent);
|
|
211
|
+
const model = createEvent.apply(undefined);
|
|
212
|
+
await this.transactionModelStore.save(model);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async handleGroupTransactionCreated(event) {
|
|
216
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
217
|
+
const updatedModel = event.apply(existingModel);
|
|
218
|
+
await this.modelStore.save(updatedModel);
|
|
219
|
+
if (this.activityEventHandler) {
|
|
220
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
221
|
+
}
|
|
222
|
+
const allGroupPerspectives = await this.groupModelStore.findAllByGroupIdAndDeletedIsFalse(event.groupId);
|
|
223
|
+
for (const groupPerspective of allGroupPerspectives) {
|
|
224
|
+
if (groupPerspective.userId !== event.userId) {
|
|
225
|
+
const perspectiveStreamId = `stream-${(0, uuid_1.v4)()}`;
|
|
226
|
+
const perspectiveSplitType = (0, splitTypeUtils_1.reverseSplitType)(event.splitType);
|
|
227
|
+
const perspectiveEvent = new _1.GroupTransactionCreated({
|
|
228
|
+
userId: groupPerspective.userId,
|
|
229
|
+
groupId: event.groupId,
|
|
230
|
+
transactionId: event.transactionId,
|
|
231
|
+
description: event.description,
|
|
232
|
+
notes: event.notes,
|
|
233
|
+
totalAmount: event.totalAmount,
|
|
234
|
+
currency: event.currency,
|
|
235
|
+
splitType: perspectiveSplitType,
|
|
236
|
+
transactionDate: event.transactionDate,
|
|
237
|
+
originalTransaction: event.originalTransaction,
|
|
238
|
+
createdAt: new Date(),
|
|
239
|
+
createdBy: event.createdBy,
|
|
240
|
+
streamId: perspectiveStreamId,
|
|
241
|
+
version: 1,
|
|
242
|
+
});
|
|
243
|
+
await this.eventStore.addEvent(groupPerspective.userId, perspectiveEvent);
|
|
244
|
+
const perspectiveModel = perspectiveEvent.apply(undefined);
|
|
245
|
+
await this.modelStore.save(perspectiveModel);
|
|
246
|
+
if (this.activityEventHandler) {
|
|
247
|
+
await this.activityEventHandler.createActivityLog(perspectiveEvent, undefined, perspectiveModel);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
await this.createFriendTransactions(event);
|
|
252
|
+
return updatedModel;
|
|
253
|
+
}
|
|
254
|
+
async handleGroupTransactionSplitDetailsChanged(event) {
|
|
255
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
256
|
+
if (!existingModel) {
|
|
257
|
+
throw new Error('GroupTransaction must exist to change split details');
|
|
258
|
+
}
|
|
259
|
+
const oldMembers = existingModel.originalTransaction.members;
|
|
260
|
+
const newMembers = event.originalTransaction.members;
|
|
261
|
+
const updatedModel = event.apply(existingModel);
|
|
262
|
+
await this.modelStore.save(updatedModel);
|
|
263
|
+
if (this.activityEventHandler) {
|
|
264
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
265
|
+
}
|
|
266
|
+
const allGroupPerspectives = await this.groupModelStore.findAllByGroupIdAndDeletedIsFalse(updatedModel.groupId);
|
|
267
|
+
for (const groupPerspective of allGroupPerspectives) {
|
|
268
|
+
if (groupPerspective.userId !== event.userId) {
|
|
269
|
+
const perspectiveStreamId = updatedModel.streamId;
|
|
270
|
+
const perspectiveSplitType = (0, splitTypeUtils_1.reverseSplitType)(event.splitType);
|
|
271
|
+
const perspectiveEvent = new _1.GroupTransactionSplitDetailsChanged({
|
|
272
|
+
userId: groupPerspective.userId,
|
|
273
|
+
totalAmount: event.totalAmount,
|
|
274
|
+
splitType: perspectiveSplitType,
|
|
275
|
+
originalTransaction: event.originalTransaction,
|
|
276
|
+
createdAt: new Date(),
|
|
277
|
+
createdBy: event.createdBy,
|
|
278
|
+
streamId: perspectiveStreamId,
|
|
279
|
+
version: updatedModel.version,
|
|
280
|
+
});
|
|
281
|
+
await this.eventStore.addEvent(groupPerspective.userId, perspectiveEvent);
|
|
282
|
+
const existingPerspectiveModel = await this.modelStore.findByUserIdAndTransactionId(groupPerspective.userId, updatedModel.transactionId);
|
|
283
|
+
if (existingPerspectiveModel) {
|
|
284
|
+
const perspectiveModel = perspectiveEvent.apply(existingPerspectiveModel);
|
|
285
|
+
await this.modelStore.save(perspectiveModel);
|
|
286
|
+
if (this.activityEventHandler) {
|
|
287
|
+
await this.activityEventHandler.createActivityLog(perspectiveEvent, existingPerspectiveModel, perspectiveModel);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
await this.updateFriendTransactions(event, existingModel, oldMembers, newMembers);
|
|
293
|
+
return updatedModel;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
exports.GroupTransactionEventHandler = GroupTransactionEventHandler;
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import { Event } from '
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
1
|
+
import { TransactionModel, Event } from '.';
|
|
2
|
+
import { TransactionModelStore } from '.';
|
|
3
|
+
import { TransactionEventStore } from '.';
|
|
4
|
+
import { UserModelStore } from '.';
|
|
5
|
+
import { ActivityEventHandler } from './ActivityEventHandler';
|
|
5
6
|
export declare class TransactionEventHandler {
|
|
6
|
-
private eventStore;
|
|
7
7
|
private modelStore;
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
private eventStore;
|
|
9
|
+
private txUserModelStore;
|
|
10
|
+
private activityEventHandler?;
|
|
11
|
+
constructor(modelStore: TransactionModelStore, eventStore: TransactionEventStore, txUserModelStore: UserModelStore, activityEventHandler?: ActivityEventHandler | undefined);
|
|
12
|
+
handleEvent(event: Event): Promise<TransactionModel>;
|
|
13
|
+
private createMirrorEvent;
|
|
10
14
|
}
|
|
11
15
|
//# sourceMappingURL=TransactionEventHandler.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TransactionEventHandler.d.ts","sourceRoot":"","sources":["../src/TransactionEventHandler.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"TransactionEventHandler.d.ts","sourceRoot":"","sources":["../src/TransactionEventHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EAQH,gBAAgB,EAChB,KAAK,EACR,MAAM,GAAG,CAAC;AACX,OAAO,EAAC,qBAAqB,EAAC,MAAM,GAAG,CAAC;AACxC,OAAO,EAAC,qBAAqB,EAAC,MAAM,GAAG,CAAC;AACxC,OAAO,EAAC,cAAc,EAAC,MAAM,GAAG,CAAC;AACjC,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAK5D,qBAAa,uBAAuB;IAE5B,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,gBAAgB;IACxB,OAAO,CAAC,oBAAoB,CAAC;gBAHrB,UAAU,EAAE,qBAAqB,EACjC,UAAU,EAAE,qBAAqB,EACjC,gBAAgB,EAAE,cAAc,EAChC,oBAAoB,CAAC,EAAE,oBAAoB,YAAA;IAGjD,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAAC,gBAAgB,CAAC;YA2B5C,iBAAiB;CA4IlC"}
|
|
@@ -1,21 +1,150 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.TransactionEventHandler = void 0;
|
|
4
|
+
const _1 = require(".");
|
|
5
|
+
const splitTypeUtils_1 = require("./utils/splitTypeUtils");
|
|
6
|
+
const userPathUtils_1 = require("./utils/userPathUtils");
|
|
7
|
+
const uuid_1 = require("uuid");
|
|
4
8
|
class TransactionEventHandler {
|
|
5
|
-
constructor(eventStore,
|
|
6
|
-
this.eventStore = eventStore;
|
|
9
|
+
constructor(modelStore, eventStore, txUserModelStore, activityEventHandler) {
|
|
7
10
|
this.modelStore = modelStore;
|
|
11
|
+
this.eventStore = eventStore;
|
|
12
|
+
this.txUserModelStore = txUserModelStore;
|
|
13
|
+
this.activityEventHandler = activityEventHandler;
|
|
8
14
|
}
|
|
9
|
-
async
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
async handleEvent(event) {
|
|
16
|
+
const validEventTypes = [
|
|
17
|
+
'TransactionCreated',
|
|
18
|
+
'TransactionDeleted',
|
|
19
|
+
'DescriptionChanged',
|
|
20
|
+
'TotalAmountChanged',
|
|
21
|
+
'SplitTypeChanged',
|
|
22
|
+
'CurrencyChanged',
|
|
23
|
+
'TransactionDateChanged'
|
|
24
|
+
];
|
|
25
|
+
const eventType = event.constructor.name;
|
|
26
|
+
if (!validEventTypes.includes(eventType)) {
|
|
27
|
+
throw new Error(`Unknown transaction event type: ${eventType}`);
|
|
28
|
+
}
|
|
13
29
|
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
30
|
+
const model = event.apply(existingModel);
|
|
31
|
+
await this.modelStore.save(model);
|
|
32
|
+
if (this.activityEventHandler) {
|
|
33
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, model);
|
|
34
|
+
}
|
|
35
|
+
await this.createMirrorEvent(event, model);
|
|
36
|
+
return model;
|
|
37
|
+
}
|
|
38
|
+
async createMirrorEvent(originalEvent, transactionModel) {
|
|
39
|
+
const eventType = originalEvent.constructor.name;
|
|
40
|
+
const recipientUserModel = await this.txUserModelStore.getByStreamId(transactionModel.recipientUserId);
|
|
41
|
+
const recipientPath = (0, userPathUtils_1.getUserEventPath)(recipientUserModel, transactionModel.recipientUserId);
|
|
42
|
+
const existingMirrorModel = await this.modelStore.findByUserAndLogicalTransactionId(transactionModel.recipientUserId, transactionModel.userId, transactionModel.logicalTransactionId);
|
|
43
|
+
const mirrorStreamId = existingMirrorModel ? existingMirrorModel.streamId : (0, uuid_1.v4)();
|
|
44
|
+
const mirrorVersion = existingMirrorModel ? existingMirrorModel.version + 1 : 1;
|
|
45
|
+
let mirrorEvent;
|
|
46
|
+
switch (eventType) {
|
|
47
|
+
case 'TransactionCreated': {
|
|
48
|
+
const txEvent = originalEvent;
|
|
49
|
+
const { amount, isOwed } = (0, splitTypeUtils_1.calculateMirrorAmountAndIsOwed)(txEvent.totalAmount, txEvent.amount, txEvent.splitType, txEvent.isOwed);
|
|
50
|
+
mirrorEvent = new _1.TransactionCreated({
|
|
51
|
+
userId: transactionModel.recipientUserId,
|
|
52
|
+
recipientUserId: transactionModel.userId,
|
|
53
|
+
logicalTransactionId: txEvent.logicalTransactionId,
|
|
54
|
+
description: txEvent.description,
|
|
55
|
+
currency: txEvent.currency,
|
|
56
|
+
splitType: (0, splitTypeUtils_1.reverseSplitType)(txEvent.splitType),
|
|
57
|
+
totalAmount: txEvent.totalAmount,
|
|
58
|
+
amount,
|
|
59
|
+
isOwed,
|
|
60
|
+
transactionDate: txEvent.transactionDate,
|
|
61
|
+
groupId: txEvent.groupId,
|
|
62
|
+
groupTransactionId: txEvent.groupTransactionId,
|
|
63
|
+
createdAt: txEvent.createdAt,
|
|
64
|
+
createdBy: txEvent.createdBy,
|
|
65
|
+
streamId: mirrorStreamId,
|
|
66
|
+
version: mirrorVersion,
|
|
67
|
+
});
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
case 'TransactionDeleted': {
|
|
71
|
+
const txEvent = originalEvent;
|
|
72
|
+
mirrorEvent = new _1.TransactionDeleted({
|
|
73
|
+
createdAt: txEvent.createdAt,
|
|
74
|
+
createdBy: txEvent.createdBy,
|
|
75
|
+
streamId: mirrorStreamId,
|
|
76
|
+
version: mirrorVersion,
|
|
77
|
+
});
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
case 'DescriptionChanged': {
|
|
81
|
+
const txEvent = originalEvent;
|
|
82
|
+
mirrorEvent = new _1.DescriptionChanged({
|
|
83
|
+
description: txEvent.description,
|
|
84
|
+
createdAt: txEvent.createdAt,
|
|
85
|
+
createdBy: txEvent.createdBy,
|
|
86
|
+
streamId: mirrorStreamId,
|
|
87
|
+
version: mirrorVersion,
|
|
88
|
+
});
|
|
89
|
+
break;
|
|
90
|
+
}
|
|
91
|
+
case 'TotalAmountChanged': {
|
|
92
|
+
const txEvent = originalEvent;
|
|
93
|
+
mirrorEvent = new _1.TotalAmountChanged({
|
|
94
|
+
totalAmount: txEvent.totalAmount,
|
|
95
|
+
createdAt: txEvent.createdAt,
|
|
96
|
+
createdBy: txEvent.createdBy,
|
|
97
|
+
streamId: mirrorStreamId,
|
|
98
|
+
version: mirrorVersion,
|
|
99
|
+
});
|
|
100
|
+
break;
|
|
101
|
+
}
|
|
102
|
+
case 'SplitTypeChanged': {
|
|
103
|
+
const txEvent = originalEvent;
|
|
104
|
+
const { amount, isOwed } = (0, splitTypeUtils_1.calculateMirrorAmountAndIsOwed)(txEvent.totalAmount, txEvent.amount, txEvent.splitType, txEvent.isOwed);
|
|
105
|
+
mirrorEvent = new _1.SplitTypeChanged({
|
|
106
|
+
splitType: (0, splitTypeUtils_1.reverseSplitType)(txEvent.splitType),
|
|
107
|
+
totalAmount: txEvent.totalAmount,
|
|
108
|
+
amount,
|
|
109
|
+
isOwed,
|
|
110
|
+
createdAt: txEvent.createdAt,
|
|
111
|
+
createdBy: txEvent.createdBy,
|
|
112
|
+
streamId: mirrorStreamId,
|
|
113
|
+
version: mirrorVersion,
|
|
114
|
+
});
|
|
115
|
+
break;
|
|
116
|
+
}
|
|
117
|
+
case 'CurrencyChanged': {
|
|
118
|
+
const txEvent = originalEvent;
|
|
119
|
+
mirrorEvent = new _1.CurrencyChanged({
|
|
120
|
+
currency: txEvent.currency,
|
|
121
|
+
createdAt: txEvent.createdAt,
|
|
122
|
+
createdBy: txEvent.createdBy,
|
|
123
|
+
streamId: mirrorStreamId,
|
|
124
|
+
version: mirrorVersion,
|
|
125
|
+
});
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
case 'TransactionDateChanged': {
|
|
129
|
+
const txEvent = originalEvent;
|
|
130
|
+
mirrorEvent = new _1.TransactionDateChanged({
|
|
131
|
+
transactionDate: txEvent.transactionDate,
|
|
132
|
+
createdAt: txEvent.createdAt,
|
|
133
|
+
createdBy: txEvent.createdBy,
|
|
134
|
+
streamId: mirrorStreamId,
|
|
135
|
+
version: mirrorVersion,
|
|
136
|
+
});
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
default:
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
await this.eventStore.addEvent(recipientPath.userId, mirrorEvent);
|
|
143
|
+
const updatedModel = mirrorEvent.apply(existingMirrorModel);
|
|
17
144
|
await this.modelStore.save(updatedModel);
|
|
18
|
-
|
|
145
|
+
if (this.activityEventHandler) {
|
|
146
|
+
await this.activityEventHandler.createActivityLog(mirrorEvent, existingMirrorModel, updatedModel);
|
|
147
|
+
}
|
|
19
148
|
}
|
|
20
149
|
}
|
|
21
150
|
exports.TransactionEventHandler = TransactionEventHandler;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { UserCreated, PlaceholderUserCreated, UserCurrencyChanged, UserPhoneNumberChanged, UserDisplayNameChanged, UserDeleted, PlaceholderUserMerged, UserConvertedToPlaceholder, UserModel } from '.';
|
|
2
|
+
import { UserModelStore } from '.';
|
|
3
|
+
import { ActivityEventHandler } from './ActivityEventHandler';
|
|
4
|
+
export declare class UserEventHandler {
|
|
5
|
+
private modelStore;
|
|
6
|
+
private activityEventHandler?;
|
|
7
|
+
constructor(modelStore: UserModelStore, activityEventHandler?: ActivityEventHandler | undefined);
|
|
8
|
+
handleUserCreated(event: UserCreated): Promise<UserModel>;
|
|
9
|
+
handlePlaceholderUserCreated(event: PlaceholderUserCreated): Promise<UserModel>;
|
|
10
|
+
handleUserCurrencyChanged(event: UserCurrencyChanged): Promise<UserModel>;
|
|
11
|
+
handleUserPhoneNumberChanged(event: UserPhoneNumberChanged): Promise<UserModel>;
|
|
12
|
+
handleUserDisplayNameChanged(event: UserDisplayNameChanged): Promise<UserModel>;
|
|
13
|
+
handleUserDeleted(event: UserDeleted): Promise<UserModel>;
|
|
14
|
+
handlePlaceholderUserMerged(event: PlaceholderUserMerged): Promise<UserModel>;
|
|
15
|
+
handleUserConvertedToPlaceholder(event: UserConvertedToPlaceholder): Promise<UserModel>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=UserEventHandler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"UserEventHandler.d.ts","sourceRoot":"","sources":["../src/UserEventHandler.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,WAAW,EACX,sBAAsB,EACtB,mBAAmB,EACnB,sBAAsB,EACtB,sBAAsB,EACtB,WAAW,EACX,qBAAqB,EACrB,0BAA0B,EAC1B,SAAS,EACZ,MAAM,GAAG,CAAC;AACX,OAAO,EAAC,cAAc,EAAC,MAAM,GAAG,CAAC;AACjC,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAE5D,qBAAa,gBAAgB;IAErB,OAAO,CAAC,UAAU;IAClB,OAAO,CAAC,oBAAoB,CAAC;gBADrB,UAAU,EAAE,cAAc,EAC1B,oBAAoB,CAAC,EAAE,oBAAoB,YAAA;IAGjD,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC;IAUzD,4BAA4B,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,CAAC;IAU/E,yBAAyB,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAC,SAAS,CAAC;IAUzE,4BAA4B,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,CAAC;IAU/E,4BAA4B,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,SAAS,CAAC;IAU/E,iBAAiB,CAAC,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,SAAS,CAAC;IAUzD,2BAA2B,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,SAAS,CAAC;IAU7E,gCAAgC,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAAC,SAAS,CAAC;CAShG"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.UserEventHandler = void 0;
|
|
4
|
+
class UserEventHandler {
|
|
5
|
+
constructor(modelStore, activityEventHandler) {
|
|
6
|
+
this.modelStore = modelStore;
|
|
7
|
+
this.activityEventHandler = activityEventHandler;
|
|
8
|
+
}
|
|
9
|
+
async handleUserCreated(event) {
|
|
10
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
11
|
+
const updatedModel = event.apply(existingModel);
|
|
12
|
+
await this.modelStore.save(updatedModel);
|
|
13
|
+
if (this.activityEventHandler) {
|
|
14
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
15
|
+
}
|
|
16
|
+
return updatedModel;
|
|
17
|
+
}
|
|
18
|
+
async handlePlaceholderUserCreated(event) {
|
|
19
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
20
|
+
const updatedModel = event.apply(existingModel);
|
|
21
|
+
await this.modelStore.save(updatedModel);
|
|
22
|
+
if (this.activityEventHandler) {
|
|
23
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
24
|
+
}
|
|
25
|
+
return updatedModel;
|
|
26
|
+
}
|
|
27
|
+
async handleUserCurrencyChanged(event) {
|
|
28
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
29
|
+
const updatedModel = event.apply(existingModel);
|
|
30
|
+
await this.modelStore.save(updatedModel);
|
|
31
|
+
if (this.activityEventHandler) {
|
|
32
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
33
|
+
}
|
|
34
|
+
return updatedModel;
|
|
35
|
+
}
|
|
36
|
+
async handleUserPhoneNumberChanged(event) {
|
|
37
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
38
|
+
const updatedModel = event.apply(existingModel);
|
|
39
|
+
await this.modelStore.save(updatedModel);
|
|
40
|
+
if (this.activityEventHandler) {
|
|
41
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
42
|
+
}
|
|
43
|
+
return updatedModel;
|
|
44
|
+
}
|
|
45
|
+
async handleUserDisplayNameChanged(event) {
|
|
46
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
47
|
+
const updatedModel = event.apply(existingModel);
|
|
48
|
+
await this.modelStore.save(updatedModel);
|
|
49
|
+
if (this.activityEventHandler) {
|
|
50
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
51
|
+
}
|
|
52
|
+
return updatedModel;
|
|
53
|
+
}
|
|
54
|
+
async handleUserDeleted(event) {
|
|
55
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
56
|
+
const updatedModel = event.apply(existingModel);
|
|
57
|
+
await this.modelStore.save(updatedModel);
|
|
58
|
+
if (this.activityEventHandler) {
|
|
59
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
60
|
+
}
|
|
61
|
+
return updatedModel;
|
|
62
|
+
}
|
|
63
|
+
async handlePlaceholderUserMerged(event) {
|
|
64
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
65
|
+
const updatedModel = event.apply(existingModel);
|
|
66
|
+
await this.modelStore.save(updatedModel);
|
|
67
|
+
if (this.activityEventHandler) {
|
|
68
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
69
|
+
}
|
|
70
|
+
return updatedModel;
|
|
71
|
+
}
|
|
72
|
+
async handleUserConvertedToPlaceholder(event) {
|
|
73
|
+
const existingModel = await this.modelStore.getByStreamIdWhereDeletedIsFalse(event.streamId);
|
|
74
|
+
const updatedModel = event.apply(existingModel);
|
|
75
|
+
await this.modelStore.save(updatedModel);
|
|
76
|
+
if (this.activityEventHandler) {
|
|
77
|
+
await this.activityEventHandler.createActivityLog(event, existingModel, updatedModel);
|
|
78
|
+
}
|
|
79
|
+
return updatedModel;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
exports.UserEventHandler = UserEventHandler;
|
package/dist/events.d.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export * from './TransactionEvents';
|
|
2
|
+
export * from './UserEvents';
|
|
3
|
+
export * from './FriendEvents';
|
|
4
|
+
export * from './GroupEvents';
|
|
5
|
+
export * from './GroupTransactionEvents';
|
|
6
|
+
export * from './ActivityLogEvents';
|
|
7
|
+
export * from './common/Event';
|
|
8
|
+
export * from './common/Model';
|
|
9
|
+
export * from './store/ModelStore';
|
|
10
|
+
export * from './store/TransactionEventStore';
|
|
11
|
+
export * from './store/TransactionModelStore';
|
|
12
|
+
export * from './store/UserEventStore';
|
|
13
|
+
export * from './store/UserModelStore';
|
|
14
|
+
export * from './store/FriendEventStore';
|
|
15
|
+
export * from './store/FriendModelStore';
|
|
16
|
+
export * from './store/GroupEventStore';
|
|
17
|
+
export * from './store/GroupModelStore';
|
|
18
|
+
export * from './store/GroupTransactionEventStore';
|
|
19
|
+
export * from './store/GroupTransactionModelStore';
|
|
20
|
+
export * from './store/ActivityLogEventStore';
|
|
21
|
+
export * from './store/ActivityLogModelStore';
|
|
22
|
+
export { TransactionEventHandler } from './TransactionEventHandler';
|
|
23
|
+
export { UserEventHandler } from './UserEventHandler';
|
|
24
|
+
export { FriendEventHandler } from './FriendEventHandler';
|
|
25
|
+
export { GroupEventHandler } from './GroupEventHandler';
|
|
26
|
+
export { GroupTransactionEventHandler } from './GroupTransactionEventHandler';
|
|
27
|
+
export { ActivityEventHandler } from './ActivityEventHandler';
|
|
28
|
+
export { reverseSplitType, calculateMirrorAmountAndIsOwed } from './utils/splitTypeUtils';
|
|
29
|
+
export { getUserEventPath } from './utils/userPathUtils';
|
|
30
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AACA,cAAc,qBAAqB,CAAC;AACpC,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,0BAA0B,CAAC;AACzC,cAAc,qBAAqB,CAAC;AAGpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,gBAAgB,CAAC;AAG/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,wBAAwB,CAAC;AACvC,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,0BAA0B,CAAC;AACzC,cAAc,yBAAyB,CAAC;AACxC,cAAc,yBAAyB,CAAC;AACxC,cAAc,oCAAoC,CAAC;AACnD,cAAc,oCAAoC,CAAC;AACnD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,+BAA+B,CAAC;AAG9C,OAAO,EAAC,uBAAuB,EAAC,MAAM,2BAA2B,CAAC;AAClE,OAAO,EAAC,gBAAgB,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAC,kBAAkB,EAAC,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAC,iBAAiB,EAAC,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAC,4BAA4B,EAAC,MAAM,gCAAgC,CAAC;AAC5E,OAAO,EAAC,oBAAoB,EAAC,MAAM,wBAAwB,CAAC;AAG5D,OAAO,EAAC,gBAAgB,EAAE,8BAA8B,EAAC,MAAM,wBAAwB,CAAC;AACxF,OAAO,EAAC,gBAAgB,EAAC,MAAM,uBAAuB,CAAC"}
|