mirror of
https://github.com/OneUptime/oneuptime.git
synced 2026-04-06 00:32:12 +02:00
feat: enhance condition handling with input type support and value coercion
This commit is contained in:
@@ -9,6 +9,7 @@ import ComponentMetadata, {
|
||||
import ComponentID from "../../../../../Types/Workflow/ComponentID";
|
||||
import Components, {
|
||||
ConditionOperator,
|
||||
ConditionValueType,
|
||||
} from "../../../../../Types/Workflow/Components/Condition";
|
||||
import CaptureSpan from "../../../../Utils/Telemetry/CaptureSpan";
|
||||
|
||||
@@ -61,30 +62,69 @@ export default class IfElse extends ComponentCode {
|
||||
* Inject dependencies
|
||||
*/
|
||||
|
||||
for (const key in args) {
|
||||
if (key === "operator") {
|
||||
continue;
|
||||
// Get explicit types from dropdowns, default to text
|
||||
let input1Type: ConditionValueType =
|
||||
(args["input-1-type"] as ConditionValueType) ||
|
||||
ConditionValueType.Text;
|
||||
let input2Type: ConditionValueType =
|
||||
(args["input-2-type"] as ConditionValueType) ||
|
||||
ConditionValueType.Text;
|
||||
|
||||
// When types differ, coerce both to the more specific type
|
||||
// so comparisons like text "true" == boolean true work correctly.
|
||||
// Priority: Null/Undefined keep as-is, Boolean > Number > Text.
|
||||
if (input1Type !== input2Type) {
|
||||
const isNullish = (t: ConditionValueType): boolean =>
|
||||
t === ConditionValueType.Null ||
|
||||
t === ConditionValueType.Undefined;
|
||||
|
||||
if (!isNullish(input1Type) && !isNullish(input2Type)) {
|
||||
const typePriority: Record<string, number> = {
|
||||
[ConditionValueType.Boolean]: 2,
|
||||
[ConditionValueType.Number]: 1,
|
||||
[ConditionValueType.Text]: 0,
|
||||
};
|
||||
|
||||
const p1: number = typePriority[input1Type] ?? 0;
|
||||
const p2: number = typePriority[input2Type] ?? 0;
|
||||
const commonType: ConditionValueType =
|
||||
p1 >= p2 ? input1Type : input2Type;
|
||||
input1Type = commonType;
|
||||
input2Type = commonType;
|
||||
}
|
||||
|
||||
const value: JSONValue = args[key];
|
||||
|
||||
let shouldHaveQuotes: boolean = false;
|
||||
|
||||
if (
|
||||
typeof value === "string" &&
|
||||
value !== "null" &&
|
||||
value !== "undefined"
|
||||
) {
|
||||
shouldHaveQuotes = true;
|
||||
}
|
||||
|
||||
if (typeof value === "object") {
|
||||
args[key] = JSON.stringify(args[key]);
|
||||
}
|
||||
|
||||
args[key] = shouldHaveQuotes ? `"${args[key]}"` : args[key];
|
||||
}
|
||||
|
||||
type FormatValueFunction = (
|
||||
value: JSONValue,
|
||||
valueType: ConditionValueType,
|
||||
) => string;
|
||||
|
||||
const formatValue: FormatValueFunction = (
|
||||
value: JSONValue,
|
||||
valueType: ConditionValueType,
|
||||
): string => {
|
||||
const strValue: string = typeof value === "object"
|
||||
? JSON.stringify(value)
|
||||
: String(value ?? "");
|
||||
|
||||
switch (valueType) {
|
||||
case ConditionValueType.Boolean:
|
||||
return strValue === "true" ? "true" : "false";
|
||||
case ConditionValueType.Number:
|
||||
return isNaN(Number(strValue)) ? "0" : String(Number(strValue));
|
||||
case ConditionValueType.Null:
|
||||
return "null";
|
||||
case ConditionValueType.Undefined:
|
||||
return "undefined";
|
||||
case ConditionValueType.Text:
|
||||
default:
|
||||
return `"${strValue}"`;
|
||||
}
|
||||
};
|
||||
|
||||
args["input-1"] = formatValue(args["input-1"], input1Type);
|
||||
args["input-2"] = formatValue(args["input-2"], input2Type);
|
||||
|
||||
type SerializeFunction = (arg: string) => string;
|
||||
|
||||
const serialize: SerializeFunction = (arg: string): string => {
|
||||
@@ -107,13 +147,13 @@ export default class IfElse extends ComponentCode {
|
||||
`;
|
||||
|
||||
if (args["operator"] === ConditionOperator.Contains) {
|
||||
code += `return input1.includes(input2);`;
|
||||
code += `return String(input1).includes(String(input2));`;
|
||||
} else if (args["operator"] === ConditionOperator.DoesNotContain) {
|
||||
code += `return !input1.includes(input2);`;
|
||||
code += `return !String(input1).includes(String(input2));`;
|
||||
} else if (args["operator"] === ConditionOperator.StartsWith) {
|
||||
code += `return input1.startsWith(input2);`;
|
||||
code += `return String(input1).startsWith(String(input2));`;
|
||||
} else if (args["operator"] === ConditionOperator.EndsWith) {
|
||||
code += `return input1.endsWith(input2);`;
|
||||
code += `return String(input1).endsWith(String(input2));`;
|
||||
} else {
|
||||
code += `return input1 ${(args["operator"] as string) || "=="} input2;`;
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ export enum ComponentInputType {
|
||||
HTML = "HTML",
|
||||
Operator = "Operator",
|
||||
Markdown = "Markdown",
|
||||
ValueType = "Value Type",
|
||||
}
|
||||
|
||||
export enum ComponentType {
|
||||
|
||||
@@ -5,6 +5,14 @@ import ComponentMetadata, {
|
||||
ComponentType,
|
||||
} from "./../Component";
|
||||
|
||||
export enum ConditionValueType {
|
||||
Text = "text",
|
||||
Boolean = "boolean",
|
||||
Number = "number",
|
||||
Null = "null",
|
||||
Undefined = "undefined",
|
||||
}
|
||||
|
||||
export enum ConditionOperator {
|
||||
EqualTo = "==",
|
||||
NotEqualTo = "!=",
|
||||
@@ -27,6 +35,15 @@ const components: Array<ComponentMetadata> = [
|
||||
iconProp: IconProp.Condition,
|
||||
componentType: ComponentType.Component,
|
||||
arguments: [
|
||||
{
|
||||
type: ComponentInputType.ValueType,
|
||||
name: "Input 1 Type",
|
||||
description:
|
||||
"Type of Input 1. Defaults to Text if not selected.",
|
||||
placeholder: "Text",
|
||||
required: false,
|
||||
id: "input-1-type",
|
||||
},
|
||||
{
|
||||
type: ComponentInputType.Text,
|
||||
name: "Input 1",
|
||||
@@ -43,6 +60,15 @@ const components: Array<ComponentMetadata> = [
|
||||
required: true,
|
||||
id: "operator",
|
||||
},
|
||||
{
|
||||
type: ComponentInputType.ValueType,
|
||||
name: "Input 2 Type",
|
||||
description:
|
||||
"Type of Input 2. Defaults to Text if not selected.",
|
||||
placeholder: "Text",
|
||||
required: false,
|
||||
id: "input-2-type",
|
||||
},
|
||||
{
|
||||
type: ComponentInputType.Text,
|
||||
name: "Input 2",
|
||||
|
||||
@@ -8,7 +8,10 @@ import ComponentMetadata, {
|
||||
import Components, { Categories } from "../../../Types/Workflow/Components";
|
||||
import BaseModelComponentFactory from "../../../Types/Workflow/Components/BaseModel";
|
||||
import Entities from "../../../Models/DatabaseModels/Index";
|
||||
import { ConditionOperator } from "../../../Types/Workflow/Components/Condition";
|
||||
import {
|
||||
ConditionOperator,
|
||||
ConditionValueType,
|
||||
} from "../../../Types/Workflow/Components/Condition";
|
||||
|
||||
type LoadComponentsAndCategoriesFunction = () => {
|
||||
components: Array<ComponentMetadata>;
|
||||
@@ -230,6 +233,34 @@ export const componentInputTypeToFormFieldType: ComponentInputTypeToFormFieldTyp
|
||||
};
|
||||
}
|
||||
|
||||
if (componentInputType === ComponentInputType.ValueType) {
|
||||
return {
|
||||
fieldType: FormFieldSchemaType.Dropdown,
|
||||
dropdownOptions: [
|
||||
{
|
||||
label: "Text",
|
||||
value: ConditionValueType.Text,
|
||||
},
|
||||
{
|
||||
label: "Boolean",
|
||||
value: ConditionValueType.Boolean,
|
||||
},
|
||||
{
|
||||
label: "Number",
|
||||
value: ConditionValueType.Number,
|
||||
},
|
||||
{
|
||||
label: "Null",
|
||||
value: ConditionValueType.Null,
|
||||
},
|
||||
{
|
||||
label: "Undefined",
|
||||
value: ConditionValueType.Undefined,
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
if (componentInputType === ComponentInputType.Date) {
|
||||
return {
|
||||
fieldType: FormFieldSchemaType.Date,
|
||||
|
||||
Reference in New Issue
Block a user