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,34 +1,42 @@
1
- from typing import Literal, Optional, TYPE_CHECKING
2
- from pydantic import BaseModel, SecretStr
3
- from flowfile_core.schemas.input_schema import (DatabaseConnection,
4
- NodeDatabaseReader,
5
- FullDatabaseConnection,
6
- NodeDatabaseWriter)
7
1
  import base64
2
+ from typing import Literal
3
+
8
4
  import polars as pl
5
+ from pydantic import BaseModel
6
+
7
+ from flowfile_core.schemas.input_schema import (
8
+ DatabaseConnection,
9
+ FullDatabaseConnection,
10
+ NodeDatabaseReader,
11
+ NodeDatabaseWriter,
12
+ )
9
13
 
10
14
 
11
15
  class ExtDatabaseConnection(DatabaseConnection):
12
16
  """Database connection configuration with password handling."""
17
+
13
18
  password: str = None
14
19
 
15
20
 
16
21
  class DatabaseExternalWriteSettings(BaseModel):
17
22
  """Settings for SQL sink."""
23
+
18
24
  connection: ExtDatabaseConnection
19
25
  table_name: str
20
- if_exists: Optional[Literal['append', 'replace', 'fail']] = 'append'
26
+ if_exists: Literal["append", "replace", "fail"] | None = "append"
21
27
  flowfile_flow_id: int = 1
22
28
  flowfile_node_id: int | str = -1
23
29
  operation: str
24
30
 
25
31
  @classmethod
26
- def create_from_from_node_database_writer(cls, node_database_writer: NodeDatabaseWriter,
27
- password: str,
28
- table_name: str,
29
- lf: pl.LazyFrame,
30
- database_reference_settings: FullDatabaseConnection = None,
31
- ) -> 'DatabaseExternalWriteSettings':
32
+ def create_from_from_node_database_writer(
33
+ cls,
34
+ node_database_writer: NodeDatabaseWriter,
35
+ password: str,
36
+ table_name: str,
37
+ lf: pl.LazyFrame,
38
+ database_reference_settings: FullDatabaseConnection = None,
39
+ ) -> "DatabaseExternalWriteSettings":
32
40
  """
33
41
  Create DatabaseExternalWriteSettings from NodeDatabaseWriter.
34
42
  Args:
@@ -45,28 +53,33 @@ class DatabaseExternalWriteSettings(BaseModel):
45
53
  else:
46
54
  database_connection = {k: v for k, v in database_reference_settings.model_dump().items() if k != "password"}
47
55
 
48
- ext_database_connection = ExtDatabaseConnection(**database_connection,
49
- password=password)
50
- return cls(connection=ext_database_connection,
51
- table_name=table_name,
52
- if_exists=node_database_writer.database_write_settings.if_exists,
53
- flowfile_flow_id=node_database_writer.flow_id,
54
- flowfile_node_id=node_database_writer.node_id,
55
- operation=base64.b64encode(lf.serialize()).decode())
56
+ ext_database_connection = ExtDatabaseConnection(**database_connection, password=password)
57
+ return cls(
58
+ connection=ext_database_connection,
59
+ table_name=table_name,
60
+ if_exists=node_database_writer.database_write_settings.if_exists,
61
+ flowfile_flow_id=node_database_writer.flow_id,
62
+ flowfile_node_id=node_database_writer.node_id,
63
+ operation=base64.b64encode(lf.serialize()).decode(),
64
+ )
56
65
 
57
66
 
58
67
  class DatabaseExternalReadSettings(BaseModel):
59
68
  """Settings for SQL source."""
69
+
60
70
  connection: ExtDatabaseConnection
61
71
  query: str
62
72
  flowfile_flow_id: int = 1
63
73
  flowfile_node_id: int | str = -1
64
74
 
65
75
  @classmethod
66
- def create_from_from_node_database_reader(cls, node_database_reader: NodeDatabaseReader,
67
- password: str,
68
- query: str,
69
- database_reference_settings: FullDatabaseConnection = None) -> 'DatabaseExternalReadSettings':
76
+ def create_from_from_node_database_reader(
77
+ cls,
78
+ node_database_reader: NodeDatabaseReader,
79
+ password: str,
80
+ query: str,
81
+ database_reference_settings: FullDatabaseConnection = None,
82
+ ) -> "DatabaseExternalReadSettings":
70
83
  """
