Skip to content

Commit 302f3f8

Browse files
authored
Merge pull request #505 from DrDroidLab/global_variable_dict
adds dict/list of dicts to global variable set
2 parents 88e95a1 + 80cd26e commit 302f3f8

File tree

5 files changed

+105
-17
lines changed

5 files changed

+105
-17
lines changed

executor/playbook_source_manager.py

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import json
12
from typing import Dict
23

34
from google.protobuf.struct_pb2 import Struct
@@ -27,6 +28,45 @@ def apply_result_transformer(result_dict, lambda_function: Lambda.Function) -> D
2728
return transformer_result
2829

2930

31+
def flatten_dict(prefix: str, d: dict) -> dict:
32+
items = {}
33+
for k, v in d.items():
34+
new_key = f"{prefix}.{k}"
35+
if isinstance(v, dict):
36+
items.update(flatten_dict(new_key, v))
37+
elif isinstance(v, str):
38+
items[new_key] = v.strip(" ")
39+
else:
40+
items[new_key] = v
41+
return items
42+
43+
44+
def get_flat_global_variables(global_variable_set: dict):
45+
flat_globals = {}
46+
for gk, gv in global_variable_set.items():
47+
parsed = None
48+
# If gv is already a dict, use it directly.
49+
if isinstance(gv, dict):
50+
parsed = gv
51+
else:
52+
try:
53+
parsed = json.loads(gv)
54+
except Exception:
55+
parsed = None
56+
if parsed is not None and isinstance(parsed, dict):
57+
# Flatten the dict with gk as the prefix.
58+
flat_from_obj = flatten_dict(gk, parsed)
59+
# Merge: only add if the key doesn't already exist.
60+
for fk, fv in flat_from_obj.items():
61+
if fk not in flat_globals:
62+
flat_globals[fk] = fv
63+
else:
64+
# Only add the direct key if it's not a dict.
65+
flat_globals[gk] = gv
66+
67+
return flat_globals
68+
69+
3070
def resolve_global_variables(form_fields: [FormField], global_variable_set: Struct,
3171
source_type_task_def: Dict) -> (Dict, Dict):
3272
all_string_fields = [ff.key_name.value for ff in form_fields if ff.data_type == LiteralType.STRING]
@@ -36,20 +76,23 @@ def resolve_global_variables(form_fields: [FormField], global_variable_set: Stru
3676
if ff.is_composite:
3777
all_composite_fields[ff.key_name.value] = ff.composite_fields
3878

79+
global_variable_set_dict = proto_to_dict(global_variable_set) if global_variable_set else {}
80+
global_variable_set = get_flat_global_variables(global_variable_set_dict)
81+
3982
task_local_variable_map = {}
4083
for gk, gv in global_variable_set.items():
4184
for tk, tv in source_type_task_def.items():
4285
if tk in all_string_fields:
4386
if gv is None:
4487
raise Exception(f"Global variable {gk} is None")
45-
source_type_task_def[tk] = tv.replace(gk, gv)
88+
source_type_task_def[tk] = tv.replace(gk, str(gv))
4689
if gk in tv:
4790
task_local_variable_map[gk] = gv
4891
elif tk in all_string_array_fields:
4992
for item in source_type_task_def[tk]:
5093
if gv is None:
5194
raise Exception(f"Global variable {gk} is None")
52-
source_type_task_def[tk] = item.replace(gk, gv)
95+
source_type_task_def[tk] = item.replace(gk, str(gv))
5396
if gk in tv:
5497
task_local_variable_map[gk] = gv
5598
elif tk in all_composite_fields:
@@ -59,7 +102,7 @@ def resolve_global_variables(form_fields: [FormField], global_variable_set: Stru
59102
if cf.data_type == LiteralType.STRING:
60103
if gv is None:
61104
raise Exception(f"Global variable {gk} is None")
62-
item[cf.key_name.value] = item[cf.key_name.value].replace(gk, gv)
105+
item[cf.key_name.value] = item[cf.key_name.value].replace(gk, str(gv))
63106
if gk in tv:
64107
task_local_variable_map[gk] = gv
65108
return source_type_task_def, task_local_variable_map

executor/views.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,16 @@ def bulk_task_run(request_message: RunPlaybookTaskRequestV3) -> Union[RunBulkPla
183183
message=Message(title="Invalid Request",
184184
description="Bulk execution variable not found in global variables"))
185185

186-
bulk_execution_var_values = execution_global_variable_set[bulk_task_var].split(',')
186+
bulk_value = execution_global_variable_set[bulk_task_var]
187+
try:
188+
parsed = json.loads(bulk_value)
189+
if isinstance(parsed, list):
190+
bulk_execution_var_values = parsed
191+
else:
192+
bulk_execution_var_values = bulk_value.split(',')
193+
except Exception:
194+
bulk_execution_var_values = bulk_value.split(',')
195+
187196
if not bulk_execution_var_values:
188197
return RunBulkPlaybookTaskResponse(meta=get_meta(tr=time_range), success=BoolValue(value=False),
189198
message=Message(title="Invalid Request",

web/src/components/common/GlobalVariable/AddVariableOverlay.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,15 @@ const AddVariableOverlay = ({ isOpen, close }) => {
6464
placeholder={"Enter variable value"}
6565
/>
6666
</div>
67-
<p className="text-xs mt-2 text-gray-500 italic">
68-
To enter an array variable, just enter the values with commas
69-
</p>
67+
<div>
68+
<p className="text-xs mt-2 text-gray-500 italic">
69+
To enter an array variable, just enter the values with commas
70+
</p>
71+
<p className="text-xs mt-2 text-gray-500 italic">
72+
To enter a list of objects(dicts) in a variable, just write the
73+
list (eg. {`[{ "key1": "value1" }, { "key2": "value1" }]`})
74+
</p>
75+
</div>
7076
<div className="flex items-center gap-2 mt-10">
7177
<CustomButton onClick={() => close()}>Cancel</CustomButton>
7278
<CustomButton onClick={handleSubmit}>Add</CustomButton>

web/src/components/common/GlobalVariable/index.tsx

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,13 @@ import {
66
updateGlobalVariable,
77
} from "../../../store/features/playbook/playbookSlice.ts";
88
import AddVariableOverlay from "./AddVariableOverlay.js";
9-
import { Add, CloseRounded } from "@mui/icons-material";
9+
import { Add, CloseRounded, DescriptionRounded } from "@mui/icons-material";
1010
import useIsPrefetched from "../../../hooks/playbooks/useIsPrefetched.ts";
1111
import CustomButton from "../CustomButton/index.tsx";
1212
import CustomInput from "../../Inputs/CustomInput.tsx";
1313
import { InputTypes } from "../../../types/inputs/inputTypes.ts";
1414
import { Tooltip } from "@mui/material";
15+
import { checkIfJSON } from "../../../utils/common/checkIfJSON.ts";
1516

1617
function GlobalVariables() {
1718
const [isAddVariableOpen, setIsAddVariableOpen] = useState(false);
@@ -61,15 +62,36 @@ function GlobalVariables() {
6162
</Tooltip>
6263
</div>
6364
<div className="flex gap-2 items-center">
64-
<CustomInput
65-
inputType={InputTypes.TEXT}
66-
value={variables[key]}
67-
placeholder={"Enter variable value"}
68-
className="!w-[200px]"
69-
handleChange={(val) => {
70-
dispatch(updateGlobalVariable({ name: key, value: val }));
71-
}}
72-
/>
65+
<div className="relative flex items-center gap-1">
66+
<CustomInput
67+
inputType={InputTypes.TEXT}
68+
value={variables[key]}
69+
placeholder={"Enter variable value"}
70+
className="!w-[200px]"
71+
handleChange={(val) => {
72+
dispatch(updateGlobalVariable({ name: key, value: val }));
73+
}}
74+
/>
75+
<div className="absolute right-2">
76+
{checkIfJSON(variables[key]) && (
77+
<Tooltip
78+
title={
79+
<pre style={{ margin: 0 }}>
80+
{JSON.stringify(
81+
JSON.parse(variables[key]),
82+
null,
83+
2,
84+
)}
85+
</pre>
86+
}>
87+
<DescriptionRounded
88+
className="text-gray-500 bg-white cursor-pointer"
89+
fontSize="small"
90+
/>
91+
</Tooltip>
92+
)}
93+
</div>
94+
</div>
7395
{!isPrefetched && (
7496
<CloseRounded
7597
onClick={() => handleDelete(key)}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
export const checkIfJSON = (str: string) => {
2+
try {
3+
JSON.parse(str);
4+
} catch (e) {
5+
return false;
6+
}
7+
return true;
8+
};

0 commit comments

Comments
 (0)