dataflow-core 2.1.15rc1__py3-none-any.whl → 2.1.15rc3__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 dataflow-core might be problematic. Click here for more details.

Files changed (35) hide show
  1. authenticator/dataflowhubauthenticator.py +19 -41
  2. dataflow/dataflow.py +45 -69
  3. dataflow/environment.py +20 -11
  4. dataflow/models/__init__.py +5 -3
  5. dataflow/models/app_types.py +8 -3
  6. dataflow/models/connection.py +5 -4
  7. dataflow/models/dataflow_zone.py +2 -3
  8. dataflow/models/environment.py +64 -21
  9. dataflow/models/git_ssh.py +2 -1
  10. dataflow/models/org_associations.py +38 -0
  11. dataflow/models/organization.py +78 -0
  12. dataflow/models/pinned_projects.py +2 -2
  13. dataflow/models/project_details.py +9 -6
  14. dataflow/models/recent_project_studio.py +1 -1
  15. dataflow/models/role.py +12 -6
  16. dataflow/models/role_server.py +2 -5
  17. dataflow/models/role_zone.py +8 -3
  18. dataflow/models/server_config.py +9 -5
  19. dataflow/models/team.py +10 -4
  20. dataflow/models/user.py +49 -12
  21. dataflow/models/user_team.py +1 -4
  22. dataflow/models/variables.py +6 -4
  23. dataflow/secrets_manager/service.py +11 -9
  24. dataflow/utils/get_current_user.py +51 -28
  25. {dataflow_core-2.1.15rc1.dist-info → dataflow_core-2.1.15rc3.dist-info}/METADATA +2 -1
  26. dataflow_core-2.1.15rc3.dist-info/RECORD +63 -0
  27. {dataflow_core-2.1.15rc1.dist-info → dataflow_core-2.1.15rc3.dist-info}/top_level.txt +1 -0
  28. dfmigration/__init__.py +0 -0
  29. dfmigration/env.py +45 -0
  30. dfmigration/versions/001_initial_baseline_migration.py +20 -0
  31. dfmigration/versions/__init__.py +0 -0
  32. dataflow/models/user_environment.py +0 -16
  33. dataflow_core-2.1.15rc1.dist-info/RECORD +0 -58
  34. {dataflow_core-2.1.15rc1.dist-info → dataflow_core-2.1.15rc3.dist-info}/WHEEL +0 -0
  35. {dataflow_core-2.1.15rc1.dist-info → dataflow_core-2.1.15rc3.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,38 @@
1
+ from sqlalchemy import Column , Integer, String, Boolean, ForeignKey, UniqueConstraint, Enum
2
+ from sqlalchemy.orm import relationship
3
+ from dataflow.db import Base
4
+ import enum
5
+ from dataflow.models.environment import EnvType
6
+
7
+ class OrganizationUser(Base):
8
+ """
9
+ Association Table between USER, ROLE, and ORGANIZATION
10
+ """
11
+ __tablename__ = "ORGANIZATION_USER"
12
+ __table_args__ = (UniqueConstraint('org_id', 'user_id', name='uq_org_user'),)
13
+
14
+ org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), primary_key=True, nullable=False)
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"), nullable=False)
17
+ active_env_short_name = Column(String, nullable=True)
18
+ active_env_type = Column(Enum(EnvType), nullable=True)
19
+ active_server_id = Column(Integer, ForeignKey('CUSTOM_SERVER.id', ondelete="SET NULL"))
20
+ show_server_page = Column(Boolean, default = True, server_default='true')
21
+ monthly_allocation = Column(Integer, nullable=True, default=0, server_default='0')
22
+
23
+ # Relationships
24
+ user = relationship("User", back_populates="org_user_assocs")
25
+ role = relationship("Role", back_populates="org_user_assocs")
26
+ organization = relationship("Organization", back_populates="org_user_assocs")
27
+
28
+ class OrganizationServer(Base):
29
+ __tablename__ = "ORGANIZATION_SERVER"
30
+
31
+ org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), primary_key=True, nullable=False)
32
+ server_id = Column(Integer, ForeignKey('SERVER_CONFIG.id', ondelete="CASCADE"), primary_key=True, nullable=False)
33
+
34
+ class OrganizationAppType(Base):
35
+ __tablename__ = "ORGANIZATION_APP_TYPE"
36
+
37
+ org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), primary_key=True, nullable=False)
38
+ app_type_id = Column(Integer, ForeignKey('APP_TYPE.id', ondelete="CASCADE"), primary_key=True, nullable=False)
@@ -0,0 +1,78 @@
1
+ from sqlalchemy import (
2
+ Column, Integer, String, Enum, DateTime, ForeignKey, Index, text
3
+ )
4
+ import uuid, enum
5
+ from sqlalchemy.dialects.postgresql import JSONB, UUID
6
+ from sqlalchemy.sql import func
7
+ from sqlalchemy.orm import relationship
8
+ from datetime import datetime
9
+ from dataflow.db import Base
10
+
11
+
12
+ class Organization(Base):
13
+ """
14
+ Organization model for the database.
15
+ """
16
+ __tablename__ = "ORGANIZATION"
17
+
18
+ id = Column(Integer, primary_key=True, autoincrement=True)
19
+ uid = Column(UUID(as_uuid=True), default=uuid.uuid4, nullable=False, unique=True, server_default=text("gen_random_uuid()"))
20
+ name = Column(String(255), nullable=False, unique=True)
21
+ invite_code = Column(String(64), nullable=False, unique=True)
22
+ email_domain = Column(String(255), nullable=False, unique=True)
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
+
26
+ # Association object link
27
+ org_user_assocs = relationship("OrganizationUser", back_populates="organization", cascade="all, delete-orphan")
28
+ custom_servers = relationship("CustomServerConfig")
29
+ onboarding_requests = relationship("UserOnboarding", back_populates="organization", cascade="all, delete-orphan")
30
+ servers = relationship("ServerConfig", secondary="ORGANIZATION_SERVER", back_populates="organizations")
31
+ apps = relationship("AppType", secondary="ORGANIZATION_APP_TYPE", back_populates="organizations")
32
+ roles = relationship("Role", cascade="all, delete-orphan")
33
+ environments = relationship("Environment", back_populates="organization")
34
+
35
+ class OnboardingStatus(enum.Enum):
36
+ pending = 'pending'
37
+ rejected = 'rejected'
38
+ accepted = 'accepted'
39
+
40
+ class OrganizationOnboarding(Base):
41
+ __tablename__ = 'ORGANIZATION_ONBOARDING'
42
+ # This prevents an org from having more than one active ('pending' or 'accepted') application
43
+ # while allowing multiple 'rejected' entries.
44
+ __table_args__ = (
45
+ Index(
46
+ 'idx_pending_org_application',
47
+ 'name',
48
+ unique=True,
49
+ postgresql_where=Column('status').in_([
50
+ OnboardingStatus.pending.value,
51
+ OnboardingStatus.accepted.value
52
+ ])
53
+ ),
54
+ )
55
+
56
+ id = Column(Integer, primary_key=True, autoincrement=True)
57
+
58
+ name = Column(String(255), nullable=False)
59
+ age = Column(Integer, nullable=True)
60
+ domain = Column(String(255), nullable=True)
61
+ no_of_employees = Column(String(50), nullable=True)
62
+ address = Column(String(500), nullable=True)
63
+
64
+ admin_first_name = Column(String(100), nullable=False)
65
+ admin_last_name = Column(String(100), nullable=True)
66
+ admin_designation = Column(String(100), nullable=False)
67
+ admin_email = Column(String(255), nullable=False, unique=True)
68
+ admin_username = Column(String(100), nullable=False, unique=True)
69
+ admin_password = Column(String(255), nullable=False)
70
+
71
+ discovery_source = Column(String(255), nullable=True)
72
+ additional_info = Column(String(1000), nullable=True)
73
+ size_of_data = Column(String(100), nullable=True)
74
+
75
+ user_id = Column(Integer, ForeignKey('USER.user_id'), nullable=False)
76
+ status = Column(Enum(OnboardingStatus), default=OnboardingStatus.pending, nullable=False)
77
+
78
+ user = relationship("User", back_populates="organization_onboarding")
@@ -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"),
@@ -1,23 +1,26 @@
1
- from sqlalchemy import Column, String, Enum, DateTime, Integer, func, ForeignKey
1
+ from sqlalchemy import Column, String, Enum, DateTime, Integer, func, ForeignKey, UniqueConstraint
2
2
  from sqlalchemy.orm import relationship