71
84
  Create DatabaseExternalReadSettings from NodeDatabaseReader.
72
85
  Args:
@@ -82,9 +95,10 @@ class DatabaseExternalReadSettings(BaseModel):
82
95
  else:
83
96
  database_connection = {k: v for k, v in database_reference_settings.model_dump().items() if k != "password"}
84
97
 
85
- ext_database_connection = ExtDatabaseConnection(**database_connection,
86
- password=password)
87
- return cls(connection=ext_database_connection,
88
- query=query,
89
- flowfile_flow_id=node_database_reader.flow_id,
90
- flowfile_node_id=node_database_reader.node_id)
98
+ ext_database_connection = ExtDatabaseConnection(**database_connection, password=password)
99
+ return cls(
100
+ connection=ext_database_connection,
101
+ query=query,
102
+ flowfile_flow_id=node_database_reader.flow_id,
103
+ flowfile_node_id=node_database_reader.node_id,
104
+ )
@@ -1,16 +1,129 @@
1
- from typing import Any, Dict, Generator, List, Optional, Literal, Tuple
1
+ import re
2
+ from collections.abc import Generator
3
+ from typing import Any, Literal
4
+
2
5
  import polars as pl
6
+ from sqlalchemy import Engine, create_engine, inspect, text
7
+
3
8
  from flowfile_core.configs import logger
9
+ from flowfile_core.flowfile.database_connection_manager.db_connections import get_local_database_connection
4
10
  from flowfile_core.flowfile.flow_data_engine.flow_file_column.main import FlowfileColumn
5
- from flowfile_core.schemas.input_schema import MinimalFieldInfo, DatabaseSettings
6
- from sqlalchemy import Engine, inspect, create_engine, text
7
- from flowfile_core.secret_manager.secret_manager import get_encrypted_secret, decrypt_secret
8
-
9
11
  from flowfile_core.flowfile.sources.external_sources.base_class import ExternalDataSource
10
- from flowfile_core.flowfile.sources.external_sources.sql_source.utils import get_polars_type, construct_sql_uri
11
- from flowfile_core.flowfile.database_connection_manager.db_connections import get_local_database_connection
12
+ from flowfile_core.flowfile.sources.external_sources.sql_source.utils import construct_sql_uri, get_polars_type
13
+ from flowfile_core.schemas.input_schema import DatabaseSettings, MinimalFieldInfo
14
+ from flowfile_core.secret_manager.secret_manager import decrypt_secret, get_encrypted_secret
15
+
16
+ QueryMode = Literal["table", "query"]
17
+
18
+
19
+ class UnsafeSQLError(ValueError):
20
+ """Raised when a SQL query contains unsafe operations."""
12
21
 
