Flowfile 0.5.1__py3-none-any.whl → 0.5.3__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 (329) hide show
  1. build_backends/main.py +25 -22
  2. build_backends/main_prd.py +10 -19
  3. flowfile/__init__.py +178 -74
  4. flowfile/__main__.py +10 -7
  5. flowfile/api.py +51 -57
  6. flowfile/web/__init__.py +14 -9
  7. flowfile/web/static/assets/AdminView-49392a9a.js +713 -0
  8. flowfile/web/static/assets/AdminView-f53bad23.css +129 -0
  9. flowfile/web/static/assets/CloudConnectionView-36bcd6df.css +72 -0
  10. flowfile/web/static/assets/{CloudConnectionManager-0dfba9f2.js → CloudConnectionView-f13f202b.js} +11 -11
  11. flowfile/web/static/assets/{CloudStorageReader-d5b1b6c9.js → CloudStorageReader-0023d4a5.js} +10 -8
  12. flowfile/web/static/assets/{CloudStorageReader-29d14fcc.css → CloudStorageReader-24c54524.css} +27 -27
  13. flowfile/web/static/assets/{CloudStorageWriter-b0ee067f.css → CloudStorageWriter-60547855.css} +26 -26
  14. flowfile/web/static/assets/{CloudStorageWriter-00d87aad.js → CloudStorageWriter-8e781e11.js} +10 -8
  15. flowfile/web/static/assets/{ColumnSelector-47996a16.css → ColumnSelector-371637fb.css} +2 -2
  16. flowfile/web/static/assets/{ColumnSelector-4685e75d.js → ColumnSelector-8ad68ea9.js} +3 -5
  17. flowfile/web/static/assets/{ContextMenu-c13f91d0.css → ContextMenu-26d4dd27.css} +6 -6
  18. flowfile/web/static/assets/{ContextMenu-23e909da.js → ContextMenu-31ee57f0.js} +3 -3
  19. flowfile/web/static/assets/{ContextMenu-70ae0c79.js → ContextMenu-69a74055.js} +3 -3
  20. flowfile/web/static/assets/{ContextMenu-f149cf7c.js → ContextMenu-8e2051c6.js} +3 -3
  21. flowfile/web/static/assets/{ContextMenu-4c74eef1.css → ContextMenu-8ec1729e.css} +6 -6
  22. flowfile/web/static/assets/{ContextMenu-63cfa99b.css → ContextMenu-9b310c60.css} +6 -6
  23. flowfile/web/static/assets/{CrossJoin-702a3edd.js → CrossJoin-03df6938.js} +12 -10
  24. flowfile/web/static/assets/{CrossJoin-1119d18e.css → CrossJoin-71b4cc10.css} +20 -20
  25. flowfile/web/static/assets/CustomNode-59e99a86.css +32 -0
  26. flowfile/web/static/assets/{CustomNode-b1519993.js → CustomNode-8479239b.js} +36 -24
  27. flowfile/web/static/assets/{DatabaseConnectionSettings-6f3e4ea5.js → DatabaseConnectionSettings-869e3efd.js} +5 -4
  28. flowfile/web/static/assets/{DatabaseConnectionSettings-0c04b2e5.css → DatabaseConnectionSettings-e91df89a.css} +13 -13
  29. flowfile/web/static/assets/{DatabaseReader-ae61773c.css → DatabaseReader-36898a00.css} +24 -24
  30. flowfile/web/static/assets/{DatabaseReader-d38c7295.js → DatabaseReader-c58b9552.js} +25 -15
  31. flowfile/web/static/assets/DatabaseView-6655afd6.css +57 -0
  32. flowfile/web/static/assets/{DatabaseManager-cf5ef661.js → DatabaseView-d26a9140.js} +11 -11
  33. flowfile/web/static/assets/{DatabaseWriter-2f570e53.css → DatabaseWriter-217a99f1.css} +19 -19
  34. flowfile/web/static/assets/{DatabaseWriter-b04ef46a.js → DatabaseWriter-4d05ddc7.js} +17 -10
  35. flowfile/web/static/assets/{designer-8da3ba3a.css → DesignerView-a6d0ee84.css} +614 -546
  36. flowfile/web/static/assets/{designer-9633482a.js → DesignerView-e6f5c0e8.js} +1107 -3170
  37. flowfile/web/static/assets/{documentation-ca400224.js → DocumentationView-2e78ef1b.js} +5 -5
  38. flowfile/web/static/assets/{documentation-12216a74.css → DocumentationView-fd46c656.css} +7 -7
  39. flowfile/web/static/assets/{ExploreData-2d0cf4db.css → ExploreData-10c5acc8.css} +13 -12
  40. flowfile/web/static/assets/{ExploreData-5fa10ed8.js → ExploreData-7b54caca.js} +18 -9
  41. flowfile/web/static/assets/{ExternalSource-d39af878.js → ExternalSource-3fa399b2.js} +9 -7
  42. flowfile/web/static/assets/{ExternalSource-e37b6275.css → ExternalSource-47ab05a3.css} +17 -17
  43. flowfile/web/static/assets/Filter-7494ea97.css +48 -0
  44. flowfile/web/static/assets/Filter-8cbbdbf3.js +287 -0
  45. flowfile/web/static/assets/{Formula-bb96803d.css → Formula-53d58c43.css} +7 -7
  46. flowfile/web/static/assets/{Formula-6b04fb1d.js → Formula-aac42b1e.js} +13 -11
  47. flowfile/web/static/assets/{FuzzyMatch-1010f966.css → FuzzyMatch-ad6361d6.css} +68 -69
  48. flowfile/web/static/assets/{FuzzyMatch-999521f4.js → FuzzyMatch-cd9bbfca.js} +12 -10
  49. flowfile/web/static/assets/{Pivot-cf333e3d.css → GraphSolver-c24dec17.css} +5 -5
  50. flowfile/web/static/assets/{GraphSolver-17dd2198.js → GraphSolver-c7e6780e.js} +13 -11
  51. flowfile/web/static/assets/{GroupBy-6b039e18.js → GroupBy-93c5d22b.js} +9 -7
  52. flowfile/web/static/assets/{GroupBy-b9505323.css → GroupBy-be7ac0bf.css} +10 -10
  53. flowfile/web/static/assets/{Join-fd79b451.css → Join-28b5e18f.css} +22 -22
  54. flowfile/web/static/assets/{Join-24d0f113.js → Join-a19b2de2.js} +13 -11
  55. flowfile/web/static/assets/LoginView-0df4ed0a.js +134 -0
  56. flowfile/web/static/assets/LoginView-d325d632.css +172 -0
  57. flowfile/web/static/assets/ManualInput-3702e677.css +293 -0
  58. flowfile/web/static/assets/{ManualInput-34639209.js → ManualInput-8d3374b2.js} +170 -116
  59. flowfile/web/static/assets/{MultiSelect-0e8724a3.js → MultiSelect-ad1b6243.js} +2 -2
  60. flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-b0e538c2.js → MultiSelect.vue_vue_type_script_setup_true_lang-e278950d.js} +1 -1
  61. flowfile/web/static/assets/NodeDesigner-40b647c9.js +2610 -0
  62. flowfile/web/static/assets/NodeDesigner-5f53be3f.css +1429 -0
  63. flowfile/web/static/assets/{NumericInput-3d63a470.js → NumericInput-7100234c.js} +2 -2
  64. flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-e0edeccc.js → NumericInput.vue_vue_type_script_setup_true_lang-5130219f.js} +5 -2
  65. flowfile/web/static/assets/{Output-283fe388.css → Output-35e97000.css} +6 -6
  66. flowfile/web/static/assets/{Output-edea9802.js → Output-f5efd2aa.js} +12 -9
  67. flowfile/web/static/assets/{GraphSolver-f0cb7bfb.css → Pivot-0eda81b4.css} +5 -5
  68. flowfile/web/static/assets/{Pivot-61d19301.js → Pivot-d981d23c.js} +11 -9
  69. flowfile/web/static/assets/PivotValidation-0e905b1a.css +13 -0
  70. flowfile/web/static/assets/{PivotValidation-f97fec5b.js → PivotValidation-39386e95.js} +3 -3
  71. flowfile/web/static/assets/PivotValidation-41b57ad6.css +13 -0
  72. flowfile/web/static/assets/{PivotValidation-de9f43fe.js → PivotValidation-63de1f73.js} +3 -3
  73. flowfile/web/static/assets/{PolarsCode-650322d1.css → PolarsCode-2b1f1f23.css} +4 -4
  74. flowfile/web/static/assets/{PolarsCode-bc3c9984.js → PolarsCode-f9d69217.js} +18 -9
  75. flowfile/web/static/assets/PopOver-b22f049e.js +939 -0
  76. flowfile/web/static/assets/PopOver-d96599db.css +33 -0
  77. flowfile/web/static/assets/{Read-e808b239.css → Read-36e7bd51.css} +12 -12
  78. flowfile/web/static/assets/{Read-64a3f259.js → Read-aec2e377.js} +14 -11
  79. flowfile/web/static/assets/{RecordCount-3d5039be.js → RecordCount-78ed6845.js} +6 -4
  80. flowfile/web/static/assets/{RecordId-597510e0.js → RecordId-2156e890.js} +8 -6
  81. flowfile/web/static/assets/{SQLQueryComponent-36cef432.css → SQLQueryComponent-1c2f26b4.css} +5 -5
  82. flowfile/web/static/assets/{SQLQueryComponent-df51adbe.js → SQLQueryComponent-48c72f5b.js} +3 -3
  83. flowfile/web/static/assets/{Sample-4be0a507.js → Sample-1352ca74.js} +6 -4
  84. flowfile/web/static/assets/SecretSelector-22b5ff89.js +113 -0
  85. flowfile/web/static/assets/SecretSelector-6329f743.css +43 -0
  86. flowfile/web/static/assets/{SecretManager-4839be57.js → SecretsView-17df66ee.js} +35 -36
  87. flowfile/web/static/assets/SecretsView-aa291340.css +38 -0
  88. flowfile/web/static/assets/{Select-9b72f201.js → Select-0aee4c54.js} +9 -7
  89. flowfile/web/static/assets/{SettingsSection-f0f75a42.js → SettingsSection-0784e157.js} +3 -3
  90. flowfile/web/static/assets/{SettingsSection-71e6b7e3.css → SettingsSection-07fbbc39.css} +4 -4
  91. flowfile/web/static/assets/{SettingsSection-5c696bee.css → SettingsSection-26fe48d4.css} +4 -4
  92. flowfile/web/static/assets/{SettingsSection-2e4d03c4.css → SettingsSection-8f980839.css} +4 -4
  93. flowfile/web/static/assets/{SettingsSection-e1e9c953.js → SettingsSection-cd341bb6.js} +3 -3
  94. flowfile/web/static/assets/{SettingsSection-7ded385d.js → SettingsSection-f2002a6d.js} +3 -3
  95. flowfile/web/static/assets/{SingleSelect-6c777aac.js → SingleSelect-460cc0ea.js} +2 -2
  96. flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-33e3ff9b.js → SingleSelect.vue_vue_type_script_setup_true_lang-30741bb2.js} +1 -1
  97. flowfile/web/static/assets/{SliderInput-7cb93e62.js → SliderInput-5d926864.js} +7 -4
  98. flowfile/web/static/assets/SliderInput-f2e4f23c.css +4 -0
  99. flowfile/web/static/assets/{Sort-6cbde21a.js → Sort-3cdc971b.js} +9 -7
  100. flowfile/web/static/assets/{Unique-f9fb0809.css → Sort-8a871341.css} +10 -10
  101. flowfile/web/static/assets/{TextInput-d9a40c11.js → TextInput-a2d0bfbd.js} +2 -2
  102. flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-5896c375.js → TextInput.vue_vue_type_script_setup_true_lang-abad1ca2.js} +5 -2
  103. flowfile/web/static/assets/{TextToRows-5d2c1190.css → TextToRows-12afb4f4.css} +10 -10
  104. flowfile/web/static/assets/{TextToRows-c4fcbf4d.js → TextToRows-918945f7.js} +11 -10
  105. flowfile/web/static/assets/{ToggleSwitch-4ef91d19.js → ToggleSwitch-f0ef5196.js} +2 -2
  106. flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-38478c20.js → ToggleSwitch.vue_vue_type_script_setup_true_lang-5605c793.js} +1 -1
  107. flowfile/web/static/assets/{UnavailableFields-5edd5322.css → UnavailableFields-54d2f518.css} +6 -6
  108. flowfile/web/static/assets/{UnavailableFields-a03f512c.js → UnavailableFields-bdad6144.js} +4 -4
  109. flowfile/web/static/assets/{Union-af6c3d9b.css → Union-d6a8d7d5.css} +7 -7
  110. flowfile/web/static/assets/{Union-bfe9b996.js → Union-e8ab8c86.js} +8 -6
  111. flowfile/web/static/assets/{Unique-5d023a27.js → Unique-8cd4f976.js} +13 -10
  112. flowfile/web/static/assets/{Sort-3643d625.css → Unique-9fb2f567.css} +10 -10
  113. flowfile/web/static/assets/{Unpivot-1e422df3.css → Unpivot-710a2948.css} +7 -7
  114. flowfile/web/static/assets/{Unpivot-91cc5354.js → Unpivot-8da14095.js} +10 -8
  115. flowfile/web/static/assets/{UnpivotValidation-7ee2de44.js → UnpivotValidation-6f7d89ff.js} +3 -3
  116. flowfile/web/static/assets/UnpivotValidation-d5ca3b7b.css +13 -0
  117. flowfile/web/static/assets/{VueGraphicWalker-e51b9924.js → VueGraphicWalker-3fb312e1.js} +4 -4
  118. flowfile/web/static/assets/{VueGraphicWalker-ed5ab88b.css → VueGraphicWalker-430f0b86.css} +1 -1
  119. flowfile/web/static/assets/{api-cf1221f0.js → api-24483f0d.js} +1 -1
  120. flowfile/web/static/assets/{api-c1bad5ca.js → api-8b81fa73.js} +1 -1
  121. flowfile/web/static/assets/{dropDown-35135ba8.css → dropDown-3d8dc5fa.css} +40 -40
  122. flowfile/web/static/assets/{dropDown-614b998d.js → dropDown-ac0fda9d.js} +3 -3
  123. flowfile/web/static/assets/{fullEditor-f7971590.js → fullEditor-5497a84a.js} +11 -10
  124. flowfile/web/static/assets/{fullEditor-178376bb.css → fullEditor-a0be62b3.css} +74 -62
  125. flowfile/web/static/assets/{genericNodeSettings-924759c7.css → genericNodeSettings-3b2507ea.css} +10 -10
  126. flowfile/web/static/assets/{genericNodeSettings-4fe5f36b.js → genericNodeSettings-99014e1d.js} +5 -5
  127. flowfile/web/static/assets/index-07dda503.js +38 -0
  128. flowfile/web/static/assets/index-3ba44389.js +2696 -0
  129. flowfile/web/static/assets/{index-50508d4d.css → index-e6289dd0.css} +1945 -569
  130. flowfile/web/static/assets/{index-5429bbf8.js → index-fb6493ae.js} +41626 -40867
  131. flowfile/web/static/assets/node.types-2c15bb7e.js +82 -0
  132. flowfile/web/static/assets/nodeInput-0eb13f1a.js +2 -0
  133. flowfile/web/static/assets/{outputCsv-076b85ab.js → outputCsv-8f8ba42d.js} +3 -3
  134. flowfile/web/static/assets/outputCsv-b9a072af.css +2499 -0
  135. flowfile/web/static/assets/{outputExcel-0fd17dbe.js → outputExcel-393f4fef.js} +3 -3
  136. flowfile/web/static/assets/{outputExcel-b41305c0.css → outputExcel-f5d272b2.css} +26 -26
  137. flowfile/web/static/assets/{outputParquet-b61e0847.js → outputParquet-07c81f65.js} +4 -4
  138. flowfile/web/static/assets/outputParquet-54597c3c.css +4 -0
  139. flowfile/web/static/assets/{readCsv-a8bb8b61.js → readCsv-07f6d9ad.js} +3 -3
  140. flowfile/web/static/assets/{readCsv-c767cb37.css → readCsv-3bfac4c3.css} +15 -15
  141. flowfile/web/static/assets/{readExcel-806d2826.css → readExcel-3db6b763.css} +13 -13
  142. flowfile/web/static/assets/{readExcel-67b4aee0.js → readExcel-ed69bc8f.js} +5 -5
  143. flowfile/web/static/assets/{readParquet-48c81530.css → readParquet-c5244ad5.css} +4 -4
  144. flowfile/web/static/assets/{readParquet-92ce1dbc.js → readParquet-e3ed4528.js} +3 -3
  145. flowfile/web/static/assets/secrets.api-002e7d7e.js +65 -0
  146. flowfile/web/static/assets/{selectDynamic-92e25ee3.js → selectDynamic-80b92899.js} +5 -5
  147. flowfile/web/static/assets/{selectDynamic-aa913ff4.css → selectDynamic-f2fb394f.css} +21 -20
  148. flowfile/web/static/assets/{vue-codemirror.esm-41b0e0d7.js → vue-codemirror.esm-0965f39f.js} +31 -640
  149. flowfile/web/static/assets/{vue-content-loader.es-2c8e608f.js → vue-content-loader.es-c506ad97.js} +1 -1
  150. flowfile/web/static/index.html +2 -2
  151. {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/METADATA +2 -3
  152. flowfile-0.5.3.dist-info/RECORD +402 -0
  153. flowfile_core/__init__.py +13 -6
  154. flowfile_core/auth/jwt.py +51 -16
  155. flowfile_core/auth/models.py +32 -7
  156. flowfile_core/auth/password.py +89 -0
  157. flowfile_core/auth/secrets.py +8 -6
  158. flowfile_core/configs/__init__.py +9 -7
  159. flowfile_core/configs/flow_logger.py +15 -14
  160. flowfile_core/configs/node_store/__init__.py +72 -4
  161. flowfile_core/configs/node_store/nodes.py +155 -172
  162. flowfile_core/configs/node_store/user_defined_node_registry.py +108 -27
  163. flowfile_core/configs/settings.py +28 -15
  164. flowfile_core/database/connection.py +7 -6
  165. flowfile_core/database/init_db.py +96 -2
  166. flowfile_core/database/models.py +3 -1
  167. flowfile_core/fileExplorer/__init__.py +17 -0
  168. flowfile_core/fileExplorer/funcs.py +123 -57
  169. flowfile_core/fileExplorer/utils.py +10 -11
  170. flowfile_core/flowfile/_extensions/real_time_interface.py +10 -8
  171. flowfile_core/flowfile/analytics/analytics_processor.py +26 -24
  172. flowfile_core/flowfile/analytics/graphic_walker.py +11 -12
  173. flowfile_core/flowfile/analytics/utils.py +1 -1
  174. flowfile_core/flowfile/code_generator/code_generator.py +358 -244
  175. flowfile_core/flowfile/connection_manager/_connection_manager.py +6 -5
  176. flowfile_core/flowfile/connection_manager/models.py +1 -1
  177. flowfile_core/flowfile/database_connection_manager/db_connections.py +60 -44
  178. flowfile_core/flowfile/database_connection_manager/models.py +1 -1
  179. flowfile_core/flowfile/extensions.py +17 -12
  180. flowfile_core/flowfile/flow_data_engine/cloud_storage_reader.py +34 -32
  181. flowfile_core/flowfile/flow_data_engine/create/funcs.py +115 -83
  182. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +481 -423
  183. flowfile_core/flowfile/flow_data_engine/flow_file_column/interface.py +2 -2
  184. flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +92 -52
  185. flowfile_core/flowfile/flow_data_engine/flow_file_column/polars_type.py +12 -11
  186. flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +6 -6
  187. flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +26 -30
  188. flowfile_core/flowfile/flow_data_engine/fuzzy_matching/prepare_for_fuzzy_match.py +31 -20
  189. flowfile_core/flowfile/flow_data_engine/join/__init__.py +1 -1
  190. flowfile_core/flowfile/flow_data_engine/join/utils.py +11 -9
  191. flowfile_core/flowfile/flow_data_engine/join/verify_integrity.py +14 -15
  192. flowfile_core/flowfile/flow_data_engine/pivot_table.py +5 -7
  193. flowfile_core/flowfile/flow_data_engine/polars_code_parser.py +95 -82
  194. flowfile_core/flowfile/flow_data_engine/read_excel_tables.py +66 -65
  195. flowfile_core/flowfile/flow_data_engine/sample_data.py +27 -21
  196. flowfile_core/flowfile/flow_data_engine/subprocess_operations/__init__.py +1 -1
  197. flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +13 -11
  198. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +190 -127
  199. flowfile_core/flowfile/flow_data_engine/threaded_processes.py +8 -8
  200. flowfile_core/flowfile/flow_data_engine/utils.py +99 -67
  201. flowfile_core/flowfile/flow_graph.py +918 -571
  202. flowfile_core/flowfile/flow_graph_utils.py +31 -49
  203. flowfile_core/flowfile/flow_node/flow_node.py +330 -233
  204. flowfile_core/flowfile/flow_node/models.py +53 -41
  205. flowfile_core/flowfile/flow_node/schema_callback.py +14 -19
  206. flowfile_core/flowfile/graph_tree/graph_tree.py +41 -41
  207. flowfile_core/flowfile/handler.py +80 -30
  208. flowfile_core/flowfile/manage/compatibility_enhancements.py +209 -126
  209. flowfile_core/flowfile/manage/io_flowfile.py +54 -57
  210. flowfile_core/flowfile/node_designer/__init__.py +15 -13
  211. flowfile_core/flowfile/node_designer/_type_registry.py +34 -37
  212. flowfile_core/flowfile/node_designer/custom_node.py +162 -36
  213. flowfile_core/flowfile/node_designer/ui_components.py +135 -34
  214. flowfile_core/flowfile/schema_callbacks.py +71 -51
  215. flowfile_core/flowfile/setting_generator/__init__.py +0 -1
  216. flowfile_core/flowfile/setting_generator/setting_generator.py +6 -5
  217. flowfile_core/flowfile/setting_generator/settings.py +64 -53
  218. flowfile_core/flowfile/sources/external_sources/base_class.py +12 -10
  219. flowfile_core/flowfile/sources/external_sources/custom_external_sources/external_source.py +27 -17
  220. flowfile_core/flowfile/sources/external_sources/custom_external_sources/sample_users.py +9 -9
  221. flowfile_core/flowfile/sources/external_sources/factory.py +0 -1
  222. flowfile_core/flowfile/sources/external_sources/sql_source/models.py +45 -31
  223. flowfile_core/flowfile/sources/external_sources/sql_source/sql_source.py +198 -73
  224. flowfile_core/flowfile/sources/external_sources/sql_source/utils.py +250 -196
  225. flowfile_core/flowfile/util/calculate_layout.py +9 -13
  226. flowfile_core/flowfile/util/execution_orderer.py +25 -17
  227. flowfile_core/flowfile/util/node_skipper.py +4 -4
  228. flowfile_core/flowfile/utils.py +19 -21
  229. flowfile_core/main.py +26 -19
  230. flowfile_core/routes/auth.py +284 -11
  231. flowfile_core/routes/cloud_connections.py +25 -25
  232. flowfile_core/routes/logs.py +21 -29
  233. flowfile_core/routes/public.py +3 -3
  234. flowfile_core/routes/routes.py +70 -34
  235. flowfile_core/routes/secrets.py +25 -27
  236. flowfile_core/routes/user_defined_components.py +483 -4
  237. flowfile_core/run_lock.py +0 -1
  238. flowfile_core/schemas/__init__.py +4 -6
  239. flowfile_core/schemas/analysis_schemas/graphic_walker_schemas.py +55 -55
  240. flowfile_core/schemas/cloud_storage_schemas.py +59 -53
  241. flowfile_core/schemas/input_schema.py +231 -144
  242. flowfile_core/schemas/output_model.py +49 -34
  243. flowfile_core/schemas/schemas.py +116 -89
  244. flowfile_core/schemas/transform_schema.py +518 -263
  245. flowfile_core/schemas/yaml_types.py +21 -7
  246. flowfile_core/secret_manager/secret_manager.py +17 -13
  247. flowfile_core/types.py +29 -9
  248. flowfile_core/utils/arrow_reader.py +7 -6
  249. flowfile_core/utils/excel_file_manager.py +3 -3
  250. flowfile_core/utils/fileManager.py +7 -7
  251. flowfile_core/utils/fl_executor.py +8 -10
  252. flowfile_core/utils/utils.py +4 -4
  253. flowfile_core/utils/validate_setup.py +5 -4
  254. flowfile_frame/__init__.py +106 -51
  255. flowfile_frame/adapters.py +2 -9
  256. flowfile_frame/adding_expr.py +73 -32
  257. flowfile_frame/cloud_storage/frame_helpers.py +27 -23
  258. flowfile_frame/cloud_storage/secret_manager.py +12 -26
  259. flowfile_frame/config.py +2 -5
  260. flowfile_frame/expr.py +311 -218
  261. flowfile_frame/expr.pyi +160 -159
  262. flowfile_frame/expr_name.py +23 -23
  263. flowfile_frame/flow_frame.py +571 -476
  264. flowfile_frame/flow_frame.pyi +123 -104
  265. flowfile_frame/flow_frame_methods.py +227 -246
  266. flowfile_frame/group_frame.py +50 -20
  267. flowfile_frame/join.py +2 -2
  268. flowfile_frame/lazy.py +129 -87
  269. flowfile_frame/lazy_methods.py +83 -30
  270. flowfile_frame/list_name_space.py +55 -50
  271. flowfile_frame/selectors.py +148 -68
  272. flowfile_frame/series.py +9 -7
  273. flowfile_frame/utils.py +19 -21
  274. flowfile_worker/__init__.py +12 -7
  275. flowfile_worker/configs.py +11 -19
  276. flowfile_worker/create/__init__.py +14 -9
  277. flowfile_worker/create/funcs.py +114 -77
  278. flowfile_worker/create/models.py +46 -43
  279. flowfile_worker/create/pl_types.py +14 -15
  280. flowfile_worker/create/read_excel_tables.py +34 -41
  281. flowfile_worker/create/utils.py +22 -19
  282. flowfile_worker/external_sources/s3_source/main.py +18 -51
  283. flowfile_worker/external_sources/s3_source/models.py +34 -27
  284. flowfile_worker/external_sources/sql_source/main.py +8 -5
  285. flowfile_worker/external_sources/sql_source/models.py +13 -9
  286. flowfile_worker/flow_logger.py +10 -8
  287. flowfile_worker/funcs.py +214 -155
  288. flowfile_worker/main.py +11 -17
  289. flowfile_worker/models.py +35 -28
  290. flowfile_worker/process_manager.py +2 -3
  291. flowfile_worker/routes.py +121 -90
  292. flowfile_worker/secrets.py +9 -6
  293. flowfile_worker/spawner.py +80 -49
  294. flowfile_worker/utils.py +3 -2
  295. shared/__init__.py +2 -7
  296. shared/storage_config.py +25 -13
  297. test_utils/postgres/commands.py +3 -2
  298. test_utils/postgres/fixtures.py +9 -9
  299. test_utils/s3/commands.py +1 -1
  300. test_utils/s3/data_generator.py +3 -4
  301. test_utils/s3/demo_data_generator.py +4 -7
  302. test_utils/s3/fixtures.py +7 -5
  303. tools/migrate/__init__.py +1 -1
  304. tools/migrate/__main__.py +16 -29
  305. tools/migrate/legacy_schemas.py +251 -190
  306. tools/migrate/migrate.py +193 -181
  307. tools/migrate/tests/conftest.py +1 -3
  308. tools/migrate/tests/test_migrate.py +36 -41
  309. tools/migrate/tests/test_migration_e2e.py +28 -29
  310. tools/migrate/tests/test_node_migrations.py +50 -20
  311. flowfile/web/static/assets/CloudConnectionManager-2dfdce2f.css +0 -86
  312. flowfile/web/static/assets/CustomNode-74a37f74.css +0 -32
  313. flowfile/web/static/assets/DatabaseManager-30fa27e5.css +0 -64
  314. flowfile/web/static/assets/Filter-9b6d08db.js +0 -164
  315. flowfile/web/static/assets/Filter-f62091b3.css +0 -20
  316. flowfile/web/static/assets/ManualInput-3246a08d.css +0 -96
  317. flowfile/web/static/assets/PivotValidation-891ddfb0.css +0 -13
  318. flowfile/web/static/assets/PivotValidation-c46cd420.css +0 -13
  319. flowfile/web/static/assets/SliderInput-b8fb6a8c.css +0 -4
  320. flowfile/web/static/assets/UnpivotValidation-0d240eeb.css +0 -13
  321. flowfile/web/static/assets/nodeInput-5d0d6b79.js +0 -41
  322. flowfile/web/static/assets/outputCsv-9cc59e0b.css +0 -2499
  323. flowfile/web/static/assets/outputParquet-cf8cf3f2.css +0 -4
  324. flowfile/web/static/assets/secretApi-68435402.js +0 -46
  325. flowfile/web/static/assets/vue-codemirror-bccfde04.css +0 -32
  326. flowfile-0.5.1.dist-info/RECORD +0 -388
  327. {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/WHEEL +0 -0
  328. {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/entry_points.txt +0 -0
  329. {flowfile-0.5.1.dist-info → flowfile-0.5.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,13 +1,17 @@
1
- from pathlib import Path
2
- from typing import List, Optional, Set, Union
1
+ import os
3
2
  from datetime import datetime
3
+ from pathlib import Path
4
+ from typing import Literal, Optional
5
+
6
+ from fastapi import HTTPException
4
7
  from pydantic import BaseModel
5
- from typing_extensions import Literal
6
- import os
8
+
9
+ from shared.storage_config import storage
7
10
 
8
11
 
9
12
  class FileInfo(BaseModel):
10
13
  """Comprehensive information about a file or directory."""
14
+
11
15
  name: str
12
16
  path: str
13
17
  is_directory: bool
@@ -19,8 +23,7 @@ class FileInfo(BaseModel):
19
23
  exists: bool = True
20
24
 
21
25
  @classmethod
22
- def from_path(cls, path: Path, sandbox_root: Optional[Path] = None,
23
- use_relative_paths: bool = False) -> 'FileInfo':
26
+ def from_path(cls, path: Path, sandbox_root: Path | None = None, use_relative_paths: bool = False) -> "FileInfo":
24
27
  """Create FileInfo instance from a path.
25
28
 
26
29
  Args:
@@ -48,10 +51,8 @@ class FileInfo(BaseModel):
48
51
  file_type=path.suffix[1:] if path.suffix else "",
49
52
  last_modified=datetime.fromtimestamp(stats.st_mtime),
50
53
  created_date=datetime.fromtimestamp(stats.st_ctime),
51
- is_hidden=path.name.startswith('.') or (
52
- os.name == 'nt' and bool(stats.st_file_attributes & 0x2)
53
- ),
54
- exists=True
54
+ is_hidden=path.name.startswith(".") or (os.name == "nt" and bool(stats.st_file_attributes & 0x2)),
55
+ exists=True,
55
56
  )
56
57
  except (PermissionError, OSError):
57
58
  # Handle error case
@@ -72,16 +73,16 @@ class FileInfo(BaseModel):
72
73
  last_modified=datetime.fromtimestamp(0),
73
74
  created_date=datetime.fromtimestamp(0),
74
75
  is_hidden=False,
75
- exists=False
76
+ exists=False,
76
77
  )
77
78
 
78
79
 
79
80
  class SecureFileExplorer:
80
81
  """File explorer with sandbox enforcement to prevent directory traversal."""
81
82
 
82
- def __init__(self, start_path: Union[str, Path],
83
- sandbox_root: Optional[Union[str, Path]] = None,
84
- use_relative_paths: bool = False):
83
+ def __init__(
84
+ self, start_path: str | Path, sandbox_root: str | Path | None = None, use_relative_paths: bool = False
85
+ ):
85
86
  """Initialize SecureFileExplorer with sandboxing.
86
87
 
87
88
  Args:
@@ -126,16 +127,17 @@ class SecureFileExplorer:
126
127
  except (ValueError, RuntimeError):
127
128
  return False
128
129
 
129
- def _sanitize_path(self, path: Union[str, Path]) -> Optional[Path]:
130
+ def _sanitize_path(self, path: str | Path) -> Path | None:
130
131
  """Sanitize and validate a path, ensuring it stays within sandbox.
131
132
 
132
133
  Returns None if path would escape sandbox.
133
134
  """
134
135
  try:
135
- # Handle relative paths from current directory
136
+ # Handle relative paths from current directoryb
136
137
  if isinstance(path, str):
138
+
137
139
  # Remove any suspicious patterns
138
- if '..' in Path(path).parts or path.startswith('/'):
140
+ if path.startswith("/"):
139
141
  # For absolute paths or parent references, resolve from sandbox root
140
142
  test_path = Path(path).expanduser()
141
143
  else:
@@ -168,7 +170,7 @@ class SecureFileExplorer:
168
170
  return "/"
169
171
 
170
172
  @property
171
- def parent_directory(self) -> Optional[str]:
173
+ def parent_directory(self) -> str | None:
172
174
  """Get the parent directory path if it exists and is within sandbox."""
173
175
  parent = self.current_path.parent
174
176
  if self._is_path_safe(parent) and parent != self.current_path:
@@ -180,26 +182,26 @@ class SecureFileExplorer:
180
182
  return None
181
183
 
182
184
  def list_contents(
183
- self,
184
- *,
185
- show_hidden: bool = False,
186
- file_types: Optional[List[str]] = None,
187
- recursive: bool = False,
188
- min_size: Optional[int] = None,
189
- max_size: Optional[int] = None,
190
- sort_by: Literal['name', 'date', 'size', 'type'] = 'name',
191
- reverse: bool = False,
192
- exclude_patterns: Optional[List[str]] = None,
193
- max_depth: int = 5 # Add depth limit for recursive operations
194
- ) -> List[FileInfo]:
185
+ self,
186
+ *,
187
+ show_hidden: bool = False,
188
+ file_types: list[str] | None = None,
189
+ recursive: bool = False,
190
+ min_size: int | None = None,
191
+ max_size: int | None = None,
192
+ sort_by: Literal["name", "date", "size", "type"] = "name",
193
+ reverse: bool = False,
194
+ exclude_patterns: list[str] | None = None,
195
+ max_depth: int = 5, # Add depth limit for recursive operations
196
+ ) -> list[FileInfo]:
195
197
  """List contents with security-conscious filtering."""
196
- contents: List[FileInfo] = []
197
- excluded_paths: Set[str] = set()
198
+ contents: list[FileInfo] = []
199
+ excluded_paths: set[str] = set()
198
200
 
199
201
  if exclude_patterns:
200
202
  for pattern in exclude_patterns:
201
203
  # Ensure patterns don't escape sandbox
202
- safe_pattern = pattern.replace('../', '').replace('..\\', '')
204
+ safe_pattern = pattern.replace("../", "").replace("..\\", "")
203
205
  excluded_paths.update(str(p) for p in self.current_path.glob(safe_pattern))
204
206
 
205
207
  def should_include(info: FileInfo) -> bool:
@@ -239,8 +241,7 @@ class SecureFileExplorer:
239
241
  continue
240
242
 
241
243
  try:
242
- file_info = FileInfo.from_path(item, self.sandbox_root,
243
- self.use_relative_paths)
244
+ file_info = FileInfo.from_path(item, self.sandbox_root, self.use_relative_paths)
244
245
  if should_include(file_info):
245
246
  contents.append(file_info)
246
247
 
@@ -258,8 +259,7 @@ class SecureFileExplorer:
258
259
  continue
259
260
 
260
261
  try:
261
- file_info = FileInfo.from_path(item, self.sandbox_root,
262
- self.use_relative_paths)
262
+ file_info = FileInfo.from_path(item, self.sandbox_root, self.use_relative_paths)
263
263
  if should_include(file_info):
264
264
  contents.append(file_info)
265
265
  except (PermissionError, OSError):
@@ -270,10 +270,10 @@ class SecureFileExplorer:
270
270
 
271
271
  # Sort results
272
272
  sort_key = {
273
- 'name': lambda x: (not x.is_directory, x.name.lower()),
274
- 'date': lambda x: (not x.is_directory, x.last_modified),
275
- 'size': lambda x: (not x.is_directory, x.size),
276
- 'type': lambda x: (not x.is_directory, x.file_type.lower(), x.name.lower())
273
+ "name": lambda x: (not x.is_directory, x.name.lower()),
274
+ "date": lambda x: (not x.is_directory, x.last_modified),
275
+ "size": lambda x: (not x.is_directory, x.size),
276
+ "type": lambda x: (not x.is_directory, x.file_type.lower(), x.name.lower()),
277
277
  }[sort_by]
278
278
 
279
279
  return sorted(contents, key=sort_key, reverse=reverse)
@@ -316,13 +316,13 @@ class SecureFileExplorer:
316
316
  def navigate_into(self, directory_name: str) -> bool:
317
317
  """Navigate into a subdirectory, with path sanitization."""
318
318
  # Sanitize directory name
319
- if '/' in directory_name or '\\' in directory_name or '..' in directory_name:
319
+ if "/" in directory_name or "\\" in directory_name or ".." in directory_name:
320
320
  return False
321
321
 
322
322
  new_path = self.current_path / directory_name
323
323
  return self.navigate_to(str(new_path))
324
324
 
325
- def get_absolute_path(self, relative_path: str) -> Optional[Path]:
325
+ def get_absolute_path(self, relative_path: str) -> Path | None:
326
326
  """Get absolute path for a file within sandbox.
327
327
 
328
328
  Returns None if the path would escape sandbox.
@@ -332,13 +332,13 @@ class SecureFileExplorer:
332
332
 
333
333
 
334
334
  def get_files_from_directory(
335
- dir_name: Union[str, Path],
336
- types: Optional[List[str]] = None,
337
- *,
338
- include_hidden: bool = False,
339
- recursive: bool = False,
340
- sandbox_root: Optional[Union[str, Path]] = None
341
- ) -> Optional[List[FileInfo]]:
335
+ dir_name: str | Path,
336
+ types: list[str] | None = None,
337
+ *,
338
+ include_hidden: bool = False,
339
+ recursive: bool = False,
340
+ sandbox_root: str | Path | None = None,
341
+ ) -> list[FileInfo] | None:
342
342
  """
343
343
  Get list of files from a directory with sandbox enforcement.
344
344
 
@@ -360,18 +360,84 @@ def get_files_from_directory(
360
360
  explorer = SecureFileExplorer(start_path=dir_name)
361
361
 
362
362
  # Use the explorer's list_contents method
363
- return explorer.list_contents(
364
- show_hidden=include_hidden,
365
- file_types=types,
366
- recursive=recursive
367
- )
363
+ return explorer.list_contents(show_hidden=include_hidden, file_types=types, recursive=recursive)
368
364
 
369
- except (ValueError, PermissionError) as e:
365
+ except (ValueError, PermissionError):
370
366
  # Return None for invalid/inaccessible directories
371
367
  return None
372
368
  except Exception as e:
373
369
  raise type(e)(f"Error scanning directory {dir_name}: {str(e)}") from e
374
370
 
375
371
 
372
+ def validate_file_path(user_path: str, allowed_base: Path) -> Optional[Path]:
373
+ """Validate a file path is safe and within allowed_base.
374
+
375
+ Uses os.path.realpath + startswith pattern recognized by CodeQL as safe.
376
+
377
+ Args:
378
+ user_path: The user-provided path string
379
+ allowed_base: The base directory that the path must be within
380
+
381
+ Returns:
382
+ The validated Path object, or None if validation fails
383
+ """
384
+ try:
385
+ # Block obvious path traversal patterns early
386
+ if '..' in user_path:
387
+ return None
388
+
389
+ # Get the base path as a normalized, real path string
390
+ base_path = os.path.realpath(str(allowed_base.resolve()))
391
+
392
+ # Always resolve the user path relative to the allowed base directory
393
+ candidate_path = os.path.join(base_path, user_path)
394
+ fullpath = os.path.realpath(candidate_path)
395
+
396
+ # Ensure the resolved path stays within the allowed base directory
397
+ if not fullpath.startswith(base_path + os.sep) and fullpath != base_path:
398
+ return None
399
+
400
+ return Path(fullpath)
401
+
402
+ except (ValueError, RuntimeError, OSError):
403
+ return None
404
+
405
+
406
+ def validate_path_under_cwd(user_path: str) -> str:
407
+ """Validate that a user-provided path resolves to within allowed directories.
408
+
409
+ Uses the exact pattern from CodeQL documentation for py/path-injection:
410
+ - os.path.normpath for path normalization
411
+ - os.path.join to combine base with user input
412
+ - startswith check to ensure path stays within base
413
+
414
+ Allowed directories:
415
+ - Current working directory (for development/testing)
416
+ - Flowfile storage directory (~/.flowfile)
417
+
418
+ Args:
419
+ user_path: The user-provided path string
420
+
421
+ Returns:
422
+ The validated, normalized full path as a string
423
+
424
+ Raises:
425
+ HTTPException: 403 if path escapes the allowed directories
426
+ """
427
+ # Try current working directory first
428
+ base_path = os.path.normpath(os.getcwd())
429
+ fullpath = os.path.normpath(os.path.join(base_path, user_path))
430
+ if fullpath.startswith(base_path):
431
+ return fullpath
432
+
433
+ # Try flowfile storage directory (~/.flowfile)
434
+ base_path = os.path.normpath(str(storage.base_directory))
435
+ fullpath = os.path.normpath(os.path.join(base_path, user_path))
436
+ if fullpath.startswith(base_path):
437
+ return fullpath
438
+
439
+ raise HTTPException(403, 'Access denied')
440
+
441
+
376
442
  # Alias for backward compatibility
377
- FileExplorer = SecureFileExplorer
443
+ FileExplorer = SecureFileExplorer
@@ -1,8 +1,8 @@
1
1
  import os
2
2
  import re
3
3
 
4
- video_types = ['mp4', "webm", "opgg"]
5
- audio_types = ['mp3', "wav", "ogg", "mpeg", "aac", "3gpp", "3gpp2", "aiff", "x-aiff", "amr", "mpga"]
4
+ video_types = ["mp4", "webm", "opgg"]
5
+ audio_types = ["mp3", "wav", "ogg", "mpeg", "aac", "3gpp", "3gpp2", "aiff", "x-aiff", "amr", "mpga"]
6
6
 
7
7
 
8
8
  def get_chunk(start_byte=None, end_byte=None, full_path=None):
@@ -11,7 +11,7 @@ def get_chunk(start_byte=None, end_byte=None, full_path=None):
11
11
  length = end_byte + 1 - start_byte
12
12
  else:
13
13
  length = file_size - start_byte
14
- with open(full_path, 'rb') as f:
14
+ with open(full_path, "rb") as f:
15
15
  f.seek(start_byte)
16
16
  chunk = f.read(length)
17
17
  return chunk, start_byte, length, file_size
@@ -19,10 +19,10 @@ def get_chunk(start_byte=None, end_byte=None, full_path=None):
19
19
 
20
20
  def get_file(file_path, mimetype):
21
21
  f = ""
22
- range_header = f.headers.get('Range', None)
22
+ range_header = f.headers.get("Range", None)
23
23
  start_byte, end_byte = 0, None
24
24
  if range_header:
25
- match = re.search(r'(\d+)-(\d*)', range_header)
25
+ match = re.search(r"(\d+)-(\d*)", range_header)
26
26
  groups = match.groups()
27
27
  if groups[0]:
28
28
  start_byte = int(groups[0])
@@ -30,15 +30,14 @@ def get_file(file_path, mimetype):
30
30
  end_byte = int(groups[1])
31
31
 
32
32
  chunk, start, length, file_size = get_chunk(start_byte, end_byte, file_path)
33
- resp = Response(chunk, 206, mimetype=f'video/{mimetype}',
34
- content_type=mimetype, direct_passthrough=True)
33
+ resp = Response(chunk, 206, mimetype=f"video/{mimetype}", content_type=mimetype, direct_passthrough=True)
35
34
  print(length)
36
- resp.headers.add('Content-Range', 'bytes {0}-{1}/{2}'.format(start, start + length - 1, file_size))
35
+ resp.headers.add("Content-Range", f"bytes {start}-{start + length - 1}/{file_size}")
37
36
  return resp
38
37
 
39
38
 
40
39
  def is_media(filepath):
41
- found_media = re.search("\.mp4$|\.mp3$", filepath, re.IGNORECASE)
40
+ found_media = re.search(r"\.mp4$|\.mp3$", filepath, re.IGNORECASE)
42
41
  if found_media:
43
42
  extension = found_media[0].lower()[1:]
44
43
  if found_media in video_types:
@@ -48,6 +47,6 @@ def is_media(filepath):
48
47
 
49
48
 
50
49
  def get_file_extension(fname):
51
- found_extension = re.search("\.[A-Za-z0-9]*$", fname, re.IGNORECASE)
50
+ found_extension = re.search(r"\.[A-Za-z0-9]*$", fname, re.IGNORECASE)
52
51
  if found_extension:
53
- return found_extension[0][1:].lower()
52
+ return found_extension[0][1:].lower()
@@ -1,8 +1,9 @@
1
+ from dataclasses import dataclass
2
+
1
3
  import polars as pl
2
4
  from polars_expr_transformer import simple_function_to_expr
5
+
3
6
  from flowfile_core.configs import logger
4
- from dataclasses import dataclass
5
- from typing import Optional
6
7
 
7
8
 
8
9
  @dataclass
@@ -10,16 +11,16 @@ class RealTimeResult:
10
11
  result_df: pl.DataFrame
11
12
  data_type: pl.DataType
12
13
  readable_result: str
13
- success: Optional[bool] = None
14
+ success: bool | None = None
14
15
 
15
16
  def __init__(self, result_value: pl.DataFrame, data_type: pl.DataType):
16
17
  self.result_df = result_value
17
18
  self.data_type = data_type
18
- if len(result_value)>0:
19
- self.readable_result = str(result_value.item(0,0))
19
+ if len(result_value) > 0:
20
+ self.readable_result = str(result_value.item(0, 0))
20
21
  self.success = True
21
22
  else:
22
- self.readable_result = ''
23
+ self.readable_result = ""
23
24
  self.success = None
24
25
 
25
26
  def is_filterable_result(self):
@@ -44,8 +45,9 @@ def get_realtime_func_results(df: pl.DataFrame | pl.LazyFrame, func_string: str,
44
45
  print(get_first_result_of_function('year(today())', df))
45
46
  """
46
47
  if isinstance(df, pl.LazyFrame):
47
- logger.warning('Performance in this case can be '
48
- 'improved by using polars.DataFrame to ensure it returns instantly')
48
+ logger.warning(
49
+ "Performance in this case can be " "improved by using polars.DataFrame to ensure it returns instantly"
50
+ )
49
51
  df = df.head(sample).collect()
50
52
  result = df.head(1).select(simple_function_to_expr(func_string))
51
53
  return RealTimeResult(result, result.dtypes[0])
@@ -1,19 +1,20 @@
1
- from typing import Dict, Set, List
2
-
3
- from flowfile_core.flowfile.analytics.graphic_walker import (get_initial_gf_data_from_ff,
4
- convert_ff_columns_to_gw_fields)
1
+ from flowfile_core.configs import logger
2
+ from flowfile_core.flowfile.analytics.graphic_walker import convert_ff_columns_to_gw_fields, get_initial_gf_data_from_ff
5
3
  from flowfile_core.flowfile.flow_node.flow_node import FlowNode
4
+ from flowfile_core.schemas.analysis_schemas.graphic_walker_schemas import (
5
+ DataModel,
6
+ GraphicWalkerInput,
7
+ MutField,
8
+ ViewField,
9
+ )
6
10
  from flowfile_core.schemas.input_schema import NodeExploreData
7
- from flowfile_core.schemas.analysis_schemas.graphic_walker_schemas import GraphicWalkerInput, DataModel, MutField, ViewField
8
- from flowfile_core.configs import logger
9
11
 
10
12
 
11
13
  class AnalyticsProcessor:
12
-
13
14
  @staticmethod
14
15
  def process_graphic_walker_input(node_step: FlowNode) -> NodeExploreData:
15
16
  node_explore_data: NodeExploreData = node_step.setting_input
16
- if hasattr(node_explore_data, 'graphic_walker_input'):
17
+ if hasattr(node_explore_data, "graphic_walker_input"):
17
18
  graphic_walker_input = node_explore_data.graphic_walker_input
18
19
  else:
19
20
  logger.error("NodeExploreData is not an instance of GraphicWalkerInput.")
@@ -24,8 +25,9 @@ class AnalyticsProcessor:
24
25
  return node_explore_data
25
26
 
26
27
  @staticmethod
27
- def create_graphic_walker_input(node_step: FlowNode,
28
- graphic_walker_input: GraphicWalkerInput = None) -> GraphicWalkerInput:
28
+ def create_graphic_walker_input(
29
+ node_step: FlowNode, graphic_walker_input: GraphicWalkerInput = None
30
+ ) -> GraphicWalkerInput:
29
31
  if not node_step.results.analysis_data_generator:
30
32
  node_step.get_predicted_schema()
31
33
  fields = convert_ff_columns_to_gw_fields(node_step.get_predicted_schema())
@@ -42,17 +44,17 @@ class AnalyticsProcessor:
42
44
  return graphic_walker_input
43
45
 
44
46
 
45
- def check_if_field_in_spec_list_encodings(spec_list: Dict, field_name: str) -> bool:
46
- for encoding in spec_list['encodings']['dimensions'].values():
47
- if field_name in encoding['fid']:
47
+ def check_if_field_in_spec_list_encodings(spec_list: dict, field_name: str) -> bool:
48
+ for encoding in spec_list["encodings"]["dimensions"].values():
49
+ if field_name in encoding["fid"]:
48
50
  return True
49
- for encoding in spec_list['encodings']['measures'].values():
50
- if field_name in encoding['fid']:
51
+ for encoding in spec_list["encodings"]["measures"].values():
52
+ if field_name in encoding["fid"]:
51
53
  return True
52
54
  return False
53
55
 
54
56
 
55
- def get_existing_encoding_fields(spec_list: Dict) -> Set[str]:
57
+ def get_existing_encoding_fields(spec_list: dict) -> set[str]:
56
58
  """
57
59
  Get the existing encoding fields from the spec_list.
58
60
 
@@ -62,8 +64,8 @@ def get_existing_encoding_fields(spec_list: Dict) -> Set[str]:
62
64
  Returns:
63
65
  Set[str]: A set of existing encoding fields.
64
66
  """
65
- dimensions = {encoding['fid'] for encoding in spec_list['encodings']['dimensions']}
66
- measures = {encoding['fid'] for encoding in spec_list['encodings']['measures']}
67
+ dimensions = {encoding["fid"] for encoding in spec_list["encodings"]["dimensions"]}
68
+ measures = {encoding["fid"] for encoding in spec_list["encodings"]["measures"]}
67
69
  return dimensions.union(measures)
68
70
 
69
71
 
@@ -72,7 +74,7 @@ def transform_mut_field_to_view_field(mut_field: MutField) -> ViewField:
72
74
  return view_field
73
75
 
74
76
 
75
- def add_field_to_spec_list(spec_list: Dict, mut_field: MutField) -> None:
77
+ def add_field_to_spec_list(spec_list: dict, mut_field: MutField) -> None:
76
78
  """
77
79
  Add a field to the spec_list.
78
80
 
@@ -84,13 +86,13 @@ def add_field_to_spec_list(spec_list: Dict, mut_field: MutField) -> None:
84
86
  None
85
87
  """
86
88
  view_field = transform_mut_field_to_view_field(mut_field)
87
- if mut_field.analyticType == 'measure':
88
- spec_list['encodings']['measures'].append(view_field.model_dump_dict())
89
+ if mut_field.analyticType == "measure":
90
+ spec_list["encodings"]["measures"].append(view_field.model_dump_dict())
89
91
  else:
90
- spec_list['encodings']['dimensions'].append(view_field.model_dump_dict())
92
+ spec_list["encodings"]["dimensions"].append(view_field.model_dump_dict())
91
93
 
92
94
 
93
- def validate_spec_list_with_data_model_types(spec_list: Dict, data_model: DataModel) -> None:
95
+ def validate_spec_list_with_data_model_types(spec_list: dict, data_model: DataModel) -> None:
94
96
  """
95
97
  Validate the spec_list with the data model types.
96
98
 
@@ -109,7 +111,7 @@ def validate_spec_list_with_data_model_types(spec_list: Dict, data_model: DataMo
109
111
  add_field_to_spec_list(spec_list, field)
110
112
 
111
113
 
112
- def validate_spec_lists_with_data_model(spec_lists: List[Dict], data_model: DataModel) -> None:
114
+ def validate_spec_lists_with_data_model(spec_lists: list[dict], data_model: DataModel) -> None:
113
115
  """
114
116
  Validate the spec lists with the data model.
115
117
 
@@ -1,23 +1,22 @@
1
1
  from flowfile_core.flowfile.flow_data_engine.flow_data_engine import FlowDataEngine, FlowfileColumn
2
2
  from flowfile_core.schemas.analysis_schemas import graphic_walker_schemas as gw_schema
3
- from typing import List
4
3
 
5
4
 
6
5
  def get_semantic_type(data_type: str) -> str:
7
6
  """Determine the semanticType based on the data_type."""
8
- if data_type in ['Utf8', 'VARCHAR', 'CHAR', 'NVARCHAR', 'String']:
9
- return 'nominal'
10
- elif data_type in ['Int64', 'Float64', 'Int32', 'Float32', 'Int16', 'Float16', 'Decimal']:
11
- return 'quantitative'
12
- elif data_type in ['Datetime', 'Date']:
13
- return 'temporal'
7
+ if data_type in ["Utf8", "VARCHAR", "CHAR", "NVARCHAR", "String"]:
8
+ return "nominal"
9
+ elif data_type in ["Int64", "Float64", "Int32", "Float32", "Int16", "Float16", "Decimal"]:
10
+ return "quantitative"
11
+ elif data_type in ["Datetime", "Date"]:
12
+ return "temporal"
14
13
  else:
15
- return 'nominal' # Default case; adjust as necessary
14
+ return "nominal" # Default case; adjust as necessary
16
15
 
17
16
 
18
17
  def get_analytic_type(semantic_type: str) -> gw_schema.AnalyticTypeLit:
19
18
  """Determine the analyticType based on the semanticType."""
20
- return 'measure' if semantic_type == 'quantitative' else 'dimension'
19
+ return "measure" if semantic_type == "quantitative" else "dimension"
21
20
 
22
21
 
23
22
  def convert_ff_column_to_gw_field(flow_file_column: FlowfileColumn) -> gw_schema.MutField:
@@ -42,11 +41,11 @@ def convert_ff_column_to_gw_field(flow_file_column: FlowfileColumn) -> gw_schema
42
41
  basename=flow_file_column.name,
43
42
  key=flow_file_column.name,
44
43
  semanticType=semantic_type,
45
- analyticType=analytic_type
44
+ analyticType=analytic_type,
46
45
  )
47
46
 
48
47
 
49
- def convert_ff_columns_to_gw_fields(ff_columns: List[FlowfileColumn]) -> [gw_schema.MutField]:
48
+ def convert_ff_columns_to_gw_fields(ff_columns: list[FlowfileColumn]) -> [gw_schema.MutField]:
50
49
  return [convert_ff_column_to_gw_field(ff_column) for ff_column in ff_columns]
51
50
 
52
51
 
@@ -55,6 +54,6 @@ def get_initial_gf_data_from_ff(flow_file: FlowDataEngine) -> gw_schema.DataMode
55
54
  return gw_schema.DataModel(fields=fields, data=[])
56
55
 
57
56
 
58
- def get_gf_data_from_ff(flow_file: FlowDataEngine, fields: List[gw_schema.MutField]) -> gw_schema.DataModel:
57
+ def get_gf_data_from_ff(flow_file: FlowDataEngine, fields: list[gw_schema.MutField]) -> gw_schema.DataModel:
59
58
  data = flow_file.to_pylist()
60
59
  return gw_schema.DataModel(fields=fields, data=data)
@@ -1,5 +1,5 @@
1
- from flowfile_core.schemas.input_schema import NodeExploreData, NodePromise
2
1
  from flowfile_core.schemas.analysis_schemas.graphic_walker_schemas import GraphicWalkerInput
2
+ from flowfile_core.schemas.input_schema import NodeExploreData, NodePromise
3
3
 
4
4
 
5
5
  def create_graphic_walker_node_from_node_promise(node_promise: NodePromise) -> NodeExploreData: