Skip to content

Commit

Permalink
TS - add length, long, lookup function
Browse files Browse the repository at this point in the history
  • Loading branch information
elisherer committed Sep 2, 2024
1 parent 0288ad5 commit be1e5df
Show file tree
Hide file tree
Showing 10 changed files with 618 additions and 15 deletions.
112 changes: 112 additions & 0 deletions javascript/json-transform/src/JsonHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,117 @@ const isEqual = (value: any, other: any): boolean => {
return areSimilar(value, other);
};

/**
* Builds a missing paths parent elements. e.g for a path of a.b.c the result would be {@code {a:{b:{c:value}}}}
*
* @param value the value to add at the end of the path
* @param location the location to build
* @return a wrapping element containing the value in it's path
*/
const wrapElement = (value: any, location: string[]) => {
let point: string | undefined;
let elm = value;

while ((point = location.pop()) != null) {
elm = { point: elm };
}
return elm;
};

/**
* Builds a deque list of paths from a jsonPath string value (including map keys selectors)
*
* @param jsonPath the json path to build from
* @return a dequeue with a list of element keys
*/
const extractPath = (jsonPath?: string | null) => {
let paths: string[] = [];
if (isNullOrUndefined(jsonPath) || jsonPath.trim() === "") {
return paths;
}
let sb = "";
const expecting: string[] = [];

for (let i = 0; i < jsonPath.length; i++) {
const c = jsonPath.charAt(i);
if (c == "." && expecting.length == 0 && sb.length > 0) {
paths.push(sb);
sb = "";
} else if (c == "[" && expecting.length == 0) {
expecting.push("]");
} else if (expecting.length > 0 && expecting[0] == c) {
expecting.shift();
} else if (c == "'" || c == '"') {
expecting.push(c);
} else {
sb += c;
}
}
if (sb.length > 0) {
paths.push(sb);
}

return paths;
};

/**
* Merges the given value object deep into the given root object at the path specified creating any missing path elements
*
* @param rootEl Object to merge values into
* @param value the value to merge into the root
* @param path the json path to merge the value at
* @return the updated root object
*/
function mergeInto(rootEl: Record<string, any>, value: any, path: string | null) {
let root = rootEl;
if (value == null || root == null) {
return root;
}
const location = extractPath(path);
let point: string | undefined;
let object = root;
while ((point = location.shift()) != null) {
if (point === "$") {
continue;
}
if (location.length == 0 && !isMap(value)) {
if (!isNullOrUndefined(value)) {
object[point] = value;
}
return root;
}
if (Object.prototype.hasOwnProperty.call(object, point)) {
const current = object[point];
if (isMap(current)) {
object = current;
} else if (Array.isArray(current)) {
current.push(wrapElement(value, location));
return root;
} else {
//we create an array and add ourselves
const arr: any[] = [];
arr.push(current);
arr.push(wrapElement(value, location));
object[point] = arr;
}
} else {
const elm = wrapElement(value, location);
if (!isNullOrUndefined(elm)) {
object[point] = elm;
}
return root;
}
}
//we merge
if (isMap(value)) {
Object.assign(object, value);
//const obj = value;
//const finalObject = object;
//Object.entries(obj).forEach(kv => finalObject[kv[0]] = kv[1]);
}
return root;
}

