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.
Files changed (201) hide show
  1. flowfile/__init__.py +8 -1
  2. flowfile/api.py +1 -3
  3. flowfile/web/static/assets/{CloudConnectionManager-c97c25f8.js → CloudConnectionManager-0dfba9f2.js} +2 -2
  4. flowfile/web/static/assets/{CloudStorageReader-f1ff509e.js → CloudStorageReader-d5b1b6c9.js} +11 -78
  5. flowfile/web/static/assets/{CloudStorageWriter-034f8b78.js → CloudStorageWriter-00d87aad.js} +12 -79
  6. flowfile/web/static/assets/{CloudStorageWriter-49c9a4b2.css → CloudStorageWriter-b0ee067f.css} +24 -24
  7. flowfile/web/static/assets/ColumnSelector-4685e75d.js +83 -0
  8. flowfile/web/static/assets/ColumnSelector-47996a16.css +10 -0
  9. flowfile/web/static/assets/ContextMenu-23e909da.js +41 -0
  10. flowfile/web/static/assets/{SettingsSection-9c836ecc.css → ContextMenu-4c74eef1.css} +0 -21
  11. flowfile/web/static/assets/ContextMenu-63cfa99b.css +26 -0
  12. flowfile/web/static/assets/ContextMenu-70ae0c79.js +41 -0
  13. flowfile/web/static/assets/ContextMenu-c13f91d0.css +26 -0
  14. flowfile/web/static/assets/ContextMenu-f149cf7c.js +41 -0
  15. flowfile/web/static/assets/{CrossJoin-41efa4cb.css → CrossJoin-1119d18e.css} +18 -18
  16. flowfile/web/static/assets/{CrossJoin-9e156ebe.js → CrossJoin-702a3edd.js} +14 -84
  17. flowfile/web/static/assets/CustomNode-74a37f74.css +32 -0
  18. flowfile/web/static/assets/CustomNode-b1519993.js +211 -0
  19. flowfile/web/static/assets/{DatabaseConnectionSettings-d5c625b3.js → DatabaseConnectionSettings-6f3e4ea5.js} +3 -3
  20. flowfile/web/static/assets/{DatabaseManager-265adc5e.js → DatabaseManager-cf5ef661.js} +2 -2
  21. flowfile/web/static/assets/{DatabaseReader-f50c6558.css → DatabaseReader-ae61773c.css} +0 -27
  22. flowfile/web/static/assets/{DatabaseReader-0b10551e.js → DatabaseReader-d38c7295.js} +14 -114
  23. flowfile/web/static/assets/{DatabaseWriter-c17c6916.js → DatabaseWriter-b04ef46a.js} +13 -74
  24. flowfile/web/static/assets/{ExploreData-5bdae813.css → ExploreData-2d0cf4db.css} +8 -14
  25. flowfile/web/static/assets/ExploreData-5fa10ed8.js +192 -0
  26. flowfile/web/static/assets/{ExternalSource-3a66556c.js → ExternalSource-d39af878.js} +8 -79
  27. flowfile/web/static/assets/{Filter-91ad87e7.js → Filter-9b6d08db.js} +12 -85
  28. flowfile/web/static/assets/{Filter-a9d08ba1.css → Filter-f62091b3.css} +3 -3
  29. flowfile/web/static/assets/{Formula-3c395ab1.js → Formula-6b04fb1d.js} +20 -87
  30. flowfile/web/static/assets/{Formula-29f19d21.css → Formula-bb96803d.css} +4 -4
  31. flowfile/web/static/assets/{FuzzyMatch-6857de82.css → FuzzyMatch-1010f966.css} +42 -42
  32. flowfile/web/static/assets/{FuzzyMatch-2df0d230.js → FuzzyMatch-999521f4.js} +16 -87
  33. flowfile/web/static/assets/{GraphSolver-d285877f.js → GraphSolver-17dd2198.js} +13 -159
  34. flowfile/web/static/assets/GraphSolver-f0cb7bfb.css +22 -0
  35. flowfile/web/static/assets/{GroupBy-0bd1cc6b.js → GroupBy-6b039e18.js} +12 -75
  36. flowfile/web/static/assets/{Unique-b5615727.css → GroupBy-b9505323.css} +8 -8
  37. flowfile/web/static/assets/{Join-5a78a203.js → Join-24d0f113.js} +15 -85
  38. flowfile/web/static/assets/{Join-f45eff22.css → Join-fd79b451.css} +20 -20
  39. flowfile/web/static/assets/{ManualInput-a71b52c6.css → ManualInput-3246a08d.css} +20 -20
  40. flowfile/web/static/assets/{ManualInput-93aef9d6.js → ManualInput-34639209.js} +11 -82
  41. flowfile/web/static/assets/MultiSelect-0e8724a3.js +5 -0
  42. flowfile/web/static/assets/MultiSelect.vue_vue_type_script_setup_true_lang-b0e538c2.js +63 -0
  43. flowfile/web/static/assets/NumericInput-3d63a470.js +5 -0
  44. flowfile/web/static/assets/NumericInput.vue_vue_type_script_setup_true_lang-e0edeccc.js +35 -0
  45. flowfile/web/static/assets/Output-283fe388.css +37 -0
  46. flowfile/web/static/assets/{Output-411ecaee.js → Output-edea9802.js} +62 -273
  47. flowfile/web/static/assets/{Pivot-89db4b04.js → Pivot-61d19301.js} +14 -138
  48. flowfile/web/static/assets/Pivot-cf333e3d.css +22 -0
  49. flowfile/web/static/assets/PivotValidation-891ddfb0.css +13 -0
  50. flowfile/web/static/assets/PivotValidation-c46cd420.css +13 -0
  51. flowfile/web/static/assets/PivotValidation-de9f43fe.js +61 -0
  52. flowfile/web/static/assets/PivotValidation-f97fec5b.js +61 -0
  53. flowfile/web/static/assets/{PolarsCode-a9f974f8.js → PolarsCode-bc3c9984.js} +13 -80
  54. flowfile/web/static/assets/Read-64a3f259.js +218 -0
  55. flowfile/web/static/assets/Read-e808b239.css +62 -0
  56. flowfile/web/static/assets/RecordCount-3d5039be.js +53 -0
  57. flowfile/web/static/assets/{RecordId-55ae7d36.js → RecordId-597510e0.js} +8 -80
  58. flowfile/web/static/assets/SQLQueryComponent-36cef432.css +27 -0
  59. flowfile/web/static/assets/SQLQueryComponent-df51adbe.js +38 -0
  60. flowfile/web/static/assets/{Sample-b4a18476.js → Sample-4be0a507.js} +8 -77
  61. flowfile/web/static/assets/{SecretManager-b066d13a.js → SecretManager-4839be57.js} +2 -2
  62. flowfile/web/static/assets/{Select-727688dc.js → Select-9b72f201.js} +11 -85
  63. flowfile/web/static/assets/SettingsSection-2e4d03c4.css +21 -0
  64. flowfile/web/static/assets/SettingsSection-5c696bee.css +20 -0
  65. flowfile/web/static/assets/SettingsSection-71e6b7e3.css +21 -0
  66. flowfile/web/static/assets/SettingsSection-7ded385d.js +45 -0
  67. flowfile/web/static/assets/{SettingsSection-695ac487.js → SettingsSection-e1e9c953.js} +2 -40
  68. flowfile/web/static/assets/SettingsSection-f0f75a42.js +53 -0
  69. flowfile/web/static/assets/SingleSelect-6c777aac.js +5 -0
  70. flowfile/web/static/assets/SingleSelect.vue_vue_type_script_setup_true_lang-33e3ff9b.js +62 -0
  71. flowfile/web/static/assets/SliderInput-7cb93e62.js +40 -0
  72. flowfile/web/static/assets/SliderInput-b8fb6a8c.css +4 -0
  73. flowfile/web/static/assets/{GroupBy-ab1ea74b.css → Sort-3643d625.css} +8 -8
  74. flowfile/web/static/assets/{Sort-be3339a8.js → Sort-6cbde21a.js} +12 -97
  75. flowfile/web/static/assets/TextInput-d9a40c11.js +5 -0
  76. flowfile/web/static/assets/TextInput.vue_vue_type_script_setup_true_lang-5896c375.js +32 -0
  77. flowfile/web/static/assets/{TextToRows-c92d1ec2.css → TextToRows-5d2c1190.css} +9 -9
  78. flowfile/web/static/assets/{TextToRows-7b8998da.js → TextToRows-c4fcbf4d.js} +14 -83
  79. flowfile/web/static/assets/ToggleSwitch-4ef91d19.js +5 -0
  80. flowfile/web/static/assets/ToggleSwitch.vue_vue_type_script_setup_true_lang-38478c20.js +31 -0
  81. flowfile/web/static/assets/{UnavailableFields-8b0cb48e.js → UnavailableFields-a03f512c.js} +2 -2
  82. flowfile/web/static/assets/{Union-8d9ac7f9.css → Union-af6c3d9b.css} +6 -6
  83. flowfile/web/static/assets/Union-bfe9b996.js +77 -0
  84. flowfile/web/static/assets/{Unique-af5a80b4.js → Unique-5d023a27.js} +23 -104
  85. flowfile/web/static/assets/{Sort-7ccfa0fe.css → Unique-f9fb0809.css} +8 -8
  86. flowfile/web/static/assets/Unpivot-1e422df3.css +30 -0
  87. flowfile/web/static/assets/{Unpivot-5195d411.js → Unpivot-91cc5354.js} +12 -166
  88. flowfile/web/static/assets/UnpivotValidation-0d240eeb.css +13 -0
  89. flowfile/web/static/assets/UnpivotValidation-7ee2de44.js +51 -0
  90. flowfile/web/static/assets/{ExploreData-18a4fe52.js → VueGraphicWalker-e51b9924.js} +4 -264
  91. flowfile/web/static/assets/VueGraphicWalker-ed5ab88b.css +6 -0
  92. flowfile/web/static/assets/{api-cb00cce6.js → api-c1bad5ca.js} +1 -1
  93. flowfile/web/static/assets/{api-023d1733.js → api-cf1221f0.js} +1 -1
  94. flowfile/web/static/assets/{designer-2197d782.css → designer-8da3ba3a.css} +859 -201
  95. flowfile/web/static/assets/{designer-6c322d8e.js → designer-9633482a.js} +2297 -733
  96. flowfile/web/static/assets/{documentation-4d1fafe1.js → documentation-ca400224.js} +1 -1
  97. flowfile/web/static/assets/{dropDown-0b46dd77.js → dropDown-614b998d.js} +1 -1
  98. flowfile/web/static/assets/{fullEditor-ec4e4f95.js → fullEditor-f7971590.js} +2 -2
  99. flowfile/web/static/assets/{genericNodeSettings-def5879b.js → genericNodeSettings-4fe5f36b.js} +3 -3
  100. flowfile/web/static/assets/{index-681a3ed0.css → index-50508d4d.css} +8 -0
  101. flowfile/web/static/assets/{index-683fc198.js → index-5429bbf8.js} +208 -31
  102. flowfile/web/static/assets/nodeInput-5d0d6b79.js +41 -0
  103. flowfile/web/static/assets/outputCsv-076b85ab.js +86 -0
  104. flowfile/web/static/assets/{Output-48f81019.css → outputCsv-9cc59e0b.css} +0 -143
  105. flowfile/web/static/assets/outputExcel-0fd17dbe.js +56 -0
  106. flowfile/web/static/assets/outputExcel-b41305c0.css +102 -0
  107. flowfile/web/static/assets/outputParquet-b61e0847.js +31 -0
  108. flowfile/web/static/assets/outputParquet-cf8cf3f2.css +4 -0
  109. flowfile/web/static/assets/readCsv-a8bb8b61.js +179 -0
  110. flowfile/web/static/assets/readCsv-c767cb37.css +52 -0
  111. flowfile/web/static/assets/readExcel-67b4aee0.js +201 -0
  112. flowfile/web/static/assets/readExcel-806d2826.css +64 -0
  113. flowfile/web/static/assets/readParquet-48c81530.css +19 -0
  114. flowfile/web/static/assets/readParquet-92ce1dbc.js +23 -0
  115. flowfile/web/static/assets/{secretApi-baceb6f9.js → secretApi-68435402.js} +1 -1
  116. flowfile/web/static/assets/{selectDynamic-de91449a.js → selectDynamic-92e25ee3.js} +7 -7
  117. flowfile/web/static/assets/{selectDynamic-b062bc9b.css → selectDynamic-aa913ff4.css} +16 -16
  118. flowfile/web/static/assets/user-defined-icon-0ae16c90.png +0 -0
  119. flowfile/web/static/assets/{vue-codemirror.esm-dc5e3348.js → vue-codemirror.esm-41b0e0d7.js} +65 -36
  120. flowfile/web/static/assets/{vue-content-loader.es-ba94b82f.js → vue-content-loader.es-2c8e608f.js} +1 -1
  121. flowfile/web/static/index.html +2 -2
  122. {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info}/METADATA +5 -3
  123. {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info}/RECORD +191 -121
  124. {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info}/WHEEL +1 -1
  125. {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info}/entry_points.txt +1 -0
  126. flowfile_core/__init__.py +3 -0
  127. flowfile_core/configs/flow_logger.py +5 -13
  128. flowfile_core/configs/node_store/__init__.py +30 -0
  129. flowfile_core/configs/node_store/nodes.py +383 -99
  130. flowfile_core/configs/node_store/user_defined_node_registry.py +193 -0
  131. flowfile_core/configs/settings.py +2 -1
  132. flowfile_core/database/connection.py +5 -21
  133. flowfile_core/fileExplorer/funcs.py +239 -121
  134. flowfile_core/flowfile/analytics/analytics_processor.py +1 -0
  135. flowfile_core/flowfile/code_generator/code_generator.py +62 -64
  136. flowfile_core/flowfile/flow_data_engine/create/funcs.py +73 -56
  137. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +77 -86
  138. flowfile_core/flowfile/flow_data_engine/flow_file_column/interface.py +4 -0
  139. flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +19 -34
  140. flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +36 -0
  141. flowfile_core/flowfile/flow_data_engine/fuzzy_matching/prepare_for_fuzzy_match.py +23 -23
  142. flowfile_core/flowfile/flow_data_engine/join/utils.py +1 -1
  143. flowfile_core/flowfile/flow_data_engine/join/verify_integrity.py +9 -4
  144. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +212 -86
  145. flowfile_core/flowfile/flow_data_engine/utils.py +2 -0
  146. flowfile_core/flowfile/flow_graph.py +240 -54
  147. flowfile_core/flowfile/flow_node/flow_node.py +48 -13
  148. flowfile_core/flowfile/flow_node/models.py +2 -1
  149. flowfile_core/flowfile/handler.py +24 -5
  150. flowfile_core/flowfile/manage/compatibility_enhancements.py +404 -41
  151. flowfile_core/flowfile/manage/io_flowfile.py +394 -0
  152. flowfile_core/flowfile/node_designer/__init__.py +47 -0
  153. flowfile_core/flowfile/node_designer/_type_registry.py +197 -0
  154. flowfile_core/flowfile/node_designer/custom_node.py +371 -0
  155. flowfile_core/flowfile/node_designer/ui_components.py +277 -0
  156. flowfile_core/flowfile/schema_callbacks.py +17 -10
  157. flowfile_core/flowfile/setting_generator/settings.py +15 -10
  158. flowfile_core/main.py +5 -1
  159. flowfile_core/routes/routes.py +73 -30
  160. flowfile_core/routes/user_defined_components.py +55 -0
  161. flowfile_core/schemas/cloud_storage_schemas.py +0 -2
  162. flowfile_core/schemas/input_schema.py +228 -65
  163. flowfile_core/schemas/output_model.py +5 -2
  164. flowfile_core/schemas/schemas.py +153 -35
  165. flowfile_core/schemas/transform_schema.py +1083 -412
  166. flowfile_core/schemas/yaml_types.py +103 -0
  167. flowfile_core/types.py +156 -0
  168. flowfile_core/utils/validate_setup.py +3 -1
  169. flowfile_frame/__init__.py +3 -1
  170. flowfile_frame/flow_frame.py +31 -24
  171. flowfile_frame/flow_frame_methods.py +12 -9
  172. flowfile_worker/__init__.py +9 -35
  173. flowfile_worker/create/__init__.py +3 -21
  174. flowfile_worker/create/funcs.py +68 -56
  175. flowfile_worker/create/models.py +130 -62
  176. flowfile_worker/main.py +5 -2
  177. flowfile_worker/routes.py +52 -13
  178. shared/__init__.py +15 -0
  179. shared/storage_config.py +258 -0
  180. tools/migrate/README.md +56 -0
  181. tools/migrate/__init__.py +12 -0
  182. tools/migrate/__main__.py +131 -0
  183. tools/migrate/legacy_schemas.py +621 -0
  184. tools/migrate/migrate.py +598 -0
  185. tools/migrate/tests/__init__.py +0 -0
  186. tools/migrate/tests/conftest.py +23 -0
  187. tools/migrate/tests/test_migrate.py +627 -0
  188. tools/migrate/tests/test_migration_e2e.py +1010 -0
  189. tools/migrate/tests/test_node_migrations.py +813 -0
  190. flowfile/web/static/assets/GraphSolver-17fd26db.css +0 -68
  191. flowfile/web/static/assets/Pivot-f415e85f.css +0 -35
  192. flowfile/web/static/assets/Read-80dc1675.css +0 -197
  193. flowfile/web/static/assets/Read-c3b1929c.js +0 -701
  194. flowfile/web/static/assets/RecordCount-4e95f98e.js +0 -122
  195. flowfile/web/static/assets/Union-89fd73dc.js +0 -146
  196. flowfile/web/static/assets/Unpivot-246e9bbd.css +0 -77
  197. flowfile/web/static/assets/nodeTitle-a16db7c3.js +0 -227
  198. flowfile/web/static/assets/nodeTitle-f4b12bcb.css +0 -134
  199. flowfile_core/flowfile/manage/open_flowfile.py +0 -135
  200. {flowfile-0.3.9.dist-info → flowfile-0.5.1.dist-info/licenses}/LICENSE +0 -0
  201. /flowfile_core/flowfile/manage/manage_flowfile.py → /tools/__init__.py +0 -0
