dataflow-core 2.1.15rc2__tar.gz → 2.1.15rc3__tar.gz

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 dataflow-core might be problematic. Click here for more details.

Files changed (68) hide show
  1. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/PKG-INFO +2 -1
  2. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/__init__.py +1 -1
  3. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/app_types.py +3 -3
  4. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/connection.py +2 -2
  5. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/dataflow_zone.py +2 -2
  6. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/environment.py +55 -11
  7. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/org_associations.py +3 -3
  8. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/organization.py +4 -5
  9. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/pinned_projects.py +2 -2
  10. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/project_details.py +2 -1
  11. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/recent_project_studio.py +1 -1
  12. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/role.py +1 -1
  13. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/role_zone.py +8 -3
  14. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/server_config.py +5 -5
  15. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/user.py +4 -4
  16. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/variables.py +3 -2
  17. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow_core.egg-info/PKG-INFO +2 -1
  18. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow_core.egg-info/SOURCES.txt +5 -1
  19. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow_core.egg-info/requires.txt +1 -0
  20. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow_core.egg-info/top_level.txt +1 -0
  21. dataflow_core-2.1.15rc3/dfmigration/__init__.py +0 -0
  22. dataflow_core-2.1.15rc3/dfmigration/env.py +45 -0
  23. dataflow_core-2.1.15rc3/dfmigration/versions/001_initial_baseline_migration.py +20 -0
  24. dataflow_core-2.1.15rc3/dfmigration/versions/__init__.py +0 -0
  25. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/setup.py +3 -2
  26. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/README.md +0 -0
  27. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/authenticator/__init__.py +0 -0
  28. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/authenticator/dataflowairflowauthenticator.py +0 -0
  29. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/authenticator/dataflowhubauthenticator.py +0 -0
  30. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/authenticator/dataflowsupersetauthenticator.py +0 -0
  31. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/__init__.py +0 -0
  32. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/configuration.py +0 -0
  33. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/database_manager.py +0 -0
  34. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/dataflow.py +0 -0
  35. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/db.py +0 -0
  36. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/environment.py +0 -0
  37. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/blacklist_library.py +0 -0
  38. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/environment_status.py +0 -0
  39. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/git_ssh.py +0 -0
  40. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/pod_activity.py +0 -0
  41. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/pod_session_history.py +0 -0
  42. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/recent_projects.py +0 -0
  43. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/role_server.py +0 -0
  44. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/session.py +0 -0
  45. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/team.py +0 -0
  46. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/models/user_team.py +0 -0
  47. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/schemas/__init__.py +0 -0
  48. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/schemas/connection.py +0 -0
  49. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/schemas/git_ssh.py +0 -0
  50. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/schemas/secret.py +0 -0
  51. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/scripts/clone_environment.sh +0 -0
  52. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/scripts/create_environment.sh +0 -0
  53. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/scripts/update_environment.sh +0 -0
  54. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/secrets_manager/__init__.py +0 -0
  55. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/secrets_manager/factory.py +0 -0
  56. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/secrets_manager/interface.py +0 -0
  57. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/secrets_manager/providers/__init__.py +0 -0
  58. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/secrets_manager/providers/aws_manager.py +0 -0
  59. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/secrets_manager/providers/azure_manager.py +0 -0
  60. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/secrets_manager/providers/gcp_manager.py +0 -0
  61. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/secrets_manager/service.py +0 -0
  62. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/utils/__init__.py +0 -0
  63. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/utils/exceptions.py +0 -0
  64. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/utils/get_current_user.py +0 -0
  65. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow/utils/logger.py +0 -0
  66. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow_core.egg-info/dependency_links.txt +0 -0
  67. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/dataflow_core.egg-info/entry_points.txt +0 -0
  68. {dataflow_core-2.1.15rc2 → dataflow_core-2.1.15rc3}/setup.cfg +0 -0
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataflow-core
3
- Version: 2.1.15rc2
3
+ Version: 2.1.15rc3
4
4
  Summary: Dataflow core package
