dataflow-core 2.1.14rc1__py3-none-any.whl → 2.1.15rc2__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.

@@ -0,0 +1,332 @@
1
+ import os, base64, json, atexit
2
+ from pathlib import Path
3
+ from google.cloud import secretmanager
4
+ from google.cloud.secretmanager_v1 import SecretManagerServiceClient
5
+ from google.cloud.secretmanager_v1.types import Secret, SecretPayload
6
+ from google.api_core.exceptions import (
7
+ AlreadyExists,
8
+ NotFound,
9
+ PermissionDenied,
10
+ Forbidden,
11
+ ResourceExhausted,
12
+ InvalidArgument,
13
+ FailedPrecondition
14
+ )
15
+ import json
16
+ from ..interface import SecretManager
17
+ from ...utils.exceptions import (
18
+ SecretNotFoundException,
19
+ SecretAlreadyExistsException,
20
+ SecretManagerAuthException,
21
+ SecretManagerServiceException
22
+ )
23
+
24
+ def _setup_gcp_credentials():
25
+ """Setup GCP credentials from base64 encoded JSON environment variable"""
26
+
27
+ # Only run if GOOGLE_APPLICATION_CREDENTIALS is not already set
28
+ if os.getenv('GOOGLE_APPLICATION_CREDENTIALS'):
29
+ return
30
+
31
+ # Get base64 encoded JSON credentials
32
+ encoded_json = os.getenv('GOOGLE_APPLICATION_CREDENTIALS_JSON')
33
+
34
+ if encoded_json:
35
+ try:
36
+ # Decode base64 to JSON string
37
+ json_credentials = base64.b64decode(encoded_json).decode('utf-8')
38
+
39
+ # Validate it's valid JSON
40
+ json.loads(json_credentials) # Just to validate
41
+
42
+ # Create credentials file in home directory
43
+ home_dir = Path.home()
44
+ credentials_dir = home_dir / '.gcp'
45
+ credentials_dir.mkdir(exist_ok=True) # Create .gcp directory if it doesn't exist
46
+
47
+ credentials_path = credentials_dir / 'credentials.json'
48
+
49
+ # Write JSON string to credentials file
50
+ with open(credentials_path, 'w') as f:
51
+ f.write(json_credentials)
52
+
53
+ # Set the standard Google environment variable that the SDK looks for
54
+ os.environ['GOOGLE_APPLICATION_CREDENTIALS'] = str(credentials_path)
55
+
56
+ # Clean up file on exit
57
+ atexit.register(lambda: credentials_path.unlink() if credentials_path.exists() else None)
58
+
59
+ print(f"GCP credentials decoded and configured at {credentials_path}")
60
+
61
+ except Exception as e:
62
+ print(f"Error setting up GCP credentials: {e}")
63
+
64
+
65
+ class GCPSecretsManager(SecretManager):
66
+ """Google Cloud Platform Secrets Manager implementation."""
67
+
68
+ def __init__(self, project_id: str, region: str):
69
+ """Initialize the GCP Secret Manager client.
70
+
71
+ Args:
72
+ project_id: The GCP project ID where secrets will be stored.
73
+ region: The GCP region where secrets will be stored.
74
+ """
75
+ self.project_id = project_id
76
+ self.region = region
77
+ try:
78
+ _setup_gcp_credentials()
79
+ self.client = secretmanager.SecretManagerServiceClient()
80
+ except PermissionDenied as e:
81
+ raise SecretManagerAuthException("initialize_gcp_client", original_error=str(e))
82
+ except Exception as e:
83
+ raise SecretManagerServiceException("initialize_gcp_client", original_error=str(e))
84
+
85
+ def _get_secret_path(self, vault_path: str) -> str:
86
+ """Get the full secret path in GCP format.
87
+
88
+ Args:
89
+ vault_path: The path/name of the secret.
90
+
91
+ Returns:
92
+ The full path to the secret in GCP format.
93
+ """
94
+ return f"projects/{self.project_id}/secrets/{vault_path}"
95
+
96
+ def _get_secret_version_path(self, vault_path: str, version: str = "latest") -> str:
97
+ """Get the full path to a specific secret version.
98
+
99
+ Args:
100
+ vault_path: The path/name of the secret.
101
+ version: The version of the secret (default is "latest").
102
+
103
+ Returns:
104
+ The full path to the secret version in GCP format.
105
+ """
106
+ return f"{self._get_secret_path(vault_path)}/versions/{version}"
107
+
108
+ def create_secret(self, vault_path: str, secret_data: dict) -> str:
109
+ """Create a new secret.
110
+
111
+ Args:
112
+ vault_path: The path/name of the secret.
113
+ region: The region where the secret will be stored.
114
+ secret_data: The data to store in the secret.
115
+
116
+ Returns:
117
+ A success message.
118
+
119
+ Raises:
120
+ SecretAlreadyExistsException: If the secret already exists.
121
+ SecretManagerAuthException: If there are permission issues.
122
+ SecretManagerServiceException: For other service errors.
123
+ """
124
+ try:
125
+ # Convert dictionary to JSON string before saving
126
+ secret_string = json.dumps(secret_data)
127
+
128
+ # First create the secret
129
+ parent = f"projects/{self.project_id}"
130
+ secret = Secret(
131
+ replication={
132
+ "user_managed": {
133
+ "replicas": [
134
+ {"location": self.region }
135
+ ]
136
+ }
137
+ }
138
+ )
139
+
140
+ self.client.create_secret(
141
+ request={
142
+ "parent": parent,
143
+ "secret_id": vault_path,
144
+ "secret": secret
145
+ }
146
+ )
147
+
148
+ # Then add the secret version with the data
149
+ secret_path = self._get_secret_path(vault_path)
150
+ self.client.add_secret_version(
151
+ request={
152
+ "parent": secret_path,
153
+ "payload": {"data": secret_string.encode("UTF-8")}
154
+ }
155
+ )
156
+
157
+ return "Secret created successfully"
158
+ except AlreadyExists as e:
159
+ raise SecretAlreadyExistsException("secret", vault_path, original_error=str(e))
160
+ except PermissionDenied as e:
161
+ raise SecretManagerAuthException("create_secret", original_error=str(e))
162
+ except Forbidden as e:
163
+ raise SecretManagerAuthException("create_secret", original_error=str(e))
164
+ except FailedPrecondition as e:
165
+ # Handle case where secret might be in recovery/deleted state
166
+ if "pending deletion" in str(e).lower() or "scheduled for deletion" in str(e).lower():
167
+ raise SecretAlreadyExistsException("secret", vault_path, original_error=str(e), is_scheduled_for_deletion=True)
168
+ else:
169
+ raise SecretManagerServiceException("create_secret", original_error=str(e))
170
+ except ResourceExhausted as e:
171
+ raise SecretManagerServiceException("create_secret", original_error=str(e))
172
+ except Exception as e:
173
+ raise SecretManagerServiceException("create_secret", original_error=str(e))
174
+
175
+ def get_secret_by_key(self, vault_path: str) -> dict:
176
+ """Get a secret by its key.
177
+
178
+ Args:
179
+ vault_path: The path/name of the secret.
180
+
181
+ Returns:
182
+ The secret data as a dictionary.
183
+
184
+ Raises:
185
+ SecretNotFoundException: If the secret doesn't exist.
186
+ SecretManagerAuthException: If there are permission issues.
187
+ SecretManagerServiceException: For other service errors.
188
+ """
189
+ try:
190
+ # Get the latest version of the secret
191
+ name = self._get_secret_version_path(vault_path)
192
+ response = self.client.access_secret_version(request={"name": name})
193
+
194
+ # Decode the payload and convert JSON string back to dictionary
195
+ secret_string = response.payload.data.decode("UTF-8")
196
+ secret_data = json.loads(secret_string)
197
+ return secret_data
198
+ except NotFound as e:
199
+ raise SecretNotFoundException("secret", vault_path, original_error=str(e))
200
+ except PermissionDenied as e:
201
+ raise SecretManagerAuthException("get_secret", original_error=str(e))
202
+ except Forbidden as e:
203
+ raise SecretManagerAuthException("get_secret", original_error=str(e))
204
+ except InvalidArgument as e:
205
+ raise SecretManagerServiceException("get_secret", original_error=str(e))
206
+ except json.JSONDecodeError as e:
207
+ raise SecretManagerServiceException("get_secret", original_error=str(e))
208
+ except Exception as e:
209
+ raise SecretManagerServiceException("get_secret", original_error=str(e))
210
+
211
+ def update_secret(self, vault_path: str, update_data: dict) -> str:
212
+ """Update an existing secret.
213
+
214
+ Args:
215
+ vault_path: The path/name of the secret.
216
+ update_data: The data to update in the secret.
217
+
218
+ Returns:
219
+ A success message.
220
+
221
+ Raises:
222
+ SecretNotFoundException: If the secret doesn't exist.
223
+ SecretManagerAuthException: If there are permission issues.
224
+ SecretManagerServiceException: For other service errors.
225
+ """
226
+ try:
227
+ # Get current secret data
228
+ current_data = self.get_secret_by_key(vault_path)
229
+
230
+ # Update with new data
231
+ current_data.update(update_data)
232
+
233
+ # Convert updated dictionary to JSON string
234
+ updated_string = json.dumps(current_data)
235
+
236
+ # Add a new version of the secret with updated data
237
+ secret_path = self._get_secret_path(vault_path)
238
+ self.client.add_secret_version(
239
+ request={
240
+ "parent": secret_path,
241
+ "payload": {"data": updated_string.encode("UTF-8")}
242
+ }
243
+ )
244
+
245
+ return "Secret updated successfully"
246
+ except SecretNotFoundException:
247
+ raise
248
+ except PermissionDenied as e:
249
+ raise SecretManagerAuthException("update_secret", original_error=str(e))
250
+ except Forbidden as e:
251
+ raise SecretManagerAuthException("update_secret", original_error=str(e))
252
+ except NotFound as e:
253
+ raise SecretNotFoundException("secret", vault_path, original_error=str(e))
254
+ except InvalidArgument as e:
255
+ raise SecretManagerServiceException("update_secret", original_error=str(e))
256
+ except json.JSONDecodeError as e:
257
+ raise SecretManagerServiceException("update_secret", original_error=str(e))
258
+ except Exception as e:
259
+ raise SecretManagerServiceException("update_secret", original_error=str(e))
260
+
261
+ def delete_secret(self, vault_path: str) -> str:
262
+ """Delete a secret.
263
+
264
+ Args:
265
+ vault_path: The path/name of the secret.
266
+
267
+ Returns:
268
+ A success message.
269
+
270
+ Raises:
271
+ SecretNotFoundException: If the secret doesn't exist.
272
+ SecretManagerAuthException: If there are permission issues.
273
+ SecretManagerServiceException: For other service errors.
274
+ """
275
+ try:
276
+ # Get the full path to the secret
277
+ name = self._get_secret_path(vault_path)
278
+
279
+ # For git-ssh secrets, destroy without recovery
280
+ if "git-ssh" in vault_path:
281
+ # Get all versions to destroy them permanently
282
+ versions = self.client.list_secret_versions(request={"parent": name})
283
+ for version in versions:
284
+ if version.state == secretmanager.SecretVersion.State.ENABLED:
285
+ version_name = f"{name}/versions/{version.name.split('/')[-1]}"
286
+ self.client.destroy_secret_version(request={"name": version_name})
287
+
288
+ # Delete the secret itself
289
+ self.client.delete_secret(request={"name": name})
290
+ else:
291
+ # For regular secrets, use the default 7-day recovery window
292
+ self.client.delete_secret(request={
293
+ "name": name,
294
+ # In GCP, the recovery window is configured at the service level,
295
+ # not per API call, so we don't specify it here
296
+ })
297
+
298
+ return "Secret deleted successfully"
299
+ except NotFound as e:
300
+ raise SecretNotFoundException("secret", vault_path, original_error=str(e))
301
+ except PermissionDenied as e:
302
+ raise SecretManagerAuthException("delete_secret", original_error=str(e))
303
+ except Forbidden as e:
304
+ raise SecretManagerAuthException("delete_secret", original_error=str(e))
305
+ except InvalidArgument as e:
306
+ raise SecretManagerServiceException("delete_secret", original_error=str(e))
307
+ except Exception as e:
308
+ raise SecretManagerServiceException("delete_secret", original_error=str(e))
309
+
310
+ def test_connection(self, vault_path: str) -> str:
311
+ """Test the connection to the secret manager by attempting to access a secret.
312
+
313
+ Args:
314
+ vault_path: The path/name of the secret to test.
315
+
316
+ Returns:
317
+ The status of the secret.
318
+
319
+ Raises:
320
+ SecretNotFoundException: If the secret doesn't exist.
321
+ SecretManagerAuthException: If there are permission issues.
322
+ SecretManagerServiceException: For other service errors.
323
+ """
324
+ try:
325
+ secret = self.get_secret_by_key(vault_path)
326
+ return secret.get('status', 'Unknown')
327
+ except SecretNotFoundException:
328
+ raise
329
+ except (SecretManagerAuthException, SecretManagerServiceException):
330
+ raise
331
+ except Exception as e:
332
+ raise SecretManagerServiceException("test_connection", original_error=str(e))
@@ -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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dataflow-core
3
- Version: 2.1.14rc1
3
+ Version: 2.1.15rc2
4
4
  Summary: Dataflow core package
