Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 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
7 changes: 6 additions & 1 deletion 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 @@ -38,6 +40,7 @@
"@graphql-tools/utils": "^8.9.0",
"@hawk.so/nodejs": "^3.1.1",
"@hawk.so/types": "^0.1.31",
"@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
127 changes: 127 additions & 0 deletions src/utils/merge.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import mergeWith from 'lodash.mergewith';
import cloneDeep from 'lodash.clonedeep';
import { patch } from '@n1ru4l/json-patch-plus';

type HawkEvent = {
payload: {
[key: string]: any;
};
}

type HawkEventRepetition = {
payload: {
[key: string]: any;
};
delta: string;
}

/**
* 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 {
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) {
if (payload && payload[field] && typeof payload[field] === 'string') {
payload[field] = JSON.parse(payload[field]);
}

return payload;
}

function stringifyPayloadField(payload: any, field: string) {
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: HawkEvent, repetition: HawkEventRepetition | undefined): HawkEvent {
/**
* 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
*/
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
*/
event.payload = repetitionAssembler(event.payload, repetition.payload);

return event;
}
34 changes: 34 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,11 @@
semver "^7.3.5"
tar "^6.1.11"

"@n1ru4l/json-patch-plus@^0.2.0":
version "0.2.0"
resolved "https://registry.yarnpkg.com/@n1ru4l/json-patch-plus/-/json-patch-plus-0.2.0.tgz#b8fa09fd980c3460dfdc109a7c4cc5590157aa6b"
integrity sha512-pLkJy83/rVfDTyQgDSC8GeXAHEdXNHGNJrB1b7wAyGQu0iv7tpMXntKVSqj0+XKNVQbco40SZffNfVALzIt0SQ==

"@phc/format@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@phc/format/-/format-1.0.0.tgz#b5627003b3216dc4362125b13f48a4daa76680e4"
Expand Down Expand Up @@ -1077,6 +1082,25 @@
"@types/koa-compose" "*"
"@types/node" "*"

"@types/lodash.clonedeep@^4.5.9":
version "4.5.9"
resolved "https://registry.yarnpkg.com/@types/lodash.clonedeep/-/lodash.clonedeep-4.5.9.tgz#ea48276c7cc18d080e00bb56cf965bcceb3f0fc1"
integrity sha512-19429mWC+FyaAhOLzsS8kZUsI+/GmBAQ0HFiCPsKGU+7pBXOQWhyrY6xNNDwUSX8SMZMJvuFVMF9O5dQOlQK9Q==
dependencies:
"@types/lodash" "*"

"@types/lodash.mergewith@^4.6.9":
version "4.6.9"
resolved "https://registry.yarnpkg.com/@types/lodash.mergewith/-/lodash.mergewith-4.6.9.tgz#7093028a36de3cae4495d03b9d92c351cab1f8bf"
integrity sha512-fgkoCAOF47K7sxrQ7Mlud2TH023itugZs2bUg8h/KzT+BnZNrR2jAOmaokbLunHNnobXVWOezAeNn/lZqwxkcw==
dependencies:
"@types/lodash" "*"

"@types/lodash@*":
version "4.17.20"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.17.20.tgz#1ca77361d7363432d29f5e55950d9ec1e1c6ea93"
integrity sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==

"@types/long@^4.0.0":
version "4.0.2"
resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a"
Expand Down Expand Up @@ -4616,6 +4640,11 @@ lockfile@^1.0.4:
dependencies:
signal-exit "^3.0.2"

lodash.clonedeep@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef"
integrity sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==

lodash.includes@^4.3.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f"
Expand Down Expand Up @@ -4646,6 +4675,11 @@ lodash.isstring@^4.0.1:
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451"
integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==

lodash.mergewith@^4.6.2:
version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz#617121f89ac55f59047c7aec1ccd6654c6590f55"
integrity sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==

lodash.once@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac"
Expand Down