Flowfile 0.4.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 (332) hide show
  1. build_backends/main.py +25 -22
  2. build_backends/main_prd.py +10 -19
  3. flowfile/__init__.py +179 -73
  4. flowfile/__main__.py +10 -7
  5. flowfile/api.py +52 -59
  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-d3248f8d.js → CloudConnectionView-f13f202b.js} +11 -11
  11. flowfile/web/static/assets/{CloudStorageReader-d65bf041.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-e83be3ed.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-cce661cf.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-11a4652a.js → ContextMenu-31ee57f0.js} +3 -3
  19. flowfile/web/static/assets/{ContextMenu-160afb08.js → ContextMenu-69a74055.js} +3 -3
  20. flowfile/web/static/assets/{ContextMenu-cf18d2cc.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-d395d38c.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-b812dc0b.js → CustomNode-8479239b.js} +36 -24
  27. flowfile/web/static/assets/{DatabaseConnectionSettings-7000bf2c.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-4f035d0c.js → DatabaseReader-c58b9552.js} +25 -15
  31. flowfile/web/static/assets/DatabaseView-6655afd6.css +57 -0
  32. flowfile/web/static/assets/{DatabaseManager-9662ec5b.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-f65dcd54.js → DatabaseWriter-4d05ddc7.js} +17 -10
  35. flowfile/web/static/assets/{designer-e3c150ec.css → DesignerView-a6d0ee84.css} +629 -538
  36. flowfile/web/static/assets/{designer-f3656d8c.js → DesignerView-e6f5c0e8.js} +1214 -3209
  37. flowfile/web/static/assets/{documentation-52b241e7.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-94c43dfc.js → ExploreData-7b54caca.js} +18 -9
  41. flowfile/web/static/assets/{ExternalSource-ac04b3cc.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-71472193.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-b317f631.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-754a234f.js → GraphSolver-c7e6780e.js} +13 -11
  51. flowfile/web/static/assets/{GroupBy-6c6f9802.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-a1b800be.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-a9640276.js → ManualInput-8d3374b2.js} +170 -116
  59. flowfile/web/static/assets/{MultiSelect-97213888.js → MultiSelect-ad1b6243.js} +2 -2
  60. flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-6ffe088a.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-e638088a.js → NumericInput-7100234c.js} +2 -2
  64. flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-90eb2cba.js → NumericInput.vue_vue_type_script_setup_true_lang-5130219f.js} +5 -2
  65. flowfile/web/static/assets/{Output-ddc9079f.css → Output-35e97000.css} +6 -6
  66. flowfile/web/static/assets/{Output-76750610.js → Output-f5efd2aa.js} +60 -38
  67. flowfile/web/static/assets/{GraphSolver-f0cb7bfb.css → Pivot-0eda81b4.css} +5 -5
  68. flowfile/web/static/assets/{Pivot-7814803f.js → Pivot-d981d23c.js} +11 -9
  69. flowfile/web/static/assets/PivotValidation-0e905b1a.css +13 -0
  70. flowfile/web/static/assets/{PivotValidation-f92137d2.js → PivotValidation-39386e95.js} +3 -3
  71. flowfile/web/static/assets/PivotValidation-41b57ad6.css +13 -0
  72. flowfile/web/static/assets/{PivotValidation-76dd431a.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-889c3008.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-6b17491f.css → Read-36e7bd51.css} +12 -12
  78. flowfile/web/static/assets/{Read-637b72a7.js → Read-aec2e377.js} +83 -105
  79. flowfile/web/static/assets/{RecordCount-2b050c41.js → RecordCount-78ed6845.js} +6 -4
  80. flowfile/web/static/assets/{RecordId-81df7784.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-88dcfe53.js → SQLQueryComponent-48c72f5b.js} +3 -3
  83. flowfile/web/static/assets/{Sample-258ad2a9.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-2a2cb7e2.js → SecretsView-17df66ee.js} +35 -36
  87. flowfile/web/static/assets/SecretsView-aa291340.css +38 -0
  88. flowfile/web/static/assets/{Select-850215fd.js → Select-0aee4c54.js} +9 -7
  89. flowfile/web/static/assets/{SettingsSection-55bae608.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-0e8d9123.js → SettingsSection-cd341bb6.js} +3 -3
  94. flowfile/web/static/assets/{SettingsSection-29b4fa6b.js → SettingsSection-f2002a6d.js} +3 -3
  95. flowfile/web/static/assets/{SingleSelect-bebd408b.js → SingleSelect-460cc0ea.js} +2 -2
  96. flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-6093741c.js → SingleSelect.vue_vue_type_script_setup_true_lang-30741bb2.js} +1 -1
  97. flowfile/web/static/assets/{SliderInput-6a05ab61.js → SliderInput-5d926864.js} +7 -4
  98. flowfile/web/static/assets/SliderInput-f2e4f23c.css +4 -0
  99. flowfile/web/static/assets/{Sort-10ab48ed.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-df9d6259.js → TextInput-a2d0bfbd.js} +2 -2
  102. flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-000e1178.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-6c2d93d8.js → TextToRows-918945f7.js} +11 -10
  105. flowfile/web/static/assets/{ToggleSwitch-0ff7ac52.js → ToggleSwitch-f0ef5196.js} +2 -2
  106. flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-c6dc3029.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-1bab97cb.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-b563478a.js → Union-e8ab8c86.js} +8 -6
  111. flowfile/web/static/assets/{Unique-f90db5db.js → Unique-8cd4f976.js} +13 -22
  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-bcb0025f.js → Unpivot-8da14095.js} +10 -8
  115. flowfile/web/static/assets/{UnpivotValidation-c4e73b04.js → UnpivotValidation-6f7d89ff.js} +3 -3
  116. flowfile/web/static/assets/UnpivotValidation-d5ca3b7b.css +13 -0
  117. flowfile/web/static/assets/{VueGraphicWalker-bb8535e2.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-4c8e3822.js → api-24483f0d.js} +1 -1
  120. flowfile/web/static/assets/{api-2d6adc4f.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-1bca8a74.js → dropDown-ac0fda9d.js} +3 -3
  123. flowfile/web/static/assets/{fullEditor-2985687e.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-0476ba4e.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-246f201c.js → index-fb6493ae.js} +41626 -40869
  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-d686eeaf.js → outputCsv-8f8ba42d.js} +3 -3
  134. flowfile/web/static/assets/outputCsv-b9a072af.css +2499 -0
  135. flowfile/web/static/assets/{outputExcel-8809ea2f.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-53ba645a.js → outputParquet-07c81f65.js} +4 -4
  138. flowfile/web/static/assets/outputParquet-54597c3c.css +4 -0
  139. flowfile/web/static/assets/{readCsv-053bf97b.js → readCsv-07f6d9ad.js} +21 -20
  140. flowfile/web/static/assets/{readCsv-bca3ed53.css → readCsv-3bfac4c3.css} +15 -15
  141. flowfile/web/static/assets/{readExcel-e1b381ea.css → readExcel-3db6b763.css} +13 -13
  142. flowfile/web/static/assets/{readExcel-ad531eab.js → readExcel-ed69bc8f.js} +10 -12
  143. flowfile/web/static/assets/{readParquet-cee068e2.css → readParquet-c5244ad5.css} +4 -4
  144. flowfile/web/static/assets/{readParquet-58e899a1.js → readParquet-e3ed4528.js} +4 -7
  145. flowfile/web/static/assets/secrets.api-002e7d7e.js +65 -0
  146. flowfile/web/static/assets/{selectDynamic-b38de2ba.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-db9b8936.js → vue-codemirror.esm-0965f39f.js} +31 -637
  149. flowfile/web/static/assets/{vue-content-loader.es-b5f3ac30.js → vue-content-loader.es-c506ad97.js} +1 -1
  150. flowfile/web/static/index.html +2 -2
  151. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/METADATA +4 -4
  152. flowfile-0.5.3.dist-info/RECORD +402 -0
  153. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/WHEEL +1 -1
  154. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/entry_points.txt +1 -0
  155. flowfile_core/__init__.py +13 -3
  156. flowfile_core/auth/jwt.py +51 -16
  157. flowfile_core/auth/models.py +32 -7
  158. flowfile_core/auth/password.py +89 -0
  159. flowfile_core/auth/secrets.py +8 -6
  160. flowfile_core/configs/__init__.py +9 -7
  161. flowfile_core/configs/flow_logger.py +15 -14
  162. flowfile_core/configs/node_store/__init__.py +72 -4
  163. flowfile_core/configs/node_store/nodes.py +155 -172
  164. flowfile_core/configs/node_store/user_defined_node_registry.py +108 -27
  165. flowfile_core/configs/settings.py +28 -15
  166. flowfile_core/database/connection.py +7 -6
  167. flowfile_core/database/init_db.py +96 -2
  168. flowfile_core/database/models.py +3 -1
  169. flowfile_core/fileExplorer/__init__.py +17 -0
  170. flowfile_core/fileExplorer/funcs.py +123 -57
  171. flowfile_core/fileExplorer/utils.py +10 -11
  172. flowfile_core/flowfile/_extensions/real_time_interface.py +10 -8
  173. flowfile_core/flowfile/analytics/analytics_processor.py +27 -24
  174. flowfile_core/flowfile/analytics/graphic_walker.py +11 -12
  175. flowfile_core/flowfile/analytics/utils.py +1 -1
  176. flowfile_core/flowfile/code_generator/code_generator.py +391 -279
  177. flowfile_core/flowfile/connection_manager/_connection_manager.py +6 -5
  178. flowfile_core/flowfile/connection_manager/models.py +1 -1
  179. flowfile_core/flowfile/database_connection_manager/db_connections.py +60 -44
  180. flowfile_core/flowfile/database_connection_manager/models.py +1 -1
  181. flowfile_core/flowfile/extensions.py +17 -12
  182. flowfile_core/flowfile/flow_data_engine/cloud_storage_reader.py +34 -32
  183. flowfile_core/flowfile/flow_data_engine/create/funcs.py +152 -103
  184. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +526 -477
  185. flowfile_core/flowfile/flow_data_engine/flow_file_column/interface.py +2 -2
  186. flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +92 -52
  187. flowfile_core/flowfile/flow_data_engine/flow_file_column/polars_type.py +12 -11
  188. flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +6 -6
  189. flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +26 -30
  190. flowfile_core/flowfile/flow_data_engine/fuzzy_matching/prepare_for_fuzzy_match.py +43 -32
  191. flowfile_core/flowfile/flow_data_engine/join/__init__.py +1 -1
  192. flowfile_core/flowfile/flow_data_engine/join/utils.py +11 -9
  193. flowfile_core/flowfile/flow_data_engine/join/verify_integrity.py +15 -11
  194. flowfile_core/flowfile/flow_data_engine/pivot_table.py +5 -7
  195. flowfile_core/flowfile/flow_data_engine/polars_code_parser.py +95 -82
  196. flowfile_core/flowfile/flow_data_engine/read_excel_tables.py +66 -65
  197. flowfile_core/flowfile/flow_data_engine/sample_data.py +27 -21
  198. flowfile_core/flowfile/flow_data_engine/subprocess_operations/__init__.py +1 -1
  199. flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +13 -11
  200. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +360 -191
  201. flowfile_core/flowfile/flow_data_engine/threaded_processes.py +8 -8
  202. flowfile_core/flowfile/flow_data_engine/utils.py +101 -67
  203. flowfile_core/flowfile/flow_graph.py +1011 -561
  204. flowfile_core/flowfile/flow_graph_utils.py +31 -49
  205. flowfile_core/flowfile/flow_node/flow_node.py +332 -232
  206. flowfile_core/flowfile/flow_node/models.py +54 -41
  207. flowfile_core/flowfile/flow_node/schema_callback.py +14 -19
  208. flowfile_core/flowfile/graph_tree/graph_tree.py +41 -41
  209. flowfile_core/flowfile/handler.py +82 -32
  210. flowfile_core/flowfile/manage/compatibility_enhancements.py +493 -47
  211. flowfile_core/flowfile/manage/io_flowfile.py +391 -0
  212. flowfile_core/flowfile/node_designer/__init__.py +15 -13
  213. flowfile_core/flowfile/node_designer/_type_registry.py +34 -37
  214. flowfile_core/flowfile/node_designer/custom_node.py +162 -36
  215. flowfile_core/flowfile/node_designer/ui_components.py +136 -35
  216. flowfile_core/flowfile/schema_callbacks.py +77 -54
  217. flowfile_core/flowfile/setting_generator/__init__.py +0 -1
  218. flowfile_core/flowfile/setting_generator/setting_generator.py +6 -5
  219. flowfile_core/flowfile/setting_generator/settings.py +72 -55
  220. flowfile_core/flowfile/sources/external_sources/base_class.py +12 -10
  221. flowfile_core/flowfile/sources/external_sources/custom_external_sources/external_source.py +27 -17
  222. flowfile_core/flowfile/sources/external_sources/custom_external_sources/sample_users.py +9 -9
  223. flowfile_core/flowfile/sources/external_sources/factory.py +0 -1
  224. flowfile_core/flowfile/sources/external_sources/sql_source/models.py +45 -31
  225. flowfile_core/flowfile/sources/external_sources/sql_source/sql_source.py +198 -73
  226. flowfile_core/flowfile/sources/external_sources/sql_source/utils.py +250 -196
  227. flowfile_core/flowfile/util/calculate_layout.py +9 -13
  228. flowfile_core/flowfile/util/execution_orderer.py +25 -17
  229. flowfile_core/flowfile/util/node_skipper.py +4 -4
  230. flowfile_core/flowfile/utils.py +19 -21
  231. flowfile_core/main.py +26 -19
  232. flowfile_core/routes/auth.py +284 -11
  233. flowfile_core/routes/cloud_connections.py +25 -25
  234. flowfile_core/routes/logs.py +21 -29
  235. flowfile_core/routes/public.py +3 -3
  236. flowfile_core/routes/routes.py +77 -43
  237. flowfile_core/routes/secrets.py +25 -27
  238. flowfile_core/routes/user_defined_components.py +483 -4
  239. flowfile_core/run_lock.py +0 -1
  240. flowfile_core/schemas/__init__.py +4 -6
  241. flowfile_core/schemas/analysis_schemas/graphic_walker_schemas.py +55 -55
  242. flowfile_core/schemas/cloud_storage_schemas.py +59 -55
  243. flowfile_core/schemas/input_schema.py +398 -154
  244. flowfile_core/schemas/output_model.py +50 -35
  245. flowfile_core/schemas/schemas.py +207 -67
  246. flowfile_core/schemas/transform_schema.py +1360 -435
  247. flowfile_core/schemas/yaml_types.py +117 -0
  248. flowfile_core/secret_manager/secret_manager.py +17 -13
  249. flowfile_core/{flowfile/node_designer/data_types.py → types.py} +33 -3
  250. flowfile_core/utils/arrow_reader.py +7 -6
  251. flowfile_core/utils/excel_file_manager.py +3 -3
  252. flowfile_core/utils/fileManager.py +7 -7
  253. flowfile_core/utils/fl_executor.py +8 -10
  254. flowfile_core/utils/utils.py +4 -4
  255. flowfile_core/utils/validate_setup.py +5 -4
  256. flowfile_frame/__init__.py +107 -50
  257. flowfile_frame/adapters.py +2 -9
  258. flowfile_frame/adding_expr.py +73 -32
  259. flowfile_frame/cloud_storage/frame_helpers.py +27 -23
  260. flowfile_frame/cloud_storage/secret_manager.py +12 -26
  261. flowfile_frame/config.py +2 -5
  262. flowfile_frame/expr.py +311 -218
  263. flowfile_frame/expr.pyi +160 -159
  264. flowfile_frame/expr_name.py +23 -23
  265. flowfile_frame/flow_frame.py +581 -489
  266. flowfile_frame/flow_frame.pyi +123 -104
  267. flowfile_frame/flow_frame_methods.py +236 -252
  268. flowfile_frame/group_frame.py +50 -20
  269. flowfile_frame/join.py +2 -2
  270. flowfile_frame/lazy.py +129 -87
  271. flowfile_frame/lazy_methods.py +83 -30
  272. flowfile_frame/list_name_space.py +55 -50
  273. flowfile_frame/selectors.py +148 -68
  274. flowfile_frame/series.py +9 -7
  275. flowfile_frame/utils.py +19 -21
  276. flowfile_worker/__init__.py +12 -4
  277. flowfile_worker/configs.py +11 -19
  278. flowfile_worker/create/__init__.py +14 -27
  279. flowfile_worker/create/funcs.py +143 -94
  280. flowfile_worker/create/models.py +139 -68
  281. flowfile_worker/create/pl_types.py +14 -15
  282. flowfile_worker/create/read_excel_tables.py +34 -41
  283. flowfile_worker/create/utils.py +22 -19
  284. flowfile_worker/external_sources/s3_source/main.py +18 -51
  285. flowfile_worker/external_sources/s3_source/models.py +34 -27
  286. flowfile_worker/external_sources/sql_source/main.py +8 -5
  287. flowfile_worker/external_sources/sql_source/models.py +13 -9
  288. flowfile_worker/flow_logger.py +10 -8
  289. flowfile_worker/funcs.py +214 -155
  290. flowfile_worker/main.py +11 -17
  291. flowfile_worker/models.py +35 -28
  292. flowfile_worker/process_manager.py +2 -3
  293. flowfile_worker/routes.py +121 -93
  294. flowfile_worker/secrets.py +9 -6
  295. flowfile_worker/spawner.py +80 -49
  296. flowfile_worker/utils.py +3 -2
  297. shared/__init__.py +2 -7
  298. shared/storage_config.py +25 -13
  299. test_utils/postgres/commands.py +3 -2
  300. test_utils/postgres/fixtures.py +9 -9
  301. test_utils/s3/commands.py +1 -1
  302. test_utils/s3/data_generator.py +3 -4
  303. test_utils/s3/demo_data_generator.py +4 -7
  304. test_utils/s3/fixtures.py +7 -5
  305. tools/migrate/README.md +56 -0
  306. tools/migrate/__init__.py +12 -0
  307. tools/migrate/__main__.py +118 -0
  308. tools/migrate/legacy_schemas.py +682 -0
  309. tools/migrate/migrate.py +610 -0
  310. tools/migrate/tests/__init__.py +0 -0
  311. tools/migrate/tests/conftest.py +21 -0
  312. tools/migrate/tests/test_migrate.py +622 -0
  313. tools/migrate/tests/test_migration_e2e.py +1009 -0
  314. tools/migrate/tests/test_node_migrations.py +843 -0
  315. flowfile/web/static/assets/CloudConnectionManager-2dfdce2f.css +0 -86
  316. flowfile/web/static/assets/CustomNode-74a37f74.css +0 -32
  317. flowfile/web/static/assets/DatabaseManager-30fa27e5.css +0 -64
  318. flowfile/web/static/assets/Filter-812dcbca.js +0 -164
  319. flowfile/web/static/assets/Filter-f62091b3.css +0 -20
  320. flowfile/web/static/assets/ManualInput-3246a08d.css +0 -96
  321. flowfile/web/static/assets/PivotValidation-891ddfb0.css +0 -13
  322. flowfile/web/static/assets/PivotValidation-c46cd420.css +0 -13
  323. flowfile/web/static/assets/SliderInput-b8fb6a8c.css +0 -4
  324. flowfile/web/static/assets/UnpivotValidation-0d240eeb.css +0 -13
  325. flowfile/web/static/assets/outputCsv-9cc59e0b.css +0 -2499
  326. flowfile/web/static/assets/outputParquet-cf8cf3f2.css +0 -4
  327. flowfile/web/static/assets/secretApi-538058f3.js +0 -46
  328. flowfile/web/static/assets/vue-codemirror-bccfde04.css +0 -32
  329. flowfile-0.4.1.dist-info/RECORD +0 -376
  330. flowfile_core/flowfile/manage/open_flowfile.py +0 -143
  331. {flowfile-0.4.1.dist-info → flowfile-0.5.3.dist-info}/licenses/LICENSE +0 -0
  332. /flowfile_core/flowfile/manage/manage_flowfile.py → /tools/__init__.py +0 -0