13
- QueryMode = Literal['table', 'query']
22
+ pass
23
+
24
+
25
+ def validate_sql_query(query: str) -> None:
26
+ """
27
+ Validate that a SQL query is safe for execution (read-only SELECT statements only).
28
+
29
+ This function checks that the query:
30
+ 1. Is a SELECT statement (not INSERT, UPDATE, DELETE, etc.)
31
+ 2. Does not contain DDL statements (DROP, CREATE, ALTER, TRUNCATE)
32
+ 3. Does not contain other dangerous operations
33
+
34
+ Args:
35
+ query: The SQL query string to validate
36
+
37
+ Raises:
38
+ UnsafeSQLError: If the query contains unsafe operations
39
+ """
40
+ if not query or not query.strip():
41
+ raise UnsafeSQLError("SQL query cannot be empty")
42
+
43
+ # Normalize the query: remove comments and extra whitespace
44
+ normalized = _remove_sql_comments(query)
45
+ normalized = " ".join(normalized.split()).upper()
46
+
47
+ # Check if query starts with SELECT (allowing for WITH clauses / CTEs)
48
+ if not _is_select_query(normalized):
49
+ raise UnsafeSQLError(
50
+ "Only SELECT queries are allowed. "
51
+ "The query must start with SELECT or WITH (for common table expressions)."
52
+ )
53
+
54
+ # Check for dangerous DDL statements
55
+ ddl_patterns = [
56
+ (r"\bDROP\s+", "DROP statements are not allowed"),
57
+ (r"\bCREATE\s+", "CREATE statements are not allowed"),
58
+ (r"\bALTER\s+", "ALTER statements are not allowed"),
59
+ (r"\bTRUNCATE\s+", "TRUNCATE statements are not allowed"),
60
+ (r"\bRENAME\s+", "RENAME statements are not allowed"),
61
+ ]
62
+
63
+ for pattern, error_msg in ddl_patterns:
64
+ if re.search(pattern, normalized):
65
+ raise UnsafeSQLError(error_msg)
66
+
67
+ # Check for dangerous DML statements (these shouldn't appear in a SELECT)
68
+ dml_patterns = [
69
+ (r"\bINSERT\s+INTO\b", "INSERT statements are not allowed"),
70
+ (r"\bUPDATE\s+\w+\s+SET\b", "UPDATE statements are not allowed"),
71
+ (r"\bDELETE\s+FROM\b", "DELETE statements are not allowed"),
72
+ ]
73
+
74
+ for pattern, error_msg in dml_patterns:
75
+ if re.search(pattern, normalized):
76
+ raise UnsafeSQLError(error_msg)
77
+
78
+ # Check for dangerous operations that could be used maliciously
79
+ dangerous_patterns = [
80
+ (r"\bEXEC(UTE)?\s*\(", "EXECUTE statements are not allowed"),
81
+ (r"\bCALL\s+", "CALL statements (stored procedures) are not allowed"),
82
+ (r"\bGRANT\s+", "GRANT statements are not allowed"),
83
+ (r"\bREVOKE\s+", "REVOKE statements are not allowed"),
84
+ ]
85
+
86
+ for pattern, error_msg in dangerous_patterns:
87
+ if re.search(pattern, normalized):
88
+ raise UnsafeSQLError(error_msg)
89
+
90
+
91
+ def _remove_sql_comments(query: str) -> str:
92
+ """
93
+ Remove SQL comments from a query string.
94
+
95
+ Handles:
96
+ - Single line comments (-- comment)
97
+ - Multi-line comments (/* comment */)
98
+ """
99
+ # Remove multi-line comments using a non-backtracking pattern
100
+ # Matches /* followed by (non-* chars OR * not followed by /) then */
101
+ result = re.sub(r"/\*(?:[^*]|\*(?!/))*\*/", " ", query)
102
+ # Remove single-line comments - explicitly match non-newline chars to avoid backtracking
103
+ result = re.sub(r"--[^\r\n]*", " ", result)
104
+ return result
105
+
106
+
107
+ def _is_select_query(normalized_query: str) -> bool:
108
+ """
109
+ Check if a normalized (uppercase, whitespace-cleaned) query is a SELECT statement.
110
+
111
+ Allows:
112
+ - SELECT ...
113
+ - WITH ... SELECT ... (CTEs)
114
+ """
115
+ # Check for direct SELECT
116
+ if normalized_query.startswith("SELECT ") or normalized_query.startswith("SELECT\t"):
117
+ return True
118
+
119
+ # Check for WITH clause (CTE) that leads to SELECT
120
+ if normalized_query.startswith("WITH ") or normalized_query.startswith("WITH\t"):
121
+ # CTEs should eventually have a SELECT
122
+ # Make sure there's a SELECT after the WITH clause and no dangerous statements
123
+ if " SELECT " in normalized_query or "\tSELECT " in normalized_query:
124
+ return True
125
+
126
+ return False
14
127
 
15
128
 
16
129
  def get_query_columns(engine: Engine, query_text: str):
@@ -36,7 +149,7 @@ def get_query_columns(engine: Engine, query_text: str):
36
149
  return list(column_names)
37
150
 
38
151
 
39
- def get_table_column_types(engine: Engine, table_name: str, schema: str = None) -> List[Tuple[str, Any]]:
152
+ def get_table_column_types(engine: Engine, table_name: str, schema: str = None) -> list[tuple[str, Any]]:
40
153
  """
41
154
  Get column types from a database table using a SQLAlchemy engine
42
155
 
@@ -51,7 +164,7 @@ def get_table_column_types(engine: Engine, table_name: str, schema: str = None)
51
164
  inspector = inspect(engine)
52
165
  columns = inspector.get_columns(table_name, schema=schema)
53
166
 
54
- return [(column['name'], column['type']) for column in columns]
167
+ return [(column["name"], column["type"]) for column in columns]
55
168
 
56
169
 
57
170
  class BaseSqlSource:
@@ -59,17 +172,20 @@ class BaseSqlSource:
59
172
  A simplified base class for SQL sources that handles query generation
60
173
  without requiring database connection details.
61
174
  """
