fix: routing-forms reporting - "in" operator logic is broken + overall refactor (#17398)
* fix: routing-forms reporting - "in" operator logic is broken * fix * refactor to make it more readable * add tests
This commit is contained in:
@@ -227,83 +227,137 @@ describe("jsonLogicToPrisma(Reporting)", () => {
|
||||
],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should support where All Match ['Equals', 'Equals'] operator", () => {
|
||||
const prismaWhere = jsonLogicToPrisma({
|
||||
logic: {
|
||||
and: [
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "a"] },
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "b"] },
|
||||
it("should support 'Any In' operator", () => {
|
||||
const prismaWhere = jsonLogicToPrisma({
|
||||
logic: { and: [{ in: [{ var: "267c7817-81a5-4bef-9d5b-d0faa4cd0d71" }, ["C", "D"]] }] },
|
||||
});
|
||||
expect(prismaWhere).toEqual({
|
||||
AND: [
|
||||
{
|
||||
OR: [
|
||||
{
|
||||
response: {
|
||||
path: ["267c7817-81a5-4bef-9d5b-d0faa4cd0d71", "value"],
|
||||
equals: "C",
|
||||
},
|
||||
},
|
||||
{
|
||||
response: {
|
||||
path: ["267c7817-81a5-4bef-9d5b-d0faa4cd0d71", "value"],
|
||||
equals: "D",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
expect(prismaWhere).toEqual({
|
||||
AND: [
|
||||
{
|
||||
response: {
|
||||
path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"],
|
||||
equals: "a",
|
||||
it("should support 'Not In' operator", () => {
|
||||
const prismaWhere = jsonLogicToPrisma({
|
||||
logic: { and: [{ "!": { in: [{ var: "267c7817-81a5-4bef-9d5b-d0faa4cd0d71" }, ["C", "D"]] } }] },
|
||||
});
|
||||
expect(prismaWhere).toEqual({
|
||||
AND: [
|
||||
{
|
||||
NOT: {
|
||||
OR: [
|
||||
{
|
||||
response: {
|
||||
path: ["267c7817-81a5-4bef-9d5b-d0faa4cd0d71", "value"],
|
||||
equals: "C",
|
||||
},
|
||||
},
|
||||
{
|
||||
response: {
|
||||
path: ["267c7817-81a5-4bef-9d5b-d0faa4cd0d71", "value"],
|
||||
equals: "D",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
response: {
|
||||
path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"],
|
||||
equals: "b",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should support where Any Match ['Equals', 'Equals'] operator", () => {
|
||||
const prismaWhere = jsonLogicToPrisma({
|
||||
logic: {
|
||||
or: [
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "a"] },
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "b"] },
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
expect(prismaWhere).toEqual({
|
||||
OR: [
|
||||
{
|
||||
response: {
|
||||
path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"],
|
||||
equals: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
response: {
|
||||
path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"],
|
||||
equals: "b",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it("should support where None Match ['Equals', 'Equals'] operator", () => {
|
||||
const prismaWhere = jsonLogicToPrisma({
|
||||
logic: {
|
||||
"!": {
|
||||
or: [
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "abc"] },
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "abcd"] },
|
||||
it("should support where All Match ['Equals', 'Equals'] operator", () => {
|
||||
const prismaWhere = jsonLogicToPrisma({
|
||||
logic: {
|
||||
and: [
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "a"] },
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "b"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(prismaWhere).toEqual({
|
||||
AND: [
|
||||
{
|
||||
response: {
|
||||
path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"],
|
||||
equals: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
response: {
|
||||
path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"],
|
||||
equals: "b",
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
expect(prismaWhere).toEqual({
|
||||
NOT: {
|
||||
it("should support where Any Match ['Equals', 'Equals'] operator", () => {
|
||||
const prismaWhere = jsonLogicToPrisma({
|
||||
logic: {
|
||||
or: [
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "a"] },
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "b"] },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(prismaWhere).toEqual({
|
||||
OR: [
|
||||
{ response: { path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"], equals: "abc" } },
|
||||
{ response: { path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"], equals: "abcd" } },
|
||||
{
|
||||
response: {
|
||||
path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"],
|
||||
equals: "a",
|
||||
},
|
||||
},
|
||||
{
|
||||
response: {
|
||||
path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"],
|
||||
equals: "b",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it("should support where None Match ['Equals', 'Equals'] operator", () => {
|
||||
const prismaWhere = jsonLogicToPrisma({
|
||||
logic: {
|
||||
"!": {
|
||||
or: [
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "abc"] },
|
||||
{ "==": [{ var: "505d3c3c-aa71-4220-93a9-6fd1e1087939" }, "abcd"] },
|
||||
],
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(prismaWhere).toEqual({
|
||||
NOT: {
|
||||
OR: [
|
||||
{ response: { path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"], equals: "abc" } },
|
||||
{ response: { path: ["505d3c3c-aa71-4220-93a9-6fd1e1087939", "value"], equals: "abcd" } },
|
||||
],
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -75,6 +75,18 @@ const GROUP_OPERATOR_MAP = {
|
||||
} as const;
|
||||
|
||||
const NumberOperators = [">", ">=", "<", "<="];
|
||||
|
||||
const negatePrismaWhereClauseIfNeeded = (data: any, isNegation: boolean) => {
|
||||
if (!isNegation) {
|
||||
return data;
|
||||
}
|
||||
return {
|
||||
NOT: {
|
||||
...data,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
const convertSingleQueryToPrismaWhereClause = (
|
||||
operatorName: keyof typeof OPERATOR_MAP,
|
||||
logicData: LogicData,
|
||||
@@ -87,31 +99,72 @@ const convertSingleQueryToPrismaWhereClause = (
|
||||
const operands =
|
||||
logicData[operatorName] instanceof Array ? logicData[operatorName] : [logicData[operatorName]];
|
||||
|
||||
const mainOperand = operatorName !== "in" ? operands[0].var : operands[1].var;
|
||||
if (operatorName === "in" && operands[0]?.var && Array.isArray(operands[1])) {
|
||||
// Case A: Item "in" array
|
||||
// operands[0]: { var: ... }
|
||||
// operands[1]: items to test against
|
||||
|
||||
// Convert 'in' operator to union of OR clauses
|
||||
return negatePrismaWhereClauseIfNeeded(
|
||||
{
|
||||
OR: (operands[1] ?? []).map((value) => ({
|
||||
response: {
|
||||
path: [operands[0].var, "value"],
|
||||
[`${OPERATOR_MAP["=="].operator}`]: value,
|
||||
},
|
||||
})),
|
||||
},
|
||||
isNegation
|
||||
);
|
||||
}
|
||||
|
||||
if (operatorName === "in" && typeof operands[0] === "string" && operands[1]?.var) {
|
||||
// Case B: String "in" string
|
||||
// operands[0]: string to test against
|
||||
// operands[1]: { var: ... }
|
||||
|
||||
return negatePrismaWhereClauseIfNeeded(
|
||||
{
|
||||
response: {
|
||||
path: [operands[1].var, "value"],
|
||||
[`${prismaOperator}`]: operands[0],
|
||||
},
|
||||
},
|
||||
isNegation
|
||||
);
|
||||
}
|
||||
|
||||
const mainOperand = operands[0].var;
|
||||
let secondaryOperand;
|
||||
|
||||
let secondaryOperand = staticSecondaryOperand || (operatorName !== "in" ? operands[1] : operands[0]) || "";
|
||||
if (operatorName === "all") {
|
||||
secondaryOperand = secondaryOperand.in[1];
|
||||
secondaryOperand = operands[1].in[1];
|
||||
} else {
|
||||
secondaryOperand = staticSecondaryOperand || operands[1] || "";
|
||||
}
|
||||
|
||||
const isNumberOperator = NumberOperators.includes(operatorName);
|
||||
const secondaryOperandAsNumber = typeof secondaryOperand === "string" ? Number(secondaryOperand) : null;
|
||||
|
||||
let prismaWhere;
|
||||
if (secondaryOperandAsNumber) {
|
||||
if (secondaryOperandAsNumber && isNumberOperator) {
|
||||
// We know that it's number operator so Prisma should query number
|
||||
// Note that if we get string values in DB(e.g. '100'), those values can't be filtered with number operators.
|
||||
if (isNumberOperator) {
|
||||
prismaWhere = {
|
||||
return negatePrismaWhereClauseIfNeeded(
|
||||
{
|
||||
response: {
|
||||
path: [mainOperand, "value"],
|
||||
[`${prismaOperator}`]: secondaryOperandAsNumber,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// We know that it's not number operator but the input field might have been a number and thus stored value in DB as number.
|
||||
// Also, even for input type=number we might accidentally get string value(e.g. '100'). So, let reporting do it's best job with both number and string.
|
||||
prismaWhere = {
|
||||
},
|
||||
isNegation
|
||||
);
|
||||
}
|
||||
|
||||
if (secondaryOperandAsNumber && !isNumberOperator) {
|
||||
// We know that it's not number operator but the input field might have been a number and thus stored value in DB as number.
|
||||
// Also, even for input type=number we might accidentally get string value(e.g. '100'). So, let reporting do it's best job with both number and string.
|
||||
return negatePrismaWhereClauseIfNeeded(
|
||||
{
|
||||
OR: [
|
||||
{
|
||||
response: {
|
||||
@@ -128,25 +181,20 @@ const convertSingleQueryToPrismaWhereClause = (
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
} else {
|
||||
prismaWhere = {
|
||||
},
|
||||
isNegation
|
||||
);
|
||||
}
|
||||
|
||||
return negatePrismaWhereClauseIfNeeded(
|
||||
{
|
||||
response: {
|
||||
path: [mainOperand, "value"],
|
||||
[`${prismaOperator}`]: secondaryOperand,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (isNegation) {
|
||||
return {
|
||||
NOT: {
|
||||
...prismaWhere,
|
||||
},
|
||||
};
|
||||
}
|
||||
return prismaWhere;
|
||||
},
|
||||
isNegation
|
||||
);
|
||||
};
|
||||
|
||||
const isNegation = (logicData: LogicData | NegatedLogicData) => {
|
||||
|
||||
Reference in New Issue
Block a user