5
5
  Author: Dataflow
6
6
  Author-email:
7
7
  Requires-Dist: sqlalchemy
8
+ Requires-Dist: alembic
8
9
  Requires-Dist: boto3
9
10
  Requires-Dist: psycopg2-binary
10
11
  Requires-Dist: pymysql
@@ -3,7 +3,7 @@
3
3
  from .role import Role
4
4
  from .user import User, UserOnboarding, OnboardingStatus
5
5
  from .team import Team
6
- from .environment import (Environment, LocalEnvironment, ArchivedEnvironment, JobLogs)
6
+ from .environment import (Environment, LocalEnvironment, ArchivedEnvironment, JobLogs, PipSource)
7
7
  from .project_details import ProjectDetails
8
8
  from .recent_projects import RecentProjects
9
9
  from .pinned_projects import PinnedProject
@@ -6,10 +6,10 @@ class AppType(Base):
6
6
  __tablename__ = "APP_TYPE"
7
7
 
8
8
  id = Column(Integer, primary_key=True, autoincrement=True, unique=True)
9
- name = Column(String, unique=True, nullable=True)
9
+ name = Column(String, unique=True, nullable=False)
10
10
  display_name = Column(String, nullable=False)
11
11
  code_based = Column(Boolean, nullable=False)
12
- studio = Column(Boolean, nullable=False, default=False)
13
- runtime = Column(Boolean, nullable=False, default=False)
12
+ studio = Column(Boolean, nullable=False, default=False, server_default='false')
13
+ runtime = Column(Boolean, nullable=False, default=False, server_default='false')
14
14
 
15
15
  organizations = relationship("Organization", secondary="ORGANIZATION_APP_TYPE", back_populates="apps")
@@ -15,11 +15,11 @@ class Connection(Base):
15
15
  conn_type = Column(String, nullable=False)
16
16
  runtime = Column(String, nullable=True)
17
17
  slug = Column(String, nullable=True)
18
- status = Column(Boolean, default=False)
18
+ status = Column(Boolean, default=False, server_default='false')
19
19
  created_by = Column(String, nullable=True)
20
20
  created_at = Column(DateTime(timezone=True), server_default=func.now())
21
21
  updated_at = Column(DateTime(timezone=True), onupdate=func.now())
22
- is_active = Column(Boolean, default=True)
22
+ is_active = Column(Boolean, default=True, server_default='true')
23
23
 
