Flowfile 0.5.4__py3-none-any.whl → 0.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- flowfile/__main__.py +94 -1
- flowfile/api.py +8 -6
- flowfile/web/static/assets/{AdminView-f9847d67.js → AdminView-C4K1DdHI.js} +28 -33
- flowfile/web/static/assets/{CloudConnectionView-faace55b.js → CloudConnectionView-BZbPvPUL.js} +39 -50
- flowfile/web/static/assets/{CloudStorageReader-24c54524.css → CloudStorageReader-BDByiqPI.css} +25 -25
- flowfile/web/static/assets/{CloudStorageReader-d86ecaa7.js → CloudStorageReader-DLVukNJ7.js} +30 -35
- flowfile/web/static/assets/{CloudStorageWriter-0f4d9a44.js → CloudStorageWriter-Bfi-C1QW.js} +32 -37
- flowfile/web/static/assets/{CloudStorageWriter-60547855.css → CloudStorageWriter-y8jL8yjG.css} +24 -24
- flowfile/web/static/assets/{ColumnActionInput-f4189ae0.js → ColumnActionInput-BpiCApw9.js} +7 -12
- flowfile/web/static/assets/{ColumnSelector-e66b33da.js → ColumnSelector-CEAwedI7.js} +1 -2
- flowfile/web/static/assets/ContextMenu-CdojQu0w.js +9 -0
- flowfile/web/static/assets/ContextMenu-D12mhsy1.js +9 -0
- flowfile/web/static/assets/ContextMenu-EWUR98va.js +9 -0
- flowfile/web/static/assets/{ContextMenu.vue_vue_type_script_setup_true_lang-a1bd6314.js → ContextMenu.vue_vue_type_script_setup_true_lang-I4rXXd6G.js} +4 -5
- flowfile/web/static/assets/{CrossJoin-24694b8f.js → CrossJoin-BOFfxkJO.js} +19 -18
- flowfile/web/static/assets/{CrossJoin-71b4cc10.css → CrossJoin-Cmbyt9im.css} +18 -18
- flowfile/web/static/assets/{CustomNode-569d45ff.js → CustomNode-Bhpezobq.js} +12 -17
- flowfile/web/static/assets/{DatabaseConnectionSettings-cfc08938.js → DatabaseConnectionSettings-Dw3bSJKB.js} +10 -11
- flowfile/web/static/assets/{DatabaseReader-5bf8c75b.css → DatabaseReader-D6pUNUCs.css} +21 -21
- flowfile/web/static/assets/{DatabaseReader-701feabb.js → DatabaseReader-m87ghlw0.js} +36 -34
- flowfile/web/static/assets/{DatabaseView-0482e5b5.js → DatabaseView-CisSAtpe.js} +30 -38
- flowfile/web/static/assets/{DatabaseWriter-16721989.js → DatabaseWriter-Bbj9JLdL.js} +33 -35
- flowfile/web/static/assets/{DatabaseWriter-bdcf2c8b.css → DatabaseWriter-RBqdFLj8.css} +17 -17
- flowfile/web/static/assets/{DesignerView-f64749fb.js → DesignerView-DemDevTQ.js} +1841 -2090
- flowfile/web/static/assets/{DesignerView-49abb835.css → DesignerView-Dm6OzlIc.css} +244 -202
- flowfile/web/static/assets/{DocumentationView-61bd2990.js → DocumentationView-BrC1ZR3H.js} +3 -4
- flowfile/web/static/assets/{ExploreData-e2735b13.js → ExploreData-BMKcDuRb.js} +8 -10
- flowfile/web/static/assets/{ExternalSource-2535c3b2.js → ExternalSource-BXrNNS-f.js} +40 -42
- flowfile/web/static/assets/{ExternalSource-7ac7373f.css → ExternalSource-NB6WVl5R.css} +14 -14
- flowfile/web/static/assets/{Filter-2cdbc93c.js → Filter-C2MjsN6P.js} +36 -33
- flowfile/web/static/assets/{Filter-7494ea97.css → Filter-DCMGGuGC.css} +9 -9
- flowfile/web/static/assets/{Formula-53d58c43.css → Formula-BYafbDj8.css} +4 -4
- flowfile/web/static/assets/{Formula-fcda3c2c.js → Formula-ufuy4mVD.js} +27 -26
- flowfile/web/static/assets/{FuzzyMatch-ad6361d6.css → FuzzyMatch-BGJAwgd0.css} +42 -42
- flowfile/web/static/assets/{FuzzyMatch-f8d3b7d3.js → FuzzyMatch-BOHODq3h.js} +36 -38
- flowfile/web/static/assets/{GraphSolver-72eaa695.js → GraphSolver-B6ZzpNGO.js} +23 -21
- flowfile/web/static/assets/{GraphSolver-4b4d7db9.css → GraphSolver-DFN83sj3.css} +4 -4
- flowfile/web/static/assets/{GroupBy-8aa0598b.js → GroupBy-B9BRNcfe.js} +30 -29
- flowfile/web/static/assets/{Sort-4abb7fae.css → GroupBy-x4ooP5np.css} +1 -1
- flowfile/web/static/assets/Join-Bx_g5bZz.css +118 -0
- flowfile/web/static/assets/{Join-e40f0ffa.js → Join-DsBEy1IH.js} +48 -43
- flowfile/web/static/assets/{LoginView-5111c9ae.js → LoginView-Ct0rhdcO.js} +1 -2
- flowfile/web/static/assets/{ManualInput-3702e677.css → ManualInput-DlZmtMdt.css} +48 -48
- flowfile/web/static/assets/{ManualInput-9b6f3224.js → ManualInput-bC4BUgnG.js} +85 -44
- flowfile/web/static/assets/{MultiSelect-ef28e19e.js → MultiSelect-DIQ8PuTC.js} +2 -2
- flowfile/web/static/assets/{MultiSelect.vue_vue_type_script_setup_true_lang-83b3bbfd.js → MultiSelect.vue_vue_type_script_setup_true_lang-BefHfqTI.js} +1 -1
- flowfile/web/static/assets/{NodeDesigner-d2b7ee2b.js → NodeDesigner-D39yzr2k.js} +178 -208
- flowfile/web/static/assets/{NodeDesigner-94cd4dd3.css → NodeDesigner-R0l6sYyY.css} +76 -76
- flowfile/web/static/assets/{NumericInput-1d789794.js → NumericInput-DMSX3oOr.js} +2 -2
- flowfile/web/static/assets/{NumericInput.vue_vue_type_script_setup_true_lang-7775f83e.js → NumericInput.vue_vue_type_script_setup_true_lang-d0YlVHAl.js} +1 -1
- flowfile/web/static/assets/{Output-cefef801.js → Output-D0VoXGcW.js} +26 -34
- flowfile/web/static/assets/{Output-692dd25d.css → Output-DsmglIDy.css} +5 -5
- flowfile/web/static/assets/{Pivot-bab1b75b.js → Pivot-BnMB4sEe.js} +26 -26
- flowfile/web/static/assets/{Pivot-0eda81b4.css → Pivot-qKTyWxop.css} +4 -4
- flowfile/web/static/assets/{PivotValidation-fba09336.js → PivotValidation-B2lWvugt.js} +7 -9
- flowfile/web/static/assets/{PivotValidation-e7941f91.js → PivotValidation-BPlhRjpL.js} +7 -9
- flowfile/web/static/assets/{PolarsCode-740e40fa.js → PolarsCode-5h0tHnWR.js} +22 -20
- flowfile/web/static/assets/PopOver-BHpt5rsj.js +134 -0
- flowfile/web/static/assets/{PopOver-d96599db.css → PopOver-CyYM4-rV.css} +1 -1
- flowfile/web/static/assets/{Read-90f366bc.css → Read-DJxkrTb_.css} +10 -10
- flowfile/web/static/assets/Read-TsLEFh3B.js +227 -0
- flowfile/web/static/assets/{RecordCount-ffc71eca.js → RecordCount-DkVixq9v.js} +18 -17
- flowfile/web/static/assets/{RecordId-a70bb8df.js → RecordId-C2UEGlCf.js} +42 -39
- flowfile/web/static/assets/{SQLQueryComponent-15a421f5.js → SQLQueryComponent-Dr5KMoD3.js} +2 -3
- flowfile/web/static/assets/{Sample-6c26afc7.js → Sample-Cb3eQNmd.js} +30 -30
- flowfile/web/static/assets/{SecretSelector-ceed9496.js → SecretSelector-De2L2bSx.js} +3 -4
- flowfile/web/static/assets/{SecretsView-214d255a.js → SecretsView-CheC9BPV.js} +13 -16
- flowfile/web/static/assets/{Select-8fc29999.js → Select-CI8TloRs.js} +41 -36
- flowfile/web/static/assets/{SettingsSection-9f0d1725.js → SettingsSection-B39ulIiI.js} +1 -2
- flowfile/web/static/assets/{SettingsSection-83090218.js → SettingsSection-BiCc7S9h.js} +1 -2
- flowfile/web/static/assets/{SettingsSection-3f70e4c3.js → SettingsSection-CITK_R7o.js} +2 -3
- flowfile/web/static/assets/{SettingsSection-26fe48d4.css → SettingsSection-D2GgY-Aq.css} +4 -4
- flowfile/web/static/assets/{SetupView-3fa0aa03.js → SetupView-C1aXRDvp.js} +3 -4
- flowfile/web/static/assets/{SetupView-e2da3442.css → SetupView-CI1nd-5Z.css} +38 -38
- flowfile/web/static/assets/{SingleSelect-a4a568cb.js → SingleSelect-Kr_hz90m.js} +2 -2
- flowfile/web/static/assets/{SingleSelect.vue_vue_type_script_setup_true_lang-c8ebdd33.js → SingleSelect.vue_vue_type_script_setup_true_lang-Rxht5Z5N.js} +1 -1
- flowfile/web/static/assets/{SliderInput-be533e71.js → SliderInput-CLqpCxCb.js} +1 -2
- flowfile/web/static/assets/{GroupBy-5792782d.css → Sort-BIt2kc_p.css} +1 -1
- flowfile/web/static/assets/{Sort-154dad81.js → Sort-Dnw_J6Qi.js} +25 -25
- flowfile/web/static/assets/{TextInput-454e2bda.js → TextInput-wdlunIZC.js} +2 -2
- flowfile/web/static/assets/{TextInput.vue_vue_type_script_setup_true_lang-e86510d0.js → TextInput.vue_vue_type_script_setup_true_lang-Bcj3ywzv.js} +1 -1
- flowfile/web/static/assets/{TextToRows-ea73433d.js → TextToRows-BhtyGWPq.js} +42 -49
- flowfile/web/static/assets/{TextToRows-12afb4f4.css → TextToRows-DivDOLDx.css} +9 -9
- flowfile/web/static/assets/{ToggleSwitch-9d7b30f1.js → ToggleSwitch-B-6WzfFf.js} +2 -2
- flowfile/web/static/assets/{ToggleSwitch.vue_vue_type_script_setup_true_lang-00f2580e.js → ToggleSwitch.vue_vue_type_script_setup_true_lang-Cj8LqT-b.js} +1 -1
- flowfile/web/static/assets/{UnavailableFields-b72a2c72.js → UnavailableFields-Yf6XSqFB.js} +2 -3
- flowfile/web/static/assets/{Union-1e44f263.js → Union-CwpjeKYC.js} +20 -23
- flowfile/web/static/assets/{Unpivot-b6ad6427.css → Union-DQJcpp3-.css} +6 -6
- flowfile/web/static/assets/{Unique-a3bc6d0a.js → Unique-25v3urqH.js} +75 -74
- flowfile/web/static/assets/{Union-d6a8d7d5.css → Unpivot-Deqh1gtI.css} +6 -6
- flowfile/web/static/assets/{Unpivot-e27935fc.js → Unpivot-sYcTTXrq.js} +34 -27
- flowfile/web/static/assets/{UnpivotValidation-72497680.js → UnpivotValidation-C5DDEKY2.js} +5 -7
- flowfile/web/static/assets/VueGraphicWalker-B8l1_Z92.js +131 -0
- flowfile/web/static/assets/VueGraphicWalker-Da_1-3me.css +21 -0
- flowfile/web/static/assets/{api-a2102880.js → api-C0LvF-0C.js} +1 -1
- flowfile/web/static/assets/{api-f75042b0.js → api-DaC83EO_.js} +1 -1
- flowfile/web/static/assets/client-C8Ygr6Gb.js +42 -0
- flowfile/web/static/assets/{dropDown-2798a109.js → dropDown-D5YXaPRR.js} +7 -12
- flowfile/web/static/assets/{fullEditor-cf7d7d93.js → fullEditor-BVYnWm05.js} +300 -18
- flowfile/web/static/assets/genericNodeSettings-2wAu-QKn.css +75 -0
- flowfile/web/static/assets/genericNodeSettings-BBtW_Cpz.js +590 -0
- flowfile/web/static/assets/{VueGraphicWalker-d9ab70a3.js → graphic-walker.es-VrK6vdGE.js} +92305 -89751
- flowfile/web/static/assets/index-BCJxPfM5.js +6693 -0
- flowfile/web/static/assets/{index-f0a6e5a5.js → index-CHPMUR0d.js} +150 -170
- flowfile/web/static/assets/index-DPkoZWq8.js +32 -0
- flowfile/web/static/assets/index-DnW_KC_I.js +277 -0
- flowfile/web/static/assets/index-UFXyfirV.css +10797 -0
- flowfile/web/static/assets/index-bcuE0Z0p.js +87456 -0
- flowfile/web/static/assets/{node.types-2c15bb7e.js → node.types-Dl4gtSW9.js} +2 -2
- flowfile/web/static/assets/{outputCsv-3c1757e8.js → outputCsv-BELuBiJZ.js} +2 -3
- flowfile/web/static/assets/outputCsv-CdGkv-fN.css +2581 -0
- flowfile/web/static/assets/{outputExcel-686e1f48.js → outputExcel-D0TTNM79.js} +1 -2
- flowfile/web/static/assets/{outputParquet-df28faa7.js → outputParquet-Cz9EbRHj.js} +1 -2
- flowfile/web/static/assets/{readCsv-e37eee21.js → readCsv-7bd3kUMI.js} +1 -2
- flowfile/web/static/assets/{readExcel-a13f14bb.js → readExcel-Cq8CCwIv.js} +3 -4
- flowfile/web/static/assets/{readParquet-c5244ad5.css → readParquet-CRDmBrsp.css} +4 -4
- flowfile/web/static/assets/{readParquet-344cf746.js → readParquet-DjR4mRaj.js} +4 -5
- flowfile/web/static/assets/{secrets.api-ae198c5c.js → secrets.api-C9o2KE5V.js} +1 -1
- flowfile/web/static/assets/{selectDynamic-6b4b0767.js → selectDynamic-Bl5FVsME.js} +5 -8
- flowfile/web/static/assets/useNodeSettings-dMS9zmh_.js +69 -0
- flowfile/web/static/assets/{vue-codemirror.esm-31ba0e0b.js → vue-codemirror.esm-CwaYwln0.js} +3469 -3064
- flowfile/web/static/assets/{vue-content-loader.es-4469c8ff.js → vue-content-loader.es-CMoRXo7N.js} +3 -3
- flowfile/web/static/index.html +2 -3
- {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/METADATA +2 -1
- flowfile-0.6.1.dist-info/RECORD +417 -0
- {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/WHEEL +1 -1
- flowfile_core/auth/password.py +1 -0
- flowfile_core/database/init_db.py +7 -5
- flowfile_core/fileExplorer/funcs.py +2 -2
- flowfile_core/flowfile/code_generator/code_generator.py +13 -11
- flowfile_core/flowfile/filter_expressions.py +327 -0
- flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +61 -59
- flowfile_core/flowfile/flow_data_engine/flow_file_column/type_registry.py +3 -29
- flowfile_core/flowfile/flow_data_engine/flow_file_column/utils.py +45 -14
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/models.py +20 -3
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/streaming.py +206 -0
- flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +158 -24
- flowfile_core/flowfile/flow_graph.py +504 -190
- flowfile_core/flowfile/flow_node/__init__.py +32 -0
- flowfile_core/flowfile/flow_node/executor.py +404 -0
- flowfile_core/flowfile/flow_node/flow_node.py +207 -106
- flowfile_core/flowfile/flow_node/models.py +40 -0
- flowfile_core/flowfile/flow_node/output_field_config_applier.py +217 -0
- flowfile_core/flowfile/flow_node/schema_utils.py +78 -0
- flowfile_core/flowfile/flow_node/state.py +155 -0
- flowfile_core/flowfile/history_manager.py +401 -0
- flowfile_core/flowfile/manage/compatibility_enhancements.py +9 -0
- flowfile_core/flowfile/manage/io_flowfile.py +3 -1
- flowfile_core/flowfile/sources/external_sources/sql_source/models.py +20 -4
- flowfile_core/flowfile/util/execution_orderer.py +89 -36
- flowfile_core/main.py +2 -4
- flowfile_core/routes/auth.py +8 -9
- flowfile_core/routes/routes.py +320 -101
- flowfile_core/routes/user_defined_components.py +18 -16
- flowfile_core/schemas/history_schema.py +220 -0
- flowfile_core/schemas/input_schema.py +130 -6
- flowfile_core/schemas/schemas.py +9 -0
- flowfile_core/schemas/transform_schema.py +27 -5
- flowfile_core/schemas/yaml_types.py +23 -5
- flowfile_frame/adding_expr.py +18 -126
- flowfile_frame/callable_utils.py +261 -0
- flowfile_frame/database/connection_manager.py +0 -1
- flowfile_frame/expr.py +8 -4
- flowfile_frame/flow_frame.py +41 -41
- flowfile_frame/lazy.py +3 -12
- flowfile_frame/lazy_methods.py +5 -64
- flowfile_frame/utils.py +13 -32
- flowfile_worker/funcs.py +6 -4
- flowfile_worker/main.py +2 -0
- flowfile_worker/models.py +31 -11
- flowfile_worker/routes.py +60 -35
- flowfile_worker/spawner.py +7 -1
- flowfile_worker/streaming.py +335 -0
- flowfile/web/static/assets/ContextMenu-49463352.js +0 -9
- flowfile/web/static/assets/ContextMenu-dd5f3f25.js +0 -9
- flowfile/web/static/assets/ContextMenu-f709b884.js +0 -9
- flowfile/web/static/assets/Join-28b5e18f.css +0 -109
- flowfile/web/static/assets/PopOver-862d7e28.js +0 -939
- flowfile/web/static/assets/Read-225cc63f.js +0 -222
- flowfile/web/static/assets/VueGraphicWalker-430f0b86.css +0 -6
- flowfile/web/static/assets/database_reader-ce1e55f3.svg +0 -24
- flowfile/web/static/assets/database_writer-b4ad0753.svg +0 -23
- flowfile/web/static/assets/element-icons-9c88a535.woff +0 -0
- flowfile/web/static/assets/element-icons-de5eb258.ttf +0 -0
- flowfile/web/static/assets/genericNodeSettings-14eac1c3.js +0 -137
- flowfile/web/static/assets/genericNodeSettings-3b2507ea.css +0 -46
- flowfile/web/static/assets/index-387a6f18.js +0 -60752
- flowfile/web/static/assets/index-6b367bb5.js +0 -38
- flowfile/web/static/assets/index-e96ab018.css +0 -10466
- flowfile/web/static/assets/nodeInput-ed2ae8d7.js +0 -2
- flowfile/web/static/assets/outputCsv-b9a072af.css +0 -2499
- flowfile-0.5.4.dist-info/RECORD +0 -407
- /flowfile/web/static/assets/{AdminView-f53bad23.css → AdminView-B2Dthl3u.css} +0 -0
- /flowfile/web/static/assets/{CloudConnectionView-cf85f943.css → CloudConnectionView-BdFYGWV7.css} +0 -0
- /flowfile/web/static/assets/{ColumnActionInput-c44b7aee.css → ColumnActionInput-dCasSIC9.css} +0 -0
- /flowfile/web/static/assets/{ColumnSelector-371637fb.css → ColumnSelector-j6sEOjo1.css} +0 -0
- /flowfile/web/static/assets/{CustomNode-edb9b939.css → CustomNode-VPlajG0j.css} +0 -0
- /flowfile/web/static/assets/{DatabaseConnectionSettings-c20a1e16.css → DatabaseConnectionSettings-B78hXYgu.css} +0 -0
- /flowfile/web/static/assets/{DatabaseView-6655afd6.css → DatabaseView-B-_adk1s.css} +0 -0
- /flowfile/web/static/assets/{DocumentationView-9ea6e871.css → DocumentationView-CL7iipFL.css} +0 -0
- /flowfile/web/static/assets/{ExploreData-10c5acc8.css → ExploreData-DHjv0Plr.css} +0 -0
- /flowfile/web/static/assets/{LoginView-d325d632.css → LoginView-DN1BXY3e.css} +0 -0
- /flowfile/web/static/assets/{PivotValidation-0e905b1a.css → PivotValidation-DK-FARWe.css} +0 -0
- /flowfile/web/static/assets/{PivotValidation-41b57ad6.css → PivotValidation-FUa9F47u.css} +0 -0
- /flowfile/web/static/assets/{PolarsCode-2b1f1f23.css → PolarsCode-G-gRSrSc.css} +0 -0
- /flowfile/web/static/assets/{SQLQueryComponent-edb90b98.css → SQLQueryComponent-oAbWw0r-.css} +0 -0
- /flowfile/web/static/assets/{SecretSelector-6329f743.css → SecretSelector-CJSadIZx.css} +0 -0
- /flowfile/web/static/assets/{SecretsView-aa291340.css → SecretsView-DbzIRAba.css} +0 -0
- /flowfile/web/static/assets/{SettingsSection-8f980839.css → SettingsSection-BGcJnH6q.css} +0 -0
- /flowfile/web/static/assets/{SettingsSection-07fbbc39.css → SettingsSection-DDWn_EGW.css} +0 -0
- /flowfile/web/static/assets/{SliderInput-f2e4f23c.css → SliderInput-BRk-q_Dk.css} +0 -0
- /flowfile/web/static/assets/{UnavailableFields-394a1f78.css → UnavailableFields-DRKDImKe.css} +0 -0
- /flowfile/web/static/assets/{Unique-2b705521.css → Unique-Absb0aON.css} +0 -0
- /flowfile/web/static/assets/{UnpivotValidation-d5ca3b7b.css → UnpivotValidation-DSBkFgS-.css} +0 -0
- /flowfile/web/static/assets/{airbyte-292aa232.png → airbyte-W0xvIXwZ.png} +0 -0
- /flowfile/web/static/assets/{cloud_storage_reader-aa1415d6.png → cloud_storage_reader-3GpSCk90.png} +0 -0
- /flowfile/web/static/assets/{cross_join-d30c0290.png → cross_join-B0qpgYoV.png} +0 -0
- /flowfile/web/static/assets/{dropDown-1d6acbd9.css → dropDown-CE0VF5_P.css} +0 -0
- /flowfile/web/static/assets/{explore_data-8a0a2861.png → explore_data-tX6olPPL.png} +0 -0
- /flowfile/web/static/assets/{fa-brands-400-808443ae.ttf → fa-brands-400-D1LuMI3I.ttf} +0 -0
- /flowfile/web/static/assets/{fa-brands-400-d7236a19.woff2 → fa-brands-400-D_cYUPeE.woff2} +0 -0
- /flowfile/web/static/assets/{fa-regular-400-e3456d12.woff2 → fa-regular-400-BjRzuEpd.woff2} +0 -0
- /flowfile/web/static/assets/{fa-regular-400-54cf6086.ttf → fa-regular-400-DZaxPHgR.ttf} +0 -0
- /flowfile/web/static/assets/{fa-solid-900-aa759986.woff2 → fa-solid-900-CTAAxXor.woff2} +0 -0
- /flowfile/web/static/assets/{fa-solid-900-d2f05935.ttf → fa-solid-900-D0aA9rwL.ttf} +0 -0
- /flowfile/web/static/assets/{fa-v4compatibility-0ce9033c.woff2 → fa-v4compatibility-C9RhG_FT.woff2} +0 -0
- /flowfile/web/static/assets/{fa-v4compatibility-30f6abf6.ttf → fa-v4compatibility-CCth-dXg.ttf} +0 -0
- /flowfile/web/static/assets/{filter-d7708bda.png → filter-WRdZyUOw.png} +0 -0
- /flowfile/web/static/assets/{formula-eeeb1611.png → formula-CgM7uHVI.png} +0 -0
- /flowfile/web/static/assets/{fullEditor-fe9f7e18.css → fullEditor-CmDI7T9F.css} +0 -0
- /flowfile/web/static/assets/{fuzzy_match-40c161b2.png → fuzzy_match-Yon3k5Tc.png} +0 -0
- /flowfile/web/static/assets/{graph_solver-8b7888b8.png → graph_solver-BlMrBttD.png} +0 -0
- /flowfile/web/static/assets/{group_by-80561fc3.png → group_by-Gici0CSS.png} +0 -0
- /flowfile/web/static/assets/{input_data-ab2eb678.png → input_data-BRdGecLc.png} +0 -0
- /flowfile/web/static/assets/{join-349043ae.png → join-BITWRu73.png} +0 -0
- /flowfile/web/static/assets/{manual_input-ae98f31d.png → manual_input-CFvo_EUS.png} +0 -0
- /flowfile/web/static/assets/{old_join-5d0eb604.png → old_join-B9bkpPqv.png} +0 -0
- /flowfile/web/static/assets/{output-06ec0371.png → output-Dp7-ZpC4.png} +0 -0
- /flowfile/web/static/assets/{outputExcel-f5d272b2.css → outputExcel-CKgRe2iT.css} +0 -0
- /flowfile/web/static/assets/{outputParquet-54597c3c.css → outputParquet-d7j407cK.css} +0 -0
- /flowfile/web/static/assets/{pivot-9660df51.png → pivot-DSxKhNlD.png} +0 -0
- /flowfile/web/static/assets/{polars_code-05ce5dc6.png → polars_code-DxiztZ1c.png} +0 -0
- /flowfile/web/static/assets/{readCsv-3bfac4c3.css → readCsv-BG-1Jilp.css} +0 -0
- /flowfile/web/static/assets/{readExcel-3db6b763.css → readExcel-DBQXKPtC.css} +0 -0
- /flowfile/web/static/assets/{record_count-dab44eb5.png → record_count-DCeaLtpS.png} +0 -0
- /flowfile/web/static/assets/{record_id-0b15856b.png → record_id-FeUjyIFh.png} +0 -0
- /flowfile/web/static/assets/{sample-693a88b5.png → sample-DeqfRiB-.png} +0 -0
- /flowfile/web/static/assets/{select-b0d0437a.png → select-D4JjbdjS.png} +0 -0
- /flowfile/web/static/assets/{selectDynamic-f2fb394f.css → selectDynamic-CjeTPUUo.css} +0 -0
- /flowfile/web/static/assets/{sort-2aa579f0.png → sort-DGwUG9WS.png} +0 -0
- /flowfile/web/static/assets/{summarize-2a099231.png → summarize-DFaNHpfp.png} +0 -0
- /flowfile/web/static/assets/{text_to_rows-859b29ea.png → text_to_rows-BdiAewrN.png} +0 -0
- /flowfile/web/static/assets/{union-2d8609f4.png → union-DCK-LSMq.png} +0 -0
- /flowfile/web/static/assets/{unique-1958b98a.png → unique-CdP3zZIq.png} +0 -0
- /flowfile/web/static/assets/{unpivot-d3cb4b5b.png → unpivot-CHttrEt8.png} +0 -0
- /flowfile/web/static/assets/{user-defined-icon-0ae16c90.png → user-defined-icon-BcIp2Vzo.png} +0 -0
- /flowfile/web/static/assets/{view-7a0f0be1.png → view-DUSRwjvq.png} +0 -0
- {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/entry_points.txt +0 -0
- {flowfile-0.5.4.dist-info → flowfile-0.6.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
|
|
2
2
|
import ast
|
|
3
3
|
import re
|
|
4
|
-
from typing import Dict, Any, List, Optional
|
|
5
4
|
from pathlib import Path
|
|
5
|
+
from typing import Any
|
|
6
6
|
|
|
7
|
-
from fastapi import APIRouter,
|
|
7
|
+
from fastapi import APIRouter, Depends, File, HTTPException, UploadFile
|
|
8
8
|
from fastapi.responses import FileResponse
|
|
9
9
|
from pydantic import BaseModel
|
|
10
10
|
|
|
11
11
|
from flowfile_core import flow_file_handler
|
|
12
|
+
|
|
12
13
|
# Core modules
|
|
13
14
|
from flowfile_core.auth.jwt import get_current_active_user
|
|
14
15
|
from flowfile_core.configs import logger
|
|
15
16
|
from flowfile_core.configs.node_store import (
|
|
16
17
|
CUSTOM_NODE_STORE,
|
|
17
18
|
add_to_custom_node_store,
|
|
18
|
-
remove_from_custom_node_store,
|
|
19
19
|
load_single_node_from_file,
|
|
20
|
+
remove_from_custom_node_store,
|
|
20
21
|
)
|
|
22
|
+
|
|
21
23
|
# File handling
|
|
22
24
|
from flowfile_core.schemas import input_schema
|
|
23
25
|
from flowfile_core.utils.utils import camel_case_to_snake_case
|
|
@@ -66,7 +68,7 @@ def get_simple_custom_object(flow_id: int, node_id: int):
|
|
|
66
68
|
|
|
67
69
|
|
|
68
70
|
@router.post("/update_user_defined_node", tags=["transform"])
|
|
69
|
-
def update_user_defined_node(input_data:
|
|
71
|
+
def update_user_defined_node(input_data: dict[str, Any], node_type: str, current_user=Depends(get_current_active_user)):
|
|
70
72
|
input_data['user_id'] = current_user.id
|
|
71
73
|
node_type = camel_case_to_snake_case(node_type)
|
|
72
74
|
flow_id = int(input_data.get('flow_id'))
|
|
@@ -149,7 +151,7 @@ def _extract_node_info_from_file(file_path: Path) -> CustomNodeInfo:
|
|
|
149
151
|
info = CustomNodeInfo(file_name=file_path.name)
|
|
150
152
|
|
|
151
153
|
try:
|
|
152
|
-
with open(file_path,
|
|
154
|
+
with open(file_path, encoding='utf-8') as f:
|
|
153
155
|
content = f.read()
|
|
154
156
|
|
|
155
157
|
tree = ast.parse(content)
|
|
@@ -199,14 +201,14 @@ def _extract_node_info_from_file(file_path: Path) -> CustomNodeInfo:
|
|
|
199
201
|
return info
|
|
200
202
|
|
|
201
203
|
|
|
202
|
-
@router.get("/list-custom-nodes", summary="List all custom nodes", response_model=
|
|
203
|
-
def list_custom_nodes() ->
|
|
204
|
+
@router.get("/list-custom-nodes", summary="List all custom nodes", response_model=list[CustomNodeInfo])
|
|
205
|
+
def list_custom_nodes() -> list[CustomNodeInfo]:
|
|
204
206
|
"""
|
|
205
207
|
List all custom node Python files in the user-defined nodes directory.
|
|
206
208
|
Returns basic metadata extracted from each file.
|
|
207
209
|
"""
|
|
208
210
|
nodes_dir = storage.user_defined_nodes_directory
|
|
209
|
-
nodes:
|
|
211
|
+
nodes: list[CustomNodeInfo] = []
|
|
210
212
|
|
|
211
213
|
if not nodes_dir.exists():
|
|
212
214
|
return nodes
|
|
@@ -223,7 +225,7 @@ def list_custom_nodes() -> List[CustomNodeInfo]:
|
|
|
223
225
|
|
|
224
226
|
|
|
225
227
|
@router.get("/get-custom-node/{file_name}", summary="Get custom node details")
|
|
226
|
-
def get_custom_node(file_name: str) ->
|
|
228
|
+
def get_custom_node(file_name: str) -> dict[str, Any]:
|
|
227
229
|
"""
|
|
228
230
|
Get the full content and parsed metadata of a custom node file.
|
|
229
231
|
This endpoint is used by the Node Designer to load an existing node for editing.
|
|
@@ -239,7 +241,7 @@ def get_custom_node(file_name: str) -> Dict[str, Any]:
|
|
|
239
241
|
raise HTTPException(status_code=404, detail=f"Node file '{safe_name}' not found")
|
|
240
242
|
|
|
241
243
|
try:
|
|
242
|
-
with open(file_path,
|
|
244
|
+
with open(file_path, encoding='utf-8') as f:
|
|
243
245
|
content = f.read()
|
|
244
246
|
except Exception as e:
|
|
245
247
|
raise HTTPException(status_code=500, detail=f"Failed to read file: {str(e)}")
|
|
@@ -320,7 +322,7 @@ def get_custom_node(file_name: str) -> Dict[str, Any]:
|
|
|
320
322
|
|
|
321
323
|
|
|
322
324
|
@router.delete("/delete-custom-node/{file_name}", summary="Delete a custom node")
|
|
323
|
-
def delete_custom_node(file_name: str) ->
|
|
325
|
+
def delete_custom_node(file_name: str) -> dict[str, Any]:
|
|
324
326
|
"""
|
|
325
327
|
Delete a custom node Python file from the user-defined nodes directory.
|
|
326
328
|
This also attempts to unregister the node from the node store.
|
|
@@ -384,14 +386,14 @@ ALLOWED_ICON_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.svg', '.gif', '.webp'}
|
|
|
384
386
|
MAX_ICON_SIZE = 5 * 1024 * 1024 # 5MB
|
|
385
387
|
|
|
386
388
|
|
|
387
|
-
@router.get("/list-icons", summary="List all available icons", response_model=
|
|
388
|
-
def list_icons() ->
|
|
389
|
+
@router.get("/list-icons", summary="List all available icons", response_model=list[IconInfo])
|
|
390
|
+
def list_icons() -> list[IconInfo]:
|
|
389
391
|
"""
|
|
390
392
|
List all icon files available for custom nodes.
|
|
391
393
|
Returns icons from the user_defined_nodes/icons directory.
|
|
392
394
|
"""
|
|
393
395
|
icons_dir = storage.user_defined_nodes_icons
|
|
394
|
-
icons:
|
|
396
|
+
icons: list[IconInfo] = []
|
|
395
397
|
|
|
396
398
|
if not icons_dir.exists():
|
|
397
399
|
return icons
|
|
@@ -406,7 +408,7 @@ def list_icons() -> List[IconInfo]:
|
|
|
406
408
|
|
|
407
409
|
|
|
408
410
|
@router.post("/upload-icon", summary="Upload a custom icon")
|
|
409
|
-
async def upload_icon(file: UploadFile = File(...)) ->
|
|
411
|
+
async def upload_icon(file: UploadFile = File(...)) -> dict[str, Any]:
|
|
410
412
|
"""
|
|
411
413
|
Upload a new icon file to the user_defined_nodes/icons directory.
|
|
412
414
|
|
|
@@ -507,7 +509,7 @@ def get_icon(file_name: str) -> FileResponse:
|
|
|
507
509
|
|
|
508
510
|
|
|
509
511
|
@router.delete("/delete-icon/{file_name}", summary="Delete a custom icon")
|
|
510
|
-
def delete_icon(file_name: str) ->
|
|
512
|
+
def delete_icon(file_name: str) -> dict[str, Any]:
|
|
511
513
|
"""
|
|
512
514
|
Delete a custom icon file from the icons directory.
|
|
513
515
|
"""
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Schema definitions for the undo/redo history system.
|
|
3
|
+
|
|
4
|
+
This module defines the Pydantic models for tracking flow graph history,
|
|
5
|
+
enabling users to undo and redo changes to their flow graphs.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import pickle
|
|
9
|
+
import zlib
|
|
10
|
+
from enum import Enum
|
|
11
|
+
from typing import TYPE_CHECKING
|
|
12
|
+
|
|
13
|
+
from pydantic import BaseModel, Field
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class HistoryActionType(str, Enum):
|
|
20
|
+
"""Enumeration of action types that can be tracked in history."""
|
|
21
|
+
|
|
22
|
+
ADD_NODE = "add_node"
|
|
23
|
+
DELETE_NODE = "delete_node"
|
|
24
|
+
MOVE_NODE = "move_node"
|
|
25
|
+
ADD_CONNECTION = "add_connection"
|
|
26
|
+
DELETE_CONNECTION = "delete_connection"
|
|
27
|
+
UPDATE_SETTINGS = "update_settings"
|
|
28
|
+
COPY_NODE = "copy_node"
|
|
29
|
+
PASTE_NODES = "paste_nodes"
|
|
30
|
+
APPLY_LAYOUT = "apply_layout"
|
|
31
|
+
BATCH = "batch"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class HistoryConfig(BaseModel):
|
|
35
|
+
"""Configuration for the history system."""
|
|
36
|
+
|
|
37
|
+
enabled: bool = Field(default=True, description="Whether history tracking is enabled")
|
|
38
|
+
max_stack_size: int = Field(default=50, description="Maximum number of history entries to keep")
|
|
39
|
+
use_compression: bool = Field(default=True, description="Whether to compress snapshots")
|
|
40
|
+
compression_level: int = Field(default=6, ge=1, le=9, description="Compression level (1-9)")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class CompressedSnapshot:
|
|
44
|
+
"""Efficiently stores a compressed flow state snapshot.
|
|
45
|
+
|
|
46
|
+
Uses zlib compression to reduce memory usage by 60-80%.
|
|
47
|
+
This is not a Pydantic model to avoid serialization overhead.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
__slots__ = ('_compressed_data', '_hash')
|
|
51
|
+
|
|
52
|
+
def __init__(self, snapshot_dict: dict, compression_level: int = 6):
|
|
53
|
+
"""Create a compressed snapshot from a dictionary.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
snapshot_dict: The flow state dictionary to compress.
|
|
57
|
+
compression_level: Compression level 1-9 (higher = smaller but slower).
|
|
58
|
+
"""
|
|
59
|
+
# Pickle and compress the snapshot
|
|
60
|
+
pickled = pickle.dumps(snapshot_dict, protocol=pickle.HIGHEST_PROTOCOL)
|
|
61
|
+
self._compressed_data = zlib.compress(pickled, level=compression_level)
|
|
62
|
+
|
|
63
|
+
# Pre-compute hash for fast comparison
|
|
64
|
+
self._hash = self._compute_hash(snapshot_dict)
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def _compute_hash(snapshot_dict: dict) -> int:
|
|
68
|
+
"""Compute a fast structural hash of the snapshot."""
|
|
69
|
+
nodes = snapshot_dict.get("nodes", [])
|
|
70
|
+
|
|
71
|
+
# Build tuple of node signatures for hashing
|
|
72
|
+
node_signatures = []
|
|
73
|
+
for n in sorted(nodes, key=lambda x: x.get("id", 0)):
|
|
74
|
+
sig = (
|
|
75
|
+
n.get("id"),
|
|
76
|
+
n.get("type"),
|
|
77
|
+
tuple(n.get("input_ids") or []),
|
|
78
|
+
n.get("left_input_id"),
|
|
79
|
+
n.get("right_input_id"),
|
|
80
|
+
tuple(n.get("outputs") or []),
|
|
81
|
+
n.get("x_position"),
|
|
82
|
+
n.get("y_position"),
|
|
83
|
+
# Include a hash of setting_input for change detection
|
|
84
|
+
hash(str(n.get("setting_input"))) if n.get("setting_input") else None,
|
|
85
|
+
)
|
|
86
|
+
node_signatures.append(sig)
|
|
87
|
+
|
|
88
|
+
settings = snapshot_dict.get("flowfile_settings", {})
|
|
89
|
+
settings_tuple = tuple(sorted(settings.items())) if isinstance(settings, dict) else hash(str(settings))
|
|
90
|
+
|
|
91
|
+
return hash((
|
|
92
|
+
snapshot_dict.get("flowfile_id"),
|
|
93
|
+
settings_tuple,
|
|
94
|
+
tuple(node_signatures),
|
|
95
|
+
))
|
|
96
|
+
|
|
97
|
+
def decompress(self) -> dict:
|
|
98
|
+
"""Decompress and return the original snapshot dictionary."""
|
|
99
|
+
pickled = zlib.decompress(self._compressed_data)
|
|
100
|
+
return pickle.loads(pickled)
|
|
101
|
+
|
|
102
|
+
@property
|
|
103
|
+
def hash(self) -> int:
|
|
104
|
+
"""Get the pre-computed hash for fast comparison."""
|
|
105
|
+
return self._hash
|
|
106
|
+
|
|
107
|
+
@property
|
|
108
|
+
def compressed_size(self) -> int:
|
|
109
|
+
"""Get the size of the compressed data in bytes."""
|
|
110
|
+
return len(self._compressed_data)
|
|
111
|
+
|
|
112
|
+
def __eq__(self, other: "CompressedSnapshot") -> bool:
|
|
113
|
+
"""Fast equality check using pre-computed hashes."""
|
|
114
|
+
if not isinstance(other, CompressedSnapshot):
|
|
115
|
+
return False
|
|
116
|
+
return self._hash == other._hash
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class HistoryEntry:
|
|
120
|
+
"""A single entry in the history stack.
|
|
121
|
+
|
|
122
|
+
Stores a compressed snapshot of the flow state along with metadata
|
|
123
|
+
about the action that created this entry.
|
|
124
|
+
|
|
125
|
+
Uses __slots__ for memory efficiency.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
__slots__ = ('_snapshot', 'action_type', 'description', 'timestamp', 'node_id')
|
|
129
|
+
|
|
130
|
+
def __init__(
|
|
131
|
+
self,
|
|
132
|
+
snapshot: CompressedSnapshot,
|
|
133
|
+
action_type: HistoryActionType,
|
|
134
|
+
description: str,
|
|
135
|
+
timestamp: float,
|
|
136
|
+
node_id: int | None = None,
|
|
137
|
+
):
|
|
138
|
+
self._snapshot = snapshot
|
|
139
|
+
self.action_type = action_type
|
|
140
|
+
self.description = description
|
|
141
|
+
self.timestamp = timestamp
|
|
142
|
+
self.node_id = node_id
|
|
143
|
+
|
|
144
|
+
@classmethod
|
|
145
|
+
def from_dict(
|
|
146
|
+
cls,
|
|
147
|
+
snapshot_dict: dict,
|
|
148
|
+
action_type: HistoryActionType,
|
|
149
|
+
description: str,
|
|
150
|
+
timestamp: float,
|
|
151
|
+
node_id: int | None = None,
|
|
152
|
+
compression_level: int = 6,
|
|
153
|
+
) -> "HistoryEntry":
|
|
154
|
+
"""Create a HistoryEntry from a snapshot dictionary.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
snapshot_dict: The flow state dictionary.
|
|
158
|
+
action_type: The type of action.
|
|
159
|
+
description: Human-readable description.
|
|
160
|
+
timestamp: Unix timestamp.
|
|
161
|
+
node_id: Optional affected node ID.
|
|
162
|
+
compression_level: Compression level 1-9.
|
|
163
|
+
"""
|
|
164
|
+
compressed = CompressedSnapshot(snapshot_dict, compression_level)
|
|
165
|
+
return cls(compressed, action_type, description, timestamp, node_id)
|
|
166
|
+
|
|
167
|
+
def get_snapshot(self) -> dict:
|
|
168
|
+
"""Decompress and return the snapshot dictionary."""
|
|
169
|
+
return self._snapshot.decompress()
|
|
170
|
+
|
|
171
|
+
@property
|
|
172
|
+
def snapshot_hash(self) -> int:
|
|
173
|
+
"""Get the hash of the snapshot for comparison."""
|
|
174
|
+
return self._snapshot.hash
|
|
175
|
+
|
|
176
|
+
@property
|
|
177
|
+
def compressed_size(self) -> int:
|
|
178
|
+
"""Get the compressed size in bytes."""
|
|
179
|
+
return self._snapshot.compressed_size
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class HistoryState(BaseModel):
|
|
183
|
+
"""Current state of the history system.
|
|
184
|
+
|
|
185
|
+
Provides information about what undo/redo operations are available.
|
|
186
|
+
"""
|
|
187
|
+
|
|
188
|
+
can_undo: bool = Field(default=False, description="Whether undo is available")
|
|
189
|
+
can_redo: bool = Field(default=False, description="Whether redo is available")
|
|
190
|
+
undo_description: str | None = Field(
|
|
191
|
+
default=None, description="Description of the action that would be undone"
|
|
192
|
+
)
|
|
193
|
+
redo_description: str | None = Field(
|
|
194
|
+
default=None, description="Description of the action that would be redone"
|
|
195
|
+
)
|
|
196
|
+
undo_count: int = Field(default=0, description="Number of available undo steps")
|
|
197
|
+
redo_count: int = Field(default=0, description="Number of available redo steps")
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class UndoRedoResult(BaseModel):
|
|
201
|
+
"""Result of an undo or redo operation."""
|
|
202
|
+
|
|
203
|
+
success: bool = Field(..., description="Whether the operation succeeded")
|
|
204
|
+
action_description: str | None = Field(
|
|
205
|
+
default=None, description="Description of the action that was undone/redone"
|
|
206
|
+
)
|
|
207
|
+
error_message: str | None = Field(
|
|
208
|
+
default=None, description="Error message if the operation failed"
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
class OperationResponse(BaseModel):
|
|
213
|
+
"""Standard response for operations that modify the flow graph.
|
|
214
|
+
|
|
215
|
+
Includes the current history state so the frontend can update its UI.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
success: bool = Field(default=True, description="Whether the operation succeeded")
|
|
219
|
+
message: str | None = Field(default=None, description="Optional message")
|
|
220
|
+
history: HistoryState = Field(..., description="Current history state after the operation")
|
|
@@ -25,6 +25,7 @@ from flowfile_core.schemas.yaml_types import (
|
|
|
25
25
|
NodeSelectYaml,
|
|
26
26
|
OutputSettingsYaml,
|
|
27
27
|
)
|
|
28
|
+
from flowfile_core.types import DataTypeStr
|
|
28
29
|
from flowfile_core.utils.utils import ensure_similarity_dicts, standardize_col_dtype
|
|
29
30
|
|
|
30
31
|
SecretRef = Annotated[
|
|
@@ -80,6 +81,28 @@ class MinimalFieldInfo(BaseModel):
|
|
|
80
81
|
data_type: str = "String"
|
|
81
82
|
|
|
82
83
|
|
|
84
|
+
class OutputFieldInfo(BaseModel):
|
|
85
|
+
"""Field information with optional default value for output field configuration."""
|
|
86
|
+
|
|
87
|
+
name: str
|
|
88
|
+
data_type: DataTypeStr = "String"
|
|
89
|
+
default_value: str | None = None # Can be a literal value or expression
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class OutputFieldConfig(BaseModel):
|
|
93
|
+
"""Configuration for output field validation and transformation behavior."""
|
|
94
|
+
|
|
95
|
+
enabled: bool = False
|
|
96
|
+
validation_mode_behavior: Literal[
|
|
97
|
+
"add_missing", # Add missing fields with defaults, remove extra columns
|
|
98
|
+
"add_missing_keep_extra", # Add missing fields with defaults, keep all incoming columns
|
|
99
|
+
"raise_on_missing", # Raise error if any fields are missing
|
|
100
|
+
"select_only" # Select only specified fields, skip missing silently
|
|
101
|
+
] = "select_only"
|
|
102
|
+
fields: list[OutputFieldInfo] = Field(default_factory=list)
|
|
103
|
+
validate_data_types: bool = False # Enable data type validation without casting
|
|
104
|
+
|
|
105
|
+
|
|
83
106
|
class InputTableBase(BaseModel):
|
|
84
107
|
"""Base settings for input file operations."""
|
|
85
108
|
|
|
@@ -334,9 +357,25 @@ class NodeBase(BaseModel):
|
|
|
334
357
|
pos_y: float | None = 0
|
|
335
358
|
is_setup: bool | None = True
|
|
336
359
|
description: str | None = ""
|
|
360
|
+
node_reference: str | None = None # Unique reference identifier for code generation (lowercase, no spaces)
|
|
337
361
|
user_id: int | None = None
|
|
338
362
|
is_flow_output: bool | None = False
|
|
339
363
|
is_user_defined: bool | None = False # Indicator if the node is a user defined node
|
|
364
|
+
output_field_config: OutputFieldConfig | None = None
|
|
365
|
+
|
|
366
|
+
@field_validator("node_reference", mode="before")
|
|
367
|
+
@classmethod
|
|
368
|
+
def validate_node_reference(cls, v):
|
|
369
|
+
"""Validates that node_reference is lowercase and contains no spaces."""
|
|
370
|
+
if v is None or v == "":
|
|
371
|
+
return None
|
|
372
|
+
if not isinstance(v, str):
|
|
373
|
+
raise ValueError("node_reference must be a string")
|
|
374
|
+
if " " in v:
|
|
375
|
+
raise ValueError("node_reference cannot contain spaces")
|
|
376
|
+
if v != v.lower():
|
|
377
|
+
raise ValueError("node_reference must be lowercase")
|
|
378
|
+
return v
|
|
340
379
|
|
|
341
380
|
|
|
342
381
|
class NodeSingleInput(NodeBase):
|
|
@@ -360,12 +399,27 @@ class NodeSelect(NodeSingleInput):
|
|
|
360
399
|
|
|
361
400
|
def to_yaml_dict(self) -> NodeSelectYaml:
|
|
362
401
|
"""Converts the select node settings to a dictionary for YAML serialization."""
|
|
363
|
-
|
|
364
|
-
"cache_results": self.cache_results,
|
|
402
|
+
result: NodeSelectYaml = {
|
|
403
|
+
"cache_results": bool(self.cache_results),
|
|
365
404
|
"keep_missing": self.keep_missing,
|
|
366
405
|
"select_input": [s.to_yaml_dict() for s in self.select_input],
|
|
367
406
|
"sorted_by": self.sorted_by,
|
|
368
407
|
}
|
|
408
|
+
if self.output_field_config:
|
|
409
|
+
result["output_field_config"] = {
|
|
410
|
+
"enabled": self.output_field_config.enabled,
|
|
411
|
+
"validation_mode_behavior": self.output_field_config.validation_mode_behavior,
|
|
412
|
+
"validate_data_types": self.output_field_config.validate_data_types,
|
|
413
|
+
"fields": [
|
|
414
|
+
{
|
|
415
|
+
"name": f.name,
|
|
416
|
+
"data_type": f.data_type,
|
|
417
|
+
"default_value": f.default_value,
|
|
418
|
+
}
|
|
419
|
+
for f in self.output_field_config.fields
|
|
420
|
+
],
|
|
421
|
+
}
|
|
422
|
+
return result
|
|
369
423
|
|
|
370
424
|
|
|
371
425
|
class NodeFilter(NodeSingleInput):
|
|
@@ -410,7 +464,7 @@ class NodeJoin(NodeMultiInput):
|
|
|
410
464
|
|
|
411
465
|
def to_yaml_dict(self) -> NodeJoinYaml:
|
|
412
466
|
"""Converts the join node settings to a dictionary for YAML serialization."""
|
|
413
|
-
|
|
467
|
+
result: NodeJoinYaml = {
|
|
414
468
|
"cache_results": self.cache_results,
|
|
415
469
|
"auto_generate_selection": self.auto_generate_selection,
|
|
416
470
|
"verify_integrity": self.verify_integrity,
|
|
@@ -419,6 +473,21 @@ class NodeJoin(NodeMultiInput):
|
|
|
419
473
|
"auto_keep_right": self.auto_keep_right,
|
|
420
474
|
"auto_keep_left": self.auto_keep_left,
|
|
421
475
|
}
|
|
476
|
+
if self.output_field_config:
|
|
477
|
+
result["output_field_config"] = {
|
|
478
|
+
"enabled": self.output_field_config.enabled,
|
|
479
|
+
"validation_mode_behavior": self.output_field_config.validation_mode_behavior,
|
|
480
|
+
"validate_data_types": self.output_field_config.validate_data_types,
|
|
481
|
+
"fields": [
|
|
482
|
+
{
|
|
483
|
+
"name": f.name,
|
|
484
|
+
"data_type": f.data_type,
|
|
485
|
+
"default_value": f.default_value,
|
|
486
|
+
}
|
|
487
|
+
for f in self.output_field_config.fields
|
|
488
|
+
],
|
|
489
|
+
}
|
|
490
|
+
return result
|
|
422
491
|
|
|
423
492
|
|
|
424
493
|
class NodeCrossJoin(NodeMultiInput):
|
|
@@ -433,7 +502,7 @@ class NodeCrossJoin(NodeMultiInput):
|
|
|
433
502
|
|
|
434
503
|
def to_yaml_dict(self) -> NodeCrossJoinYaml:
|
|
435
504
|
"""Converts the cross join node settings to a dictionary for YAML serialization."""
|
|
436
|
-
|
|
505
|
+
result: NodeCrossJoinYaml = {
|
|
437
506
|
"cache_results": self.cache_results,
|
|
438
507
|
"auto_generate_selection": self.auto_generate_selection,
|
|
439
508
|
"verify_integrity": self.verify_integrity,
|
|
@@ -442,6 +511,21 @@ class NodeCrossJoin(NodeMultiInput):
|
|
|
442
511
|
"auto_keep_right": self.auto_keep_right,
|
|
443
512
|
"auto_keep_left": self.auto_keep_left,
|
|
444
513
|
}
|
|
514
|
+
if self.output_field_config:
|
|
515
|
+
result["output_field_config"] = {
|
|
516
|
+
"enabled": self.output_field_config.enabled,
|
|
517
|
+
"validation_mode_behavior": self.output_field_config.validation_mode_behavior,
|
|
518
|
+
"validate_data_types": self.output_field_config.validate_data_types,
|
|
519
|
+
"fields": [
|
|
520
|
+
{
|
|
521
|
+
"name": f.name,
|
|
522
|
+
"data_type": f.data_type,
|
|
523
|
+
"default_value": f.default_value,
|
|
524
|
+
}
|
|
525
|
+
for f in self.output_field_config.fields
|
|
526
|
+
],
|
|
527
|
+
}
|
|
528
|
+
return result
|
|
445
529
|
|
|
446
530
|
|
|
447
531
|
class NodeFuzzyMatch(NodeJoin):
|
|
@@ -451,7 +535,7 @@ class NodeFuzzyMatch(NodeJoin):
|
|
|
451
535
|
|
|
452
536
|
def to_yaml_dict(self) -> NodeFuzzyMatchYaml:
|
|
453
537
|
"""Converts the fuzzy match node settings to a dictionary for YAML serialization."""
|
|
454
|
-
|
|
538
|
+
result: NodeFuzzyMatchYaml = {
|
|
455
539
|
"cache_results": self.cache_results,
|
|
456
540
|
"auto_generate_selection": self.auto_generate_selection,
|
|
457
541
|
"verify_integrity": self.verify_integrity,
|
|
@@ -460,6 +544,21 @@ class NodeFuzzyMatch(NodeJoin):
|
|
|
460
544
|
"auto_keep_right": self.auto_keep_right,
|
|
461
545
|
"auto_keep_left": self.auto_keep_left,
|
|
462
546
|
}
|
|
547
|
+
if self.output_field_config:
|
|
548
|
+
result["output_field_config"] = {
|
|
549
|
+
"enabled": self.output_field_config.enabled,
|
|
550
|
+
"validation_mode_behavior": self.output_field_config.validation_mode_behavior,
|
|
551
|
+
"validate_data_types": self.output_field_config.validate_data_types,
|
|
552
|
+
"fields": [
|
|
553
|
+
{
|
|
554
|
+
"name": f.name,
|
|
555
|
+
"data_type": f.data_type,
|
|
556
|
+
"default_value": f.default_value,
|
|
557
|
+
}
|
|
558
|
+
for f in self.output_field_config.fields
|
|
559
|
+
],
|
|
560
|
+
}
|
|
561
|
+
return result
|
|
463
562
|
|
|
464
563
|
|
|
465
564
|
class NodeDatasource(NodeBase):
|
|
@@ -485,6 +584,16 @@ class RawData(BaseModel):
|
|
|
485
584
|
columns = [MinimalFieldInfo(name=c, data_type=str(next(data_types))) for c in pylist[0].keys()]
|
|
486
585
|
return cls(columns=columns, data=values)
|
|
487
586
|
|
|
587
|
+
@classmethod
|
|
588
|
+
def from_pydict(cls, pydict: dict[str, list]):
|
|
589
|
+
"""Creates a RawData object from a dictionary of lists."""
|
|
590
|
+
if len(pydict) == 0:
|
|
591
|
+
return cls(columns=[], data=[])
|
|
592
|
+
values = [standardize_col_dtype(column_values) for column_values in pydict.values()]
|
|
593
|
+
data_types = (pl.DataType.from_python(type(next((v for v in column_values), None))) for column_values in values)
|
|
594
|
+
columns = [MinimalFieldInfo(name=c, data_type=str(next(data_types))) for c in pydict.keys()]
|
|
595
|
+
return cls(columns=columns, data=values)
|
|
596
|
+
|
|
488
597
|
def to_pylist(self) -> list[dict]:
|
|
489
598
|
"""Converts the RawData object back into a list of Python dictionaries."""
|
|
490
599
|
return [{c.name: self.data[ci][ri] for ci, c in enumerate(self.columns)} for ri in range(len(self.data[0]))]
|
|
@@ -691,10 +800,25 @@ class NodeOutput(NodeSingleInput):
|
|
|
691
800
|
|
|
692
801
|
def to_yaml_dict(self) -> NodeOutputYaml:
|
|
693
802
|
"""Converts the output node settings to a dictionary for YAML serialization."""
|
|
694
|
-
|
|
803
|
+
result: NodeOutputYaml = {
|
|
695
804
|
"cache_results": self.cache_results,
|
|
696
805
|
"output_settings": self.output_settings.to_yaml_dict(),
|
|
697
806
|
}
|
|
807
|
+
if self.output_field_config:
|
|
808
|
+
result["output_field_config"] = {
|
|
809
|
+
"enabled": self.output_field_config.enabled,
|
|
810
|
+
"validation_mode_behavior": self.output_field_config.validation_mode_behavior,
|
|
811
|
+
"validate_data_types": self.output_field_config.validate_data_types,
|
|
812
|
+
"fields": [
|
|
813
|
+
{
|
|
814
|
+
"name": f.name,
|
|
815
|
+
"data_type": f.data_type,
|
|
816
|
+
"default_value": f.default_value,
|
|
817
|
+
}
|
|
818
|
+
for f in self.output_field_config.fields
|
|
819
|
+
],
|
|
820
|
+
}
|
|
821
|
+
return result
|
|
698
822
|
|
|
699
823
|
|
|
700
824
|
class NodeOutputConnection(BaseModel):
|
flowfile_core/schemas/schemas.py
CHANGED
|
@@ -106,6 +106,8 @@ class FlowGraphConfig(BaseModel):
|
|
|
106
106
|
path (str): The file path associated with the flow.
|
|
107
107
|
execution_mode (ExecutionModeLiteral): The mode of execution ('Development' or 'Performance').
|
|
108
108
|
execution_location (ExecutionLocationsLiteral): The location for execution ('local', 'remote').
|
|
109
|
+
max_parallel_workers (int): Maximum number of threads used for parallel node execution within a
|
|
110
|
+
stage. Set to 1 to disable parallelism. Defaults to 4.
|
|
109
111
|
"""
|
|
110
112
|
|
|
111
113
|
flow_id: int = Field(default_factory=create_unique_id, description="Unique identifier for the flow.")
|
|
@@ -115,6 +117,7 @@ class FlowGraphConfig(BaseModel):
|
|
|
115
117
|
path: str = ""
|
|
116
118
|
execution_mode: ExecutionModeLiteral = "Performance"
|
|
117
119
|
execution_location: ExecutionLocationsLiteral = Field(default_factory=get_global_execution_location)
|
|
120
|
+
max_parallel_workers: int = Field(default=4, ge=1, description="Max threads for parallel node execution.")
|
|
118
121
|
|
|
119
122
|
@field_validator("execution_location", mode="before")
|
|
120
123
|
def validate_and_set_execution_location(cls, v: ExecutionLocationsLiteral | None) -> ExecutionLocationsLiteral:
|
|
@@ -143,6 +146,7 @@ class FlowSettings(FlowGraphConfig):
|
|
|
143
146
|
show_detailed_progress (bool): Flag to show detailed progress during execution.
|
|
144
147
|
is_running (bool): Indicates if the flow is currently running.
|
|
145
148
|
is_canceled (bool): Indicates if the flow execution has been canceled.
|
|
149
|
+
track_history (bool): Flag to enable or disable undo/redo history tracking.
|
|
146
150
|
"""
|
|
147
151
|
|
|
148
152
|
auto_save: bool = False
|
|
@@ -150,6 +154,7 @@ class FlowSettings(FlowGraphConfig):
|
|
|
150
154
|
show_detailed_progress: bool = True
|
|
151
155
|
is_running: bool = False
|
|
152
156
|
is_canceled: bool = False
|
|
157
|
+
track_history: bool = True
|
|
153
158
|
|
|
154
159
|
@classmethod
|
|
155
160
|
def from_flow_settings_input(cls, flow_graph_config: FlowGraphConfig):
|
|
@@ -190,6 +195,7 @@ class FlowfileSettings(BaseModel):
|
|
|
190
195
|
execution_location: ExecutionLocationsLiteral = "local"
|
|
191
196
|
auto_save: bool = False
|
|
192
197
|
show_detailed_progress: bool = True
|
|
198
|
+
max_parallel_workers: int = Field(default=4, ge=1)
|
|
193
199
|
|
|
194
200
|
|
|
195
201
|
class FlowfileNode(BaseModel):
|
|
@@ -199,6 +205,7 @@ class FlowfileNode(BaseModel):
|
|
|
199
205
|
type: str
|
|
200
206
|
is_start_node: bool = False
|
|
201
207
|
description: str | None = ""
|
|
208
|
+
node_reference: str | None = None # Unique reference identifier for code generation
|
|
202
209
|
x_position: int | None = 0
|
|
203
210
|
y_position: int | None = 0
|
|
204
211
|
left_input_id: int | None = None
|
|
@@ -214,6 +221,7 @@ class FlowfileNode(BaseModel):
|
|
|
214
221
|
"pos_y",
|
|
215
222
|
"is_setup",
|
|
216
223
|
"description",
|
|
224
|
+
"node_reference",
|
|
217
225
|
"user_id",
|
|
218
226
|
"is_flow_output",
|
|
219
227
|
"is_user_defined",
|
|
@@ -286,6 +294,7 @@ class NodeInformation(BaseModel):
|
|
|
286
294
|
is_setup: bool | None = None
|
|
287
295
|
is_start_node: bool = False
|
|
288
296
|
description: str | None = ""
|
|
297
|
+
node_reference: str | None = None # Unique reference identifier for code generation
|
|
289
298
|
x_position: int | None = 0
|
|
290
299
|
y_position: int | None = 0
|
|
291
300
|
left_input_id: int | None = None
|