3
3
  from dataflow.db import Base
4
4
 
5
5
  class ProjectDetails(Base):
6
6
  __tablename__ = "PROJECT_DETAIL"
7
+ __table_args__ = (UniqueConstraint('org_id', 'slug', name='uq_project_org_slug'),)
7
8
 
8
9
  project_id = Column(Integer, primary_key=True, autoincrement=True)
9
10
  project_name = Column(String, nullable=False)
10
11
  git_url = Column(String)
11
12
  git_branch = Column(String, nullable=True)
12
13
  git_folder = Column(String, nullable=True)
13
- type = Column(String, ForeignKey('RUNTIME_APP_TYPE.name', ondelete="CASCADE"), nullable=False)
14
- slug = Column(String, nullable=False, unique=True)
14
+ type = Column(String, ForeignKey('APP_TYPE.name'), nullable=False)
15
+ slug = Column(String, nullable=False)
15
16
  runtime = Column(String, nullable=False)
16
- py_env = Column(String, nullable=True)
17
+ py_env = Column(Integer, nullable=True)
17
18
  launch_url = Column(String, nullable=True)
18
- 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")
19
20
  last_deployed = Column(DateTime, nullable=True)
20
21
  created_at = Column(DateTime, nullable=False, server_default=func.now())
21
22
  created_by = Column(String, nullable=False)
