Skip to content

Commit

Permalink
fix(web): parse properties(feature) recursively before using in evalu…
Browse files Browse the repository at this point in the history
…ator at core (#479)
  • Loading branch information
pyshx authored Jun 7, 2023
1 parent 9dc0356 commit 20635bf
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 24 deletions.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { JSONPath } from "jsonpath-plus";

import { generateRandomString } from "../utils";

import { JPLiteral } from "./expression";
import { generateRandomString } from "./utils";

export const VARIABLE_PREFIX = "czm_";

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export function replaceVariables(expression: string, feature?: any): [string, JPLiteral[]] {
let exp = expression;
let result = "";
const literalJP: JPLiteral[] = [];
let i = exp.indexOf("${");
const featureDefined = typeof feature !== "undefined";
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const jsonPathCache: Record<string, any[]> = {};
const varExpRegex = /^\$./;
while (i >= 0) {
Expand Down
13 changes: 10 additions & 3 deletions web/src/beta/lib/core/mantle/evaluator/simple/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { ConditionalExpression } from "./conditionalExpression";
import { clearExpressionCaches, Expression } from "./expression";
import { evalTimeInterval } from "./interval";
import { recursiveJSONParse } from "./utils";

export async function evalSimpleLayer(
layer: LayerSimple,
Expand Down Expand Up @@ -65,6 +66,7 @@ export function evalLayerAppearances(
);
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function recursiveValEval(obj: any, layer: LayerSimple, feature?: Feature): any {
return Object.fromEntries(
Object.entries(obj).map(([k, v]) => {
Expand All @@ -88,6 +90,7 @@ export function clearAllExpressionCaches(
});
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function recursiveClear(obj: any, layer: LayerSimple | undefined, feature: Feature | undefined) {
Object.entries(obj).forEach(([, v]) => {
// if v is an object itself and not a null, recurse deeper
Expand All @@ -110,30 +113,34 @@ function recursiveClear(obj: any, layer: LayerSimple | undefined, feature: Featu
});
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function hasExpression(e: any): e is ExpressionContainer {
return typeof e === "object" && e && "expression" in e;
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function hasNonExpressionObject(v: any): boolean {
return typeof v === "object" && v && !("expression" in v);
}

function evalExpression(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
expressionContainer: any,
layer: LayerSimple,
feature?: Feature,
): unknown | undefined {
try {
if (hasExpression(expressionContainer)) {
const styleExpression = expressionContainer.expression;
const parsedFeature = recursiveJSONParse(feature);
if (typeof styleExpression === "undefined") {
return undefined;
} else if (typeof styleExpression === "object" && styleExpression.conditions) {
return new ConditionalExpression(styleExpression, feature, layer.defines).evaluate();
return new ConditionalExpression(styleExpression, parsedFeature, layer.defines).evaluate();
} else if (typeof styleExpression === "boolean" || typeof styleExpression === "number") {
return new Expression(String(styleExpression), feature, layer.defines).evaluate();
return new Expression(String(styleExpression), parsedFeature, layer.defines).evaluate();
} else if (typeof styleExpression === "string") {
return new Expression(styleExpression, feature, layer.defines).evaluate();
return new Expression(styleExpression, parsedFeature, layer.defines).evaluate();
}
return styleExpression;
}
Expand Down
56 changes: 56 additions & 0 deletions web/src/beta/lib/core/mantle/evaluator/simple/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { expect, test, describe } from "vitest";

import { recursiveJSONParse } from "./utils"; // Import the function from your module/file

describe("recursiveJSONParse", () => {
test("should parse nested JSON strings within an object", () => {
const obj = {
name: "John",
age: "25",
data: '{"city": "New York", "country": "USA"}',
};
const expected = {
name: "John",
age: "25",
data: {
city: "New York",
country: "USA",
},
};

const result = recursiveJSONParse(obj);
expect(result).toEqual(expected);
});

test("should handle non-object values and null", () => {
const str = "Test";
const num = 123;
const bool = true;
const arr = ["one", "two"];
const emptyObj = {};
const nullValue = null;

expect(recursiveJSONParse(str)).toBe(str);
expect(recursiveJSONParse(num)).toBe(num);
expect(recursiveJSONParse(bool)).toBe(bool);
expect(recursiveJSONParse(arr)).toEqual(arr);
expect(recursiveJSONParse(emptyObj)).toEqual(emptyObj);
expect(recursiveJSONParse(nullValue)).toBe(nullValue);
});

test("should handle invalid JSON strings", () => {
const obj = {
name: "John",
age: "25",
data: '{city: "New York", country: "USA"}', // Invalid JSON string
};
const expected = {
name: "John",
age: "25",
data: '{city: "New York", country: "USA"}', // Invalid JSON string remains unchanged
};

const result = recursiveJSONParse(obj);
expect(result).toEqual(expected);
});
});
31 changes: 31 additions & 0 deletions web/src/beta/lib/core/mantle/evaluator/simple/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
const usableChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

export const generateRandomString = (len: number): string => {
return Array.from(window.crypto.getRandomValues(new Uint8Array(len)))
.map(n => usableChars[n % len])
.join("")
.toLowerCase();
};

// eslint-disable-next-line @typescript-eslint/no-explicit-any
export const recursiveJSONParse = (obj: any): any => {
if (typeof obj !== "object" || obj === null) {
return obj;
}

for (const key in obj) {
if (typeof obj[key] === "string") {
try {
if (obj[key].startsWith("{") && obj[key].endsWith("}")) {
obj[key] = JSON.parse(obj[key]);
}
} catch (error) {
console.error("Invalid JSON:", obj[key]);
}
} else if (typeof obj[key] === "object") {
obj[key] = recursiveJSONParse(obj[key]);
}
}

return obj;
};

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { JSONPath } from "jsonpath-plus";

import { generateRandomString } from "../utils";

import { JPLiteral } from "./expression";
import { generateRandomString } from "./utils";

export const VARIABLE_PREFIX = "czm_";

Expand Down
8 changes: 5 additions & 3 deletions web/src/classic/core/mantle/evaluator/simple/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
import { ConditionalExpression } from "./conditionalExpression";
import { clearExpressionCaches, Expression } from "./expression";
import { evalTimeInterval } from "./interval";
import { recursiveJSONParse } from "./utils";

export async function evalSimpleLayer(
layer: LayerSimple,
Expand Down Expand Up @@ -126,14 +127,15 @@ function evalExpression(
try {
if (hasExpression(expressionContainer)) {
const styleExpression = expressionContainer.expression;
const parsedFeature = recursiveJSONParse(feature);
if (typeof styleExpression === "undefined") {
return undefined;
} else if (typeof styleExpression === "object" && styleExpression.conditions) {
return new ConditionalExpression(styleExpression, feature, layer.defines).evaluate();
return new ConditionalExpression(styleExpression, parsedFeature, layer.defines).evaluate();
} else if (typeof styleExpression === "boolean" || typeof styleExpression === "number") {
return new Expression(String(styleExpression), feature, layer.defines).evaluate();
return new Expression(String(styleExpression), parsedFeature, layer.defines).evaluate();
} else if (typeof styleExpression === "string") {
return new Expression(styleExpression, feature, layer.defines).evaluate();
return new Expression(styleExpression, parsedFeature, layer.defines).evaluate();
}
return styleExpression;
}
Expand Down
56 changes: 56 additions & 0 deletions web/src/classic/core/mantle/evaluator/simple/utils.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { expect, test, describe } from "vitest";

import { recursiveJSONParse } from "./utils"; // Import the function from your module/file

describe("recursiveJSONParse", () => {
test("should parse nested JSON strings within an object", () => {
const obj = {
name: "John",
age: "25",
data: '{"city": "New York", "country": "USA"}',
};
const expected = {
name: "John",
age: "25",
data: {
city: "New York",
country: "USA",
},
};

const result = recursiveJSONParse(obj);
expect(result).toEqual(expected);
});

test("should handle non-object values and null", () => {
const str = "Test";
const num = 123;
const bool = true;
const arr = ["one", "two"];
const emptyObj = {};
const nullValue = null;

expect(recursiveJSONParse(str)).toBe(str);
expect(recursiveJSONParse(num)).toBe(num);
expect(recursiveJSONParse(bool)).toBe(bool);
expect(recursiveJSONParse(arr)).toEqual(arr);
expect(recursiveJSONParse(emptyObj)).toEqual(emptyObj);
expect(recursiveJSONParse(nullValue)).toBe(nullValue);
});

test("should handle invalid JSON strings", () => {
const obj = {
name: "John",
age: "25",
data: '{city: "New York", country: "USA"}', // Invalid JSON string
};
const expected = {
name: "John",
age: "25",
data: '{city: "New York", country: "USA"}', // Invalid JSON string remains unchanged
};

const result = recursiveJSONParse(obj);
expect(result).toEqual(expected);
});
});
30 changes: 30 additions & 0 deletions web/src/classic/core/mantle/evaluator/simple/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
const usableChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

export const generateRandomString = (len: number): string => {
return Array.from(window.crypto.getRandomValues(new Uint8Array(len)))
.map(n => usableChars[n % len])
.join("")
.toLowerCase();
};

export const recursiveJSONParse = (obj: any): any => {
if (typeof obj !== "object" || obj === null) {
return obj;
}

for (const key in obj) {
if (typeof obj[key] === "string") {
try {
if (obj[key].startsWith("{") && obj[key].endsWith("}")) {
obj[key] = JSON.parse(obj[key]);
}
} catch (error) {
console.error("Invalid JSON:", obj[key]);
}
} else if (typeof obj[key] === "object") {
obj[key] = recursiveJSONParse(obj[key]);
}
}

return obj;
};

0 comments on commit 20635bf

Please sign in to comment.