Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hawk.api",
"version": "1.1.29",
"version": "1.1.30",
"main": "index.ts",
"license": "UNLICENSED",
"scripts": {
Expand All @@ -21,6 +21,8 @@
"devDependencies": {
"@shelf/jest-mongodb": "^1.2.2",
"@types/jest": "^26.0.8",
"@types/lodash.clonedeep": "^4.5.9",
"@types/lodash.mergewith": "^4.6.9",
"eslint": "^6.7.2",
"eslint-config-codex": "1.2.4",
"eslint-plugin-import": "^2.19.1",
Expand All @@ -37,7 +39,8 @@
"@graphql-tools/schema": "^8.5.1",
"@graphql-tools/utils": "^8.9.0",
"@hawk.so/nodejs": "^3.1.1",
"@hawk.so/types": "^0.1.31",
"@hawk.so/types": "^0.1.33",
"@n1ru4l/json-patch-plus": "^0.2.0",
"@types/amqp-connection-manager": "^2.0.4",
"@types/bson": "^4.0.5",
"@types/debug": "^4.1.5",
Expand Down Expand Up @@ -70,6 +73,8 @@
"graphql-upload": "^13",
"jsonwebtoken": "^8.5.1",
"lodash": "^4.17.15",
"lodash.clonedeep": "^4.5.0",
"lodash.mergewith": "^4.6.2",
"migrate-mongo": "^7.0.1",
"mime-types": "^2.1.25",
"mongodb": "^3.7.3",
Expand Down
115 changes: 115 additions & 0 deletions src/utils/merge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import mergeWith from 'lodash.mergewith';
import cloneDeep from 'lodash.clonedeep';
import { patch } from '@n1ru4l/json-patch-plus';
import { GroupedEventDBScheme, RepetitionDBScheme } from '@hawk.so/types';

/**
* One of the features of the events is that their repetition is the difference
* between the original, which greatly optimizes storage. So we need to restore
* the original repetition payload using the very first event and its difference
* between its repetition
*
* @param originalEvent - the very first event we received
* @param repetition - the difference with its repetition, for the repetition we want to display
* @returns fully assembled payload of the current repetition
*/
export function repetitionAssembler(originalEvent: Record<string, any>, repetition: { [key: string]: any }): any {
Comment thread
neSpecc marked this conversation as resolved.
Outdated
Comment thread
neSpecc marked this conversation as resolved.
Outdated
const customizer = (originalParam: any, repetitionParam: any): any => {
if (repetitionParam === null) {
return originalParam;
}

if (typeof repetitionParam === 'object' && typeof originalParam === 'object') {
/**
* If original event has null but repetition has some value, we need to return repetition value
*/
if (originalParam === null) {
return repetitionParam;
/**
* Otherwise, we need to recursively merge original and repetition values
*/
} else {
return repetitionAssembler(originalParam, repetitionParam);
}
}

return repetitionParam;
};

return mergeWith(cloneDeep(originalEvent), cloneDeep(repetition), customizer);
}

function parsePayloadField(payload: any, field: string) {
Comment thread
neSpecc marked this conversation as resolved.
Outdated
Comment thread
neSpecc marked this conversation as resolved.
Outdated
if (payload && payload[field] && typeof payload[field] === 'string') {
payload[field] = JSON.parse(payload[field]);
}

return payload;
}

function stringifyPayloadField(payload: any, field: string) {
Comment thread
neSpecc marked this conversation as resolved.
Outdated
if (payload && payload[field]) {
payload[field] = JSON.stringify(payload[field]);
}

return payload;
}

/**
* Helps to merge original event and repetition due to delta format,
* in case of old delta format, we need to patch the payload
* in case of new delta format, we need to assemble the payload
*
* @param originalEvent {HawkEvent} - The original event
* @param repetition {HawkEventRepetition} - The repetition to process
* @returns {HawkEvent} Updated event with processed repetition payload
*/
export function composeFullRepetitionEvent(originalEvent: GroupedEventDBScheme, repetition: RepetitionDBScheme | undefined): GroupedEventDBScheme {
/**
* Make a deep copy of the original event, because we need to avoid mutating the original event
*/
const event = cloneDeep(originalEvent);

if (!repetition) {
return event;
}

/**
* New delta format (repetition.delta is not null)
*/
if (repetition.delta) {
/**
* Parse addons and context fields from string to object before patching
*/
Comment thread
neSpecc marked this conversation as resolved.
event.payload = parsePayloadField(event.payload, 'addons');
event.payload = parsePayloadField(event.payload, 'context');

event.payload = patch({
left: event.payload,
delta: JSON.parse(repetition.delta),
});

/**
* Stringify addons and context fields from object to string after patching
*/
event.payload = stringifyPayloadField(event.payload, 'addons');
event.payload = stringifyPayloadField(event.payload, 'context');

return event;
}

/**
* New delta format (repetition.payload is null) and repetition.delta is null (there is no delta between original and repetition)
*/
if (!repetition.payload) {
return event;
}

/**
* Old delta format (repetition.payload is not null)
* @todo remove after July 5 2025
Comment thread
neSpecc marked this conversation as resolved.
Outdated
*/
event.payload = repetitionAssembler(event.payload, repetition.payload);

return event;
}
Loading
Loading