23
+ org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete='CASCADE'), nullable=False)
24
+ airflow_config_file = Column(String, nullable=True)
22
25
 
23
- app_type = relationship("AppType")
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'),
dataflow/models/role.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """models.py"""
2
- from sqlalchemy import Column, Integer, String, Enum
2
+ from sqlalchemy import Column, Integer, String, Enum, ForeignKey, UniqueConstraint
3
3
  from sqlalchemy.orm import relationship
4
4
  from dataflow.db import Base
5
5
  import enum
@@ -11,19 +11,25 @@ class BaseRoleField(enum.Enum):
11
11
 
12
12
  class Role(Base):
13
13
  """
14
- Table Role
14
+ Table ROLE
15
15
  """
16
16
 
17
17
  __tablename__='ROLE'
18
+ __table_args__ = (
19
+ UniqueConstraint('name', 'org_id', name='uq_role_name_org'),
20
+ )
18
21
 
19
22
  id = Column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False)
20
- name = Column(String, unique=True, nullable=False)
23
+ name = Column(String, nullable=False)
24
+ org_id = Column(Integer, ForeignKey('ORGANIZATION.id'))
21
25
  description = Column(String, nullable=True)
22
- 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)
23
27
 
24
- users = relationship("User", back_populates="role_details", cascade="all, delete-orphan")
25
- role_server_assocs = relationship("RoleServer", back_populates="role")
28
+ # Relationships
26
29
  role_zone_assocs = relationship("RoleZone", back_populates="role")
30
+ org_user_assocs = relationship("OrganizationUser", back_populates="role", cascade="all, delete-orphan")
31
+ organization = relationship("Organization", back_populates="roles")
32
+ servers = relationship("CustomServerConfig", secondary="ROLE_SERVER", back_populates="roles")
27
33
 
28
34
  def __repr__(self):
29
35
  return f"<Role(id={self.id}, name='{self.name}', base_role='{self.base_role}')>"
@@ -5,10 +5,7 @@ from dataflow.db import Base
5
5
 