5
5
  Author: Dataflow
6
6
  Author-email:
@@ -11,6 +11,8 @@ Requires-Dist: pymysql
11
11
  Requires-Dist: requests
12
12
  Requires-Dist: azure-identity
13
13
  Requires-Dist: azure-keyvault-secrets
14
+ Requires-Dist: google-auth
15
+ Requires-Dist: google-cloud-secret-manager
14
16
  Dynamic: author
15
17
  Dynamic: requires-dist
16
18
  Dynamic: summary
@@ -1,37 +1,38 @@
1
1
  authenticator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- authenticator/dataflowairflowauthenticator.py,sha256=Gp-QR83Dh3-z4kQ3EVBr6bY6FP88ICkok7QOO60pOPg,2467
3
- authenticator/dataflowhubauthenticator.py,sha256=wI9S-1pcav_WHUL4ibEn-HLhjOtZebQbqXTkcrXXFAA,13463
2
+ authenticator/dataflowairflowauthenticator.py,sha256=gEdCiL2yJQ7lYvAwbrjcAkccVMfehoMJldw9eU7cc2s,2243
3
+ authenticator/dataflowhubauthenticator.py,sha256=drs6M9tp0QFLzmj-eNaa6zefKat9cN0cJvwGLrSeYvQ,12531
4
4
  authenticator/dataflowsupersetauthenticator.py,sha256=NkAmDaIc-ui-qEolu4xz_UY7P_2g8111hwNjPvAOW1Q,2839