62
- table_name: Optional[str] = None
63
- query: Optional[str] = None
64
- schema_name: Optional[str] = None
65
- query_mode: QueryMode = 'table'
66
- schema: Optional[List[FlowfileColumn]] = None
67
-
68
- def __init__(self,
69
- query: str = None,
70
- table_name: str = None,
71
- schema_name: str = None,
72
- fields: Optional[List[MinimalFieldInfo]] = None):
175
+
176
+ table_name: str | None = None
177
+ query: str | None = None
178
+ schema_name: str | None = None
179
+ query_mode: QueryMode = "table"
180
+ schema: list[FlowfileColumn] | None = None
181
+
182
+ def __init__(
183
+ self,
184
+ query: str = None,
185
+ table_name: str = None,
186
+ schema_name: str = None,
187
+ fields: list[MinimalFieldInfo] | None = None,
188
+ ):
73
189
  """
74
190
  Initialize a BaseSqlSource object.
75
191
 
@@ -79,7 +195,7 @@ class BaseSqlSource:
79
195
  schema_name: Optional database schema name
80
196
  fields: Optional list of field information
81
197
  """
82
- if schema_name == '':
198
+ if schema_name == "":
83
199
  schema_name = None
84
200
 
85
201
  # Validate inputs
@@ -90,15 +206,17 @@ class BaseSqlSource:
90
206
 
91
207
  # Set query mode and build query if needed
92
208
  if query is not None:
93
- self.query_mode = 'query'
209
+ # Validate user-provided queries for safety (read-only SELECT only)
210
+ validate_sql_query(query)
211
+ self.query_mode = "query"
94
212
  self.query = query
95
213
  elif table_name is not None:
96
- self.query_mode = 'table'
214
+ self.query_mode = "table"
97
215
  self.table_name = table_name
98
216
  self.schema_name = schema_name
99
217
 
100
218
  # Generate the basic query
101
- if schema_name is not None and schema_name != '':
219
+ if schema_name is not None and schema_name != "":
102
220
  self.query = f"SELECT * FROM {schema_name}.{table_name}"
103
221
  else:
104
222
  self.query = f"SELECT * FROM {table_name}"
@@ -111,13 +229,13 @@ class BaseSqlSource:
111
229
  """
112
230
  Get a sample query that returns a limited number of rows.
113
231
  """
114
- if self.query_mode == 'query':
232
+ if self.query_mode == "query":
115
233
  return f"select * from ({self.query}) as main_query LIMIT 1"
116
234
  else:
117
235
  return f"{self.query} LIMIT 1"
118
236
 
119
237
  @staticmethod
120
- def _parse_table_name(table_name: str) -> tuple[Optional[str], str]:
238
+ def _parse_table_name(table_name: str) -> tuple[str | None, str]:
121
239
  """
122
240
  Parse a table name that may include a schema.
123
241
 
@@ -127,10 +245,10 @@ class BaseSqlSource:
127
245
  Returns:
128
246
  Tuple of (schema, table_name)
129
247
  """
130
- table_parts = table_name.split('.')
248
+ table_parts = table_name.split(".")
131
249
  if len(table_parts) > 1:
132
250
  # Handle schema.table_name format
133
- schema = '.'.join(table_parts[:-1])
251
+ schema = ".".join(table_parts[:-1])
134
252
  table = table_parts[-1]
135
253
  return schema, table
136
254
  else:
@@ -138,16 +256,17 @@ class BaseSqlSource:
138
256
 
139
257
 
140
258
  class SqlSource(BaseSqlSource, ExternalDataSource):
141
- connection_string: Optional[str]
142
- read_result: Optional[pl.DataFrame] = None
143
-
144
- def __init__(self,
145
- connection_string: str,
146
- query: str = None,
147
- table_name: str = None,
148
- schema_name: str = None,
149
- fields: Optional[List[MinimalFieldInfo]] = None):
150
-
259
+ connection_string: str | None
260
+ read_result: pl.DataFrame | None = None
261
+
262
+ def __init__(
263
+ self,
264
+ connection_string: str,
265
+ query: str = None,
266
+ table_name: str = None,
267
+ schema_name: str = None,
268
+ fields: list[MinimalFieldInfo] | None = None,
269
+ ):
151
270
  # Initialize the base class first