6
6
  class RoleServer(Base):
7
7
  __tablename__ = 'ROLE_SERVER'
8
- __table_args__ = (UniqueConstraint('role_id', 'server_id', name='_role_server_uc'),)
8
+ __table_args__ = (UniqueConstraint('role_id', 'custom_server_id', name='_role_server_uc'),)
9
9
 
10
10
  role_id = Column(Integer, ForeignKey('ROLE.id', ondelete="CASCADE"), nullable=False, primary_key=True)
11
- server_id = Column(Integer, ForeignKey('CUSTOM_SERVER.id', ondelete="CASCADE"), nullable=False, primary_key=True)
12
-
13
- role = relationship("Role", back_populates="role_server_assocs")
14
- server = relationship("CustomServerConfig", back_populates="role_server_assocs")
11
+ custom_server_id = Column(Integer, ForeignKey('CUSTOM_SERVER.id', ondelete="CASCADE"), nullable=False, primary_key=True)
@@ -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,20 +14,24 @@ 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
+ # Relationships
23
+ organizations = relationship("Organization", secondary="ORGANIZATION_SERVER", back_populates="servers")
22
24
 
23
25
  class CustomServerConfig(Base):
24
26
  __tablename__ = "CUSTOM_SERVER"
25
27
 
26
28
  id = Column(Integer, primary_key=True, autoincrement=True)
27
29
  base_server_id = Column(Integer, ForeignKey(ServerConfig.id, ondelete="CASCADE"), nullable=False)
30
+ org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), nullable=False)
28
31
  display_name = Column(String, nullable=False, unique=True, index=True)
29
32
  description = Column(Text, nullable=True)
30
33
 
31
34
  # Relationship to the server_config table
32
35
  server_config = relationship(ServerConfig)
33
- role_server_assocs = relationship("RoleServer", back_populates="server", cascade="all, delete-orphan")
36
+ organization = relationship("Organization", back_populates="custom_servers")
37
+ roles = relationship("Role", secondary="ROLE_SERVER", back_populates="servers")
dataflow/models/team.py CHANGED
@@ -1,5 +1,5 @@
1
1
  """models.py"""
2
- from sqlalchemy import Column, Integer, String
2
+ from sqlalchemy import Column, Integer, String, ForeignKey, UniqueConstraint
3
3
  from sqlalchemy.orm import relationship
4
4
  from dataflow.db import Base
5
5
 
@@ -9,9 +9,15 @@ class Team(Base):
9
9
  """
10
10
 
11
11
  __tablename__='TEAM'
12
+ __table_args__ = (
13
+ UniqueConstraint('team_name', 'org_id', name='uc_team_name_org_id'),
14
+ )
12
15
 
13
16
  team_id = Column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False)
14
- team_name = Column(String, unique=True, nullable=False)
17
+ team_name = Column(String, nullable=False)
18
+ org_id = Column(Integer, ForeignKey('ORGANIZATION.id', ondelete="CASCADE"), nullable=False)
15
19
  description = Column(String, nullable=True)
16
-
17
- user_team_assocs = relationship("UserTeam", back_populates="team")
20
+
21
+ # relationships
22
+ users = relationship("User", secondary="USER_TEAM", back_populates="teams")
23
+ organization = relationship("Organization")
dataflow/models/user.py CHANGED
@@ -1,31 +1,68 @@
1
1
  """models.py"""
2
- from sqlalchemy import Column, Integer, String, Boolean, LargeBinary, ForeignKey
2
+ from sqlalchemy import Column, Integer, String, Boolean, LargeBinary, Enum, ForeignKey, DateTime, func
3
+ from sqlalchemy.dialects.postgresql import ENUM
4
+ from sqlalchemy import Index
3
5
  from sqlalchemy.orm import relationship
4
6
  from dataflow.db import Base
7
+ import enum
5
8
 
6
9
  class User(Base):
7
10
  """
8
11
  Table USER