5
5
  dataflow/__init__.py,sha256=WTRg8HMpMWSgxYJ9ZGVldx4k07fAbta3mBmZ1hG9mWE,30
6
6
  dataflow/configuration.py,sha256=7To6XwH1eESiYp39eqPcswXWwrdBUdPF6xN6WnazOF0,663
7
7
  dataflow/database_manager.py,sha256=tJHMuOZ9Muskrh9t4uLRlTuFU0VkHAzoHlGP5DORIC4,899
8
- dataflow/dataflow.py,sha256=0kPDIpFgrEcK81QYeLQGb-rQTrAH-83gLpn566yvBGA,14004
8
+ dataflow/dataflow.py,sha256=EzeOMtMCqG0WJ0vld6ArKbKNFAmA9sbjBqVJ8sd2sQw,13026
9
9
  dataflow/db.py,sha256=73ojGqpCTRVTlPszD73Ozhjih_BI2KTHmazqxxL6iWk,3780
10
- dataflow/environment.py,sha256=qiyuRRPpVLVWiYccRHXnGyWMr_ZBPWzixAyDtAzxQYE,28277
11
- dataflow/models/__init__.py,sha256=5Ai-sUHtT1dtc0l4nL1fuwVbx0GcwaGQg_LXqOzWb4c,981
12
- dataflow/models/app_types.py,sha256=yE_ZB13lhpK7AZ7PyBwnQlf0RlIHYs_-vdMKx7_RMlY,379
10
+ dataflow/environment.py,sha256=7XiSojSIOnMOz5YVk-k6w1yQCKbBTTvUxv5cnn6cqJg,28830
11
+ dataflow/models/__init__.py,sha256=UKaiCxJJLXvWOqZlHmDWr9fTwBncKbnyjSIsPWKvlVI,1167
12
+ dataflow/models/app_types.py,sha256=NMHUHBoGnxYHbtlBVppNqQ6NW2T6pSedmQ6-T4t2JAc,640
13
13
  dataflow/models/blacklist_library.py,sha256=B2oi3Z8GcR_glhLAyinFk0W8c9txXvm3uOER6dY-q7I,991