152
271
  BaseSqlSource.__init__(self, query=query, table_name=table_name, schema_name=schema_name, fields=fields)
153
272
 
@@ -155,13 +274,13 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
155
274
  self.connection_string = connection_string
156
275
  self.read_result = None
157
276
 
158
- def get_initial_data(self) -> List[Dict[str, Any]]:
277
+ def get_initial_data(self) -> list[dict[str, Any]]:
159
278
  return []
160
279
 
161
280
  def validate(self) -> None:
162
281
  try:
163
282
  engine = create_engine(self.connection_string)
164
- if self.query_mode == 'table':
283
+ if self.query_mode == "table":
165
284
  try:
166
285
  if self.schema_name is not None:
167
286
  self._get_columns_from_table_and_schema(engine, self.table_name, self.schema_name)
@@ -180,8 +299,8 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
180
299
  logger.error(f"Error validating SQL source: {e}")
181
300
  raise e
182
301
 
183
- def get_iter(self) -> Generator[Dict[str, Any], None, None]:
184
- logger.warning('Getting data in iteration, this is suboptimal')
302
+ def get_iter(self) -> Generator[dict[str, Any], None, None]:
303
+ logger.warning("Getting data in iteration, this is suboptimal")
185
304
  data = self.data_getter()
186
305
  for row in data:
187
306
  yield row
@@ -190,8 +309,8 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
190
309
  df = self.get_pl_df()
191
310
  return df.to_pandas()
192
311
 
193
- def get_sample(self, n: int = 10000) -> Generator[Dict[str, Any], None, None]:
194
- if self.query_mode == 'table':
312
+ def get_sample(self, n: int = 10000) -> Generator[dict[str, Any], None, None]:
313
+ if self.query_mode == "table":
195
314
  query = f"{self.query} LIMIT {n}"
196
315
  try:
197
316
  df = pl.read_database_uri(query, self.connection_string)
@@ -204,7 +323,7 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
204
323
  rows = df.head(n).to_dicts()
205
324
  return (r for r in rows)
206
325
 
207
- def data_getter(self) -> Generator[Dict[str, Any], None, None]:
326
+ def data_getter(self) -> Generator[dict[str, Any], None, None]:
208
327
  df = self.get_pl_df()
209
328
  rows = df.to_dicts()
210
329
  return (r for r in rows)
@@ -214,7 +333,7 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
214
333
  self.read_result = pl.read_database_uri(self.query, self.connection_string)
215
334
  return self.read_result
216
335
 
217
- def get_flow_file_columns(self) -> List[FlowfileColumn]:
336
+ def get_flow_file_columns(self) -> list[FlowfileColumn]:
218
337
  """
219
338
  Get column information from the SQL source and convert to FlowfileColumn objects
220
339
 
@@ -223,7 +342,7 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
223
342
  """
224
343
  engine = create_engine(self.connection_string)
225
344
 
226
- if self.query_mode == 'table':
345
+ if self.query_mode == "table":
227
346
  try:
228
347
  if self.schema_name is not None:
229
348
  return self._get_columns_from_table_and_schema(engine, self.table_name, self.schema_name)
@@ -235,7 +354,7 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
235
354
  return self._get_columns_from_query(engine, self.get_sample_query())
236
355
 
237
356
  @staticmethod
238
- def _get_columns_from_table(engine: Engine, table_name: str) -> List[FlowfileColumn]:
357
+ def _get_columns_from_table(engine: Engine, table_name: str) -> list[FlowfileColumn]:
239
358
  """
240
359
  Get FlowfileColumn objects from a database table
241
360
 
@@ -248,8 +367,10 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
248
367
  """
249
368
  schema_name, table = BaseSqlSource._parse_table_name(table_name)
250
369
  column_types = get_table_column_types(engine, table, schema=schema_name)
