Skip to content

Commit c12d698

Browse files
committed
Merge branch 'master' into refactor-project-overview
2 parents 0758a12 + 055f47e commit c12d698

File tree

4 files changed

+541
-6
lines changed

4 files changed

+541
-6
lines changed

package.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "hawk.api",
3-
"version": "1.1.29",
3+
"version": "1.1.30",
44
"main": "index.ts",
55
"license": "UNLICENSED",
66
"scripts": {
@@ -21,6 +21,8 @@
2121
"devDependencies": {
2222
"@shelf/jest-mongodb": "^1.2.2",
2323
"@types/jest": "^26.0.8",
24+
"@types/lodash.clonedeep": "^4.5.9",
25+
"@types/lodash.mergewith": "^4.6.9",
2426
"eslint": "^6.7.2",
2527
"eslint-config-codex": "1.2.4",
2628
"eslint-plugin-import": "^2.19.1",
@@ -37,7 +39,8 @@
3739
"@graphql-tools/schema": "^8.5.1",
3840
"@graphql-tools/utils": "^8.9.0",
3941
"@hawk.so/nodejs": "^3.1.1",
40-
"@hawk.so/types": "^0.1.31",
42+
"@hawk.so/types": "^0.1.33",
43+
"@n1ru4l/json-patch-plus": "^0.2.0",
4144
"@types/amqp-connection-manager": "^2.0.4",
4245
"@types/bson": "^4.0.5",
4346
"@types/debug": "^4.1.5",
@@ -70,6 +73,8 @@
7073
"graphql-upload": "^13",
7174
"jsonwebtoken": "^8.5.1",
7275
"lodash": "^4.17.15",
76+
"lodash.clonedeep": "^4.5.0",
77+
"lodash.mergewith": "^4.6.2",
7378
"migrate-mongo": "^7.0.1",
7479
"mime-types": "^2.1.25",
7580
"mongodb": "^3.7.3",

src/utils/merge.ts

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
import mergeWith from 'lodash.mergewith';
2+
import cloneDeep from 'lodash.clonedeep';
3+
import { patch } from '@n1ru4l/json-patch-plus';
4+
import { GroupedEventDBScheme, RepetitionDBScheme } from '@hawk.so/types';
5+
6+
/**
7+
* One of the features of the events is that their repetition is the difference
8+
* between the original, which greatly optimizes storage. So we need to restore
9+
* the original repetition payload using the very first event and its difference
10+
* between its repetition
11+
*
12+
* @deprecated remove after 6 september 2025
13+
* @param originalEvent - the very first event we received
14+
* @param repetition - the difference with its repetition, for the repetition we want to display
15+
* @returns fully assembled payload of the current repetition
16+
*/
17+
export function repetitionAssembler(originalEvent: GroupedEventDBScheme['payload'], repetition: GroupedEventDBScheme['payload']): GroupedEventDBScheme['payload'] {
18+
const customizer = (originalParam: any, repetitionParam: any): any => {
19+
if (repetitionParam === null) {
20+
return originalParam;
21+
}
22+
23+
if (typeof repetitionParam === 'object' && typeof originalParam === 'object') {
24+
/**
25+
* If original event has null but repetition has some value, we need to return repetition value
26+
*/
27+
if (originalParam === null) {
28+
return repetitionParam;
29+
/**
30+
* Otherwise, we need to recursively merge original and repetition values
31+
*/
32+
} else {
33+
return repetitionAssembler(originalParam, repetitionParam);
34+
}
35+
}
36+
37+
return repetitionParam;
38+
};
39+
40+
return mergeWith(cloneDeep(originalEvent), cloneDeep(repetition), customizer);
41+
}
42+
43+
/**
44+
* Parse addons and context fields from string to object, in db it stores as string
45+
*
46+
* @param payload - the payload of the event
47+
* @param field - the field to parse, can be 'addons' or 'context'
48+
* @returns the payload with parsed field
49+
*/
50+
function parsePayloadField(payload: GroupedEventDBScheme['payload'], field: 'addons' | 'context') {
51+
if (payload && payload[field] && typeof payload[field] === 'string') {
52+
payload[field] = JSON.parse(payload[field] as string);
53+
}
54+
55+
return payload;
56+
}
57+
58+
/**
59+
* Stringify addons and context fields from object to string, in db it stores as string
60+
*
61+
* @param payload - the payload of the event
62+
* @param field - the field to stringify, can be 'addons' or 'context'
63+
* @returns the payload with stringified field
64+
*/
65+
function stringifyPayloadField(payload: GroupedEventDBScheme['payload'], field: 'addons' | 'context') {
66+
if (payload && payload[field]) {
67+
payload[field] = JSON.stringify(payload[field]);
68+
}
69+
70+
return payload;
71+
}
72+
73+
/**
74+
* Helps to merge original event and repetition due to delta format,
75+
* in case of old delta format, we need to patch the payload
76+
* in case of new delta format, we need to assemble the payload
77+
*
78+
* @param originalEvent {HawkEvent} - The original event
79+
* @param repetition {HawkEventRepetition} - The repetition to process
80+
* @returns {HawkEvent} Updated event with processed repetition payload
81+
*/
82+
export function composeFullRepetitionEvent(originalEvent: GroupedEventDBScheme, repetition: RepetitionDBScheme | undefined): GroupedEventDBScheme {
83+
/**
84+
* Make a deep copy of the original event, because we need to avoid mutating the original event
85+
*/
86+
const event = cloneDeep(originalEvent);
87+
88+
if (!repetition) {
89+
return event;
90+
}
91+
92+
/**
93+
* New delta format (repetition.delta is not null)
94+
*/
95+
if (repetition.delta) {
96+
/**
97+
* Parse addons and context fields from string to object before patching
98+
*/
99+
event.payload = parsePayloadField(event.payload, 'addons');
100+
event.payload = parsePayloadField(event.payload, 'context');
101+
102+
event.payload = patch({
103+
left: event.payload,
104+
delta: JSON.parse(repetition.delta),
105+
});
106+
107+
/**
108+
* Stringify addons and context fields from object to string after patching
109+
*/
110+
event.payload = stringifyPayloadField(event.payload, 'addons');
111+
event.payload = stringifyPayloadField(event.payload, 'context');
112+
113+
return event;
114+
}
115+
116+
/**
117+
* New delta format (repetition.payload is null) and repetition.delta is null (there is no delta between original and repetition)
118+
*/
119+
if (!repetition.payload) {
120+
return event;
121+
}
122+
123+
/**
124+
* Old delta format (repetition.payload is not null)
125+
* @todo remove after 6 september 2025
126+
*/
127+
event.payload = repetitionAssembler(event.payload, repetition.payload);
128+
129+
return event;
130+
}

0 commit comments

Comments
 (0)