@@ -1,9 +1,16 @@
1
1
  # Fixed custom_node.py with proper type hints
2
2
 
3
+ from typing import Any, TypeVar
4
+
3
5
  import polars as pl
4
6
  from pydantic import BaseModel
5
- from typing import Any, Dict, Optional, TypeVar, Callable
6
- from flowfile_core.flowfile.node_designer.ui_components import FlowfileInComponent, IncomingColumns, Section
7
+
8
+ from flowfile_core.flowfile.node_designer.ui_components import (
9
+ FlowfileInComponent,
10
+ IncomingColumns,
11
+ SecretSelector,
12
+ Section,
13
+ )
7
14
  from flowfile_core.schemas.schemas import NodeTemplate, NodeTypeLiteral, TransformTypeLiteral
8
15
 
9
16
 
@@ -22,9 +29,9 @@ def to_frontend_schema(model_instance: BaseModel) -> dict:
22
29
  A dictionary representation of the model.
23
30
  """
24
31
  result = {}
25
- extra_fields = getattr(model_instance, '__pydantic_extra__', {})
32
+ extra_fields = getattr(model_instance, "__pydantic_extra__", {})
26
33
  model_fields = {k: getattr(model_instance, k) for k in model_instance.model_fields.keys()}
27
- for key, value in (extra_fields|model_fields).items():
34
+ for key, value in (extra_fields | model_fields).items():
28
35
  result[key] = _convert_value(value)
29
36
  return result
30
37
 
@@ -34,25 +41,18 @@ def _convert_value(value: Any) -> Any:
34
41
  Helper function to convert any value to a frontend-ready format.
35
42
  """