export {
isNullOrUndefined,
isMap,
Expand All @@ -200,4 +311,5 @@ export {
lenientJsonParse,
isTruthy,
isEqual,
mergeInto,
};
48 changes: 48 additions & 0 deletions javascript/json-transform/src/__tests__/JsonHelpers.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { describe, expect, test } from "vitest";
import { mergeInto } from "../JsonHelpers";

describe("JsonHelpers", () => {
test("mergeInto - GivenMutuallyExclusiveKeysWithDot", () => {
var root = {
"numbers.roman": { I: 1, II: 2 },
};
var mergee = {
"numbers.exist": true,
};
var expected = {
"numbers.roman": { I: 1, II: 2 },
"numbers.exist": true,
};
expect(expected).toEqual(mergeInto(root, mergee, null));
});

test("mergeInto - GivenNoPath", () => {
var root = {
"a.b.c[0]": "foovalue",
};
var mergee = {
a: { z: "barvalue" },
};
var expected = {
a: { z: "barvalue" },
"a.b.c[0]": "foovalue",
};
expect(expected).toEqual(mergeInto(root, mergee, null));
});

test("mergeInto - GivenMutuallyExclusiveKeysAndDollarPath", () => {
var root = {
roman: { I: 1, II: 2 },
};
var mergee = {
arithmetics: { exist: true },
symbols: ["I", "V", "X", "L", "C", "D", "M"],
};
var expected = {
roman: { I: 1, II: 2 },
arithmetics: { exist: true },
symbols: ["I", "V", "X", "L", "C", "D", "M"],
};
expect(expected).toEqual(mergeInto(root, mergee, "$"));
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { describe, test } from "vitest";
import { assertTransformation } from "../BaseTransformationTest";

describe("TransformerFunctionLength", () => {
test("autoDetect", async () => {
const str = "Hello World";
await assertTransformation(str, "$$length:hello world", 11);
await assertTransformation(str, "$$length():$", 11);
const arr = ["Hello", "World"];
await assertTransformation(arr, "$$length:$", 2);
const obj = { a: "Hello", b: "World", c: "foo", d: "bar" };
await assertTransformation(obj, "$$length:$", 4);
});

test("stringOnly", async () => {
const str = "Hello World";
await assertTransformation(str, "$$length(STRING):hello world", 11);
await assertTransformation(str, "$$length(STRING):$", 11);
const arr = ["Hello", "World"];
await assertTransformation(arr, "$$length(STRING):$", null);
const obj = { a: "Hello", b: "World", c: "foo", d: "bar" };
await assertTransformation(obj, "$$length(STRING):$", null);
});

test("arrayOnly", async () => {
const str = "Hello World";
await assertTransformation(str, "$$length(ARRAY):hello world", null);
await assertTransformation(str, "$$length(ARRAY):$", null);
const arr = ["Hello", "World"];
await assertTransformation(arr, "$$length(ARRAY):$", 2);
const obj = { a: "Hello", b: "World", c: "foo", d: "bar" };
await assertTransformation(obj, "$$length(ARRAY):$", null);
});

test("objectOnly", async () => {
const str = "Hello World";
await assertTransformation(str, "$$length(OBJECT):hello world", null);
await assertTransformation(str, "$$length(OBJECT):$", null);
const arr = ["Hello", "World"];
await assertTransformation(arr, "$$length(OBJECT):$", null);
const obj = { a: "Hello", b: "World", c: "foo", d: "bar" };
await assertTransformation(obj, "$$length(OBJECT):$", 4);
});

test("zeroDefault", async () => {
await assertTransformation(null, "$$length:$", null);
await assertTransformation(null, "$$length(AUTO,true):$", 0);
await assertTransformation(42, "$$length(AUTO,true):$", 0);
await assertTransformation(true, "$$length(AUTO,true):$", 0);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { describe, test } from "vitest";
import { assertTransformation } from "../BaseTransformationTest";
import { BigDecimal } from "../../functions/common/FunctionHelpers";
import BigNumber from "bignumber.js";

describe("TransformerFunctionLong", () => {
test("convert", async () => {
const decimals = "123456789.87654321";
const longVal = BigInt(new BigDecimal(decimals).toFixed(0, BigNumber.ROUND_DOWN));
await assertTransformation(decimals, "$$long:$", longVal);
await assertTransformation(decimals, "$$long():$", longVal);
await assertTransformation(123456789.87654321, "$$long:$", longVal);
await assertTransformation(null, "$$long:$", null);
});
test("maxLong", async () => {
const max = BigInt("9223372036854775807"); // MAX_LONG
await assertTransformation(max, "$$long:$", max);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import { describe, test } from "vitest";
import { assertTransformation } from "../BaseTransformationTest";
import { BigDecimal } from "../../functions/common/FunctionHelpers";
import BigNumber from "bignumber.js";

describe("TransformerFunctionLookup", () => {
test("mergeWithBy", async () => {
await assertTransformation(
{
a: [
{ id: 2, a: 1 },
{ id: 5, a: 2 },
],
b: [
{ id: 2, a: "x" },
{ id: 5, e: true },
],
},

{
$$lookup: "$.a",
using: [
{
with: "$.b",
as: "match",
on: {
$$is: "##current.id",
eq: "##match.id",
},
},
],
},
[
{ id: 2, a: "x" },
{ id: 5, a: 2, e: true },
],
);

await assertTransformation(
{
a: [
{ id: 2, a: 1 },
{ id: 5, a: 2 },
],
b: [
{ key: 2, a: "x" },
{ key: 5, e: true },
],
},

{
$$lookup: "$.a",
using: [
{
with: "$.b",
as: "match",
on: {
$$is: "##current.id",
eq: "##match.key",
},
},
],
},
[
{ id: 2, a: "x", key: 2 },
{ id: 5, a: 2, e: true, key: 5 },
],
);
});

test("mergeWithTo", async () => {
// don't override a, just copy e
await assertTransformation(
{
a: [
{ id: 2, a: 1 },
{ id: 5, a: 2 },
],
b: [
{ id: 2, a: "x" },
{ id: 5, e: true },
],
},

{
$$lookup: "$.a",
using: [
{
with: "$.b",
as: "match",
on: {
$$is: "##current.id",
eq: "##match.id",
},
},
],
to: {
"*": "##current",
e: "##match.e",
},
},
[
{ id: 2, a: 1 },
{ id: 5, a: 2, e: true },
],
);

await assertTransformation(
{
a1: [
{ id: "aaa", val: "a" },
{ id: "bbb", val: "b" },
],
a2: [
{ name: "aaa", val: "A" },
{ name: "bbb", val: "B" },
],
},
{
$$lookup: "$.a1",
using: [
{
with: "$.a2",
as: "a2",
on: {
$$is: "##current.id",
eq: "##a2.name",
},
},
],
to: ["##current.val", "##a2.val"],
},
[
["a", "A"],
["b", "B"],
],
);
});
});
Loading

0 comments on commit be1e5df

Please sign in to comment.