Flowfile 0.3.9__py3-none-any.whl → 0.5.1__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.
- flowfile/__init__.py +8 -1
- flowfile/api.py +1 -3
- flowfile/web/static/assets/{CloudConnectionManager-c97c25f8.js → CloudConnectionManager-0dfba9f2.js} +2 -2
- flowfile/web/static/assets/{CloudStorageReader-f1ff509e.js → CloudStorageReader-d5b1b6c9.js} +11 -78
- flowfile/web/static/assets/{CloudStorageWriter-034f8b78.js → CloudStorageWriter-00d87aad.js} +12 -79
- flowfile/web/static/assets/{CloudStorageWriter-49c9a4b2.css → CloudStorageWriter-b0ee067f.css} +24 -24
- flowfile/web/static/assets/ColumnSelector-4685e75d.js +83 -0
- flowfile/web/static/assets/ColumnSelector-47996a16.css +10 -0
- flowfile/web/static/assets/ContextMenu-23e909da.js +41 -0
- flowfile/web/static/assets/{SettingsSection-9c836ecc.css → ContextMenu-4c74eef1.css} +0 -21
- flowfile/web/static/assets/ContextMenu-63cfa99b.css +26 -0
- flowfile/web/static/assets/ContextMenu-70ae0c79.js +41 -0
- flowfile/web/static/assets/ContextMenu-c13f91d0.css +26 -0
- flowfile/web/static/assets/ContextMenu-f149cf7c.js +41 -0
- flowfile/web/static/assets/{CrossJoin-41efa4cb.css → CrossJoin-1119d18e.css} +18 -18
- flowfile/web/static/assets/{CrossJoin-9e156ebe.js → CrossJoin-702a3edd.js} +14 -84
- flowfile/web/static/assets/CustomNode-74a37f74.css +32 -0
- flowfile/web/static/assets/CustomNode-b1519993.js +211 -0
- flowfile/web/static/assets/{DatabaseConnectionSettings-d5c625b3.js → DatabaseConnectionSettings-6f3e4ea5.js} +3 -3
- flowfile/web/static/assets/{DatabaseManager-265adc5e.js → DatabaseManager-cf5ef661.js} +2 -2
- flowfile/web/static/assets/{DatabaseReader-f50c6558.css → DatabaseReader-ae61773c.css} +0 -27
- flowfile/web/static/assets/{DatabaseReader-0b10551e.js → DatabaseReader-d38c7295.js} +14 -114
- flowfile/web/static/assets/{DatabaseWriter-c17c6916.js → DatabaseWriter-b04ef46a.js} +13 -74
- flowfile/web/static/assets/{ExploreData-5bdae813.css → ExploreData-2d0cf4db.css} +8 -14
- flowfile/web/static/assets/ExploreData-5fa10ed8.js +192 -0
- flowfile/web/static/assets/{ExternalSource-3a66556c.js → ExternalSource-d39af878.js} +8 -79
- flowfile/web/static/assets/{Filter-91ad87e7.js → Filter-9b6d08db.js} +12 -85
- flowfile/web/static/assets/{Filter-a9d08ba1.css → Filter-f62091b3.css} +3 -3
- flowfile/web/static/assets/{Formula-3c395ab1.js → Formula-6b04fb1d.js} +20 -87
- flowfile/web/static/assets/{Formula-29f19d21.css → Formula-bb96803d.css} +4 -4
- flowfile/web/static/assets/{FuzzyMatch-6857de82.css → FuzzyMatch-1010f966.css} +42 -42
- flowfile/web/static/assets/{FuzzyMatch-2df0d230.js → FuzzyMatch-999521f4.js} +16 -87
- flowfile/web/static/assets/{GraphSolver-d285877f.js → GraphSolver-17dd2198.js} +13 -159
- flowfile/web/static/assets/GraphSolver-f0cb7bfb.css +22 -0
- flowfile/web/static/assets/{GroupBy-0bd1cc6b.js → GroupBy-6b039e18.js} +12 -75
- flowfile/web/static/assets/{Unique-b5615727.css → GroupBy-b9505323.css} +8 -8
- flowfile/web/static/assets/{Join-5a78a203.js → Join-24d0f113.js} +15 -85
- flowfile/web/static/assets/{Join-f45eff22.css → Join-fd79b451.css} +20 -20
- flowfile/web/static/assets/{ManualInput-a71b52c6.css → ManualInput-3246a08d.css} +20 -20
- flowfile/web/static/assets/{ManualInput-93aef9d6.js → ManualInput-34639209.js} +11 -82
- flowfile/web/static/assets/MultiSelect-0e8724a3.js +5 -0
- flowfile/web/static/assets/MultiSelect.vue_vue_type_script_setup_true_lang-b0e538c2.js +63 -0
- flowfile/web/static/assets/NumericInput-3d63a470.js +5 -0
- flowfile/web/static/assets/NumericInput.vue_vue_type_script_setup_true_lang-e0edeccc.js +35 -0
- flowfile/web/static/assets/Output-283fe388.css +37 -0
- flowfile/web/static/assets/{Output-411ecaee.js → Output-edea9802.js} +62 -273
- flowfile/web/static/assets/{Pivot-89db4b04.js → Pivot-61d19301.js} +14 -138
- flowfile/web/static/assets/Pivot-cf333e3d.css +22 -0
- flowfile/web/static/assets/PivotValidation-891ddfb0.css +13 -0
- flowfile/web/static/assets/PivotValidation-c46cd420.css +13 -0
- flowfile/web/static/assets/PivotValidation-de9f43fe.js +61 -0
- flowfile/web/static/assets/PivotValidation-f97fec5b.js +61 -0
- flowfile/web/static/assets/{PolarsCode-a9f974f8.js → PolarsCode-bc3c9984.js} +13 -80
- flowfile/web/static/assets/Read-64a3f259.js +218 -0
- flowfile/web/static/assets/Read-e808b239.css +62 -0
- flowfile/web/static/assets/RecordCount-3d5039be.js +53 -0
- flowfile/web/static/assets/{RecordId-55ae7d36.js → RecordId-597510e0.js} +8 -80
- flowfile/web/static/assets/SQLQueryComponent-36cef432.css +27 -0
- flowfile/web/static/assets/SQLQueryComponent-df51adbe.js +38 -0
- flowfile/web/static/assets/{Sample-b4a18476.js → Sample-4be0a507.js} +8 -77
- flowfile/web/static/assets/{SecretManager-b066d13a.js → SecretManager-4839be57.js} +2 -2
- flowfile/web/static/assets/{Select-727688dc.js → Select-9b72f201.js} +11 -85
- flowfile/web/static/assets/SettingsSection-2e4d03c4.css +21 -0
- flowfile/web/static/assets/SettingsSection-5c696bee.css +20 -0
- flowfile/web/static/assets/SettingsSection-71e6b7e3.css +21 -0
- flowfile/web/static/assets/SettingsSection-7ded385d.js +45 -0
- flowfile/web/static/assets/{SettingsSection-695ac487.js → SettingsSection-e1e9c953.js} +2 -40
- flowfile/web/static/assets/SettingsSection-f0f75a42.js +53 -0
- flowfile/web/static/assets/SingleSelect-6c777aac.js +5 -0
- flowfile/web/static/assets/SingleSelect.vue_vue_type_script_setup_true_lang-33e3ff9b.js +62 -0
- flowfile/web/static/assets/SliderInput-7cb93e62.js +40 -0
- flowfile/web/static/assets/SliderInput-b8fb6a8c.css +4 -0
- flowfile/web/static/assets/{GroupBy-ab1ea74b.css → Sort-3643d625.css} +8 -8
- flowfile/web/static/assets/{Sort-be3339a8.js → Sort-6cbde21a.js} +12 -97
- flowfile/web/static/assets/TextInput-d9a40c11.js +5 -0
- flowfile/web/static/assets/TextInput.vue_vue_type_script_setup_true_lang-5896c375.js +32 -0
- flowfile/web/static/assets/{TextToRows-c92d1ec2.css → TextToRows-5d2c1190.css} +9 -9
- flowfile/web/static/assets/{TextToRows-7b8998da.js → TextToRows-c4fcbf4d.js} +14 -83
- flowfile/web/static/assets/ToggleSwitch-4ef91d19.js +5 -0
- flowfile/web/static/assets/ToggleSwitch.vue_vue_type_script_setup_true_lang-38478c20.js +31 -0
- flowfile/web/static/assets/{UnavailableFields-8b0cb48e.js → UnavailableFields-a03f512c.js} +2 -2
- flowfile/web/static/assets/{Union-8d9ac7f9.css → Union-af6c3d9b.css} +6 -6
- flowfile/web/static/assets/Union-bfe9b996.js +77 -0
- flowfile/web/static/assets/{Unique-af5a80b4.js → Unique-5d023a27.js} +23 -104
- flowfile/web/static/assets/{Sort-7ccfa0fe.css → Unique-f9fb0809.css} +8 -8
- flowfile/web/static/assets/Unpivot-1e422df3.css +30 -0
- flowfile/web/static/assets/{Unpivot-5195d411.js → Unpivot-91cc5354.js} +12 -166
- flowfile/web/static/assets/UnpivotValidation-0d240eeb.css +13 -0
- flowfile/web/static/assets/UnpivotValidation-7ee2de44.js +51 -0
- flowfile/web/static/assets/{ExploreData-18a4fe52.js → VueGraphicWalker-e51b9924.js} +4 -264
- flowfile/web/static/assets/VueGraphicWalker-ed5ab88b.css +6 -0
- flowfile/web/static/assets/{api-cb00cce6.js → api-c1bad5ca.js} +1 -1
- flowfile/web/static/assets/{api-023d1733.js → api-cf1221f0.js} +1 -1
- flowfile/web/static/assets/{designer-2197d782.css → designer-8da3ba3a.css} +859 -201
- flowfile/web/static/assets/{designer-6c322d8e.js → designer-9633482a.js} +2297 -733
- flowfile/web/static/assets/{documentation-4d1fafe1.js → documentation-ca400224.js} +1 -1
- flowfile/web/static/assets/{dropDown-0b46dd77.js → dropDown-614b998d.js} +1 -1
- flowfile/web/static/assets/{fullEditor-ec4e4f95.js → fullEditor-f7971590.js} +2 -2
- flowfile/web/static/assets/{genericNodeSettings-def5879b.js → genericNodeSettings-4fe5f36b.js} +3 -3
- flowfile/web/static/assets/{index-681a3ed0.css → index-50508d4d.css} +8 -0
- flowfile/web/static/assets/{index-683fc198.js → index-5429bbf8.js} +208 -31
- flowfile/web/static/assets/nodeInput-5d0d6b79.js +41 -0
- flowfile/web/static/assets/outputCsv-076b85ab.js +86 -0
- flowfile/web/static/assets/{Output-48f81019.css → outputCsv-9cc59e0b.css} +0 -143
- flowfile/web/static/assets/outputExcel-0fd17dbe.js +56 -0
- flowfile/web/static/assets/outputExcel-b41305c0.css +102 -0
- flowfile/web/static/assets/outputParquet-b61e0847.js +31 -0
- flowfile/web/static/assets/outputParquet-cf8cf3f2.css +4 -0
- flowfile/web/static/assets/readCsv-a8bb8b61.js +179 -0
- flowfile/web/static/assets/readCsv-c767cb37.css +52 -0
- flowfile/web/static/assets/readExcel-67b4aee0.js +201 -0
- flowfile/web/static/assets/readExcel-806d2826.css +64 -0
- flowfile/web/static/assets/readParquet-48c81530.css +19 -0
- flowfile/web/static/assets/readParquet-92ce1dbc.js +23 -0
- flowfile/web/static/assets/{secretApi-baceb6f9.js → secretApi-68435402.js} +1 -1
- flowfile/web/static/assets/{selectDynamic-de91449a.js → selectDynamic-92e25ee3.js} +7 -7
- flowfile/web/static/assets/{selectDynamic-b062bc9b.css → selectDynamic-aa913ff4.css} +16 -16
- flowfile/web/static/assets/user-defined-icon-0ae16c90.png +0 -0
- flowfile/web/static/assets/{vue-codemirror.esm-dc5e3348.js → vue-codemirror.esm-41b0e0d7.js} +65 -36
- flowfile/web/static/assets/{vue-content-loader.es-ba94b82f.js → vue-content-loader.es-2c8e608f.js} +1 -1
- flowfile/web/static/index.html +2 -2
- {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info}/METADATA +5 -3
- {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info}/RECORD +191 -121
- {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info}/WHEEL +1 -1
- {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info}/entry_points.txt +1 -0
- flowfile_core/__init__.py +3 -0
- flowfile_core/configs/flow_logger.py +5 -13
- flowfile_core/configs/node_store/__init__.py +30 -0
- flowfile_core/configs/node_store/nodes.py +383 -99
- flowfile_core/configs/node_store/user_defined_node_registry.py +193 -0
- flowfile_core/configs/settings.py +2 -1
- flowfile_core/database/connection.py +5 -21
- flowfile_core/fileExplorer/funcs.py +239 -121
- flowfile_core/flowfile/analytics/analytics_processor.py +1 -0
- flowfile_core/flowfile/code_generator/code_generator.py +62 -64
- flowfile_core/flowfile/flow_data_engine/create/funcs.py +73 -56
- flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +77 -86
- flowfile_core/flowfile/flow_data_engine/flow_file_column/interface.py +4 -0
- flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +19 -34
- flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +36 -0
- flowfile_core/flowfile/flow_data_engine/fuzzy_matching/prepare_for_fuzzy_match.py +23 -23
- flowfile_core/flowfile/flow_data_engine/join/utils.py +1 -1
- flowfile_core/flowfile/flow_data_engine/join/verify_integrity.py +9 -4
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +212 -86
- flowfile_core/flowfile/flow_data_engine/utils.py +2 -0
- flowfile_core/flowfile/flow_graph.py +240 -54
- flowfile_core/flowfile/flow_node/flow_node.py +48 -13
- flowfile_core/flowfile/flow_node/models.py +2 -1
- flowfile_core/flowfile/handler.py +24 -5
- flowfile_core/flowfile/manage/compatibility_enhancements.py +404 -41
- flowfile_core/flowfile/manage/io_flowfile.py +394 -0
- flowfile_core/flowfile/node_designer/__init__.py +47 -0
- flowfile_core/flowfile/node_designer/_type_registry.py +197 -0
- flowfile_core/flowfile/node_designer/custom_node.py +371 -0
- flowfile_core/flowfile/node_designer/ui_components.py +277 -0
- flowfile_core/flowfile/schema_callbacks.py +17 -10
- flowfile_core/flowfile/setting_generator/settings.py +15 -10
- flowfile_core/main.py +5 -1
- flowfile_core/routes/routes.py +73 -30
- flowfile_core/routes/user_defined_components.py +55 -0
- flowfile_core/schemas/cloud_storage_schemas.py +0 -2
- flowfile_core/schemas/input_schema.py +228 -65
- flowfile_core/schemas/output_model.py +5 -2
- flowfile_core/schemas/schemas.py +153 -35
- flowfile_core/schemas/transform_schema.py +1083 -412
- flowfile_core/schemas/yaml_types.py +103 -0
- flowfile_core/types.py +156 -0
- flowfile_core/utils/validate_setup.py +3 -1
- flowfile_frame/__init__.py +3 -1
- flowfile_frame/flow_frame.py +31 -24
- flowfile_frame/flow_frame_methods.py +12 -9
- flowfile_worker/__init__.py +9 -35
- flowfile_worker/create/__init__.py +3 -21
- flowfile_worker/create/funcs.py +68 -56
- flowfile_worker/create/models.py +130 -62
- flowfile_worker/main.py +5 -2
- flowfile_worker/routes.py +52 -13
- shared/__init__.py +15 -0
- shared/storage_config.py +258 -0
- tools/migrate/README.md +56 -0
- tools/migrate/__init__.py +12 -0
- tools/migrate/__main__.py +131 -0
- tools/migrate/legacy_schemas.py +621 -0
- tools/migrate/migrate.py +598 -0
- tools/migrate/tests/__init__.py +0 -0
- tools/migrate/tests/conftest.py +23 -0
- tools/migrate/tests/test_migrate.py +627 -0
- tools/migrate/tests/test_migration_e2e.py +1010 -0
- tools/migrate/tests/test_node_migrations.py +813 -0
- flowfile/web/static/assets/GraphSolver-17fd26db.css +0 -68
- flowfile/web/static/assets/Pivot-f415e85f.css +0 -35
- flowfile/web/static/assets/Read-80dc1675.css +0 -197
- flowfile/web/static/assets/Read-c3b1929c.js +0 -701
- flowfile/web/static/assets/RecordCount-4e95f98e.js +0 -122
- flowfile/web/static/assets/Union-89fd73dc.js +0 -146
- flowfile/web/static/assets/Unpivot-246e9bbd.css +0 -77
- flowfile/web/static/assets/nodeTitle-a16db7c3.js +0 -227
- flowfile/web/static/assets/nodeTitle-f4b12bcb.css +0 -134
- flowfile_core/flowfile/manage/open_flowfile.py +0 -135
- {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info/licenses}/LICENSE +0 -0
- /flowfile_core/flowfile/manage/manage_flowfile.py → /tools/__init__.py +0 -0
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import importlib.util
|
|
3
|
+
import inspect
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Dict, Type, List
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
from flowfile_core.flowfile.node_designer.custom_node import CustomNodeBase, NodeSettings
|
|
9
|
+
from shared import storage
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def get_all_custom_nodes() -> Dict[str, Type[CustomNodeBase]]:
|
|
16
|
+
"""
|
|
17
|
+
Scan the user-defined nodes directory and import all CustomNodeBase subclasses.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Dictionary mapping node names to node classes
|
|
21
|
+
"""
|
|
22
|
+
custom_nodes = {}
|
|
23
|
+
|
|
24
|
+
# Get the directory path where user-defined nodes are stored
|
|
25
|
+
nodes_directory = storage.user_defined_nodes_icons
|
|
26
|
+
|
|
27
|
+
# Convert to Path object for easier handling
|
|
28
|
+
nodes_path = Path(nodes_directory)
|
|
29
|
+
|
|
30
|
+
if not nodes_path.exists() or not nodes_path.is_dir():
|
|
31
|
+
print(f"Warning: Nodes directory {nodes_path} does not exist or is not a directory")
|
|
32
|
+
return custom_nodes
|
|
33
|
+
|
|
34
|
+
# Scan all Python files in the directory
|
|
35
|
+
for file_path in nodes_path.glob("*.py"):
|
|
36
|
+
# Skip __init__.py and other special files
|
|
37
|
+
if file_path.name.startswith("__"):
|
|
38
|
+
continue
|
|
39
|
+
|
|
40
|
+
try:
|
|
41
|
+
# Load the module dynamically
|
|
42
|
+
module_name = file_path.stem # filename without extension
|
|
43
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
44
|
+
|
|
45
|
+
if spec and spec.loader:
|
|
46
|
+
module = importlib.util.module_from_spec(spec)
|
|
47
|
+
|
|
48
|
+
# Add to sys.modules to handle imports within the module
|
|
49
|
+
sys.modules[module_name] = module
|
|
50
|
+
|
|
51
|
+
# Execute the module
|
|
52
|
+
spec.loader.exec_module(module)
|
|
53
|
+
|
|
54
|
+
# Inspect the module for CustomNodeBase subclasses
|
|
55
|
+
for name, obj in inspect.getmembers(module):
|
|
56
|
+
# Check if it's a class and a subclass of CustomNodeBase
|
|
57
|
+
# but not CustomNodeBase itself
|
|
58
|
+
if (inspect.isclass(obj) and
|
|
59
|
+
issubclass(obj, CustomNodeBase) and
|
|
60
|
+
obj is not CustomNodeBase):
|
|
61
|
+
|
|
62
|
+
# Use the node_name attribute if it exists, otherwise use class name
|
|
63
|
+
node_name = getattr(obj, 'node_name', name)
|
|
64
|
+
custom_nodes[node_name] = obj
|
|
65
|
+
print(f"Loaded custom node: {node_name} from {file_path.name}")
|
|
66
|
+
|
|
67
|
+
except Exception as e:
|
|
68
|
+
print(f"Error loading module from {file_path}: {e}")
|
|
69
|
+
# Continue with other files even if one fails
|
|
70
|
+
continue
|
|
71
|
+
|
|
72
|
+
return custom_nodes
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def get_all_custom_nodes_with_validation() -> Dict[str, Type[CustomNodeBase]]:
|
|
76
|
+
"""
|
|
77
|
+
Enhanced version that validates the nodes before adding them.
|
|
78
|
+
"""
|
|
79
|
+
|
|
80
|
+
custom_nodes = {}
|
|
81
|
+
nodes_path = storage.user_defined_nodes_directory
|
|
82
|
+
|
|
83
|
+
if not nodes_path.exists():
|
|
84
|
+
return custom_nodes
|
|
85
|
+
|
|
86
|
+
for file_path in nodes_path.glob("*.py"):
|
|
87
|
+
if file_path.name.startswith("__"):
|
|
88
|
+
continue
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
module_name = file_path.stem
|
|
92
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
93
|
+
|
|
94
|
+
if spec and spec.loader:
|
|
95
|
+
module = importlib.util.module_from_spec(spec)
|
|
96
|
+
sys.modules[module_name] = module
|
|
97
|
+
spec.loader.exec_module(module)
|
|
98
|
+
|
|
99
|
+
for name, obj in inspect.getmembers(module):
|
|
100
|
+
if (inspect.isclass(obj) and
|
|
101
|
+
issubclass(obj, CustomNodeBase) and
|
|
102
|
+
obj is not CustomNodeBase):
|
|
103
|
+
|
|
104
|
+
try:
|
|
105
|
+
_obj = obj()
|
|
106
|
+
# Validate that the node has required attributes
|
|
107
|
+
if not hasattr(_obj, 'node_name'):
|
|
108
|
+
logger.error(f"Warning: {name} missing node_name attribute")
|
|
109
|
+
raise ValueError(f"Node {name} must implement a node_name attribute")
|
|
110
|
+
|
|
111
|
+
if not hasattr(_obj, 'settings_schema'):
|
|
112
|
+
logger.error(f"Warning: {name} missing settings_schema attribute")
|
|
113
|
+
raise ValueError(f"Node {name} must implement a settings_schema attribute")
|
|
114
|
+
|
|
115
|
+
if not hasattr(_obj, 'process'):
|
|
116
|
+
logger.error(f"Warning: {name} missing process method")
|
|
117
|
+
raise ValueError(f"Node {name} must implement a process method")
|
|
118
|
+
if not (storage.user_defined_nodes_icons / _obj.node_icon).exists():
|
|
119
|
+
logger.warning(
|
|
120
|
+
f"Warning: Icon file does not exist for node {_obj.node_name} at {_obj.node_icon} "
|
|
121
|
+
"Falling back to default icon."
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
node_name = _obj.to_node_template().item
|
|
125
|
+
custom_nodes[node_name] = obj
|
|
126
|
+
print(f"✓ Loaded: {node_name} from {file_path.name}")
|
|
127
|
+
except Exception as e:
|
|
128
|
+
print(f"Error validating node {name} in {file_path}: {e}")
|
|
129
|
+
continue
|
|
130
|
+
except SyntaxError as e:
|
|
131
|
+
print(f"Syntax error in {file_path}: {e}")
|
|
132
|
+
except ImportError as e:
|
|
133
|
+
print(f"Import error in {file_path}: {e}")
|
|
134
|
+
except Exception as e:
|
|
135
|
+
print(f"Unexpected error loading {file_path}: {e}")
|
|
136
|
+
|
|
137
|
+
return custom_nodes
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def get_custom_nodes_lazy() -> List[Type[CustomNodeBase]]:
|
|
141
|
+
"""
|
|
142
|
+
Returns a list of custom node classes without instantiating them.
|
|
143
|
+
Useful for registration or catalog purposes.
|
|
144
|
+
"""
|
|
145
|
+
nodes = []
|
|
146
|
+
nodes_path = Path(storage.user_defined_nodes_directory)
|
|
147
|
+
|
|
148
|
+
if not nodes_path.exists():
|
|
149
|
+
return nodes
|
|
150
|
+
|
|
151
|
+
for file_path in nodes_path.glob("*.py"):
|
|
152
|
+
if file_path.name.startswith("__"):
|
|
153
|
+
continue
|
|
154
|
+
|
|
155
|
+
try:
|
|
156
|
+
# Create a unique module name to avoid conflicts
|
|
157
|
+
module_name = f"custom_node_{file_path.stem}_{id(file_path)}"
|
|
158
|
+
spec = importlib.util.spec_from_file_location(module_name, file_path)
|
|
159
|
+
|
|
160
|
+
if spec and spec.loader:
|
|
161
|
+
module = importlib.util.module_from_spec(spec)
|
|
162
|
+
spec.loader.exec_module(module)
|
|
163
|
+
|
|
164
|
+
for name, obj in inspect.getmembers(module):
|
|
165
|
+
if (inspect.isclass(obj) and
|
|
166
|
+
issubclass(obj, CustomNodeBase) and
|
|
167
|
+
obj is not CustomNodeBase and
|
|
168
|
+
obj.__module__ == module.__name__): # Only get classes defined in this module
|
|
169
|
+
nodes.append(obj)
|
|
170
|
+
|
|
171
|
+
except Exception as e:
|
|
172
|
+
print(f"Error processing {file_path}: {e}")
|
|
173
|
+
continue
|
|
174
|
+
|
|
175
|
+
return nodes
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# Example usage function that matches your original pattern
|
|
179
|
+
def add_custom_node(node_class: Type[CustomNodeBase], registry: Dict[str, Type[CustomNodeBase]]):
|
|
180
|
+
"""Add a single custom node to the registry."""
|
|
181
|
+
if hasattr(node_class, 'node_name'):
|
|
182
|
+
registry[node_class.node_name] = node_class
|
|
183
|
+
else:
|
|
184
|
+
registry[node_class.__name__] = node_class
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def get_all_nodes_from_standard_location() -> Dict[str, Type[CustomNodeBase]]:
|
|
188
|
+
"""
|
|
189
|
+
Main function to get all custom nodes from the standard location.
|
|
190
|
+
This matches your original function signature.
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
return get_all_custom_nodes_with_validation()
|
|
@@ -7,6 +7,7 @@ import argparse
|
|
|
7
7
|
|
|
8
8
|
from passlib.context import CryptContext
|
|
9
9
|
from starlette.config import Config
|
|
10
|
+
from shared.storage_config import storage
|
|
10
11
|
|
|
11
12
|
from flowfile_core.configs.utils import MutableBool
|
|
12
13
|
|
|
@@ -93,7 +94,7 @@ FILE_LOCATION = config("FILE_LOCATION", cast=str, default=".\\files\\")
|
|
|
93
94
|
AVAILABLE_RAM = config("AVAILABLE_RAM", cast=int, default=8)
|
|
94
95
|
WORKER_URL = config("FLOWFILE_WORKER_URL", cast=str, default=get_default_worker_url(WORKER_PORT))
|
|
95
96
|
IS_RUNNING_IN_DOCKER = os.getenv('RUNNING_IN_DOCKER', 'false').lower() == 'true'
|
|
96
|
-
TEMP_DIR =
|
|
97
|
+
TEMP_DIR = storage.temp_directory
|
|
97
98
|
|
|
98
99
|
ALGORITHM = "HS256"
|
|
99
100
|
ACCESS_TOKEN_EXPIRE_MINUTES = 120
|
|
@@ -5,37 +5,21 @@ import os
|
|
|
5
5
|
import sys
|
|
6
6
|
from pathlib import Path
|
|
7
7
|
from flowfile_core.configs import logger
|
|
8
|
+
from shared.storage_config import storage
|
|
8
9
|
|
|
9
10
|
|
|
10
11
|
def get_app_data_dir() -> Path:
|
|
11
12
|
"""Get the appropriate application data directory for the current platform."""
|
|
12
|
-
app_name = "Flowfile"
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
# Windows: C:\Users\{username}\AppData\Local\flowfile
|
|
16
|
-
base_dir = os.environ.get("LOCALAPPDATA")
|
|
17
|
-
if not base_dir:
|
|
18
|
-
base_dir = os.path.join(os.path.expanduser("~"), "AppData", "Local")
|
|
19
|
-
elif sys.platform == "darwin":
|
|
20
|
-
# macOS: ~/Library/Application Support/flowfile
|
|
21
|
-
base_dir = os.path.join(os.path.expanduser("~"), "Library", "Application Support")
|
|
22
|
-
else:
|
|
23
|
-
# Linux: ~/.local/share/flowfile or use XDG_DATA_HOME
|
|
24
|
-
base_dir = os.environ.get("XDG_DATA_HOME")
|
|
25
|
-
if not base_dir:
|
|
26
|
-
base_dir = os.path.join(os.path.expanduser("~"), ".local", "share")
|
|
27
|
-
|
|
28
|
-
app_dir = Path(base_dir) / app_name
|
|
29
|
-
app_dir.mkdir(parents=True, exist_ok=True)
|
|
30
|
-
|
|
31
|
-
return app_dir
|
|
14
|
+
return storage.database_directory
|
|
32
15
|
|
|
33
16
|
|
|
34
17
|
def get_database_url():
|
|
35
18
|
"""Get the database URL based on the current environment."""
|
|
36
19
|
if os.environ.get("TESTING") == "True":
|
|
37
20
|
# Use a temporary test database
|
|
38
|
-
test_db_path =
|
|
21
|
+
test_db_path = storage.temp_directory / "test_flowfile.db"
|
|
22
|
+
logger.debug(f"Using TESTING database URL: sqlite:///{test_db_path}")
|
|
39
23
|
return f"sqlite:///{test_db_path}"
|
|
40
24
|
|
|
41
25
|
custom_db_path = os.environ.get("FLOWFILE_DB_PATH")
|
|
@@ -94,4 +78,4 @@ def get_database_info():
|
|
|
94
78
|
"path": str(get_database_path()) if get_database_path() else None,
|
|
95
79
|
"app_data_dir": str(get_app_data_dir()),
|
|
96
80
|
"platform": sys.platform
|
|
97
|
-
}
|
|
81
|
+
}
|