hexdag 0.5.0.dev1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- hexdag/__init__.py +116 -0
- hexdag/__main__.py +30 -0
- hexdag/adapters/executors/__init__.py +5 -0
- hexdag/adapters/executors/local_executor.py +316 -0
- hexdag/builtin/__init__.py +6 -0
- hexdag/builtin/adapters/__init__.py +51 -0
- hexdag/builtin/adapters/anthropic/__init__.py +5 -0
- hexdag/builtin/adapters/anthropic/anthropic_adapter.py +151 -0
- hexdag/builtin/adapters/database/__init__.py +6 -0
- hexdag/builtin/adapters/database/csv/csv_adapter.py +249 -0
- hexdag/builtin/adapters/database/pgvector/__init__.py +5 -0
- hexdag/builtin/adapters/database/pgvector/pgvector_adapter.py +478 -0
- hexdag/builtin/adapters/database/sqlalchemy/sqlalchemy_adapter.py +252 -0
- hexdag/builtin/adapters/database/sqlite/__init__.py +5 -0
- hexdag/builtin/adapters/database/sqlite/sqlite_adapter.py +410 -0
- hexdag/builtin/adapters/local/README.md +59 -0
- hexdag/builtin/adapters/local/__init__.py +7 -0
- hexdag/builtin/adapters/local/local_observer_manager.py +696 -0
- hexdag/builtin/adapters/memory/__init__.py +47 -0
- hexdag/builtin/adapters/memory/file_memory_adapter.py +297 -0
- hexdag/builtin/adapters/memory/in_memory_memory.py +216 -0
- hexdag/builtin/adapters/memory/schemas.py +57 -0
- hexdag/builtin/adapters/memory/session_memory.py +178 -0
- hexdag/builtin/adapters/memory/sqlite_memory_adapter.py +215 -0
- hexdag/builtin/adapters/memory/state_memory.py +280 -0
- hexdag/builtin/adapters/mock/README.md +89 -0
- hexdag/builtin/adapters/mock/__init__.py +15 -0
- hexdag/builtin/adapters/mock/hexdag.toml +50 -0
- hexdag/builtin/adapters/mock/mock_database.py +225 -0
- hexdag/builtin/adapters/mock/mock_embedding.py +223 -0
- hexdag/builtin/adapters/mock/mock_llm.py +177 -0
- hexdag/builtin/adapters/mock/mock_tool_adapter.py +192 -0
- hexdag/builtin/adapters/mock/mock_tool_router.py +232 -0
- hexdag/builtin/adapters/openai/__init__.py +5 -0
- hexdag/builtin/adapters/openai/openai_adapter.py +634 -0
- hexdag/builtin/adapters/secret/__init__.py +7 -0
- hexdag/builtin/adapters/secret/local_secret_adapter.py +248 -0
- hexdag/builtin/adapters/unified_tool_router.py +280 -0
- hexdag/builtin/macros/__init__.py +17 -0
- hexdag/builtin/macros/conversation_agent.py +390 -0
- hexdag/builtin/macros/llm_macro.py +151 -0
- hexdag/builtin/macros/reasoning_agent.py +423 -0
- hexdag/builtin/macros/tool_macro.py +380 -0
- hexdag/builtin/nodes/__init__.py +38 -0
- hexdag/builtin/nodes/_discovery.py +123 -0
- hexdag/builtin/nodes/agent_node.py +696 -0
- hexdag/builtin/nodes/base_node_factory.py +242 -0
- hexdag/builtin/nodes/composite_node.py +926 -0
- hexdag/builtin/nodes/data_node.py +201 -0
- hexdag/builtin/nodes/expression_node.py +487 -0
- hexdag/builtin/nodes/function_node.py +454 -0
- hexdag/builtin/nodes/llm_node.py +491 -0
- hexdag/builtin/nodes/loop_node.py +920 -0
- hexdag/builtin/nodes/mapped_input.py +518 -0
- hexdag/builtin/nodes/port_call_node.py +269 -0
- hexdag/builtin/nodes/tool_call_node.py +195 -0
- hexdag/builtin/nodes/tool_utils.py +390 -0
- hexdag/builtin/prompts/__init__.py +68 -0
- hexdag/builtin/prompts/base.py +422 -0
- hexdag/builtin/prompts/chat_prompts.py +303 -0
- hexdag/builtin/prompts/error_correction_prompts.py +320 -0
- hexdag/builtin/prompts/tool_prompts.py +160 -0
- hexdag/builtin/tools/builtin_tools.py +84 -0
- hexdag/builtin/tools/database_tools.py +164 -0
- hexdag/cli/__init__.py +17 -0
- hexdag/cli/__main__.py +7 -0
- hexdag/cli/commands/__init__.py +27 -0
- hexdag/cli/commands/build_cmd.py +812 -0
- hexdag/cli/commands/create_cmd.py +208 -0
- hexdag/cli/commands/docs_cmd.py +293 -0
- hexdag/cli/commands/generate_types_cmd.py +252 -0
- hexdag/cli/commands/init_cmd.py +188 -0
- hexdag/cli/commands/pipeline_cmd.py +494 -0
- hexdag/cli/commands/plugin_dev_cmd.py +529 -0
- hexdag/cli/commands/plugins_cmd.py +441 -0
- hexdag/cli/commands/studio_cmd.py +101 -0
- hexdag/cli/commands/validate_cmd.py +221 -0
- hexdag/cli/main.py +84 -0
- hexdag/core/__init__.py +83 -0
- hexdag/core/config/__init__.py +20 -0
- hexdag/core/config/loader.py +479 -0
- hexdag/core/config/models.py +150 -0
- hexdag/core/configurable.py +294 -0
- hexdag/core/context/__init__.py +37 -0
- hexdag/core/context/execution_context.py +378 -0
- hexdag/core/docs/__init__.py +26 -0
- hexdag/core/docs/extractors.py +678 -0
- hexdag/core/docs/generators.py +890 -0
- hexdag/core/docs/models.py +120 -0
- hexdag/core/domain/__init__.py +10 -0
- hexdag/core/domain/dag.py +1225 -0
- hexdag/core/exceptions.py +234 -0
- hexdag/core/expression_parser.py +569 -0
- hexdag/core/logging.py +449 -0
- hexdag/core/models/__init__.py +17 -0
- hexdag/core/models/base.py +138 -0
- hexdag/core/orchestration/__init__.py +46 -0
- hexdag/core/orchestration/body_executor.py +481 -0
- hexdag/core/orchestration/components/__init__.py +97 -0
- hexdag/core/orchestration/components/adapter_lifecycle_manager.py +113 -0
- hexdag/core/orchestration/components/checkpoint_manager.py +134 -0
- hexdag/core/orchestration/components/execution_coordinator.py +360 -0
- hexdag/core/orchestration/components/health_check_manager.py +176 -0
- hexdag/core/orchestration/components/input_mapper.py +143 -0
- hexdag/core/orchestration/components/lifecycle_manager.py +583 -0
- hexdag/core/orchestration/components/node_executor.py +377 -0
- hexdag/core/orchestration/components/secret_manager.py +202 -0
- hexdag/core/orchestration/components/wave_executor.py +158 -0
- hexdag/core/orchestration/constants.py +17 -0
- hexdag/core/orchestration/events/README.md +312 -0
- hexdag/core/orchestration/events/__init__.py +104 -0
- hexdag/core/orchestration/events/batching.py +330 -0
- hexdag/core/orchestration/events/decorators.py +139 -0
- hexdag/core/orchestration/events/events.py +573 -0
- hexdag/core/orchestration/events/observers/__init__.py +30 -0
- hexdag/core/orchestration/events/observers/core_observers.py +690 -0
- hexdag/core/orchestration/events/observers/models.py +111 -0
- hexdag/core/orchestration/events/taxonomy.py +269 -0
- hexdag/core/orchestration/hook_context.py +237 -0
- hexdag/core/orchestration/hooks.py +437 -0
- hexdag/core/orchestration/models.py +418 -0
- hexdag/core/orchestration/orchestrator.py +910 -0
- hexdag/core/orchestration/orchestrator_factory.py +275 -0
- hexdag/core/orchestration/port_wrappers.py +327 -0
- hexdag/core/orchestration/prompt/__init__.py +32 -0
- hexdag/core/orchestration/prompt/template.py +332 -0
- hexdag/core/pipeline_builder/__init__.py +21 -0
- hexdag/core/pipeline_builder/component_instantiator.py +386 -0
- hexdag/core/pipeline_builder/include_tag.py +265 -0
- hexdag/core/pipeline_builder/pipeline_config.py +133 -0
- hexdag/core/pipeline_builder/py_tag.py +223 -0
- hexdag/core/pipeline_builder/tag_discovery.py +268 -0
- hexdag/core/pipeline_builder/yaml_builder.py +1196 -0
- hexdag/core/pipeline_builder/yaml_validator.py +569 -0
- hexdag/core/ports/__init__.py +65 -0
- hexdag/core/ports/api_call.py +133 -0
- hexdag/core/ports/database.py +489 -0
- hexdag/core/ports/embedding.py +215 -0
- hexdag/core/ports/executor.py +237 -0
- hexdag/core/ports/file_storage.py +117 -0
- hexdag/core/ports/healthcheck.py +87 -0
- hexdag/core/ports/llm.py +551 -0
- hexdag/core/ports/memory.py +70 -0
- hexdag/core/ports/observer_manager.py +130 -0
- hexdag/core/ports/secret.py +145 -0
- hexdag/core/ports/tool_router.py +94 -0
- hexdag/core/ports_builder.py +623 -0
- hexdag/core/protocols.py +273 -0
- hexdag/core/resolver.py +304 -0
- hexdag/core/schema/__init__.py +9 -0
- hexdag/core/schema/generator.py +742 -0
- hexdag/core/secrets.py +242 -0
- hexdag/core/types.py +413 -0
- hexdag/core/utils/async_warnings.py +206 -0
- hexdag/core/utils/schema_conversion.py +78 -0
- hexdag/core/utils/sql_validation.py +86 -0
- hexdag/core/validation/secure_json.py +148 -0
- hexdag/core/yaml_macro.py +517 -0
- hexdag/mcp_server.py +3120 -0
- hexdag/studio/__init__.py +10 -0
- hexdag/studio/build_ui.py +92 -0
- hexdag/studio/server/__init__.py +1 -0
- hexdag/studio/server/main.py +100 -0
- hexdag/studio/server/routes/__init__.py +9 -0
- hexdag/studio/server/routes/execute.py +208 -0
- hexdag/studio/server/routes/export.py +558 -0
- hexdag/studio/server/routes/files.py +207 -0
- hexdag/studio/server/routes/plugins.py +419 -0
- hexdag/studio/server/routes/validate.py +220 -0
- hexdag/studio/ui/index.html +13 -0
- hexdag/studio/ui/package-lock.json +2992 -0
- hexdag/studio/ui/package.json +31 -0
- hexdag/studio/ui/postcss.config.js +6 -0
- hexdag/studio/ui/public/hexdag.svg +5 -0
- hexdag/studio/ui/src/App.tsx +251 -0
- hexdag/studio/ui/src/components/Canvas.tsx +408 -0
- hexdag/studio/ui/src/components/ContextMenu.tsx +187 -0
- hexdag/studio/ui/src/components/FileBrowser.tsx +123 -0
- hexdag/studio/ui/src/components/Header.tsx +181 -0
- hexdag/studio/ui/src/components/HexdagNode.tsx +193 -0
- hexdag/studio/ui/src/components/NodeInspector.tsx +512 -0
- hexdag/studio/ui/src/components/NodePalette.tsx +262 -0
- hexdag/studio/ui/src/components/NodePortsSection.tsx +403 -0
- hexdag/studio/ui/src/components/PluginManager.tsx +347 -0
- hexdag/studio/ui/src/components/PortsEditor.tsx +481 -0
- hexdag/studio/ui/src/components/PythonEditor.tsx +195 -0
- hexdag/studio/ui/src/components/ValidationPanel.tsx +105 -0
- hexdag/studio/ui/src/components/YamlEditor.tsx +196 -0
- hexdag/studio/ui/src/components/index.ts +8 -0
- hexdag/studio/ui/src/index.css +92 -0
- hexdag/studio/ui/src/main.tsx +10 -0
- hexdag/studio/ui/src/types/index.ts +123 -0
- hexdag/studio/ui/src/vite-env.d.ts +1 -0
- hexdag/studio/ui/tailwind.config.js +29 -0
- hexdag/studio/ui/tsconfig.json +37 -0
- hexdag/studio/ui/tsconfig.node.json +13 -0
- hexdag/studio/ui/vite.config.ts +35 -0
- hexdag/visualization/__init__.py +69 -0
- hexdag/visualization/dag_visualizer.py +1020 -0
- hexdag-0.5.0.dev1.dist-info/METADATA +369 -0
- hexdag-0.5.0.dev1.dist-info/RECORD +261 -0
- hexdag-0.5.0.dev1.dist-info/WHEEL +4 -0
- hexdag-0.5.0.dev1.dist-info/entry_points.txt +4 -0
- hexdag-0.5.0.dev1.dist-info/licenses/LICENSE +190 -0
- hexdag_plugins/.gitignore +43 -0
- hexdag_plugins/README.md +73 -0
- hexdag_plugins/__init__.py +1 -0
- hexdag_plugins/azure/LICENSE +21 -0
- hexdag_plugins/azure/README.md +414 -0
- hexdag_plugins/azure/__init__.py +21 -0
- hexdag_plugins/azure/azure_blob_adapter.py +450 -0
- hexdag_plugins/azure/azure_cosmos_adapter.py +383 -0
- hexdag_plugins/azure/azure_keyvault_adapter.py +314 -0
- hexdag_plugins/azure/azure_openai_adapter.py +415 -0
- hexdag_plugins/azure/pyproject.toml +107 -0
- hexdag_plugins/azure/tests/__init__.py +1 -0
- hexdag_plugins/azure/tests/test_azure_blob_adapter.py +350 -0
- hexdag_plugins/azure/tests/test_azure_cosmos_adapter.py +323 -0
- hexdag_plugins/azure/tests/test_azure_keyvault_adapter.py +330 -0
- hexdag_plugins/azure/tests/test_azure_openai_adapter.py +329 -0
- hexdag_plugins/hexdag_etl/README.md +168 -0
- hexdag_plugins/hexdag_etl/__init__.py +53 -0
- hexdag_plugins/hexdag_etl/examples/01_simple_pandas_transform.py +270 -0
- hexdag_plugins/hexdag_etl/examples/02_simple_pandas_only.py +149 -0
- hexdag_plugins/hexdag_etl/examples/03_file_io_pipeline.py +109 -0
- hexdag_plugins/hexdag_etl/examples/test_pandas_transform.py +84 -0
- hexdag_plugins/hexdag_etl/hexdag.toml +25 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/__init__.py +48 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/__init__.py +13 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/api_extract.py +230 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/base_node_factory.py +181 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/file_io.py +415 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/outlook.py +492 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/pandas_transform.py +563 -0
- hexdag_plugins/hexdag_etl/hexdag_etl/nodes/sql_extract_load.py +112 -0
- hexdag_plugins/hexdag_etl/pyproject.toml +82 -0
- hexdag_plugins/hexdag_etl/test_transform.py +54 -0
- hexdag_plugins/hexdag_etl/tests/test_plugin_integration.py +62 -0
- hexdag_plugins/mysql_adapter/LICENSE +21 -0
- hexdag_plugins/mysql_adapter/README.md +224 -0
- hexdag_plugins/mysql_adapter/__init__.py +6 -0
- hexdag_plugins/mysql_adapter/mysql_adapter.py +408 -0
- hexdag_plugins/mysql_adapter/pyproject.toml +93 -0
- hexdag_plugins/mysql_adapter/tests/test_mysql_adapter.py +259 -0
- hexdag_plugins/storage/README.md +184 -0
- hexdag_plugins/storage/__init__.py +19 -0
- hexdag_plugins/storage/file/__init__.py +5 -0
- hexdag_plugins/storage/file/local.py +325 -0
- hexdag_plugins/storage/ports/__init__.py +5 -0
- hexdag_plugins/storage/ports/vector_store.py +236 -0
- hexdag_plugins/storage/sql/__init__.py +7 -0
- hexdag_plugins/storage/sql/base.py +187 -0
- hexdag_plugins/storage/sql/mysql.py +27 -0
- hexdag_plugins/storage/sql/postgresql.py +27 -0
- hexdag_plugins/storage/tests/__init__.py +1 -0
- hexdag_plugins/storage/tests/test_local_file_storage.py +161 -0
- hexdag_plugins/storage/tests/test_sql_adapters.py +212 -0
- hexdag_plugins/storage/vector/__init__.py +7 -0
- hexdag_plugins/storage/vector/chromadb.py +223 -0
- hexdag_plugins/storage/vector/in_memory.py +285 -0
- hexdag_plugins/storage/vector/pgvector.py +502 -0
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "hexdag-etl"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "ETL infrastructure for hexDAG pipelines"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "hexDAG Team" }]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 3 - Alpha",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3.12",
|
|
17
|
+
]
|
|
18
|
+
keywords = ["etl", "data-pipeline", "hexdag", "plugin"]
|
|
19
|
+
requires-python = ">=3.12"
|
|
20
|
+
dependencies = [
|
|
21
|
+
"pandas>=2.0.0",
|
|
22
|
+
"aiohttp>=3.8.0",
|
|
23
|
+
"hexdag", # <-- plain dependency name
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
[project.optional-dependencies]
|
|
27
|
+
dev = [
|
|
28
|
+
"pytest>=7.0.0",
|
|
29
|
+
"pytest-asyncio>=0.21.0",
|
|
30
|
+
"pytest-cov>=4.0.0",
|
|
31
|
+
"ruff>=0.1.0",
|
|
32
|
+
"mypy>=1.0.0",
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
[tool.hatch.metadata]
|
|
36
|
+
allow-direct-references = true # now harmless but not strictly needed
|
|
37
|
+
|
|
38
|
+
[tool.hatch.build.targets.wheel]
|
|
39
|
+
packages = ["hexdag_etl"]
|
|
40
|
+
|
|
41
|
+
[tool.hatch.build.targets.sdist]
|
|
42
|
+
include = ["/hexdag_etl", "/tests", "/examples", "/README.md"]
|
|
43
|
+
|
|
44
|
+
[tool.uv.sources]
|
|
45
|
+
hexdag = { path = "../.." } # <-- tell uv to take hexdag from repo root
|
|
46
|
+
|
|
47
|
+
[tool.ruff]
|
|
48
|
+
target-version = "py312"
|
|
49
|
+
line-length = 120
|
|
50
|
+
|
|
51
|
+
[tool.ruff.lint]
|
|
52
|
+
select = [
|
|
53
|
+
"E", # pycodestyle errors
|
|
54
|
+
"W", # pycodestyle warnings
|
|
55
|
+
"F", # pyflakes
|
|
56
|
+
"I", # isort
|
|
57
|
+
"UP", # pyupgrade
|
|
58
|
+
]
|
|
59
|
+
ignore = [
|
|
60
|
+
"E501", # line too long
|
|
61
|
+
"SIM105", # contextlib.suppress doesn't work with async
|
|
62
|
+
"E402", # module import not at top of file (test files)
|
|
63
|
+
]
|
|
64
|
+
|
|
65
|
+
[tool.mypy]
|
|
66
|
+
python_version = "3.12"
|
|
67
|
+
warn_return_any = true
|
|
68
|
+
warn_unused_configs = true
|
|
69
|
+
disallow_untyped_defs = true
|
|
70
|
+
disallow_incomplete_defs = true
|
|
71
|
+
check_untyped_defs = true
|
|
72
|
+
disallow_untyped_decorators = true
|
|
73
|
+
no_implicit_optional = true
|
|
74
|
+
warn_redundant_casts = true
|
|
75
|
+
warn_unused_ignores = true
|
|
76
|
+
warn_no_return = true
|
|
77
|
+
warn_unreachable = true
|
|
78
|
+
strict_equality = true
|
|
79
|
+
|
|
80
|
+
[[tool.mypy.overrides]]
|
|
81
|
+
module = "tests.*"
|
|
82
|
+
disallow_untyped_defs = false
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Test the pandas transform step by step."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
|
|
5
|
+
sys.path.insert(0, "/Users/jankwapisz/Documents/Praca/Omniviser/hexdag")
|
|
6
|
+
sys.path.insert(0, "/Users/jankwapisz/Documents/Praca/Omniviser/hexdag/hexdag_plugins")
|
|
7
|
+
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
from hexdag_etl.nodes.pandas_transform import PandasTransformNode
|
|
11
|
+
|
|
12
|
+
# Create the node
|
|
13
|
+
node_factory = PandasTransformNode()
|
|
14
|
+
|
|
15
|
+
# Build operations
|
|
16
|
+
operations = [
|
|
17
|
+
{"type": "transform", "method": "pandas.DataFrame.sort_values", "kwargs": {"by": "amount", "ascending": False}},
|
|
18
|
+
{
|
|
19
|
+
"type": "transform",
|
|
20
|
+
"method": "pandas.DataFrame.assign",
|
|
21
|
+
"kwargs": {"amount_doubled": "{{ lambda df: df['amount'] * 2 }}"},
|
|
22
|
+
},
|
|
23
|
+
{"type": "transform", "method": "pandas.DataFrame.head", "args": [3]},
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
# Create node spec
|
|
27
|
+
node_spec = node_factory(name="test_transform", operations=operations)
|
|
28
|
+
|
|
29
|
+
# Test input
|
|
30
|
+
df = pd.DataFrame(
|
|
31
|
+
{
|
|
32
|
+
"customer_id": ["C001", "C002", "C003", "C004", "C005"],
|
|
33
|
+
"name": ["Alice", "Bob", "Carol", "David", "Emma"],
|
|
34
|
+
"amount": [150.0, 299.99, 150.0, 29.99, 299.99],
|
|
35
|
+
"category": ["A", "B", "A", "C", "B"],
|
|
36
|
+
}
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
print("Input DataFrame:")
|
|
40
|
+
print(df)
|
|
41
|
+
print()
|
|
42
|
+
|
|
43
|
+
# Try to run the function
|
|
44
|
+
import asyncio
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
async def test():
|
|
48
|
+
result = await node_spec.fn({"data": df})
|
|
49
|
+
print("Output:")
|
|
50
|
+
print(result)
|
|
51
|
+
return result
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
asyncio.run(test())
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Test hexDAG ETL plugin integration."""
|
|
2
|
+
|
|
3
|
+
from hexdag.core.domain.dag import NodeSpec
|
|
4
|
+
from hexdag.core.registry import registry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def test_plugin_components_registered():
|
|
8
|
+
"""Test that all ETL plugin components are properly registered."""
|
|
9
|
+
|
|
10
|
+
# Test artifact storage adapter
|
|
11
|
+
artifact_adapter = registry.get("local", namespace="etl")
|
|
12
|
+
assert artifact_adapter is not None, "Local artifact adapter should be registered"
|
|
13
|
+
|
|
14
|
+
# Test nodes
|
|
15
|
+
|
|
16
|
+
# Note: Nodes are factory classes, not directly in registry like adapters
|
|
17
|
+
# They're registered as node factories that create NodeSpecs
|
|
18
|
+
pandas_transform_factory = registry.get("pandas_transform", namespace="etl")
|
|
19
|
+
assert pandas_transform_factory is not None, "Pandas transform node should be registered"
|
|
20
|
+
|
|
21
|
+
api_extract_factory = registry.get("api_extract", namespace="etl")
|
|
22
|
+
assert api_extract_factory is not None, "API extract node should be registered"
|
|
23
|
+
|
|
24
|
+
sql_extract_factory = registry.get("sql_extract", namespace="etl")
|
|
25
|
+
assert sql_extract_factory is not None, "SQL extract node should be registered"
|
|
26
|
+
|
|
27
|
+
sql_load_factory = registry.get("sql_load", namespace="etl")
|
|
28
|
+
assert sql_load_factory is not None, "SQL load node should be registered"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_artifact_adapter_instantiation():
|
|
32
|
+
"""Test that artifact adapter can be instantiated."""
|
|
33
|
+
from hexdag.core.registry import registry
|
|
34
|
+
|
|
35
|
+
adapter_cls = registry.get("local", namespace="etl")
|
|
36
|
+
assert adapter_cls is not None
|
|
37
|
+
|
|
38
|
+
adapter = adapter_cls(base_path="/tmp/test_artifacts")
|
|
39
|
+
assert adapter is not None
|
|
40
|
+
assert hasattr(adapter, "write")
|
|
41
|
+
assert hasattr(adapter, "read")
|
|
42
|
+
assert hasattr(adapter, "list")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def test_pandas_transform_node_creation():
|
|
46
|
+
"""Test that pandas transform node can be created."""
|
|
47
|
+
from hexdag.core.registry import registry
|
|
48
|
+
|
|
49
|
+
factory_cls = registry.get("pandas_transform", namespace="etl")
|
|
50
|
+
assert factory_cls is not None
|
|
51
|
+
|
|
52
|
+
factory = factory_cls()
|
|
53
|
+
|
|
54
|
+
# Create a simple node spec
|
|
55
|
+
node_spec = factory(
|
|
56
|
+
name="test_transform",
|
|
57
|
+
operations=[{"type": "transform", "method": "pandas.DataFrame.head", "kwargs": {"n": 5}}],
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
assert isinstance(node_spec, NodeSpec)
|
|
61
|
+
assert node_spec.name == "test_transform"
|
|
62
|
+
assert "operations" in node_spec.params
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 HexDAG Team
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# HexDAG MySQL Adapter
|
|
2
|
+
|
|
3
|
+
A production-ready MySQL database adapter plugin for the hexDAG framework.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🚀 **Production-Ready**: Built for high-performance production deployments
|
|
8
|
+
- 📦 **JSON Document Storage**: Leverage MySQL's native JSON support
|
|
9
|
+
- 🔄 **Async Operations**: Full async/await support for all operations
|
|
10
|
+
- 🔒 **Transaction Support**: ACID compliance with proper transaction handling
|
|
11
|
+
- 🎯 **Type-Safe**: Full type hints and Pydantic validation
|
|
12
|
+
- 🔌 **Plugin Architecture**: Seamlessly integrates with hexDAG's plugin system
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
### Local Development
|
|
17
|
+
|
|
18
|
+
For local development within the hexDAG project:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
# Using uv (recommended for hexDAG development)
|
|
22
|
+
uv pip install -e hexdag_plugins/mysql_adapter/
|
|
23
|
+
|
|
24
|
+
# Or using pip
|
|
25
|
+
uv pip install -e hexdag_plugins/mysql_adapter/
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### With development dependencies
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
pip install -e "hexdag_plugins/mysql_adapter/[dev]"
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Configuration
|
|
35
|
+
|
|
36
|
+
### Environment Variables
|
|
37
|
+
|
|
38
|
+
Set these environment variables for default connection:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
export MYSQL_HOST=localhost
|
|
42
|
+
export MYSQL_PORT=3306
|
|
43
|
+
export MYSQL_USER=your_user
|
|
44
|
+
export MYSQL_PASSWORD=your_password
|
|
45
|
+
export MYSQL_DATABASE=hexdag
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### Programmatic Configuration
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
from hexdag_plugins.mysql_adapter import MySQLAdapter
|
|
52
|
+
|
|
53
|
+
adapter = MySQLAdapter(
|
|
54
|
+
host="your-mysql-host",
|
|
55
|
+
port=3306,
|
|
56
|
+
user="your-user",
|
|
57
|
+
password="your-password",
|
|
58
|
+
database="hexdag",
|
|
59
|
+
charset="utf8mb4"
|
|
60
|
+
)
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Usage
|
|
64
|
+
|
|
65
|
+
### Basic Operations
|
|
66
|
+
|
|
67
|
+
```python
|
|
68
|
+
import asyncio
|
|
69
|
+
from hexdag_plugins.mysql_adapter import MySQLAdapter
|
|
70
|
+
|
|
71
|
+
async def main():
|
|
72
|
+
# Initialize adapter
|
|
73
|
+
adapter = MySQLAdapter(
|
|
74
|
+
host="localhost",
|
|
75
|
+
user="root",
|
|
76
|
+
password="password",
|
|
77
|
+
database="hexdag"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
# Insert document
|
|
81
|
+
doc_id = await adapter.ainsert("users", {
|
|
82
|
+
"name": "John Doe",
|
|
83
|
+
"email": "john@example.com",
|
|
84
|
+
"metadata": {
|
|
85
|
+
"role": "admin",
|
|
86
|
+
"permissions": ["read", "write", "delete"]
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
# Query documents
|
|
91
|
+
users = await adapter.aquery(
|
|
92
|
+
"users",
|
|
93
|
+
filter={"metadata.role": "admin"}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
# Update document
|
|
97
|
+
await adapter.aupdate("users", doc_id, {
|
|
98
|
+
"last_login": "2024-01-01T12:00:00Z"
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
# Delete document
|
|
102
|
+
await adapter.adelete("users", doc_id)
|
|
103
|
+
|
|
104
|
+
asyncio.run(main())
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Integration with hexDAG
|
|
108
|
+
|
|
109
|
+
The adapter automatically registers with hexDAG's registry system:
|
|
110
|
+
|
|
111
|
+
```python
|
|
112
|
+
from hexdag.core.bootstrap import bootstrap_registry
|
|
113
|
+
from hexdag.core.registry import registry
|
|
114
|
+
|
|
115
|
+
# Bootstrap with MySQL plugin
|
|
116
|
+
bootstrap_registry()
|
|
117
|
+
|
|
118
|
+
# Get MySQL adapter from registry
|
|
119
|
+
mysql_info = registry.get_info("mysql", namespace="plugin")
|
|
120
|
+
adapter = mysql_info.get_instance(
|
|
121
|
+
host="localhost",
|
|
122
|
+
user="root",
|
|
123
|
+
password="password"
|
|
124
|
+
)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Database Schema
|
|
128
|
+
|
|
129
|
+
The adapter creates a flexible document store schema:
|
|
130
|
+
|
|
131
|
+
```sql
|
|
132
|
+
CREATE TABLE hexdag_documents (
|
|
133
|
+
id VARCHAR(255) PRIMARY KEY,
|
|
134
|
+
collection VARCHAR(255) NOT NULL,
|
|
135
|
+
document JSON NOT NULL,
|
|
136
|
+
metadata JSON,
|
|
137
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
138
|
+
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
|
139
|
+
INDEX idx_collection (collection),
|
|
140
|
+
INDEX idx_created (created_at),
|
|
141
|
+
INDEX idx_updated (updated_at)
|
|
142
|
+
);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Advanced Features
|
|
146
|
+
|
|
147
|
+
### JSON Path Queries
|
|
148
|
+
|
|
149
|
+
Leverage MySQL's JSON functions for complex queries:
|
|
150
|
+
|
|
151
|
+
```python
|
|
152
|
+
# Query nested JSON fields
|
|
153
|
+
results = await adapter.aquery(
|
|
154
|
+
"products",
|
|
155
|
+
filter={
|
|
156
|
+
"specs.memory": "16GB",
|
|
157
|
+
"price": {"$lt": 1000}
|
|
158
|
+
}
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
### Concurrent Operations
|
|
163
|
+
|
|
164
|
+
The adapter supports concurrent operations safely:
|
|
165
|
+
|
|
166
|
+
```python
|
|
167
|
+
import asyncio
|
|
168
|
+
|
|
169
|
+
async def bulk_insert(adapter, collection, documents):
|
|
170
|
+
tasks = [
|
|
171
|
+
adapter.ainsert(collection, doc)
|
|
172
|
+
for doc in documents
|
|
173
|
+
]
|
|
174
|
+
return await asyncio.gather(*tasks)
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### Connection Pooling
|
|
178
|
+
|
|
179
|
+
The adapter uses PyMySQL with connection management for optimal performance.
|
|
180
|
+
|
|
181
|
+
## Testing
|
|
182
|
+
|
|
183
|
+
### Run Tests
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
pytest tests/
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Test with Docker MySQL
|
|
190
|
+
|
|
191
|
+
```bash
|
|
192
|
+
# Start MySQL container
|
|
193
|
+
docker run -d \
|
|
194
|
+
--name mysql-test \
|
|
195
|
+
-e MYSQL_ROOT_PASSWORD=test \
|
|
196
|
+
-e MYSQL_DATABASE=hexdag_test \
|
|
197
|
+
-p 3306:3306 \
|
|
198
|
+
mysql:8.0
|
|
199
|
+
|
|
200
|
+
# Run tests
|
|
201
|
+
MYSQL_TEST_HOST=localhost \
|
|
202
|
+
MYSQL_TEST_USER=root \
|
|
203
|
+
MYSQL_TEST_PASSWORD=test \
|
|
204
|
+
MYSQL_TEST_DATABASE=hexdag_test \
|
|
205
|
+
pytest tests/
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Requirements
|
|
209
|
+
|
|
210
|
+
- Python 3.12+
|
|
211
|
+
- MySQL 5.7+ (8.0+ recommended for full JSON support)
|
|
212
|
+
- PyMySQL 1.1.0+
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT License - see LICENSE file for details.
|
|
217
|
+
|
|
218
|
+
## Contributing
|
|
219
|
+
|
|
220
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
221
|
+
|
|
222
|
+
## Support
|
|
223
|
+
|
|
224
|
+
For issues and questions, please use the GitHub issue tracker.
|