9
12
  """
10
-
11
- __tablename__='USER'
13
+ __tablename__ = 'USER'
12
14
 
13
15
  user_id = Column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False)
14
16
  user_name = Column(String, unique=True, nullable=False)
15
17
  first_name = Column(String)
16
18
  last_name = Column(String)
17
19
  email = Column(String, unique=True)
18
- role_id = Column(Integer, ForeignKey('ROLE.id'), nullable=False)
19
20
  image = Column(LargeBinary)
20
21
  image_url = Column(String, nullable=True)
21
- active = Column(Boolean, nullable=False, default=True)
22
+ active = Column(Boolean, nullable=False, default=True, server_default='true')
22
23
  password = Column(String, nullable=False)
23
- active_env = Column(String)
24
- active_env_type = Column(String, nullable=True)
25
- current_server = Column(String)
26
- show_server_page = Column(Boolean, default = True)
27
- monthly_allocation = Column(Integer, nullable=True, default=0)
24
+ active_org_id = Column(Integer, ForeignKey('ORGANIZATION.id'))
25
+
26
+ # Relationships
27
+ org_user_assocs = relationship("OrganizationUser", back_populates="user", cascade="all, delete-orphan")
28
+ teams = relationship("Team", secondary="USER_TEAM", back_populates="users")
29
+ onboarding_requests = relationship("UserOnboarding", back_populates="user", cascade="all, delete-orphan")
30
+ organization_onboarding = relationship("OrganizationOnboarding", back_populates="user", cascade="all, delete-orphan")
31
+
32
+
33
+ class OnboardingStatus(enum.Enum):
34
+ pending = 'pending'
35
+ rejected = 'rejected'
36
+ accepted = 'accepted'
37
+
38
+ class UserOnboarding(Base):
39
+ """
40
+ SQLAlchemy model for the "USER_ONBOARDING" table.
41
+ This table stores user applications to organizations.
42
+ """
43
+ __tablename__ = "USER_ONBOARDING"
44
+ # This prevents a user from having more than one active ('pending' or 'accepted') application
45
+ # for a given organization, while allowing multiple 'rejected' entries.
46
+ __table_args__ = (
47
+ Index(
48
+ 'idx_pending_user_org_application',
49
+ 'user_id',
50
+ 'org_id',
51
+ unique=True,
52
+ postgresql_where=Column('status').in_([
53
+ OnboardingStatus.pending.value,
54
+ OnboardingStatus.accepted.value
55
+ ])
56
+ ),
57
+ )
28
58
 
29
- role_details = relationship("Role")
59
+ id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
60
+ user_id = Column(Integer, ForeignKey("USER.user_id", ondelete="CASCADE"), nullable=False)
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, 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())
30
65
 
31
- user_team_assocs = relationship("UserTeam", back_populates="user")
66
+ # Relationships
67
+ user = relationship("User", back_populates="onboarding_requests")
68
+ organization = relationship("Organization", back_populates="onboarding_requests")
@@ -8,7 +8,4 @@ class UserTeam(Base):
8
8
  __table_args__ = (UniqueConstraint('user_id', 'team_id', name='_user_team_uc'),)
9
9
 
10
10
  user_id = Column(Integer, ForeignKey('USER.user_id', ondelete="CASCADE"), nullable=False, primary_key=True)
11
- team_id = Column(Integer, ForeignKey('TEAM.team_id', ondelete="CASCADE"), nullable=False, primary_key=True)
12
-
13
- user = relationship("User", back_populates="user_team_assocs")
14
- team = relationship("Team", back_populates="user_team_assocs")
11
+ team_id = Column(Integer, ForeignKey('TEAM.team_id', ondelete="CASCADE"), nullable=False, primary_key=True)
@@ -14,7 +14,8 @@ class Variable(Base):
14
14
  __tablename__ = 'VARIABLE'
15
15
 
16
16
  id = Column(Integer, primary_key=True, index=True, autoincrement=True, nullable=False)
17
- key = Column(String, nullable=False)
17
+ key = Column(String, index=True, nullable=False)
18
+ org_id = Column(Integer, ForeignKey("ORGANIZATION.id"), index=True, nullable=False)
18
19
  value = Column(Text, nullable=False)
19
20
  type = Column(String, nullable=False)
20
21
  description = Column(Text, nullable=True)
@@ -24,11 +25,12 @@ class Variable(Base):
24
25
  created_at = Column(DateTime, server_default=func.now())
25
26
  updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
26
27
  created_by = Column(String, ForeignKey('USER.user_name'), nullable=True)
27
- is_active = Column(Boolean, default=True, nullable=False)
28
+ is_active = Column(Boolean, default=True, nullable=False, server_default='true')
28
29
  datatype = Column(Enum(DataType, name="data_type"), nullable=False)
29
- set_as_env = Column(Boolean, default=False, nullable=False)
30
+ set_as_env = Column(Boolean, default=False, nullable=False, server_default='false')
31
+
30
32
 
31
33
  __table_args__ = (
32
34
  CheckConstraint(type.in_(['variable', 'secret']), name='check_variable_type'),
33
- UniqueConstraint('key', 'runtime', 'slug', 'created_by', name='unique_key'),
35
+ UniqueConstraint('key', 'org_id', 'runtime', 'slug', 'created_by', name='unique_key'),
34
36
  )
@@ -117,27 +117,29 @@ class _BaseAccessor(ABC):
117
117
  # These classes implement the logic for building the vault path based on the context.
118
118
  # --------------------------------------------------------------------------
119
119
  class _RuntimeAccessor(_BaseAccessor):
120
- def __init__(self, secret_manager: SecretManager, runtime_env: str, slug: str = None):
120
+ def __init__(self, secret_manager: SecretManager, org_id: str, runtime_env: str, slug: str = None):
121
121
  super().__init__(secret_manager)
122
+ self.org_id = org_id
122
123
  self.runtime_env = runtime_env
123
124
  self.slug = slug
124
125
 
125
126
  def _get_vault_path(self, secret_type: str, key: str) -> str:
126
127
  # Special case for git-ssh in runtime context
127
128
  if secret_type == "git-ssh":
128
- return f"{self.runtime_env}-{secret_type}-{self.slug}"
129
+ return f"{self.org_id}-{self.runtime_env}-{secret_type}-{self.slug}"
129
130
 
130
131
  # Standard format for all other secret types
131
132
  context = self.slug if self.slug else "global"
132
- return f"{self.runtime_env}-{context}-{secret_type}-{key}"
133
+ return f"{self.org_id}-{self.runtime_env}-{context}-{secret_type}-{key}"
133
134
 
134
135
  class _StudioAccessor(_BaseAccessor):
135
- def __init__(self, secret_manager: SecretManager, user_name: str):
136
+ def __init__(self, secret_manager: SecretManager, org_id: str, user_name: str):
136
137
  super().__init__(secret_manager)
138
+ self.org_id = org_id
137
139
  self.user_name = user_name
138
140
 
139
141
  def _get_vault_path(self, secret_type: str, key: str) -> str:
140
- return f"{self.user_name}-{secret_type}-{key}"
142
+ return f"{self.org_id}-{self.user_name}-{secret_type}-{key}"
141
143
 
142
144
  # --------------------------------------------------------------------------
143
145
  # PUBLIC INTERFACE CLASS
@@ -147,10 +149,10 @@ class SecretsService:
147
149
  def __init__(self, secret_manager: SecretManager):
148
150
  self._secret_manager = secret_manager
149
151
 
150
- def runtime(self, env: str, slug: str = None) -> _RuntimeAccessor:
152
+ def runtime(self, org_id: int, env: str, slug: str = None) -> _RuntimeAccessor:
151
153
  """Sets the context to RUNTIME and returns the appropriate accessor."""
152
- return _RuntimeAccessor(self._secret_manager, env, slug)
154
+ return _RuntimeAccessor(self._secret_manager, str(org_id), env, slug)
153
155
 
154
- def studio(self, user: str) -> _StudioAccessor:
156
+ def studio(self, org_id: int, user: str) -> _StudioAccessor:
155
157
  """Sets the context to STUDIO and returns the appropriate accessor."""
156
- return _StudioAccessor(self._secret_manager, user)
158
+ return _StudioAccessor(self._secret_manager, str(org_id), user)
@@ -1,37 +1,60 @@
1
- from fastapi import HTTPException, status
1
+ from fastapi import HTTPException
2
2
  from sqlalchemy.orm import Session
3
- from dataflow.models import user as m_user
4
- from dataflow.models import session as m_session
3
+ from sqlalchemy import and_
4
+ from dataflow.models import (
5
+ user as m_user,
6
+ session as m_session,
7
+ org_associations as m_org_associations,
8
+ role as m_role,
9
+ organization as m_organization
10
+ )
5
11
 
6
12
  def get_user_from_session(session_id: str, db: Session):
7
13
  """