14
- dataflow/models/connection.py,sha256=_VJL3KuIrm8t4lJmtunIL3-AXF9Yvi5wUolzdR3tE0E,1017
15
- dataflow/models/dataflow_zone.py,sha256=yFCvQXos5M1cU7ksbVSO338_RkT3hkdw2wr3kCJ_rec,769
16
- dataflow/models/environment.py,sha256=3W-Pvkuiufjw0MWy9F6uWSd0HDPjRNNJe3TnhpHxcGg,2920
14
+ dataflow/models/connection.py,sha256=Kv0vo4V5z7EBfJUSnXWxKOB-AXPWIte9ttauNdM5lyk,1127
15
+ dataflow/models/dataflow_zone.py,sha256=ScCM7963Rd5G4e4eFQGF0gNeg6abaWEwIgGOqoezfrg,719
16
+ dataflow/models/environment.py,sha256=Pnma45hzDdr5jQ0k6mSAH6eZQGTQWm87AnebovtVBdU,3263
17
17
  dataflow/models/environment_status.py,sha256=lvPDNUsUoTW9D97B07aKqJQHRKp4LvPM28pQDMPH1ac,536
18
- dataflow/models/git_ssh.py,sha256=W15SDypxzGOz_aZkHEnVZ6DIMVsjAsbSIXVIEt2mPYU,694
18
+ dataflow/models/git_ssh.py,sha256=xRkzK8lZ4Fa25zhE3Y9J78BiWv6xtd10YDBNf1XDKXE,797
19
+ dataflow/models/org_associations.py,sha256=Kw1zK-Rg97GBV6U61O5V57sohPTHSchalMDAelo2hUQ,1915
20
+ dataflow/models/organization.py,sha256=SGonZsaGRPu7Z9VLqFE32l68mwLdlki-OLxqdMpHtyU,3353
19
21
  dataflow/models/pinned_projects.py,sha256=I-XMQq7__XJJi2lyOdEvQEfhPRz8D6KHA6Cbavbf05o,606