@@ -12,11 +12,12 @@ from flowfile_core.configs.flow_logger import main_logger
12
12
  from flowfile_core.flowfile.flow_data_engine.flow_file_column.main import FlowfileColumn, PlType
13
13
  from flowfile_core.schemas import transform_schema
14
14
  from flowfile_core.schemas import input_schema
15
+ from flowfile_core.schemas.transform_schema import FuzzyMatchInputManager
15
16
 
16
17
 
17
18
  def _ensure_all_columns_have_select(left_cols: List[str],
18
19
  right_cols: List[str],
19
- fuzzy_match_input: transform_schema.FuzzyMatchInput):
20
+ fuzzy_match_input: transform_schema.FuzzyMatchInputManager):
20
21
  """
21
22
  Ensure that all columns in the left and right FlowDataEngines are included in the fuzzy match input's select
22
23
  statements.
@@ -38,7 +39,7 @@ def _ensure_all_columns_have_select(left_cols: List[str],
38
39
  )
39
40
 
40
41
 
41
- def _order_join_inputs_based_on_col_order(col_order: List[str], join_inputs: transform_schema.JoinInputs) -> None:
42
+ def _order_join_inputs_based_on_col_order(col_order: List[str], join_inputs: transform_schema.JoinInputsManager) -> None:
42
43
  """
