fix(http_request): simplify JSON handling in requests (#9616)

This commit is contained in:
-LAN- 2024-10-22 15:37:37 +08:00 committed by GitHub
parent d992a809f5
commit 7263af13ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 174 additions and 13 deletions

View File

@ -1,5 +1,5 @@
import json import json
from collections.abc import Mapping, Sequence from collections.abc import Mapping
from copy import deepcopy from copy import deepcopy
from random import randint from random import randint
from typing import Any, Literal from typing import Any, Literal
@ -60,7 +60,7 @@ class Executor:
self.method = node_data.method self.method = node_data.method
self.auth = node_data.authorization self.auth = node_data.authorization
self.timeout = timeout self.timeout = timeout
self.params = None self.params = {}
self.headers = {} self.headers = {}
self.content = None self.content = None
self.files = None self.files = None
@ -108,8 +108,10 @@ class Executor:
case "raw-text": case "raw-text":
self.content = self.variable_pool.convert_template(data[0].value).text self.content = self.variable_pool.convert_template(data[0].value).text
case "json": case "json":
json_object = json.loads(data[0].value) json_string = self.variable_pool.convert_template(data[0].value).text
self.json = self._parse_object_contains_variables(json_object) json_object = json.loads(json_string)
self.json = json_object
# self.json = self._parse_object_contains_variables(json_object)
case "binary": case "binary":
file_selector = data[0].file file_selector = data[0].file
file_variable = self.variable_pool.get_file(file_selector) file_variable = self.variable_pool.get_file(file_selector)
@ -274,14 +276,6 @@ class Executor:
return raw return raw
def _parse_object_contains_variables(self, obj: str | dict | list, /) -> Mapping[str, Any] | Sequence[Any] | str:
if isinstance(obj, dict):
return {k: self._parse_object_contains_variables(v) for k, v in obj.items()}
elif isinstance(obj, list):
return [self._parse_object_contains_variables(v) for v in obj]
elif isinstance(obj, str):
return self.variable_pool.convert_template(obj).text
def _plain_text_to_dict(text: str, /) -> dict[str, str]: def _plain_text_to_dict(text: str, /) -> dict[str, str]:
""" """

View File

@ -1,3 +1,5 @@
import json
import httpx import httpx
from core.app.entities.app_invoke_entities import InvokeFrom from core.app.entities.app_invoke_entities import InvokeFrom
@ -14,7 +16,8 @@ from core.workflow.nodes.http_request import (
HttpRequestNodeBody, HttpRequestNodeBody,
HttpRequestNodeData, HttpRequestNodeData,
) )
from core.workflow.nodes.http_request.executor import _plain_text_to_dict from core.workflow.nodes.http_request.entities import HttpRequestNodeTimeout
from core.workflow.nodes.http_request.executor import Executor, _plain_text_to_dict
from models.enums import UserFrom from models.enums import UserFrom
from models.workflow import WorkflowNodeExecutionStatus, WorkflowType from models.workflow import WorkflowNodeExecutionStatus, WorkflowType
@ -200,3 +203,167 @@ def test_http_request_node_form_with_file(monkeypatch):
assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED assert result.status == WorkflowNodeExecutionStatus.SUCCEEDED
assert result.outputs is not None assert result.outputs is not None
assert result.outputs["body"] == "" assert result.outputs["body"] == ""
def test_executor_with_json_body_and_number_variable():
# Prepare the variable pool
variable_pool = VariablePool(
system_variables={},
user_inputs={},
)
variable_pool.add(["pre_node_id", "number"], 42)
# Prepare the node data
node_data = HttpRequestNodeData(
title="Test JSON Body with Number Variable",
method="post",
url="https://api.example.com/data",
authorization=HttpRequestNodeAuthorization(type="no-auth"),
headers="Content-Type: application/json",
params="",
body=HttpRequestNodeBody(
type="json",
data=[
BodyData(
key="",
type="text",
value='{"number": {{#pre_node_id.number#}}}',
)
],
),
)
# Initialize the Executor
executor = Executor(
node_data=node_data,
timeout=HttpRequestNodeTimeout(connect=10, read=30, write=30),
variable_pool=variable_pool,
)
# Check the executor's data
assert executor.method == "post"
assert executor.url == "https://api.example.com/data"
assert executor.headers == {"Content-Type": "application/json"}
assert executor.params == {}
assert executor.json == {"number": 42}
assert executor.data is None
assert executor.files is None
assert executor.content is None
# Check the raw request (to_log method)
raw_request = executor.to_log()
assert "POST /data HTTP/1.1" in raw_request
assert "Host: api.example.com" in raw_request
assert "Content-Type: application/json" in raw_request
assert '{"number": 42}' in raw_request
def test_executor_with_json_body_and_object_variable():
# Prepare the variable pool
variable_pool = VariablePool(
system_variables={},
user_inputs={},
)
variable_pool.add(["pre_node_id", "object"], {"name": "John Doe", "age": 30, "email": "john@example.com"})
# Prepare the node data
node_data = HttpRequestNodeData(
title="Test JSON Body with Object Variable",
method="post",
url="https://api.example.com/data",
authorization=HttpRequestNodeAuthorization(type="no-auth"),
headers="Content-Type: application/json",
params="",
body=HttpRequestNodeBody(
type="json",
data=[
BodyData(
key="",
type="text",
value="{{#pre_node_id.object#}}",
)
],
),
)
# Initialize the Executor
executor = Executor(
node_data=node_data,
timeout=HttpRequestNodeTimeout(connect=10, read=30, write=30),
variable_pool=variable_pool,
)
# Check the executor's data
assert executor.method == "post"
assert executor.url == "https://api.example.com/data"
assert executor.headers == {"Content-Type": "application/json"}
assert executor.params == {}
assert executor.json == {"name": "John Doe", "age": 30, "email": "john@example.com"}
assert executor.data is None
assert executor.files is None
assert executor.content is None
# Check the raw request (to_log method)
raw_request = executor.to_log()
assert "POST /data HTTP/1.1" in raw_request
assert "Host: api.example.com" in raw_request
assert "Content-Type: application/json" in raw_request
assert '"name": "John Doe"' in raw_request
assert '"age": 30' in raw_request
assert '"email": "john@example.com"' in raw_request
def test_executor_with_json_body_and_nested_object_variable():
# Prepare the variable pool
variable_pool = VariablePool(
system_variables={},
user_inputs={},
)
variable_pool.add(["pre_node_id", "object"], {"name": "John Doe", "age": 30, "email": "john@example.com"})
# Prepare the node data
node_data = HttpRequestNodeData(
title="Test JSON Body with Nested Object Variable",
method="post",
url="https://api.example.com/data",
authorization=HttpRequestNodeAuthorization(type="no-auth"),
headers="Content-Type: application/json",
params="",
body=HttpRequestNodeBody(
type="json",
data=[
BodyData(
key="",
type="text",
value='{"object": {{#pre_node_id.object#}}}',
)
],
),
)
# Initialize the Executor
executor = Executor(
node_data=node_data,
timeout=HttpRequestNodeTimeout(connect=10, read=30, write=30),
variable_pool=variable_pool,
)
# Check the executor's data
assert executor.method == "post"
assert executor.url == "https://api.example.com/data"
assert executor.headers == {"Content-Type": "application/json"}
assert executor.params == {}
assert executor.json == {"object": {"name": "John Doe", "age": 30, "email": "john@example.com"}}
assert executor.data is None
assert executor.files is None
assert executor.content is None
# Check the raw request (to_log method)
raw_request = executor.to_log()
assert "POST /data HTTP/1.1" in raw_request
assert "Host: api.example.com" in raw_request
assert "Content-Type: application/json" in raw_request
assert '"object": {' in raw_request
assert '"name": "John Doe"' in raw_request
assert '"age": 30' in raw_request
assert '"email": "john@example.com"' in raw_request