20
22
  dataflow/models/pod_activity.py,sha256=4NQplUtckS4d2Uc-Iyi8PfgciblHiExbXo_VTMaukB8,751
21
23
  dataflow/models/pod_session_history.py,sha256=-O8DHtH1AtGLntJ1shI6eiciTJGL_vYvG3gfupvr0zY,778
22
- dataflow/models/project_details.py,sha256=iNb95L3UGjcC_Ws6FiUTDANCUFhYWpnsKraS6rmCyRU,1098
24
+ dataflow/models/project_details.py,sha256=pjunEIjgFW66N7kP23cVtRU37LVOnKaqT8K7WBekAVg,1260
23
25
  dataflow/models/recent_project_studio.py,sha256=m12KGCsv453C1ijHjfVD8E7cJ7Og_0N8uc7_9VlfkYw,812
24
26
  dataflow/models/recent_projects.py,sha256=OFd5MSRXVRHs9UbvUNoJBBnh9rgsJ0lwE23wm5_Hc5w,321
25
- dataflow/models/role.py,sha256=0fgLjCx7aETRwCV5SW0-4PQBWzWddWO9a_ObJA14VOY,962
26
- dataflow/models/role_server.py,sha256=mMcfjsGX1cY8hOAOBBmrZgw8ozdfuvjKJoBlR6F0Kdc,689
27
+ dataflow/models/role.py,sha256=LQoEWSc_Pm79IjNC1uFKGFfkhaZkhI3JSq6Sj6Xrm_g,1266
28
+ dataflow/models/role_server.py,sha256=Qz9VqGSpVGvfLz9_lKITUfkiuR7pmVc8SIIfYrDPDso,547
27
29
  dataflow/models/role_zone.py,sha256=uH8JheWyzUWcRECRzYuzdphUAUXeU5zRVA1Bhuz3h9w,772
