Add JSONLoaderDynamic node with JS frontend for auto-discovered outputs
Dynamic node reads JSON keys and exposes them as outputs automatically via 32 AnyType slots managed by a JS extension (show/hide/rename). Includes /json_manager/get_keys API route, bool-safe type handling, and workflow save/reload support. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,7 +3,10 @@ import os
|
||||
|
||||
import pytest
|
||||
|
||||
from json_loader import to_float, to_int, get_batch_item, read_json_data
|
||||
from json_loader import (
|
||||
to_float, to_int, get_batch_item, read_json_data,
|
||||
JSONLoaderDynamic, MAX_DYNAMIC_OUTPUTS,
|
||||
)
|
||||
|
||||
|
||||
class TestToFloat:
|
||||
@@ -75,3 +78,88 @@ class TestReadJsonData:
|
||||
p = tmp_path / "ok.json"
|
||||
p.write_text(json.dumps({"key": "val"}))
|
||||
assert read_json_data(str(p)) == {"key": "val"}
|
||||
|
||||
|
||||
class TestJSONLoaderDynamic:
|
||||
def _make_json(self, tmp_path, data):
|
||||
p = tmp_path / "test.json"
|
||||
p.write_text(json.dumps(data))
|
||||
return str(p)
|
||||
|
||||
def test_known_keys(self, tmp_path):
|
||||
path = self._make_json(tmp_path, {"name": "alice", "age": 30, "score": 9.5})
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(path, 1, output_keys="name,age,score")
|
||||
assert result[0] == "alice"
|
||||
assert result[1] == 30
|
||||
assert result[2] == 9.5
|
||||
|
||||
def test_empty_output_keys(self, tmp_path):
|
||||
path = self._make_json(tmp_path, {"name": "alice"})
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(path, 1, output_keys="")
|
||||
assert len(result) == MAX_DYNAMIC_OUTPUTS
|
||||
assert all(v == "" for v in result)
|
||||
|
||||
def test_pads_to_max(self, tmp_path):
|
||||
path = self._make_json(tmp_path, {"a": "1", "b": "2"})
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(path, 1, output_keys="a,b")
|
||||
assert len(result) == MAX_DYNAMIC_OUTPUTS
|
||||
assert result[0] == "1"
|
||||
assert result[1] == "2"
|
||||
assert all(v == "" for v in result[2:])
|
||||
|
||||
def test_type_preservation_int(self, tmp_path):
|
||||
path = self._make_json(tmp_path, {"count": 42})
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(path, 1, output_keys="count")
|
||||
assert result[0] == 42
|
||||
assert isinstance(result[0], int)
|
||||
|
||||
def test_type_preservation_float(self, tmp_path):
|
||||
path = self._make_json(tmp_path, {"rate": 3.14})
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(path, 1, output_keys="rate")
|
||||
assert result[0] == 3.14
|
||||
assert isinstance(result[0], float)
|
||||
|
||||
def test_type_preservation_str(self, tmp_path):
|
||||
path = self._make_json(tmp_path, {"label": "hello"})
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(path, 1, output_keys="label")
|
||||
assert result[0] == "hello"
|
||||
assert isinstance(result[0], str)
|
||||
|
||||
def test_bool_becomes_string(self, tmp_path):
|
||||
path = self._make_json(tmp_path, {"flag": True, "off": False})
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(path, 1, output_keys="flag,off")
|
||||
assert result[0] == "true"
|
||||
assert result[1] == "false"
|
||||
assert isinstance(result[0], str)
|
||||
|
||||
def test_missing_key_returns_empty_string(self, tmp_path):
|
||||
path = self._make_json(tmp_path, {"a": "1"})
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(path, 1, output_keys="a,nonexistent")
|
||||
assert result[0] == "1"
|
||||
assert result[1] == ""
|
||||
|
||||
def test_missing_file_returns_all_empty(self, tmp_path):
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(str(tmp_path / "nope.json"), 1, output_keys="a,b")
|
||||
assert len(result) == MAX_DYNAMIC_OUTPUTS
|
||||
assert result[0] == ""
|
||||
assert result[1] == ""
|
||||
|
||||
def test_batch_data(self, tmp_path):
|
||||
path = self._make_json(tmp_path, {
|
||||
"batch_data": [
|
||||
{"sequence_number": 1, "x": "first"},
|
||||
{"sequence_number": 2, "x": "second"},
|
||||
]
|
||||
})
|
||||
loader = JSONLoaderDynamic()
|
||||
result = loader.load_dynamic(path, 2, output_keys="x")
|
||||
assert result[0] == "second"
|
||||
|
||||
Reference in New Issue
Block a user