24
24
  __table_args__ = (
25
25
  UniqueConstraint('conn_id', 'org_id', 'runtime', 'slug', 'is_active', 'created_by', name='uq_active_conn_with_runtime_slug'),
@@ -8,9 +8,9 @@ class DataflowZone(Base):
8
8
  id = Column(Integer, primary_key=True, autoincrement=True)
9
9
  slug = Column(String, unique=True, nullable=False)
10
10
  display_name = Column(String, nullable=False)
11
- is_runtime = Column(Boolean, default=False)
11
+ is_runtime = Column(Boolean, default=False, server_default='false')
12
12
  subdomain = Column(String)
13
- display_order = Column(Integer, default=0)
13
+ display_order = Column(Integer, default=0, server_default='0')
14
14
 
15
15
  role_zone_assocs = relationship("RoleZone", back_populates="zone")
16
16
 
@@ -1,5 +1,8 @@
1
- from sqlalchemy import Column, Integer, String, Boolean, Text, ForeignKey, DateTime, UniqueConstraint
2
- from sqlalchemy.orm import relationship
1
+ from sqlalchemy import (
2
+ Column, Integer, String, Boolean, Text,
3
+ ForeignKey, DateTime, UniqueConstraint, CheckConstraint
4
+ )
5
+ from sqlalchemy.orm import relationship, Session
3
6
  from sqlalchemy.sql import func
4
7
  from datetime import datetime, timezone
5
8
  from dataflow.db import Base
@@ -11,14 +14,14 @@ class EnvironmentAttributes(Base):
11
14
  """
12
15
  __abstract__ = True
13
16
 
14
- name = Column(String)
17
+ name = Column(String, nullable=False)
15
18
  url = Column(String)
16
- enabled = Column(Boolean, default=True)
17
- version = Column(String, default=0)
18
- is_latest = Column(Boolean, default=True)
19
- base_env_id = Column(Integer, nullable=True)
19
+ enabled = Column(Boolean, default=True, server_default='true')
20
+ version = Column(String, default=0, server_default='0')
21
+ is_latest = Column(Boolean, default=True, server_default='true')
22
+ base_env_id = Column(Integer, default=None)
20
23
  short_name = Column(String(5))
21
- status = Column(String, default="Saved")
24
+ status = Column(String, default="Saved", server_default="Saved")
22
25
  icon = Column(String)
23
26
  py_version = Column(String)
24
27
  r_version = Column(String)
@@ -52,7 +55,7 @@ class JobLogs(Base):
52
55
  __table_args__ = (UniqueConstraint('log_file_name', 'org_id', name='_job_log_file_org_uc'),)
53
56
 
54
57
  id = Column(Integer, primary_key=True, index=True)
55
- created_at = Column(DateTime, default=datetime.now)
58
+ created_at = Column(DateTime, default=datetime.now, server_default=func.now())
56
59
  completed_at = Column(DateTime, nullable=True)
57
60
  log_file_name = Column(String, nullable=False)
58
61
  log_file_location = Column(String, nullable=False)
@@ -71,11 +74,52 @@ class LocalEnvironment(Base):
71
74
  py_version = Column(String)
72
75
  pip_libraries = Column(Text)
73
76
  conda_libraries = Column(Text)
74
- status = Column(String, default="Created")
77
+ status = Column(String, default="Created", server_default="Created")
75
78
  cloned_from = Column(String, nullable=True)
76
79
  updated_at = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc))
77
- need_refresh = Column(Boolean, default=False)
80
+ need_refresh = Column(Boolean, default=False, server_default='false')
78
81
 
79
82
  class EnvType(str, Enum):
80
83
  dataflow = "dataflow"
81
84
  local = "local"
85
+
86
+ class PipSource(Base):
87
+ __tablename__ = "PIP_SOURCE"
88
+
89
+ id = Column(Integer, primary_key=True, autoincrement=True)
90
+
91
+ org_id = Column(Integer, ForeignKey("ORGANIZATION.id", ondelete="CASCADE"), nullable=False, index=True)
92
+ user_name = Column(String, ForeignKey("USER.user_name", ondelete="CASCADE"), nullable=True, index=True)
93
+
94
+ name = Column(String, nullable=False)
95
+ url = Column(String, nullable=False)
96
+ is_index = Column(Boolean, default=False, nullable=False, server_default='false')
97
+
98
+ created_at = Column(DateTime, default=datetime.now(timezone.utc), nullable=False)
99
+ updated_at = Column(DateTime, default=datetime.now(timezone.utc), onupdate=datetime.now(timezone.utc), nullable=False)
100
+
101
+ __table_args__ = (
102
+ UniqueConstraint("org_id", "name", "user_name", name="uq_pip_source_per_user_org"),
103
+ CheckConstraint("NOT (is_index = TRUE AND user_name IS NOT NULL)", name="check_no_user_index_url"),
104
+ )
105
+
106
+ @classmethod
107
+ def get_org_sources(cls, session: Session, org_id: int):
108
+ """
109
+ Returns all sources for the given org (org-level).
110
+ """
111
+ return session.query(cls).filter(
112
+ cls.org_id == org_id,
113
+ cls.user_name == None
114
+ ).all()
115
+
116
+ @classmethod
117
+ def get_user_sources(cls, session: Session, org_id: int, user_name: str):
118
+ """
119
+ Returns merged sources for a user in an org (org-level + user-level personal sources).
120
+ """
121
+ return session.query(cls).filter(
122
+ cls.org_id == org_id,
123
+ ((cls.user_name == None) | (cls.user_name == user_name))
124
+ ).all()
125
+
@@ -13,12 +13,12 @@ class OrganizationUser(Base):
13
13
 
14
14
  org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), primary_key=True, nullable=False)
15
15
  user_id = Column(Integer, ForeignKey('USER.user_id', ondelete="CASCADE"), primary_key=True, nullable=False)
16
- role_id = Column(Integer, ForeignKey('ROLE.id', ondelete="SET NULL"))
16
+ role_id = Column(Integer, ForeignKey('ROLE.id', ondelete="SET NULL"), nullable=False)
17
17
  active_env_short_name = Column(String, nullable=True)
18
18
  active_env_type = Column(Enum(EnvType), nullable=True)
19
19
  active_server_id = Column(Integer, ForeignKey('CUSTOM_SERVER.id', ondelete="SET NULL"))
20
- show_server_page = Column(Boolean, default=True)
21
- monthly_allocation = Column(Integer, default=0)
20
+ show_server_page = Column(Boolean, default = True, server_default='true')
21
+ monthly_allocation = Column(Integer, nullable=True, default=0, server_default='0')
22
22
 
23
23
  # Relationships
24
24
  user = relationship("User", back_populates="org_user_assocs")
@@ -1,5 +1,5 @@
1
1
  from sqlalchemy import (
2
- Column, Integer, String, Enum, DateTime, ForeignKey, Index
2
+ Column, Integer, String, Enum, DateTime, ForeignKey, Index, text
3
3
  )
4
4
  import uuid, enum
5
5
  from sqlalchemy.dialects.postgresql import JSONB, UUID
@@ -16,12 +16,12 @@ class Organization(Base):
16
16
  __tablename__ = "ORGANIZATION"
17
17
 
18
18
  id = Column(Integer, primary_key=True, autoincrement=True)
19
- uid = Column(UUID(as_uuid=True), default=uuid.uuid4, nullable=False, unique=True)
19
+ uid = Column(UUID(as_uuid=True), default=uuid.uuid4, nullable=False, unique=True, server_default=text("gen_random_uuid()"))
20
20
  name = Column(String(255), nullable=False, unique=True)
21
21
  invite_code = Column(String(64), nullable=False, unique=True)
22
22
  email_domain = Column(String(255), nullable=False, unique=True)
23
- spark_enabled_zones = Column(JSONB, default=func.json([])) # List of zone IDs where Spark is enabled
24
- created_at = Column(DateTime, default=datetime.utcnow)
23
+ spark_enabled_zones = Column(JSONB, default=func.json([]), server_default=text("'[]'::jsonb")) # List of zone IDs where Spark is enabled
24
+ created_at = Column(DateTime, default=datetime.utcnow, server_default=func.now())
25
25
 
26
26
  # Association object link
27
27
  org_user_assocs = relationship("OrganizationUser", back_populates="organization", cascade="all, delete-orphan")
@@ -68,7 +68,6 @@ class OrganizationOnboarding(Base):
68
68
  admin_username = Column(String(100), nullable=False, unique=True)
69
69
  admin_password = Column(String(255), nullable=False)
70
70
 
71
- # New fields
72
71
  discovery_source = Column(String(255), nullable=True)
73
72
  additional_info = Column(String(1000), nullable=True)
74
73
  size_of_data = Column(String(100), nullable=True)
@@ -1,4 +1,4 @@
1
- from sqlalchemy import Column, Integer, ForeignKey, DateTime, UniqueConstraint
1
+ from sqlalchemy import Column, Integer, ForeignKey, DateTime, UniqueConstraint, func
2
2
  from dataflow.db import Base
3
3
  from datetime import datetime
4
4
 
@@ -8,7 +8,7 @@ class PinnedProject(Base):
8
8
  id = Column(Integer, primary_key=True)
9
9
  user_id = Column(Integer, ForeignKey('USER.user_id', ondelete="CASCADE"), index=True)
10
10
  project_id = Column(Integer, ForeignKey('PROJECT_DETAIL.project_id', ondelete="CASCADE"), index=True)
11
- pinned_at = Column(DateTime, default=datetime.utcnow)
11
+ pinned_at = Column(DateTime, default=datetime.utcnow, server_default=func.now())
12
12
 
13
13
  __table_args__ = (
14
14
  UniqueConstraint("user_id", "project_id", name="uix_user_project"),
@@ -16,10 +16,11 @@ class ProjectDetails(Base):
16
16
  runtime = Column(String, nullable=False)
17
17
  py_env = Column(Integer, nullable=True)
18
18
  launch_url = Column(String, nullable=True)
19
- status = Column(Enum("pending", "created" ,"deployed", "stopped", "failed", name="deployment_status"), default="created")
19
+ status = Column(Enum("pending", "created" ,"deployed", "stopped", "failed", name="deployment_status"), default="created", server_default="created")
20
20
  last_deployed = Column(DateTime, nullable=True)
21
21
  created_at = Column(DateTime, nullable=False, server_default=func.now())
22
22
  created_by = Column(String, nullable=False)
23
23
  org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete='CASCADE'), nullable=False)
24
+ airflow_config_file = Column(String, nullable=True)
24
25
 
25
26
  app_type = relationship("AppType")
@@ -12,7 +12,7 @@ class RecentProjectStudio(Base):
12
12
  project_name = Column(String, nullable=False)
13
13
  project_path = Column(String, nullable=False)
14
14
  last_opened_date = Column(DateTime, server_default=func.now(), nullable=False)
15
- remember = Column(Boolean, default=False)
15
+ remember = Column(Boolean, default=False, server_default='false')
16
16
 
17
17
  __table_args__ = (
18
18
  UniqueConstraint(user_name, project_path, app_name, name='user_name_project_path_app_name_unique'),
@@ -23,7 +23,7 @@ class Role(Base):
23
23
  name = Column(String, nullable=False)
24
24
  org_id = Column(Integer, ForeignKey('ORGANIZATION.id'))
25
25
  description = Column(String, nullable=True)
26
- base_role = Column(Enum(BaseRoleField), nullable=False, default=BaseRoleField.user)
26
+ base_role = Column(Enum(BaseRoleField), nullable=False, default=BaseRoleField.user, server_default=BaseRoleField.user.value)
27
27
 
28
28
  # Relationships
29
29
  role_zone_assocs = relationship("RoleZone", back_populates="role")
@@ -1,14 +1,19 @@
1
1
  from typing import Dict, List, Optional
2
- from sqlalchemy import Column, Integer, ForeignKey, UniqueConstraint, Boolean
2
+ from sqlalchemy import Column, Integer, ForeignKey, UniqueConstraint, Boolean, Index
3
3
  from sqlalchemy.orm import relationship
4
4
  from dataflow.db import Base
5
5
 
6
6
  class RoleZone(Base):
7
7
  __tablename__ = 'ROLE_ZONE'
8
-
8
+
9
9
  role_id = Column(Integer, ForeignKey('ROLE.id', ondelete="CASCADE"), primary_key=True)
10
10
  zone_id = Column(Integer, ForeignKey('DATAFLOW_ZONE.id', ondelete="CASCADE"), primary_key=True)
11
- is_default = Column(Boolean, default=False, nullable=False)
11
+ is_default = Column(Boolean, default=False, nullable=False, server_default='false')
12
+
13
+ __table_args__ = (
14
+ Index('idx_role_runtime_default', 'role_id', unique=True,
15
+ postgresql_where=is_default.is_(True)),
16
+ )
12
17
 
13
18
  role = relationship("Role", back_populates="role_zone_assocs")
14
19
  zone = relationship("DataflowZone", back_populates="role_zone_assocs")
@@ -1,4 +1,4 @@
1
- from sqlalchemy import Column, Integer, String, Boolean, Text, ForeignKey
1
+ from sqlalchemy import Column, Integer, String, Boolean, Text, ForeignKey, text
2
2
  from sqlalchemy.dialects.postgresql import JSONB
3
3
  from sqlalchemy.sql import func
4
4
  from sqlalchemy.orm import relationship
@@ -14,10 +14,10 @@ class ServerConfig(Base):
14
14
  ram = Column(String, nullable=False)
15
15
  cpu = Column(String, nullable=False)
16
16
  gpu = Column(String)
17
- default = Column(Boolean, default=False)
18
- tags = Column(JSONB, default=func.json([]))
17
+ default = Column(Boolean, default=False, server_default='false')
18
+ tags = Column(JSONB, default=func.json([]), server_default=text("'[]'::jsonb"))
19
19
  description = Column(Text, nullable=True)
20
- kubespawner_override = Column(JSONB, default=func.json({}))
20
+ kubespawner_override = Column(JSONB, default=func.json({}), server_default=text("'{}'::jsonb"))
21
21
 
22
22
  # Relationships
23
23
  organizations = relationship("Organization", secondary="ORGANIZATION_SERVER", back_populates="servers")
@@ -27,7 +27,7 @@ class CustomServerConfig(Base):
27
27
 
28
28
  id = Column(Integer, primary_key=True, autoincrement=True)
29
29
  base_server_id = Column(Integer, ForeignKey(ServerConfig.id, ondelete="CASCADE"), nullable=False)
30
- org_id = Column(Integer, ForeignKey('ORGANIZATION.id'), nullable=False)
30
+ org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), nullable=False)
31
31
  display_name = Column(String, nullable=False, unique=True, index=True)
32
32
  description = Column(Text, nullable=True)
33
33
 
@@ -19,7 +19,7 @@ class User(Base):
19
19
  email = Column(String, unique=True)
20
20
  image = Column(LargeBinary)
21
21
  image_url = Column(String, nullable=True)
22
- active = Column(Boolean, nullable=False, default=True)
22
+ active = Column(Boolean, nullable=False, default=True, server_default='true')
23
23
  password = Column(String, nullable=False)
24
24
  active_org_id = Column(Integer, ForeignKey('ORGANIZATION.id'))
25
25
 
@@ -59,9 +59,9 @@ class UserOnboarding(Base):
59
59
  id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
60
60
  user_id = Column(Integer, ForeignKey("USER.user_id", ondelete="CASCADE"), nullable=False)
61
61
  org_id = Column(Integer, ForeignKey("ORGANIZATION.id", ondelete="CASCADE"), nullable=False)
62
- status = Column(Enum(OnboardingStatus, name='onboarding_status'), nullable=False, default=OnboardingStatus.pending.value)
63
- created_at = Column(DateTime, default=func.now(), nullable=False)
64
- updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False)
62
+ status = Column(Enum(OnboardingStatus, name='onboarding_status'), nullable=False, default=OnboardingStatus.pending.value, server_default='pending')
63
+ created_at = Column(DateTime, default=func.now(), nullable=False, server_default=func.now())
64
+ updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False, server_default=func.now())
65
65
 
66
66
  # Relationships
67
67
  user = relationship("User", back_populates="onboarding_requests")
@@ -25,9 +25,10 @@ class Variable(Base):
25
25
  created_at = Column(DateTime, server_default=func.now())
26
26
  updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
27
27
  created_by = Column(String, ForeignKey('USER.user_name'), nullable=True)
28
- is_active = Column(Boolean, default=True, nullable=False)
28
+ is_active = Column(Boolean, default=True, nullable=False, server_default='true')
29
29
  datatype = Column(Enum(DataType, name="data_type"), nullable=False)
30
- set_as_env = Column(Boolean, default=False, nullable=False)
30
+ set_as_env = Column(Boolean, default=False, nullable=False, server_default='false')
31
+
31
32
 
32
33
  __table_args__ = (
33
34
  CheckConstraint(type.in_(['variable', 'secret']), name='check_variable_type'),
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataflow-core
3
- Version: 2.1.15rc2
3
+ Version: 2.1.15rc3
4
4
  Summary: Dataflow core package
5
5
  Author: Dataflow
6
6
  Author-email:
7
7
  Requires-Dist: sqlalchemy
8
+ Requires-Dist: alembic
8
9
  Requires-Dist: boto3
9
10
  Requires-Dist: psycopg2-binary
10
11
  Requires-Dist: pymysql
@@ -59,4 +59,8 @@ dataflow_core.egg-info/SOURCES.txt
59
59
  dataflow_core.egg-info/dependency_links.txt
60
60
  dataflow_core.egg-info/entry_points.txt
61
61
  dataflow_core.egg-info/requires.txt
62
- dataflow_core.egg-info/top_level.txt
62
+ dataflow_core.egg-info/top_level.txt
63
+ dfmigration/__init__.py
64
+ dfmigration/env.py
65
+ dfmigration/versions/001_initial_baseline_migration.py
66
+ dfmigration/versions/__init__.py
@@ -1,4 +1,5 @@
1
1
  sqlalchemy
2
+ alembic
2
3
  boto3
3
4
  psycopg2-binary
4
5
  pymysql
@@ -1,2 +1,3 @@
1
1
  authenticator
2
2
  dataflow
3
+ dfmigration
File without changes
@@ -0,0 +1,45 @@
1
+ from sqlalchemy import engine_from_config, pool
2
+ from alembic import context
3
+ import os
4
+
5
+ config = context.config
6
+ target_metadata = None
7
+
8
+ def get_url():
9
+ return os.getenv('DATABASE_URL')
10
+
11
+ def run_migrations_offline() -> None:
12
+ url = get_url()
13
+ context.configure(
14
+ url=url,
15
+ target_metadata=target_metadata,
16
+ literal_binds=True,
17
+ dialect_opts={"paramstyle": "named"},
18
+ )
19
+
20
+ with context.begin_transaction():
21
+ context.run_migrations()
22
+
23
+ def run_migrations_online() -> None:
24
+ configuration = config.get_section(config.config_ini_section) or {}
25
+ configuration['sqlalchemy.url'] = get_url()
26
+
27
+ connectable = engine_from_config(
28
+ configuration,
29
+ prefix="sqlalchemy.",
30
+ poolclass=pool.NullPool,
31
+ )
32
+
33
+ with connectable.connect() as connection:
34
+ context.configure(
35
+ connection=connection,
36
+ target_metadata=target_metadata
37
+ )
38
+
39
+ with context.begin_transaction():
40
+ context.run_migrations()
41
+
42
+ if context.is_offline_mode():
43
+ run_migrations_offline()
44
+ else:
45
+ run_migrations_online()
@@ -0,0 +1,20 @@
1
+ """Initial baseline migration
2
+
3
+ Revision ID: 001
4
+ Revises: None
5
+ Create Date: 2025-09-10 15:00:00.000000
6
+
7
+ """
8
+ from alembic import op
9
+ import sqlalchemy as sa
10
+
11
+ revision = '001'
12
+ down_revision = None
13
+ branch_labels = None
14
+ depends_on = None
15
+
16
+ def upgrade() -> None:
17
+ pass
18
+
19
+ def downgrade() -> None:
20
+ pass
@@ -14,14 +14,15 @@ class PostInstall(install):
14
14
 
15
15
  setup(
16
16
  name="dataflow-core",
17
- version="2.1.15rc2",
18
- packages=find_packages(include=["dataflow", "dataflow.*", "authenticator", "authenticator.*"]),
17
+ version="2.1.15rc3",
18
+ packages=find_packages(include=["dataflow", "dataflow.*", "authenticator", "authenticator.*", "dfmigration", "dfmigration.*"]),
19
19
  include_package_data=True,
20
20
  package_data={
21
21
  "dataflow": ["scripts/*.sh"],
22
22
  },
23
23
  install_requires=[
24
24
  'sqlalchemy',
25
+ 'alembic',
25
26
  'boto3',
26
27
  'psycopg2-binary',
27
28
  'pymysql',