28
- dataflow/models/server_config.py,sha256=8ocKT8tPen9tedO8BLPEfkWxUEIHaqvA2L-qEhrFND0,1385
30
+ dataflow/models/server_config.py,sha256=Npf4xP7zQ6eXp9kqwM9Owsm8C0e8Qw6jei2YX5k0in0,1646
29
31
  dataflow/models/session.py,sha256=c8TI6qXsM8utzp5vSQtAOXJSbgasnyu-a0qSAvA-rWs,459
30
- dataflow/models/team.py,sha256=fjkqF0N4PSwSYTgHjEQl9wuC7yumd0iOb5nNFePI6q4,488
31
- dataflow/models/user.py,sha256=-XEpulg7UAL-WDMyigPasDS61btna2mKtJPsanWA4lw,1108
32
- dataflow/models/user_environment.py,sha256=yI9NutULcLiwlycuEin6ROe6o1Sjdv_sgw2MEkJFeYg,568
33
- dataflow/models/user_team.py,sha256=r_fmKvf6JuGgiiI9TXWjVG2QZ3WOvDrOwYWVQ3r8oWo,659
34
- dataflow/models/variables.py,sha256=thWqX9KbR2x0oBI-bVcW5TV1eKf0TYNxgGmZWTUs2g4,1414
32
+ dataflow/models/team.py,sha256=u5VKOxi-0wGgxssIhXr1Ydn3X9IEAHNWxz029KaVm5g,780
33
+ dataflow/models/user.py,sha256=XSd3PuqMRzm4qglNaUF0S-9RjH_f016eZX4I0iDtq2Q,2856
34
+ dataflow/models/user_team.py,sha256=vD6BsiMfx1iIMQiT8fpwhsQ8AGuPuBO83F22Cy7j-nU,524
35
+ dataflow/models/variables.py,sha256=v6a1r5OgmKOAlyVdmOFM6Axre7DEBEsgrF4yTV5h3U0,1524
35
36
  dataflow/schemas/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
36
37
  dataflow/schemas/connection.py,sha256=ut2sqz06yOjmFKzHry92FEt7DN09Bj30GYse35__Cuw,2467
37
38
  dataflow/schemas/git_ssh.py,sha256=N1O7HM6ZbygIBZn2rKvNR0e7IM3ZJMAH6aJtjaghDr0,1283
@@ -40,18 +41,19 @@ dataflow/scripts/clone_environment.sh,sha256=Qy0GylsA3kUVUL_L1MirxIWujOFhT1tikKq
40
41
  dataflow/scripts/create_environment.sh,sha256=3FHgNplJuEZvyTsLqlCJNX9oyfXgsfqn80VZk2xtvso,828
41
42
  dataflow/scripts/update_environment.sh,sha256=2dtn2xlNi6frpig-sqlGE1_IKRbbkqYOCpf_qyMKKII,992
42
43
  dataflow/secrets_manager/__init__.py,sha256=idGqIDtYl0De2WIK9Obl-N7SDPSYtVM0D-wXfZjCiy4,559
43
- dataflow/secrets_manager/factory.py,sha256=k1sIyXBKtas1upWJpq8Mks2d8kjLAHU7CFvjeuMXXxs,2160
44
+ dataflow/secrets_manager/factory.py,sha256=LblshkGG9q2C3RHYp0QykianUtpOOQz7sBdlerutyWY,2479
44
45
  dataflow/secrets_manager/interface.py,sha256=HhrKpQrprWIbDsVfU_qc59OXmSIuHXv106OXv6-Epqc,506