8
- Retrieve a user based on session ID.
9
-
10
- Args:
11
- session_id (str): The unique session identifier
12
- db (Session): Database session
13
-
14
- Returns:
15
- User: User object if found
16
-
17
- Raises:
18
- HTTPException: If session is invalid or user not found
14
+ Retrieve user information based on the session ID.
19
15
  """
20
- session_record = db.query(m_session.Session).filter(m_session.Session.session_id == session_id).first()
21
- if not session_record:
22
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid session")
16
+ user = (
17
+ db.query(
18
+ m_user.User,
19
+ m_org_associations.OrganizationUser.role_id,
20
+ m_role.Role.name,
21
+ m_role.Role.base_role,
22
+ m_org_associations.OrganizationUser.active_server_id,
23
+ m_org_associations.OrganizationUser.show_server_page,
24
+ m_org_associations.OrganizationUser.active_env_short_name,
25
+ m_org_associations.OrganizationUser.active_env_type,
26
+ m_org_associations.OrganizationUser.monthly_allocation,
27
+ m_organization.Organization.uid
28
+ )
29
+ .join(m_session.Session, m_session.Session.user_id == m_user.User.user_id)
30
+ .outerjoin(
31
+ m_org_associations.OrganizationUser,
32
+ and_(
33
+ m_org_associations.OrganizationUser.user_id == m_user.User.user_id,
34
+ m_org_associations.OrganizationUser.org_id == m_user.User.active_org_id
35
+ )
36
+ )
37
+ .outerjoin(m_role.Role, m_role.Role.id == m_org_associations.OrganizationUser.role_id)
38
+ .outerjoin(
39
+ m_organization.Organization,
40
+ m_organization.Organization.id == m_user.User.active_org_id # join to Organization
41
+ )
42
+ .filter(m_session.Session.session_id == session_id)
43
+ .first()
44
+ )
23
45
 
24
- user = db.query(m_user.User).filter(m_user.User.user_id == session_record.user_id).first()
25
46
  if not user:
26
- raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not found")
27
-
28
- base_role = user.role_details.base_role
29
- role_id = user.role_details.id
30
- role_name = user.role_details.name
31
- user.base_role = base_role
32
- user.role = role_name
33
- user.role_id = role_id
34
-
35
- return user
47
+ raise HTTPException(status_code=401, detail="Invalid session")
36
48
 
49
+ user_obj, role_id, role_name, base_role, active_server_id, show_server_page, active_env_short_name, active_env_type, monthly_allocation, uid = user
50
+ user_obj.role_id = role_id
51
+ user_obj.role = role_name
52
+ user_obj.base_role = base_role
53
+ user_obj.current_server_id = active_server_id
54
+ user_obj.show_server_page = show_server_page
55
+ user_obj.active_env = active_env_short_name
56
+ user_obj.active_env_type = active_env_type
57
+ user_obj.monthly_allocation = monthly_allocation
58
+ user_obj.org_uid = str(uid)
37
59
 
60
+ return user_obj
@@ -1,10 +1,11 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataflow-core
3
- Version: 2.1.15rc1
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