Flowfile 0.3.5__py3-none-any.whl → 0.3.7__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.

Potentially problematic release.


This version of Flowfile might be problematic. Click here for more details.

Files changed (145) hide show
  1. flowfile/__init__.py +27 -6
  2. flowfile/api.py +1 -0
  3. flowfile/web/__init__.py +2 -2
  4. flowfile/web/static/assets/CloudConnectionManager-2dfdce2f.css +86 -0
  5. flowfile/web/static/assets/CloudConnectionManager-c20a740f.js +783 -0
  6. flowfile/web/static/assets/CloudStorageReader-29d14fcc.css +143 -0
  7. flowfile/web/static/assets/CloudStorageReader-960b400a.js +437 -0
  8. flowfile/web/static/assets/CloudStorageWriter-49c9a4b2.css +138 -0
  9. flowfile/web/static/assets/CloudStorageWriter-e3decbdd.js +430 -0
  10. flowfile/web/static/assets/{CrossJoin-dfcf7351.js → CrossJoin-d67e2405.js} +8 -8
  11. flowfile/web/static/assets/{DatabaseConnectionSettings-b2afb1d7.js → DatabaseConnectionSettings-a81e0f7e.js} +2 -2
  12. flowfile/web/static/assets/{DatabaseManager-824a49b2.js → DatabaseManager-9ea35e84.js} +2 -2
  13. flowfile/web/static/assets/{DatabaseReader-a48124d8.js → DatabaseReader-9578bfa5.js} +9 -9
  14. flowfile/web/static/assets/{DatabaseWriter-b47cbae2.js → DatabaseWriter-19531098.js} +9 -9
  15. flowfile/web/static/assets/{ExploreData-fdfc45a4.js → ExploreData-40476474.js} +47141 -43697
  16. flowfile/web/static/assets/{ExternalSource-861b0e71.js → ExternalSource-2297ef96.js} +6 -6
  17. flowfile/web/static/assets/{Filter-f87bb897.js → Filter-f211c03a.js} +8 -8
  18. flowfile/web/static/assets/{Formula-b8cefc31.css → Formula-29f19d21.css} +10 -0
  19. flowfile/web/static/assets/{Formula-1e2ed720.js → Formula-4207ea31.js} +75 -9
  20. flowfile/web/static/assets/{FuzzyMatch-b6cc4fdd.js → FuzzyMatch-bf120df0.js} +9 -9
  21. flowfile/web/static/assets/{GraphSolver-6a371f4c.js → GraphSolver-5bb7497a.js} +5 -5
  22. flowfile/web/static/assets/{GroupBy-f7b7f472.js → GroupBy-92c81b65.js} +6 -6
  23. flowfile/web/static/assets/{Join-eec38203.js → Join-4e49a274.js} +23 -15
  24. flowfile/web/static/assets/{Join-41c0f331.css → Join-f45eff22.css} +20 -20
  25. flowfile/web/static/assets/{ManualInput-9aaa46fb.js → ManualInput-90998ae8.js} +106 -34
  26. flowfile/web/static/assets/{ManualInput-ac7b9972.css → ManualInput-a71b52c6.css} +29 -17
  27. flowfile/web/static/assets/{Output-3b2ca045.js → Output-81e3e917.js} +4 -4
  28. flowfile/web/static/assets/{Pivot-a4f5d88f.js → Pivot-a3419842.js} +6 -6
  29. flowfile/web/static/assets/{PolarsCode-49ce444f.js → PolarsCode-72710deb.js} +6 -6
  30. flowfile/web/static/assets/{Read-07acdc9a.js → Read-c4059daf.js} +6 -6
  31. flowfile/web/static/assets/{RecordCount-6a21da56.js → RecordCount-c2b5e095.js} +5 -5
  32. flowfile/web/static/assets/{RecordId-949bdc17.js → RecordId-10baf191.js} +6 -6
  33. flowfile/web/static/assets/{Sample-7afca6e1.js → Sample-3ed9a0ae.js} +5 -5
  34. flowfile/web/static/assets/{SecretManager-b41c029d.js → SecretManager-0d49c0e8.js} +2 -2
  35. flowfile/web/static/assets/{Select-32b28406.js → Select-8a02a0b3.js} +8 -8
  36. flowfile/web/static/assets/{SettingsSection-a0f15a05.js → SettingsSection-4c0f45f5.js} +1 -1
  37. flowfile/web/static/assets/{Sort-fc6ba0e2.js → Sort-f55c9f9d.js} +6 -6
  38. flowfile/web/static/assets/{TextToRows-23127596.js → TextToRows-5dbc2145.js} +8 -8
  39. flowfile/web/static/assets/{UnavailableFields-c42880a3.js → UnavailableFields-a1768e52.js} +2 -2
  40. flowfile/web/static/assets/{Union-39eecc6c.js → Union-f2aefdc9.js} +5 -5
  41. flowfile/web/static/assets/{Unique-a0e8fe61.js → Unique-46b250da.js} +8 -8
  42. flowfile/web/static/assets/{Unpivot-1e2d43f0.js → Unpivot-25ac84cc.js} +5 -5
  43. flowfile/web/static/assets/api-6ef0dcef.js +80 -0
  44. flowfile/web/static/assets/{api-44ca9e9c.js → api-a0abbdc7.js} +1 -1
  45. flowfile/web/static/assets/cloud_storage_reader-aa1415d6.png +0 -0
  46. flowfile/web/static/assets/{designer-267d44f1.js → designer-13eabd83.js} +36 -34
  47. flowfile/web/static/assets/{documentation-6c0810a2.js → documentation-b87e7f6f.js} +1 -1
  48. flowfile/web/static/assets/{dropDown-52790b15.js → dropDown-13564764.js} +1 -1
  49. flowfile/web/static/assets/{fullEditor-e272b506.js → fullEditor-fd2cd6f9.js} +2 -2
  50. flowfile/web/static/assets/{genericNodeSettings-4bdcf98e.js → genericNodeSettings-71e11604.js} +3 -3
  51. flowfile/web/static/assets/{index-e235a8bc.js → index-f6c15e76.js} +59 -22
  52. flowfile/web/static/assets/{nodeTitle-fc3fc4b7.js → nodeTitle-988d9efe.js} +3 -3
  53. flowfile/web/static/assets/{secretApi-cdc2a3fd.js → secretApi-dd636aa2.js} +1 -1
  54. flowfile/web/static/assets/{selectDynamic-96aa82cd.js → selectDynamic-af36165e.js} +3 -3
  55. flowfile/web/static/assets/{vue-codemirror.esm-25e75a08.js → vue-codemirror.esm-2847001e.js} +2 -1
  56. flowfile/web/static/assets/{vue-content-loader.es-6c4b1c24.js → vue-content-loader.es-0371da73.js} +1 -1
  57. flowfile/web/static/index.html +1 -1
  58. {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/METADATA +9 -4
  59. {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/RECORD +131 -124
  60. {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/entry_points.txt +2 -0
  61. flowfile_core/__init__.py +3 -0
  62. flowfile_core/auth/jwt.py +39 -0
  63. flowfile_core/configs/node_store/nodes.py +9 -6
  64. flowfile_core/configs/settings.py +6 -5
  65. flowfile_core/database/connection.py +63 -15
  66. flowfile_core/database/init_db.py +0 -1
  67. flowfile_core/database/models.py +49 -2
  68. flowfile_core/flowfile/code_generator/code_generator.py +472 -17
  69. flowfile_core/flowfile/connection_manager/models.py +1 -1
  70. flowfile_core/flowfile/database_connection_manager/db_connections.py +216 -2
  71. flowfile_core/flowfile/extensions.py +1 -1
  72. flowfile_core/flowfile/flow_data_engine/cloud_storage_reader.py +259 -0
  73. flowfile_core/flowfile/flow_data_engine/create/funcs.py +19 -8
  74. flowfile_core/flowfile/flow_data_engine/flow_data_engine.py +1062 -311
  75. flowfile_core/flowfile/flow_data_engine/flow_file_column/main.py +12 -2
  76. flowfile_core/flowfile/flow_data_engine/fuzzy_matching/settings_validator.py +1 -1
  77. flowfile_core/flowfile/flow_data_engine/join/__init__.py +2 -1
  78. flowfile_core/flowfile/flow_data_engine/join/utils.py +25 -0
  79. flowfile_core/flowfile/flow_data_engine/polars_code_parser.py +3 -1
  80. flowfile_core/flowfile/flow_data_engine/subprocess_operations/subprocess_operations.py +29 -22
  81. flowfile_core/flowfile/flow_data_engine/utils.py +1 -40
  82. flowfile_core/flowfile/flow_graph.py +718 -253
  83. flowfile_core/flowfile/flow_graph_utils.py +2 -2
  84. flowfile_core/flowfile/flow_node/flow_node.py +563 -117
  85. flowfile_core/flowfile/flow_node/models.py +154 -20
  86. flowfile_core/flowfile/flow_node/schema_callback.py +3 -2
  87. flowfile_core/flowfile/handler.py +2 -33
  88. flowfile_core/flowfile/manage/open_flowfile.py +1 -2
  89. flowfile_core/flowfile/sources/external_sources/__init__.py +0 -2
  90. flowfile_core/flowfile/sources/external_sources/factory.py +4 -7
  91. flowfile_core/flowfile/util/calculate_layout.py +0 -2
  92. flowfile_core/flowfile/utils.py +35 -26
  93. flowfile_core/main.py +35 -15
  94. flowfile_core/routes/cloud_connections.py +77 -0
  95. flowfile_core/routes/logs.py +2 -7
  96. flowfile_core/routes/public.py +1 -0
  97. flowfile_core/routes/routes.py +130 -90
  98. flowfile_core/routes/secrets.py +72 -14
  99. flowfile_core/schemas/__init__.py +8 -0
  100. flowfile_core/schemas/cloud_storage_schemas.py +215 -0
  101. flowfile_core/schemas/input_schema.py +121 -71
  102. flowfile_core/schemas/output_model.py +19 -3
  103. flowfile_core/schemas/schemas.py +150 -12
  104. flowfile_core/schemas/transform_schema.py +175 -35
  105. flowfile_core/utils/utils.py +40 -1
  106. flowfile_core/utils/validate_setup.py +41 -0
  107. flowfile_frame/__init__.py +9 -1
  108. flowfile_frame/cloud_storage/frame_helpers.py +39 -0
  109. flowfile_frame/cloud_storage/secret_manager.py +73 -0
  110. flowfile_frame/expr.py +28 -1
  111. flowfile_frame/expr.pyi +76 -61
  112. flowfile_frame/flow_frame.py +481 -208
  113. flowfile_frame/flow_frame.pyi +140 -91
  114. flowfile_frame/flow_frame_methods.py +160 -22
  115. flowfile_frame/group_frame.py +3 -0
  116. flowfile_frame/utils.py +25 -3
  117. flowfile_worker/external_sources/s3_source/main.py +216 -0
  118. flowfile_worker/external_sources/s3_source/models.py +142 -0
  119. flowfile_worker/funcs.py +51 -6
  120. flowfile_worker/models.py +22 -2
  121. flowfile_worker/routes.py +40 -38
  122. flowfile_worker/utils.py +1 -1
  123. test_utils/s3/commands.py +46 -0
  124. test_utils/s3/data_generator.py +292 -0
  125. test_utils/s3/demo_data_generator.py +186 -0
  126. test_utils/s3/fixtures.py +214 -0
  127. flowfile/web/static/assets/AirbyteReader-1ac35765.css +0 -314
  128. flowfile/web/static/assets/AirbyteReader-e08044e5.js +0 -922
  129. flowfile/web/static/assets/dropDownGeneric-60f56a8a.js +0 -72
  130. flowfile/web/static/assets/dropDownGeneric-895680d6.css +0 -10
  131. flowfile_core/flowfile/sources/external_sources/airbyte_sources/airbyte.py +0 -159
  132. flowfile_core/flowfile/sources/external_sources/airbyte_sources/models.py +0 -172
  133. flowfile_core/flowfile/sources/external_sources/airbyte_sources/settings.py +0 -173
  134. flowfile_core/schemas/defaults.py +0 -9
  135. flowfile_core/schemas/external_sources/airbyte_schemas.py +0 -20
  136. flowfile_core/schemas/models.py +0 -193
  137. flowfile_worker/external_sources/airbyte_sources/cache_manager.py +0 -161
  138. flowfile_worker/external_sources/airbyte_sources/main.py +0 -89
  139. flowfile_worker/external_sources/airbyte_sources/models.py +0 -133
  140. flowfile_worker/external_sources/airbyte_sources/settings.py +0 -0
  141. {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/LICENSE +0 -0
  142. {flowfile-0.3.5.dist-info → flowfile-0.3.7.dist-info}/WHEEL +0 -0
  143. {flowfile_core/flowfile/sources/external_sources/airbyte_sources → flowfile_frame/cloud_storage}/__init__.py +0 -0
  144. {flowfile_core/schemas/external_sources → flowfile_worker/external_sources/s3_source}/__init__.py +0 -0
  145. {flowfile_worker/external_sources/airbyte_sources → test_utils/s3}/__init__.py +0 -0
@@ -15,10 +15,10 @@ from flowfile_core.configs.utils import MutableBool
15
15
  DEFAULT_SERVER_HOST = "0.0.0.0"
16
16
  DEFAULT_SERVER_PORT = 63578
17
17
  DEFAULT_WORKER_PORT = 63579
18
- SINGLE_FILE_MODE: bool = os.environ.get("SINGLE_FILE_MODE", "0") == "1"
18
+ SINGLE_FILE_MODE: bool = os.environ.get("FLOWFILE_SINGLE_FILE_MODE", "0") == "1"
19
19
 
20
20
 
21
- OFFLOAD_TO_WORKER = MutableBool(True)
21
+ OFFLOAD_TO_WORKER = MutableBool(not SINGLE_FILE_MODE)
22
22
 
23
23
 
24
24
  def parse_args():
@@ -61,7 +61,7 @@ def get_default_worker_url(worker_port=None):
61
61
  worker_host = os.getenv("WORKER_HOST", None)
62
62
 
63
63
  if worker_port is None:
64
- worker_port = os.getenv("WORKER_PORT", DEFAULT_WORKER_PORT)
64
+ worker_port = os.getenv("FLOWFILE_WORKER_PORT", DEFAULT_WORKER_PORT)
65
65
 
66
66
  # Convert to int if it's a string
67
67
  worker_port = int(worker_port) if isinstance(worker_port, str) else worker_port
@@ -81,14 +81,15 @@ args = parse_args()
81
81
 
82
82
  SERVER_HOST = args.host if args.host is not None else DEFAULT_SERVER_HOST
83
83
  SERVER_PORT = args.port if args.port is not None else DEFAULT_SERVER_PORT
84
- WORKER_PORT = args.worker_port if args.worker_port is not None else int(os.getenv("WORKER_PORT", DEFAULT_WORKER_PORT))
84
+ WORKER_PORT = args.worker_port if args.worker_port is not None else int(os.getenv("FLOWFILE_WORKER_PORT",
85
+ DEFAULT_WORKER_PORT))
85
86
  WORKER_HOST = os.getenv("WORKER_HOST", "0.0.0.0" if platform.system() != "Windows" else "127.0.0.1")
86
87
 
87
88
  config = Config(".env")
88
89
  DEBUG: bool = config("DEBUG", cast=bool, default=False)
89
90
  FILE_LOCATION = config("FILE_LOCATION", cast=str, default=".\\files\\")
90
91
  AVAILABLE_RAM = config("AVAILABLE_RAM", cast=int, default=8)
91
- WORKER_URL = config("WORKER_URL", cast=str, default=get_default_worker_url(WORKER_PORT))
92
+ WORKER_URL = config("FLOWFILE_WORKER_URL", cast=str, default=get_default_worker_url(WORKER_PORT))
92
93
  IS_RUNNING_IN_DOCKER = os.getenv('RUNNING_IN_DOCKER', 'false').lower() == 'true'
93
94
  TEMP_DIR = get_temp_dir()
94
95
 
@@ -1,33 +1,69 @@
1
-
2
1
  from sqlalchemy import create_engine
3
2
  from contextlib import contextmanager
4
3
  from sqlalchemy.orm import sessionmaker
5
4
  import os
5
+ import sys
6
+ from pathlib import Path
7
+ from flowfile_core.configs import logger
8
+
9
+
10
+ def get_app_data_dir() -> Path:
11
+ """Get the appropriate application data directory for the current platform."""
12
+ app_name = "Flowfile"
13
+
14
+ if sys.platform == "win32":
15
+ # Windows: C:\Users\{username}\AppData\Local\flowfile
16
+ base_dir = os.environ.get("LOCALAPPDATA")
17
+ if not base_dir:
18
+ base_dir = os.path.join(os.path.expanduser("~"), "AppData", "Local")
19
+ elif sys.platform == "darwin":
20
+ # macOS: ~/Library/Application Support/flowfile
21
+ base_dir = os.path.join(os.path.expanduser("~"), "Library", "Application Support")
22
+ else:
23
+ # Linux: ~/.local/share/flowfile or use XDG_DATA_HOME
24
+ base_dir = os.environ.get("XDG_DATA_HOME")
25
+ if not base_dir:
26
+ base_dir = os.path.join(os.path.expanduser("~"), ".local", "share")
27
+
28
+ app_dir = Path(base_dir) / app_name
29
+
30
+ print(f"Using application data directory: {app_dir}")
31
+ app_dir.mkdir(parents=True, exist_ok=True)
32
+
33
+ return app_dir
6
34
 
7
35
 
8
36
  def get_database_url():
37
+ """Get the database URL based on the current environment."""
9
38
  if os.environ.get("TESTING") == "True":
10
- # Use a file-based test database instead of in-memory
39
+ # Use a temporary test database
11
40
  test_db_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../test_flowfile.db")
12
41
  return f"sqlite:///{test_db_path}"
13
- # elif os.environ.get("FLOWFILE_MODE") == "electron":
14
42
 
15
- return "sqlite:///./flowfile.db"
43
+ custom_db_path = os.environ.get("FLOWFILE_DB_PATH")
44
+ if custom_db_path:
45
+ # logger.error("Using database URL:", os.environ.get("FLOWFILE_DB_URL"))
46
+ return f"sqlite:///{custom_db_path}"
47
+ # Use centralized location
48
+ app_dir = get_app_data_dir()
49
+
50
+ db_path = app_dir / "flowfile.db"
51
+ logger.info(f"Using database URL: sqlite:///{db_path}")
52
+ return f"sqlite:///{db_path}"
53
+
54
+
55
+ def get_database_path() -> Path:
56
+ """Get the actual path to the database file (useful for backup/info purposes)."""
57
+ url = get_database_url()
58
+ if url.startswith("sqlite:///"):
59
+ return Path(url.replace("sqlite:///", ""))
60
+ return None
16
61
 
17
- # else:
18
- # # Use PostgreSQL for Docker mode
19
- # host = os.environ.get("DB_HOST", "localhost")
20
- # port = os.environ.get("DB_PORT", "5432")
21
- # user = os.environ.get("DB_USER", "postgres")
22
- # password = os.environ.get("DB_PASSWORD", "postgres")
23
- # db = os.environ.get("DB_NAME", "flowfile")
24
- # return f"postgresql://{user}:{password}@{host}:{port}/{db}"
25
- #
26
62
 
27
63
  # Create database engine
28
64
  engine = create_engine(
29
65
  get_database_url(),
30
- connect_args={"check_same_thread": False} if os.environ.get("FLOWFILE_MODE") == "electron" else {}
66
+ connect_args={"check_same_thread": False} if "sqlite" in get_database_url() else {}
31
67
  )
32
68
 
33
69
  # Create session factory
@@ -35,6 +71,7 @@ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
35
71
 
36
72
 
37
73
  def get_db():
74
+ """Dependency for FastAPI to get database session."""
38
75
  db = SessionLocal()
39
76
  try:
40
77
  yield db
@@ -44,8 +81,19 @@ def get_db():
44
81
 
45
82
  @contextmanager
46
83
  def get_db_context():
84
+ """Context manager for getting database session."""
47
85
  db = SessionLocal()
48
86
  try:
49
87
  yield db
50
88
  finally:
51
- db.close()
89
+ db.close()
90
+
91
+
92
+ def get_database_info():
93
+ """Get information about the current database configuration."""
94
+ return {
95
+ "url": get_database_url(),
96
+ "path": str(get_database_path()) if get_database_path() else None,
97
+ "app_data_dir": str(get_app_data_dir()),
98
+ "platform": sys.platform
99
+ }
@@ -13,7 +13,6 @@ db_models.Base.metadata.create_all(bind=engine)
13
13
 
14
14
  def create_default_local_user(db: Session):
15
15
  local_user = db.query(db_models.User).filter(db_models.User.username == "local_user").first()
16
-
17
16
  if not local_user:
18
17
  random_password = ''.join(secrets.choice(string.ascii_letters + string.digits) for _ in range(32))
19
18
  hashed_password = pwd_context.hash(random_password)
@@ -1,6 +1,6 @@
1
-
2
- from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text
1
+ from sqlalchemy import Boolean, Column, ForeignKey, Integer, String, Text, DateTime
3
2
  from sqlalchemy.ext.declarative import declarative_base
3
+ from sqlalchemy.sql import func
4
4
 
5
5
  Base = declarative_base()
6
6
 
@@ -39,3 +39,50 @@ class DatabaseConnection(Base):
39
39
  ssl_enabled = Column(Boolean, default=False)
40
40
  password_id = Column(Integer, ForeignKey("secrets.id"))
41
41
  user_id = Column(Integer, ForeignKey("users.id"))
42
+
43
+
44
+ class CloudStorageConnection(Base):
45
+ __tablename__ = "cloud_storage_connections"
46
+
47
+ id = Column(Integer, primary_key=True, index=True)
48
+ connection_name = Column(String, index=True, nullable=False)
49
+ storage_type = Column(String, nullable=False) # 's3', 'adls', 'gcs'
50
+ auth_method = Column(String, nullable=False) # 'access_key', 'iam_role', etc.
51
+
52
+ # AWS S3 fields
53
+ aws_region = Column(String, nullable=True)
54
+ aws_access_key_id = Column(String, nullable=True)
55
+ aws_secret_access_key_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
56
+ aws_session_token_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
57
+ aws_role_arn = Column(String, nullable=True)
58
+ aws_allow_unsafe_html = Column(Boolean, nullable=True)
59
+
60
+ # Azure ADLS fields
61
+ azure_account_name = Column(String, nullable=True)
62
+ azure_account_key_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
63
+ azure_tenant_id = Column(String, nullable=True)
64
+ azure_client_id = Column(String, nullable=True)
65
+ azure_client_secret_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
66
+ azure_sas_token_id = Column(Integer, ForeignKey("secrets.id"), nullable=True)
67
+
68
+ # Common fields
69
+ endpoint_url = Column(String, nullable=True)
70
+ extra_config = Column(Text, nullable=True) # JSON field for additional config
71
+ verify_ssl = Column(Boolean, default=True)
72
+
73
+ # Metadata
74
+ user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
75
+ created_at = Column(DateTime, default=func.now(), nullable=False)
76
+ updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False)
77
+
78
+
79
+ class CloudStoragePermission(Base):
80
+ __tablename__ = "cloud_storage_permissions"
81
+
82
+ id = Column(Integer, primary_key=True, index=True)
83
+ connection_id = Column(Integer, ForeignKey("cloud_storage_connections.id"), nullable=False)
84
+ resource_path = Column(String, nullable=False) # e.g., "s3://bucket-name"
85
+ can_read = Column(Boolean, default=True)
86
+ can_write = Column(Boolean, default=False)
87
+ can_delete = Column(Boolean, default=False)
88
+ can_list = Column(Boolean, default=True)