251
- columns = [FlowfileColumn.create_from_polars_dtype(column_name, get_polars_type(column_type))
252
- for column_name, column_type in column_types]
370
+ columns = [
371
+ FlowfileColumn.create_from_polars_dtype(column_name, get_polars_type(column_type))
372
+ for column_name, column_type in column_types
373
+ ]
253
374
 
254
375
  return columns
255
376
 
@@ -266,12 +387,14 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
266
387
  List of FlowfileColumn objects
267
388
  """
268
389
  column_types = get_table_column_types(engine, table_name, schema=schema_name)
269
- columns = [FlowfileColumn.create_from_polars_dtype(column_name, get_polars_type(column_type))
270
- for column_name, column_type in column_types]
390
+ columns = [
391
+ FlowfileColumn.create_from_polars_dtype(column_name, get_polars_type(column_type))
392
+ for column_name, column_type in column_types
393
+ ]
271
394
  return columns
272
395
 
273
396
  @staticmethod
274
- def _get_columns_from_query(engine: Engine, query: str) -> List[FlowfileColumn]:
397
+ def _get_columns_from_query(engine: Engine, query: str) -> list[FlowfileColumn]:
275
398
  """
276
399
  Get FlowfileColumn objects from a SQL query
277
400
 
@@ -285,17 +408,18 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
285
408
  try:
286
409
  column_names = get_query_columns(engine, query)
287
410
 
288
- columns = [FlowfileColumn.create_from_polars_dtype(column_name, pl.String()) for column_name in
289
- column_names]
411
+ columns = [
412
+ FlowfileColumn.create_from_polars_dtype(column_name, pl.String()) for column_name in column_names
413
+ ]
290
414
  return columns
291
415
  except Exception as e:
292
416
  logger.error(f"Error getting column info for query: {e}")
293
417
  raise e
294
418
 
295
- def parse_schema(self) -> List[FlowfileColumn]:
419
+ def parse_schema(self) -> list[FlowfileColumn]:
296
420
  return self.get_schema()
297
421
 
298
- def get_schema(self) -> List[FlowfileColumn]:
422
+ def get_schema(self) -> list[FlowfileColumn]:
299
423
  if self.schema is None:
300
424
  self.schema = self.get_flow_file_columns()
301
425
  return self.schema
@@ -303,26 +427,27 @@ class SqlSource(BaseSqlSource, ExternalDataSource):
303
427
 
304
428
  def create_sql_source_from_db_settings(database_settings: DatabaseSettings, user_id: int) -> SqlSource:
305
429
  database_connection = database_settings.database_connection
306
- if database_settings.connection_mode == 'inline':
430
+ if database_settings.connection_mode == "inline":
307
431
  if database_connection is None:
308
432
  raise ValueError("Database connection is required in inline mode")
309
- encrypted_secret = get_encrypted_secret(current_user_id=user_id,
310
- secret_name=database_connection.password_ref)
433
+ encrypted_secret = get_encrypted_secret(current_user_id=user_id, secret_name=database_connection.password_ref)
311
434
  else:
312
435
  database_connection = get_local_database_connection(database_settings.database_connection_name, user_id)
313
436
  encrypted_secret = database_connection.password.get_secret_value()
314
437
  if encrypted_secret is None:
315
438
  raise ValueError(f"Secret with name {database_connection.password_ref} not found for user {user_id}")
316
439
 
317
- sql_source = SqlSource(connection_string=
318
- construct_sql_uri(database_type=database_connection.database_type,
319
- host=database_connection.host,
320
- port=database_connection.port,
321
- database=database_connection.database,
322
- username=database_connection.username,
323
- password=decrypt_secret(encrypted_secret)),
324
- query=None if database_settings.query_mode == 'table' else database_settings.query,
325
- table_name=database_settings.table_name,
326
- schema_name=database_settings.schema_name,
327
- )
328
- return sql_source
440
+ sql_source = SqlSource(
441
+ connection_string=construct_sql_uri(
442
+ database_type=database_connection.database_type,
443
+ host=database_connection.host,
444
+ port=database_connection.port,
445
+ database=database_connection.database,
446
+ username=database_connection.username,
447
+ password=decrypt_secret(encrypted_secret),
448
+ ),
449
+ query=None if database_settings.query_mode == "table" else database_settings.query,
450
+ table_name=database_settings.table_name,
451
+ schema_name=database_settings.schema_name,
452
+ )
453
+ return sql_source