43
44
  Ensure that the select columns in the fuzzy match input match the order of the incoming columns.
44
45
  This function modifies the join_inputs object in-place.
@@ -46,36 +47,42 @@ def _order_join_inputs_based_on_col_order(col_order: List[str], join_inputs: tra
46
47
  Returns:
47
48
  None
48
49
  """
49
- select_map = {select.new_name: select for select in join_inputs.renames}
50
+ select_map = {select.old_name: select for select in join_inputs.renames}
50
51
  ordered_renames = [select_map[col] for col in col_order if col in select_map]
51
- join_inputs.renames = ordered_renames
52
+ join_inputs.select_inputs.renames = ordered_renames
52
53
 
53
54
 
54
- def calculate_fuzzy_match_schema(fm_input: transform_schema.FuzzyMatchInput,
55
+ def calculate_fuzzy_match_schema(fm_input: transform_schema.FuzzyMatchInputManager,
55
56
  left_schema: List[FlowfileColumn],
56
57
  right_schema: List[FlowfileColumn]):
57
58
  _ensure_all_columns_have_select(left_cols=[col.column_name for col in left_schema],
58
59
  right_cols=[col.column_name for col in right_schema],
59
60
  fuzzy_match_input=fm_input)
61
+
60
62
  _order_join_inputs_based_on_col_order(col_order=[col.column_name for col in left_schema],
61
63
  join_inputs=fm_input.left_select)
62
64
  _order_join_inputs_based_on_col_order(col_order=[col.column_name for col in right_schema],
63
65
  join_inputs=fm_input.right_select)
66
+ for column in fm_input.left_select.renames:
67
+ if column.join_key:
68
+ column.keep = True
69
+ for column in fm_input.right_select.renames:
70
+ if column.join_key:
71
+ column.keep = True
72
+
64
73
  left_schema_dict, right_schema_dict = ({ls.name: ls for ls in left_schema}, {rs.name: rs for rs in right_schema})
65
74
  fm_input.auto_rename()
66
-
67
75
  right_renames = {column.old_name: column.new_name for column in fm_input.right_select.renames}
68
76
  new_join_mapping = rename_fuzzy_right_mapping(fm_input.join_mapping, right_renames)
69
-
70
77
  output_schema = []
71
78
  for column in fm_input.left_select.renames:
72
79
  column_schema = left_schema_dict.get(column.old_name)
73
- if column_schema and column.keep:
80
+ if column_schema and (column.keep or column.join_key):
74
81
  output_schema.append(FlowfileColumn.from_input(column.new_name, column_schema.data_type,
75
82
  example_values=column_schema.example_values))
76
83
  for column in fm_input.right_select.renames:
77
84
  column_schema = right_schema_dict.get(column.old_name)
78
- if column_schema and column.keep:
85
+ if column_schema and (column.keep or column.join_key):
79
86
  output_schema.append(FlowfileColumn.from_input(column.new_name, column_schema.data_type,
80
87
  example_values=column_schema.example_values))
81
88
  set_name_in_fuzzy_mappings(new_join_mapping)
@@ -117,7 +124,7 @@ def pre_calculate_pivot_schema(node_input_schema: List[FlowfileColumn],
117
124
  pivot_input.index_columns]
118
125
  val_column_schema = get_schema_of_column(node_input_schema, pivot_input.value_col)
119
126
  if output_fields is not None and len(output_fields) > 0:
120
- return index_columns_schema+[FlowfileColumn(PlType(Plcolumn_name=output_field.name,
127
+ return index_columns_schema+[FlowfileColumn(PlType(column_name=output_field.name,
121
128
  pl_datatype=output_field.data_type)) for output_field in
122
129
  output_fields]
123
130
 
@@ -39,11 +39,14 @@ def join(node_data: "NodeData") -> NodeData:
39
39
  join_key = overlapping_cols[0]
40
40
  else:
41
41
  join_key = ''
42
- ji = transform_schema.JoinInput(join_mapping=join_key,
43
- left_select=node_data.main_input.columns,
44
- right_select=node_data.right_input.columns
45
- )
46
- ji.auto_rename()
42
+ join_input_manager = transform_schema.JoinInputManager(
43
+ transform_schema.JoinInput(join_mapping=join_key,
44
+ left_select=node_data.main_input.columns,
45
+ right_select=node_data.right_input.columns
46
+ )
47
+ )
48
+ join_input_manager.auto_rename()
49
+ ji = join_input_manager.to_join_input()
47
50
  node_data.setting_input = input_schema.NodeJoin(flow_id=node_data.flow_id,
48
51
  node_id=node_data.node_id,
49
52
  join_input=ji)
@@ -53,13 +56,15 @@ def join(node_data: "NodeData") -> NodeData:
53
56
  @setting_generator_method
54
57
  def cross_join(node_data: "NodeData") -> NodeData:
55
58
  if node_data.right_input and node_data.main_input:
56
- ji = transform_schema.CrossJoinInput(left_select=node_data.main_input.columns,
57
- right_select=node_data.right_input.columns)
58
- ji.auto_rename()
59
- print(ji)
59
+ cj_input_manager = transform_schema.CrossJoinInputManager(
60
+ transform_schema.CrossJoinInput(left_select=node_data.main_input.columns,
61
+ right_select=node_data.right_input.columns)
62
+ )
63
+ cj_input_manager.auto_rename()
64
+ cj = cj_input_manager.to_cross_join_input()
60
65
  node_data.setting_input = input_schema.NodeCrossJoin(flow_id=node_data.flow_id,
61
66
  node_id=node_data.node_id,
62
- cross_join_input=ji)
67
+ cross_join_input=cj)
63
68
  return node_data
64
69
 
65
70
 
flowfile_core/main.py CHANGED
@@ -7,6 +7,8 @@ import uvicorn
7
7
  from fastapi import FastAPI
8
8
  from fastapi.middleware.cors import CORSMiddleware
9
9
 
10
+ from shared.storage_config import storage
11
+
10
12
  from flowfile_core import ServerRun
11
13
  from flowfile_core.configs.settings import (SERVER_HOST, SERVER_PORT, WORKER_HOST, WORKER_PORT, WORKER_URL,)
12
14
 
@@ -16,15 +18,16 @@ from flowfile_core.routes.routes import router
16
18
  from flowfile_core.routes.public import router as public_router
17
19
  from flowfile_core.routes.logs import router as logs_router
18
20
  from flowfile_core.routes.cloud_connections import router as cloud_connections_router
21
+ from flowfile_core.routes.user_defined_components import router as user_defined_components_router
19
22
 
20
23
  from flowfile_core.configs.flow_logger import clear_all_flow_logs
24
+ storage.cleanup_directories()
21
25
 
22
26
  os.environ["FLOWFILE_MODE"] = "electron"
23
27
 
24
28
  should_exit = False
25
29
  server_instance = None
26
30
 
27
-
28
31
  @asynccontextmanager
29
32
  async def shutdown_handler(app: FastAPI):
30
33
  """Handles the graceful startup and shutdown of the FastAPI application.
@@ -77,6 +80,7 @@ app.include_router(logs_router, tags=["logs"])
77
80
  app.include_router(auth_router, prefix="/auth", tags=["auth"])
78
81
  app.include_router(secrets_router, prefix="/secrets", tags=["secrets"])
79
82
  app.include_router(cloud_connections_router, prefix="/cloud_connections", tags=["cloud_connections"])
83
+ app.include_router(user_defined_components_router, prefix="/user_defined_components", tags=["user_defined_components"])
80
84
 
81
85
 
82
86
  @app.post("/shutdown")
@@ -12,49 +12,49 @@ import logging
12
12
  import os
13
13
  from pathlib import Path
14
14
  from typing import List, Dict, Any, Optional
15
- from sqlalchemy.orm import Session
16
15
 
17
16
  from fastapi import APIRouter, File, UploadFile, BackgroundTasks, HTTPException, status, Body, Depends
18
17
  from fastapi.responses import JSONResponse, Response
19
18
  # External dependencies
20
19
  from polars_expr_transformer.function_overview import get_all_expressions, get_expression_overview
20
+ from sqlalchemy.orm import Session
21
21
 
22
+ from flowfile_core import flow_file_handler
22
23
  # Core modules
23
24
  from flowfile_core.auth.jwt import get_current_active_user
24
25
  from flowfile_core.configs import logger
25
- from flowfile_core.configs.node_store import nodes
26
- from flowfile_core.configs.settings import IS_RUNNING_IN_DOCKER
26
+ from flowfile_core.configs.node_store import nodes_list, check_if_has_default_setting
27
+ from flowfile_core.database.connection import get_db
27
28
  # File handling
28
29
  from flowfile_core.fileExplorer.funcs import (
29
- FileExplorer,
30
+ SecureFileExplorer,
30
31
  FileInfo,
31
32
  get_files_from_directory
32
33
  )
33
- from flowfile_core.flowfile.flow_graph import add_connection, delete_connection
34
- from flowfile_core.flowfile.code_generator.code_generator import export_flow_to_polars
35
34
  from flowfile_core.flowfile.analytics.analytics_processor import AnalyticsProcessor
35
+ from flowfile_core.flowfile.code_generator.code_generator import export_flow_to_polars
36
+ from flowfile_core.flowfile.database_connection_manager.db_connections import (store_database_connection,
37
+ get_database_connection,
38
+ delete_database_connection,
39
+ get_all_database_connections_interface)
36
40
  from flowfile_core.flowfile.extensions import get_instant_func_results
37
- # Flow handling
38
-
41
+ from flowfile_core.flowfile.flow_graph import add_connection, delete_connection
39
42
  from flowfile_core.flowfile.sources.external_sources.sql_source.sql_source import create_sql_source_from_db_settings
40
43
  from flowfile_core.run_lock import get_flow_run_lock
41
- # Schema and models
42
44
  from flowfile_core.schemas import input_schema, schemas, output_model
43
45
  from flowfile_core.utils import excel_file_manager
44
- from flowfile_core.utils.fileManager import create_dir, remove_paths
46
+ from flowfile_core.utils.fileManager import create_dir
45
47
  from flowfile_core.utils.utils import camel_case_to_snake_case
46
- from flowfile_core import flow_file_handler
47
- from flowfile_core.flowfile.database_connection_manager.db_connections import (store_database_connection,
48
- get_database_connection,
49
- delete_database_connection,
50
- get_all_database_connections_interface)
51
- from flowfile_core.database.connection import get_db
48
+ from shared.storage_config import storage
52
49
 
53
50
 
54
51
  router = APIRouter(dependencies=[Depends(get_current_active_user)])
55
52
 
56
53
  # Initialize services
57
- file_explorer = FileExplorer('/app/shared' if IS_RUNNING_IN_DOCKER else None)
54
+ file_explorer = SecureFileExplorer(
55
+ start_path=storage.user_data_directory,
56
+ sandbox_root=storage.user_data_directory
57
+ )
58
58
 
59
59
 
60
60
  def get_node_model(setting_name_ref: str):
@@ -148,7 +148,7 @@ async def get_directory_contents(directory: str, file_types: List[str] = None,
148
148
  Returns:
149
149
  A list of `FileInfo` objects representing the directory's contents.
150
150
  """
151
- directory_explorer = FileExplorer(directory)
151
+ directory_explorer = SecureFileExplorer(directory, storage.user_data_directory)
152
152
  try:
153
153
  return directory_explorer.list_contents(show_hidden=include_hidden, file_types=file_types)
154
154
  except Exception as e:
@@ -198,6 +198,24 @@ async def get_active_flow_file_sessions() -> List[schemas.FlowSettings]:
198
198
  return [flf.flow_settings for flf in flow_file_handler.flowfile_flows]
199
199
 
200
200
 
201
+ @router.post("/node/trigger_fetch_data", tags=['editor'])
202
+ async def trigger_fetch_node_data(flow_id: int, node_id: int, background_tasks: BackgroundTasks):
203
+ """Fetches and refreshes the data for a specific node."""
204
+ flow = flow_file_handler.get_flow(flow_id)
205
+ lock = get_flow_run_lock(flow_id)
206
+ async with lock:
207
+ if flow.flow_settings.is_running:
208
+ raise HTTPException(422, 'Flow is already running')
209
+ try:
210
+ flow.validate_if_node_can_be_fetched(node_id)
211
+ except Exception as e:
212
+ raise HTTPException(422, str(e))
213
+ background_tasks.add_task(flow.trigger_fetch_node, node_id)
214
+ return JSONResponse(content={"message": "Data started",
215
+ "flow_id": flow_id,
216
+ "node_id": node_id}, status_code=status.HTTP_200_OK)
217
+
218
+
201
219
  @router.post('/flow/run/', tags=['editor'])
202
220
  async def run_flow(flow_id: int, background_tasks: BackgroundTasks) -> JSONResponse:
203
221
  """Executes a flow in a background task.
@@ -228,6 +246,16 @@ def cancel_flow(flow_id: int):
228
246
  flow.cancel()
229
247
 
230
248
 
249
+ @router.post("/flow/apply_standard_layout/", tags=["editor"])
250
+ def apply_standard_layout(flow_id: int):
251
+ flow = flow_file_handler.get_flow(flow_id)
252
+ if not flow:
253
+ raise HTTPException(status_code=404, detail="Flow not found")
254
+ if flow.flow_settings.is_running:
255
+ raise HTTPException(422, "Flow is running")
256
+ flow.apply_layout()
257
+
258
+
231
259
  @router.get('/flow/run_status/', tags=['editor'],
232
260
  response_model=output_model.RunInformation)
233
261
  def get_run_status(flow_id: int, response: Response):
@@ -240,8 +268,8 @@ def get_run_status(flow_id: int, response: Response):
240
268
  raise HTTPException(status_code=404, detail="Flow not found")
241
269
  if flow.flow_settings.is_running:
242
270
  response.status_code = status.HTTP_202_ACCEPTED
243
- return flow.get_run_info()
244
- response.status_code = status.HTTP_200_OK
271
+ else:
272
+ response.status_code = status.HTTP_200_OK
245
273
  return flow.get_run_info()
246
274
 
247
275
 
@@ -324,7 +352,7 @@ def add_node(flow_id: int, node_id: int, node_type: str, pos_x: int = 0, pos_y:
324
352
  logger.info("Adding node")
325
353
  flow.add_node_promise(node_promise)
326
354
 
327
- if nodes.check_if_has_default_setting(node_type):
355
+ if check_if_has_default_setting(node_type):
328
356
  logger.info(f'Found standard settings for {node_type}, trying to upload them')
329
357
  setting_name_ref = 'node' + node_type.replace('_', '')
330
358
  node_model = get_node_model(setting_name_ref)
@@ -439,11 +467,24 @@ def get_generated_code(flow_id: int) -> str:
439
467
 
440
468
 
441
469
  @router.post('/editor/create_flow/', tags=['editor'])
442
- def create_flow(flow_path: str):
470
+ def create_flow(flow_path: str = None, name: str = None):
443
471
  """Creates a new, empty flow file at the specified path and registers a session for it."""
444
- flow_path = Path(flow_path)
445
- logger.info('Creating flow')
446
- return flow_file_handler.add_flow(name=flow_path.stem, flow_path=str(flow_path))
472
+ if flow_path is not None and name is None:
473
+ name = Path(flow_path).stem
474
+ elif flow_path is not None and name is not None:
475
+ if name not in flow_path and (flow_path.endswith(".yaml") or flow_path.endswith(".yml")):
476
+ raise HTTPException(422, 'The name must be part of the flow path when a full path is provided')
477
+ elif name in flow_path and not (flow_path.endswith(".yaml") or flow_path.endswith(".yml")):
478
+ flow_path = str(Path(flow_path) / (name + ".yaml"))
479
+ elif name not in flow_path and (name.endswith(".yaml") or name.endswith(".yml")):
480
+ flow_path = str(Path(flow_path) / name)
481
+ elif name not in flow_path and not (name.endswith(".yaml") or name.endswith(".yml")):
482
+ flow_path = str(Path(flow_path) / (name + ".yaml"))
483
+ if flow_path is not None:
484
+ flow_path_ref = Path(flow_path)
485
+ if not flow_path_ref.parent.exists():
486
+ raise HTTPException(422, 'The directory does not exist')
487
+ return flow_file_handler.add_flow(name=name, flow_path=flow_path)
447
488
 
448
489
 
449
490
  @router.post('/editor/close_flow/', tags=['editor'])
@@ -471,6 +512,7 @@ def add_generic_settings(input_data: Dict[str, Any], node_type: str, current_use
471
512
  add_func = getattr(flow, 'add_' + node_type)
472
513
  parsed_input = None
473
514
  setting_name_ref = 'node' + node_type.replace('_', '')
515
+
474
516
  if add_func is None:
475
517
  raise HTTPException(404, 'could not find the function')
476
518
  try:
@@ -496,10 +538,11 @@ def get_list_of_saved_flows(path: str):
496
538
  except:
497
539
  return []
498
540
 
499
- @router.get('/node_list', response_model=List[nodes.NodeTemplate])
500
- def get_node_list() -> List[nodes.NodeTemplate]:
541
+
542
+ @router.get('/node_list', response_model=List[schemas.NodeTemplate])
543
+ def get_node_list() -> List[schemas.NodeTemplate]:
501
544
  """Retrieves the list of all available node types and their templates."""
502
- return nodes.nodes_list
545
+ return nodes_list
503
546
 
504
547
 
505
548
  @router.get('/node', response_model=output_model.NodeData, tags=['editor'])
@@ -555,7 +598,7 @@ async def get_downstream_node_ids(flow_id: int, node_id: int) -> List[int]:
555
598
 
556
599
  @router.get('/import_flow/', tags=['editor'], response_model=int)
557
600
  def import_saved_flow(flow_path: str) -> int:
558
- """Imports a flow from a saved `.flowfile` and registers it as a new session."""
601
+ """Imports a flow from a saved `.yaml` and registers it as a new session."""
559
602
  flow_path = Path(flow_path)
560
603
  if not flow_path.exists():
561
604
  raise HTTPException(404, 'File not found')
@@ -564,7 +607,7 @@ def import_saved_flow(flow_path: str) -> int:
564
607
 
565
608
  @router.get('/save_flow', tags=['editor'])
566
609
  def save_flow(flow_id: int, flow_path: str = None):
567
- """Saves the current state of a flow to a `.flowfile`."""
610
+ """Saves the current state of a flow to a `.yaml`."""
568
611
  flow = flow_file_handler.get_flow(flow_id)
569
612
  flow.save_flow(flow_path=flow_path)
570
613
 
@@ -0,0 +1,55 @@
1
+
2
+ from typing import Dict, Any
3
+
4
+ from fastapi import APIRouter, HTTPException, Depends
5
+
6
+ from flowfile_core import flow_file_handler
7
+ # Core modules
8
+ from flowfile_core.auth.jwt import get_current_active_user
9
+ from flowfile_core.configs import logger
10
+ from flowfile_core.configs.node_store import CUSTOM_NODE_STORE
11
+ # File handling
12
+ from flowfile_core.schemas import input_schema
13
+ from flowfile_core.utils.utils import camel_case_to_snake_case
14
+
15
+ # External dependencies
16
+
17
+
18
+ router = APIRouter()
19
+
20
+
21
+ @router.get("/custom-node-schema", summary="Get a simple UI schema")
22
+ def get_simple_custom_object(flow_id: int, node_id: int):
23
+ """
24
+ This endpoint returns a hardcoded JSON object that represents the UI
25
+ for our SimpleFilterNode.
26
+ """
27
+ try:
28
+ node = flow_file_handler.get_node(flow_id=flow_id, node_id=node_id)
29
+ except Exception as e:
30
+ raise HTTPException(status_code=404, detail=str(e))
31
+ user_defined_node = CUSTOM_NODE_STORE.get(node.node_type)
32
+
33
+ if not user_defined_node:
34
+ raise HTTPException(status_code=404, detail=f"Node type '{node.node_type}' not found")
35
+ if node.is_setup:
36
+ settings = node.setting_input.settings
37
+ return user_defined_node.from_settings(settings).get_frontend_schema()
38
+ return user_defined_node().get_frontend_schema()
39
+
40
+
41
+ @router.post("/update_user_defined_node", tags=["transform"])
42
+ def update_user_defined_node(input_data: Dict[str, Any], node_type: str, current_user=Depends(get_current_active_user)):
43
+ input_data['user_id'] = current_user.id
44
+ node_type = camel_case_to_snake_case(node_type)
45
+ flow_id = int(input_data.get('flow_id'))
46
+ logger.info(f'Updating the data for flow: {flow_id}, node {input_data["node_id"]}')
47
+ flow = flow_file_handler.get_flow(flow_id)
48
+ user_defined_model = CUSTOM_NODE_STORE.get(node_type)
49
+ if not user_defined_model:
50
+ raise HTTPException(status_code=404, detail=f"Node type '{node_type}' not found")
51
+
52
+ user_defined_node_settings = input_schema.UserDefinedNode.model_validate(input_data)
53
+ initialized_model = user_defined_model.from_settings(user_defined_node_settings.settings)
54
+
55
+ flow.add_user_defined_node(custom_node=initialized_model, user_defined_node_settings=user_defined_node_settings)
@@ -136,11 +136,9 @@ class CloudStorageReadSettings(CloudStorageSettings):
136
136
 
137
137
  scan_mode: Literal["single_file", "directory"] = "single_file"
138
138
  file_format: Literal["csv", "parquet", "json", "delta", "iceberg"] = "parquet"
139
- # CSV specific options
140
139
  csv_has_header: Optional[bool] = True
141
140
  csv_delimiter: Optional[str] = ","
142
141
  csv_encoding: Optional[str] = "utf8"
143
- # Deltalake specific settings
144
142
  delta_version: Optional[int] = None
145
143
 
146
144