45
- dataflow/secrets_manager/service.py,sha256=SSWgTXJTAwVPqMIc76cB2hR6nghNVOoMpIN9M0i7Su0,7241
46
+ dataflow/secrets_manager/service.py,sha256=A4PgiSS1bzlzXPi_GvsiepChULqAa5yONGkaU-d5MLM,7419
46
47
  dataflow/secrets_manager/providers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
47
48
  dataflow/secrets_manager/providers/aws_manager.py,sha256=16peXyKeuAjv2RVTMUjrzArPYENK9Zu7jREWVgMfScA,8671
48
49
  dataflow/secrets_manager/providers/azure_manager.py,sha256=sWOz-7ALnLt6vyM3lt14GBpzpmDnlH3hkdqtuApqkgU,9430
50
+ dataflow/secrets_manager/providers/gcp_manager.py,sha256=AJgotHZRQraxtmfJX1Z8u2Gcr7KJLRJTN_qbth3A5Xk,13738
49
51
  dataflow/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
50
52
  dataflow/utils/exceptions.py,sha256=8GRFoYZ5dPGQckVm2znaHpPi0ZAs69fK-RGKukEsapk,4432
51
- dataflow/utils/get_current_user.py,sha256=4nSO3SPVMZhW-MsIgxR3f9ZzrFaIZIuyrM6hvfyE7PQ,1202
53
+ dataflow/utils/get_current_user.py,sha256=iPyUpEpuxitdmFwvaPiRWIlII8JaaGoxUhQcmNWi0vI,2319
52
54
  dataflow/utils/logger.py,sha256=7BFrOq5Oiqn8P4XZbgJzMP5O07d2fpdECbbfsjrUuHw,1213
53
- dataflow_core-2.1.14rc1.dist-info/METADATA,sha256=akGTrwmInmlMGEyHbPlFx7lpwLE8l03du5zHb9UxEOc,373
54
- dataflow_core-2.1.14rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
55
- dataflow_core-2.1.14rc1.dist-info/entry_points.txt,sha256=ppj_EIbYrJJwCPg1kfdsZk5q1N-Ejfis1neYrnjhO8o,117
56
- dataflow_core-2.1.14rc1.dist-info/top_level.txt,sha256=SZsUOpSCK9ntUy-3Tusxzf5A2e8ebwD8vouPb1dPt_8,23
57
- dataflow_core-2.1.14rc1.dist-info/RECORD,,
55
+ dataflow_core-2.1.15rc2.dist-info/METADATA,sha256=t8vhx-r4NQ9rhkLblo_9sZRH1nvOnaYH7UpkqBPUaCw,443
56
+ dataflow_core-2.1.15rc2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
57
+ dataflow_core-2.1.15rc2.dist-info/entry_points.txt,sha256=ppj_EIbYrJJwCPg1kfdsZk5q1N-Ejfis1neYrnjhO8o,117
58
+ dataflow_core-2.1.15rc2.dist-info/top_level.txt,sha256=SZsUOpSCK9ntUy-3Tusxzf5A2e8ebwD8vouPb1dPt_8,23
59
+ dataflow_core-2.1.15rc2.dist-info/RECORD,,
@@ -1,16 +0,0 @@
1
- from sqlalchemy import Column, Integer, String, DateTime
2
- from sqlalchemy.sql.schema import ForeignKey
3
- from sqlalchemy.sql.expression import func
4
- from dataflow.db import Base
5
-
6
- class UserEnvironment(Base):
7
- """
8
- Table USER_ENVIRONMENT
9
- """
10
-
11
- __tablename__ = 'USER_ENVIRONMENT'
12
-
13
- id = Column(Integer, primary_key=True, index=True, autoincrement=True)
14
- user_id = Column(Integer, ForeignKey('USER.user_id', ondelete="CASCADE"), nullable=False)
15
- env_name = Column(String)
16
- timestamp = Column(DateTime, server_default=func.now(), onupdate=func.now())