36
43
  if isinstance(value, Section):
37
- section_data = value.model_dump(
38
- include={'title', 'description', 'hidden'},
39
- exclude_none=True
40
- )
44
+ section_data = value.model_dump(include={"title", "description", "hidden"}, exclude_none=True)
41
45
  section_data["component_type"] = "Section"
42
- section_data["components"] = {
43
- key: _convert_value(comp)
44
- for key, comp in value.get_components().items()
45
- }
46
+ section_data["components"] = {key: _convert_value(comp) for key, comp in value.get_components().items()}
46
47
  return section_data
47
48
 
48
49
  elif isinstance(value, FlowfileInComponent):
49
50
  component_dict = value.model_dump(exclude_none=True)
50
- if 'options' in component_dict:
51
- if component_dict['options'] is IncomingColumns or (
52
- isinstance(component_dict['options'], type) and
53
- issubclass(component_dict['options'], IncomingColumns)
51
+ if "options" in component_dict:
52
+ if component_dict["options"] is IncomingColumns or (
53
+ isinstance(component_dict["options"], type) and issubclass(component_dict["options"], IncomingColumns)
54
54
  ):
55
- component_dict['options'] = {"__type__": "IncomingColumns"}
55
+ component_dict["options"] = {"__type__": "IncomingColumns"}
56
56
  return component_dict
57
57
  elif isinstance(value, BaseModel):
58
58
  return to_frontend_schema(value)
@@ -67,7 +67,7 @@ def _convert_value(value: Any) -> Any:
67
67
 
68
68
 
69
69
  # Type variable for the Section factory
70
- T = TypeVar('T', bound=Section)
70
+ T = TypeVar("T", bound=Section)
71
71
 
72
72
 
73
73
  def create_section(**components: FlowfileInComponent) -> Section:
@@ -104,17 +104,30 @@ class NodeSettings(BaseModel):
104
104
  main_config = main_config_section
105
105
  advanced_options = advanced_config_section
106
106
  """
107
+
107
108
  class Config:
108
- extra = 'allow'
109
+ extra = "allow"
109
110
  arbitrary_types_allowed = True
110
111
 
112
+ def has_sections(self) -> bool:
113
+ """Check if this settings class has any sections defined."""
114
+ if self.model_fields:
115
+ return True
116
+ extra = getattr(self, "__pydantic_extra__", {})
117
+ return bool(extra)
118
+
119
+ def is_empty(self) -> bool:
120
+ """Check if this is an empty settings class with no configuration."""
121
+
122
+ return not self.has_sections()
123
+
111
124
  def __init__(self, **sections):
112
125
  """
113
126
  Initialize NodeSettings with sections as keyword arguments.
114
127
  """
115
128
  super().__init__(**sections)
116
129
 
117
- def populate_values(self, values: Dict[str, Any]) -> 'NodeSettings':
130
+ def populate_values(self, values: dict[str, Any]) -> "NodeSettings":
118
131
  """
119
132
  Populates the settings with values received from the frontend.
120
133
 
@@ -133,7 +146,7 @@ class NodeSettings(BaseModel):
133
146
  all_sections = {}
134
147
 
135
148
  # Get extra fields
136
- extra_fields = getattr(self, '__pydantic_extra__', {})
149
+ extra_fields = getattr(self, "__pydantic_extra__", {})
137
150
  all_sections.update(extra_fields)
138
151
 
139
152
  # Get defined fields that are Sections
@@ -150,6 +163,80 @@ class NodeSettings(BaseModel):
150
163
  component.set_value(section_values[component_name])
151
164
  return self
152
165
 
166
+ def get_value(self, field_name: str) -> Any:
167
+ """
168
+ Gets the current value of a field by name.
169
+
170
+ Searches through direct fields, extra fields, and sections.
171
+
172
+ Args:
173
+ field_name: The name of the field to retrieve.
174
+
175
+ Returns:
176
+ The current value of the field, or None if not found.
177
+ """
178
+ # Check direct model fields
179
+ if field_name in self.model_fields:
180
+ component = getattr(self, field_name, None)
181
+ if component is not None:
182
+ if isinstance(component, FlowfileInComponent):
183
+ return component.value
184
+ return component
185
+
186
+ # Check pydantic extra fields
187
+ extras = getattr(self, "__pydantic_extra__", {}) or {}
188
+ if field_name in extras:
189
+ component = extras[field_name]
190
+ if isinstance(component, FlowfileInComponent):
191
+ return component.value
192
+ return component
193
+
194
+ # Check within sections (both in model_fields and extras)
195
+ all_fields = {**{k: getattr(self, k) for k in self.model_fields}, **extras}
196
+ for value in all_fields.values():
197
+ if isinstance(value, Section):
198
+ components = value.get_components()
199
+ if field_name in components:
200
+ component = components[field_name]
201
+ if isinstance(component, FlowfileInComponent):
202
+ return component.value
203
+ return component
204
+
205
+ return None
206
+
207
+ def get_all_components(self) -> dict[str, FlowfileInComponent]:
208
+ """
209
+ Returns all UI components in the settings, including those nested in sections.
210
+
211
+ Returns:
212
+ Dictionary mapping field names to their FlowfileInComponent instances.
213
+ """
214
+ components = {}
215
+
216
+ # Get from model fields
217
+ for field_name in self.model_fields:
218
+ value = getattr(self, field_name, None)
219
+ if isinstance(value, FlowfileInComponent):
220
+ components[field_name] = value
221
+ elif isinstance(value, Section):
222
+ components.update(value.get_components())
223
+
224
+ # Get from extra fields
225
+ extras = getattr(self, "__pydantic_extra__", {}) or {}
226
+ for field_name, value in extras.items():
227
+ if isinstance(value, FlowfileInComponent):
228
+ components[field_name] = value
229
+ elif isinstance(value, Section):
230
+ components.update(value.get_components())
231
+
232
+ return components
233
+
234
+ def set_secret_context(self, user_id: int, accessed_secrets: set):
235
+ """Inject execution context into all SecretSelector components."""
236
+ for component in self.get_all_components().values():
237
+ if isinstance(component, SecretSelector):
238
+ component.set_execution_context(user_id, accessed_secrets)
239
+
153
240
 
154
241
  def create_node_settings(**sections: Section) -> NodeSettings:
155
242
  """
@@ -187,13 +274,13 @@ class SectionBuilder:
187
274
  advanced_section = builder.build()
188
275
  """
189
276
 
190
- def __init__(self, title: Optional[str] = None, description: Optional[str] = None, hidden: bool = False):
277
+ def __init__(self, title: str | None = None, description: str | None = None, hidden: bool = False):
191
278
  self._section = Section(title=title, description=description, hidden=hidden)
192
279
 
193
- def add_component(self, name: str, component: FlowfileInComponent) -> 'SectionBuilder':
280
+ def add_component(self, name: str, component: FlowfileInComponent) -> "SectionBuilder":
194
281
  """Add a component to the section."""
195
282
  setattr(self._section, name, component)
196
- extra = getattr(self._section, '__pydantic_extra__', {})
283
+ extra = getattr(self._section, "__pydantic_extra__", {})
197
284
  extra[name] = component
198
285
  return self
199
286
 
@@ -219,10 +306,10 @@ class NodeSettingsBuilder:
219
306
  def __init__(self):
220
307
  self._settings = NodeSettings()
221
308
 
222
- def add_section(self, name: str, section: Section) -> 'NodeSettingsBuilder':
309
+ def add_section(self, name: str, section: Section) -> "NodeSettingsBuilder":
223
310
  """Add a section to the node settings."""
224
311
  setattr(self._settings, name, section)
225
- extra = getattr(self._settings, '__pydantic_extra__', {})
312
+ extra = getattr(self._settings, "__pydantic_extra__", {})
226
313
  extra[name] = section
227
314
  return self
228
315
 
@@ -238,25 +325,29 @@ class CustomNodeBase(BaseModel):
238
325
  To create a new node, you should inherit from this class and define its
239
326
  attributes and the `process` method.
240
327
  """
328
+
241
329
  # Core node properties
242
330
  node_name: str
243
331
  node_category: str = "Custom"
244
332
  node_icon: str = "user-defined-icon.png"
245
- settings_schema: Optional[NodeSettings] = None
333
+ settings_schema: NodeSettings | None = None
246
334
 
247
335
  # I/O configuration
248
336
  number_of_inputs: int = 1
249
337
  number_of_outputs: int = 1
250
338
 
251
339
  # Display properties in the UI
252
- node_group: Optional[str] = "custom"
253
- title: Optional[str] = "Custom Node"
254
- intro: Optional[str] = "A custom node for data processing"
340
+ node_group: str | None = "custom"
341
+ title: str | None = "Custom Node"
342
+ intro: str | None = "A custom node for data processing"
255
343
 
256
344
  # Behavior properties
257
345
  node_type: NodeTypeLiteral = "process"
258
346
  transform_type: TransformTypeLiteral = "wide"
259
347
 
348
+ _user_id: int | None = None
349
+ accessed_secrets: set[str] = set()
350
+
260
351
  @property
261
352
  def item(self):
262
353
  """A unique identifier for the node, derived from its name."""
@@ -269,11 +360,46 @@ class CustomNodeBase(BaseModel):
269
360
  """
270
361
  Initialize the node, optionally populating settings from initial values.
271
362
  """
272
- initial_values = data.pop('initial_values', None)
363
+ initial_values = data.pop("initial_values", None)
273
364
  super().__init__(**data)
274
365
  if self.settings_schema and initial_values:
275
366
  self.settings_schema.populate_values(initial_values)
276
367
 
368
+ def set_execution_context(self, user_id: int):
369
+ """
370
+ Sets the execution context for the node.
371
+ Called by the framework before executing the node.
372
+
373
+ Args:
374
+ user_id: The ID of the user executing this node.
375
+ """
376
+ self._user_id = user_id
377
+ self.accessed_secrets = set()
378
+
379
+ def get_accessed_secrets(self) -> set[str]:
380
+ """
381
+ Returns the set of secret values accessed during this execution.
382
+ Used by the output scanner to detect accidental leaks.
383
+ """
384
+ return self.accessed_secrets.copy()
385
+
386
+ def get_secret_names(self) -> list[str]:
387
+ """
388
+ Returns a list of all SecretSelector field names in the settings schema.
389
+ Useful for validation and debugging.
390
+ """
391
+ from flowfile_core.flowfile.node_designer.ui_components import SecretSelector
392
+
393
+ if self.settings_schema is None:
394
+ return []
395
+
396
+ secret_fields = []
397
+ for name, field in self.settings_schema.get_all_components().items():
398
+ if isinstance(field, SecretSelector):
399
+ secret_fields.append(name)
400
+
401
+ return secret_fields
402
+
277
403
  def get_frontend_schema(self) -> dict:
278
404
  """
279
405
  Get the frontend-ready schema with current values.
@@ -303,20 +429,20 @@ class CustomNodeBase(BaseModel):
303
429
  return schema
304
430
 
305
431
  @classmethod
306
- def from_frontend_schema(cls, schema: dict) -> 'CustomNodeBase':
432
+ def from_frontend_schema(cls, schema: dict) -> "CustomNodeBase":
307
433
  """
308
434
  Create a node instance from a frontend schema.
309
435
 
310
436
  This is used when loading a node from a saved flow.
311
437
  """
312
- settings_values = schema.pop('settings_schema', {})
438
+ settings_values = schema.pop("settings_schema", {})
313
439
  node = cls(**schema)
314
440
  if settings_values and node.settings_schema:
315
441
  node.settings_schema.populate_values(settings_values)
316
442
  return node
317
443
 
318
444
  @classmethod
319
- def from_settings(cls, settings_values: dict) -> 'CustomNodeBase':
445
+ def from_settings(cls, settings_values: dict) -> "CustomNodeBase":
320
446
  """
321
447
  Create a node instance with just its settings values.
322
448
 
@@ -327,7 +453,7 @@ class CustomNodeBase(BaseModel):
327
453
  node.settings_schema.populate_values(settings_values)
328
454
  return node
329
455
 
330
- def update_settings(self, values: Dict[str, Any]) -> 'CustomNodeBase':
456
+ def update_settings(self, values: dict[str, Any]) -> "CustomNodeBase":
331
457
  """
332
458
  Update the settings with new values from the frontend.
333
459
  """
@@ -367,5 +493,5 @@ class CustomNodeBase(BaseModel):
367
493
  drawer_intro=self.intro,
368
494
  node_type=self.node_type,
369
495
  transform_type=self.transform_type,
370
- custom_node=True
496
+ custom_node=True,
371
497
  )
@@ -1,19 +1,17 @@
1
- # ui_components.py - Updated ColumnSelector
1
+ from typing import Any, Literal
2
2
 
3
- from typing import List, Optional, Any, Literal, Union, Type, Tuple, Dict
4
-
5
- from pydantic import Field, BaseModel, computed_field
3
+ from pydantic import BaseModel, Field, SecretStr, computed_field
6
4
 
7
5
  from flowfile_core.flowfile.node_designer._type_registry import normalize_type_spec
6
+ from flowfile_core.secret_manager.secret_manager import decrypt_secret, get_encrypted_secret
7
+
8
8
  # Public API import
9
- from flowfile_core.flowfile.node_designer.data_types import DataType, TypeSpec
9
+ from flowfile_core.types import DataType, TypeSpec
10
10
 
11
11
  InputType = Literal["text", "number", "secret", "array", "date", "boolean"]
12
12
 
13
13
 
14
- def normalize_input_to_data_types(
15
- v: Any
16
- ) -> Union[Literal["ALL"], List[DataType]]:
14
+ def normalize_input_to_data_types(v: Any) -> Literal["ALL"] | list[DataType]:
17
15
  """
18
16
  Normalizes a wide variety of inputs to either 'ALL' or a sorted list of DataType enums.
19
17
  This function is used as a Pydantic BeforeValidator.
@@ -46,9 +44,10 @@ class FlowfileInComponent(BaseModel):
46
44
  It's not meant to be used directly, but rather to be inherited by specific
47
45
  component classes.
48
46
  """
47
+
49
48
  component_type: str = Field(..., description="Type of the UI component")
50
49
  value: Any = None
51
- label: Optional[str] = None
50
+ label: str | None = None
52
51
  input_type: InputType
53
52
 
54
53
  def set_value(self, value: Any):
@@ -83,6 +82,7 @@ class IncomingColumns:
83
82
  options=IncomingColumns
84
83
  )
85
84
  """
85
+
86
86
  pass
87
87
 
88
88
 
@@ -94,25 +94,21 @@ class ColumnSelector(FlowfileInComponent):
94
94
  This is particularly useful when a node operation should only be applied
95
95
  to columns of a specific type (e.g., numeric, string, date).
96
96
  """
97
+
97
98
  component_type: Literal["ColumnSelector"] = "ColumnSelector"
98
99
  required: bool = False
99
100
  multiple: bool = False
100
101
  input_type: InputType = "text"
101
102
 
102
103
  # Normalized output: either "ALL" or list of DataType enums
103
- data_type_filter_input: TypeSpec = Field(
104
- default="ALL",
105
- alias="data_types",
106
- repr=False,
107
- exclude=True
108
- )
104
+ data_type_filter_input: TypeSpec = Field(default="ALL", alias="data_types", repr=False, exclude=True)
109
105
 
110
106
  class Config:
111
107
  arbitrary_types_allowed = True
112
108
 
113
109
  @computed_field
114
110
  @property
115
- def data_types_filter(self) -> Union[Literal["ALL"], List[DataType]]:
111
+ def data_types_filter(self) -> Literal["ALL"] | list[DataType]:
116
112
  """
117
113
  A computed field that normalizes the `data_type_filter_input` into a
118
114
  standardized format for the frontend.
@@ -125,16 +121,17 @@ class ColumnSelector(FlowfileInComponent):
125
121
  correct format for the frontend.
126
122
  """
127
123
  data = super().model_dump(**kwargs)
128
- if 'data_types_filter' in data and data['data_types_filter'] != "ALL":
129
- data['data_types'] = sorted([dt.value for dt in data['data_types_filter']])
124
+ if "data_types_filter" in data and data["data_types_filter"] != "ALL":
125
+ data["data_types"] = sorted([dt.value for dt in data["data_types_filter"]])
130
126
  return data
131
127
 
132
128
 
133
129
  class TextInput(FlowfileInComponent):
134
130
  """A standard text input field for capturing string values."""
131
+
135
132
  component_type: Literal["TextInput"] = "TextInput"
136
- default: Optional[str] = ""
137
- placeholder: Optional[str] = ""
133
+ default: str | None = ""
134
+ placeholder: str | None = ""
138
135
  input_type: InputType = "text"
139
136
 
140
137
  def __init__(self, **data):
@@ -145,23 +142,43 @@ class TextInput(FlowfileInComponent):
145
142
 
146
143
  class NumericInput(FlowfileInComponent):
147
144
  """A numeric input field with optional minimum and maximum value validation."""
145
+
148
146
  component_type: Literal["NumericInput"] = "NumericInput"
149
- default: Optional[float] = None
150
- min_value: Optional[float] = None
151
- max_value: Optional[float] = None
147
+ default: float | None = None
148
+ min_value: float | None = None
149
+ max_value: float | None = None
150
+ input_type: InputType = "number"
151
+
152
+ def __init__(self, **data):
153
+ super().__init__(**data)
154
+ if self.value is None and self.default is not None:
155
+ self.value = self.default
156
+
157
+
158
+ class SliderInput(FlowfileInComponent):
159
+ """A slider input for selecting a numeric value within a range."""
160
+
161
+ component_type: Literal["SliderInput"] = "SliderInput"
162
+ default: float | None = None
163
+ min_value: float = 0
164
+ max_value: float = 100
165
+ step: float = 1
152
166
  input_type: InputType = "number"
153
167
 
154
168
  def __init__(self, **data):
155
169
  super().__init__(**data)
156
170
  if self.value is None and self.default is not None:
157
171
  self.value = self.default
172
+ elif self.value is None:
173
+ self.value = self.min_value
158
174
 
159
175
 
160
176
  class ToggleSwitch(FlowfileInComponent):
161
177
  """A boolean toggle switch, typically used for enabling or disabling a feature."""
178
+
162
179
  component_type: Literal["ToggleSwitch"] = "ToggleSwitch"
163
180
  default: bool = False
164
- description: Optional[str] = None
181
+ description: str | None = None
165
182
  input_type: InputType = "boolean"
166
183
 
167
184
  def __init__(self, **data):
@@ -182,9 +199,10 @@ class SingleSelect(FlowfileInComponent):
182
199
  dynamically populated from the input dataframe's columns by using the
183
200
  `IncomingColumns` marker.
184
201
  """
202
+
185
203
  component_type: Literal["SingleSelect"] = "SingleSelect"
186
- options: Union[List[Union[str, Tuple[str, Any]]], Type[IncomingColumns]]
187
- default: Optional[Any] = None
204
+ options: list[str | tuple[str, Any]] | type[IncomingColumns]
205
+ default: Any | None = None
188
206
  input_type: InputType = "text"
189
207
 
190
208
  def __init__(self, **data):
@@ -200,9 +218,10 @@ class MultiSelect(FlowfileInComponent):
200
218
  Like `SingleSelect`, the options can be static or dynamically populated
201
219
  from the input columns using the `IncomingColumns` marker.
202
220
  """
221
+
203
222
  component_type: Literal["MultiSelect"] = "MultiSelect"
204
- options: Union[List[Union[str, Tuple[str, Any]]], Type[IncomingColumns]]
205
- default: List[Any] = Field(default_factory=list)
223
+ options: list[str | tuple[str, Any]] | type[IncomingColumns]
224
+ default: list[Any] = Field(default_factory=list)
206
225
  input_type: InputType = "array"
207
226
 
208
227
  def __init__(self, **data):
@@ -226,12 +245,13 @@ class Section(BaseModel):
226
245
  my_text_input=TextInput(label="Enter a value")
227
246
  )
228
247
  """
229
- title: Optional[str] = None
230
- description: Optional[str] = None
248
+
249
+ title: str | None = None
250
+ description: str | None = None
231
251
  hidden: bool = False
232
252
 
233
253
  class Config:
234
- extra = 'allow'
254
+ extra = "allow"
235
255
  arbitrary_types_allowed = True
236
256
 
237
257
  def __init__(self, **data):
@@ -240,7 +260,7 @@ class Section(BaseModel):
240
260
  """
241
261
  super().__init__(**data)
242
262
 
243
- def __call__(self, **kwargs) -> 'Section':
263
+ def __call__(self, **kwargs) -> "Section":
244
264
  """
245
265
  Allows adding components to the section after initialization.
246
266
 
@@ -250,7 +270,7 @@ class Section(BaseModel):
250
270
  setattr(self, key, value)
251
271
  return self
252
272
 
253
- def get_components(self) -> Dict[str, FlowfileInComponent]:
273
+ def get_components(self) -> dict[str, FlowfileInComponent]:
254
274
  """
255
275
  Get all FlowfileInComponent instances from the section.
256
276
 
@@ -263,15 +283,96 @@ class Section(BaseModel):
263
283
  components = {}
264
284
 
265
285
  # Get from extra fields
266
- for key, value in getattr(self, '__pydantic_extra__', {}).items():
286
+ for key, value in getattr(self, "__pydantic_extra__", {}).items():
267
287
  if isinstance(value, FlowfileInComponent):
268
288
  components[key] = value
269
289
 
270
290
  # Get from defined fields (excluding metadata)
271
291
  for field_name in self.model_fields:
272
- if field_name not in {'title', 'description', 'hidden'}:
292
+ if field_name not in {"title", "description", "hidden"}:
273
293
  value = getattr(self, field_name, None)
274
294
  if isinstance(value, FlowfileInComponent):
275
295
  components[field_name] = value
276
296
 
277
297
  return components
298
+
299
+
300
+ class AvailableSecrets:
301
+ """
302
+ A marker class used in `SecretSelector` components.
303
+
304
+ When `options` is set to this class, the component will be dynamically
305
+ populated with the secret names available to the current user.
306
+ This allows users to select from available secrets at runtime.
307
+
308
+ Example:
309
+ class MyNodeSettings(NodeSettings):
310
+ api_key = SecretSelector(
311
+ label="Select an API Key",
312
+ options=AvailableSecrets
313
+ )
314
+ """
315
+
316
+ pass
317
+
318
+
319
+ class SecretSelector(FlowfileInComponent):
320
+ component_type: Literal["SecretSelector"] = "SecretSelector"
321
+ options: type[AvailableSecrets] = AvailableSecrets
322
+ required: bool = False
323
+ description: str | None = None
324
+ input_type: InputType = "secret"
325
+ name_prefix: str | None = None
326
+
327
+ # Private fields for runtime context
328
+ _user_id: int | None = None
329
+ _accessed_secrets: set | None = None # Reference to node's tracking set
330
+
331
+ def set_execution_context(self, user_id: int, accessed_secrets: set):
332
+ """Called by framework before process() runs."""
333
+ self._user_id = user_id
334
+ self._accessed_secrets = accessed_secrets
335
+
336
+ @property
337
+ def secret_value(self) -> SecretStr | None:
338
+ """
339
+ Get the decrypted secret value.
340
+
341
+ Can only be called during node execution (after context is set).
342
+ Returns None if no secret is selected.
343
+ """
344
+ if self.value is None:
345
+ return None
346
+
347
+ if self._user_id is None:
348
+ raise ValueError(
349
+ "Secret can only be accessed during node execution. "
350
+ "Ensure you're calling this from within the process() method."
351
+ )
352
+
353
+ encrypted = get_encrypted_secret(current_user_id=self._user_id, secret_name=self.value)
354
+
355
+ if encrypted is None:
356
+ raise ValueError(
357
+ f"Secret '{self.value}' not found for user. " f"Please ensure the secret exists in your secrets store."
358
+ )
359
+
360
+ decrypted = decrypt_secret(encrypted)
361
+
362
+ if self._accessed_secrets is not None:
363
+ self._accessed_secrets.add(decrypted.get_secret_value())
364
+ else:
365
+ self._accessed_secrets = {decrypted.get_secret_value()}
366
+ return decrypted
367
+
368
+ def model_dump(self, **kwargs) -> dict:
369
+ """
370
+ Overrides the default `model_dump` to signal to the frontend
371
+ that this needs dynamic population from available secrets.
372
+ """
373
+ data = super().model_dump(**kwargs)
374
+ # Signal to frontend that options should be fetched from /secrets endpoint
375
+ data["options"] = {"__type__": "AvailableSecrets"}
376
+ if self.name_prefix:
377
+ data["name_prefix"] = self.name_prefix
378
+ return data