gcp-platforms-auto 0.8.4__py3-none-any.whl → 0.8.5__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.
- gcp_platforms_auto/db.py +110 -29
- {gcp_platforms_auto-0.8.4.dist-info → gcp_platforms_auto-0.8.5.dist-info}/METADATA +7 -1
- {gcp_platforms_auto-0.8.4.dist-info → gcp_platforms_auto-0.8.5.dist-info}/RECORD +5 -5
- {gcp_platforms_auto-0.8.4.dist-info → gcp_platforms_auto-0.8.5.dist-info}/WHEEL +0 -0
- {gcp_platforms_auto-0.8.4.dist-info → gcp_platforms_auto-0.8.5.dist-info}/top_level.txt +0 -0
gcp_platforms_auto/db.py
CHANGED
|
@@ -4,27 +4,95 @@ import sqlalchemy
|
|
|
4
4
|
from sqlalchemy.orm import sessionmaker, scoped_session, declarative_base
|
|
5
5
|
from sqlalchemy import text
|
|
6
6
|
|
|
7
|
+
import google.auth
|
|
8
|
+
from google.cloud.sql.connector import Connector, IPTypes
|
|
9
|
+
|
|
7
10
|
# Configure the logger
|
|
8
11
|
logger = logging.getLogger("uvicorn")
|
|
9
12
|
logger.setLevel(logging.INFO)
|
|
10
13
|
|
|
11
14
|
Base = declarative_base()
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
+
|
|
17
|
+
def get_sql_engine(
|
|
18
|
+
db_host=None,
|
|
19
|
+
db_user=None,
|
|
20
|
+
db_pass=None,
|
|
21
|
+
db_name=None,
|
|
22
|
+
db_port=5432,
|
|
23
|
+
instance_connection_name=None,
|
|
24
|
+
) -> sqlalchemy.engine.base.Engine:
|
|
25
|
+
"""Initializes a connection pool for a Cloud SQL instance.
|
|
26
|
+
|
|
27
|
+
If instance_connection_name is provided (or found in INSTANCE_CONNECTION_NAME env var),
|
|
28
|
+
it uses the Cloud SQL Python Connector with IAM authentication.
|
|
29
|
+
Otherwise, it falls back to a TCP connection.
|
|
30
|
+
|
|
16
31
|
Args:
|
|
17
32
|
db_host: Database host address (e.g., '127.0.0.1')
|
|
18
|
-
db_user: Database username
|
|
19
|
-
db_pass: Database password
|
|
20
|
-
db_name: Database name
|
|
33
|
+
db_user: Database username (for IAM, defaults to detecting the service account email)
|
|
34
|
+
db_pass: Database password (not used for IAM)
|
|
35
|
+
db_name: Database name (defaults to DB_NAME env var)
|
|
21
36
|
db_port: Database port (default: 5432)
|
|
22
|
-
|
|
37
|
+
instance_connection_name: Cloud SQL instance connection name (PROJECT:REGION:INSTANCE)
|
|
38
|
+
|
|
23
39
|
Returns:
|
|
24
40
|
SQLAlchemy Engine instance
|
|
25
41
|
"""
|
|
42
|
+
# Try to get defaults from environment if not provided
|
|
43
|
+
instance_connection_name = instance_connection_name or os.getenv(
|
|
44
|
+
"INSTANCE_CONNECTION_NAME"
|
|
45
|
+
)
|
|
46
|
+
db_name = db_name or os.getenv("DB_NAME")
|
|
47
|
+
|
|
48
|
+
if instance_connection_name:
|
|
49
|
+
# If db_user is not provided, try to detect the service account email
|
|
50
|
+
if not db_user:
|
|
51
|
+
try:
|
|
52
|
+
credentials, _ = google.auth.default()
|
|
53
|
+
if hasattr(credentials, "service_account_email"):
|
|
54
|
+
# For Postgres IAM, the user is the service account email without .gserviceaccount.com
|
|
55
|
+
sa_email = credentials.service_account_email
|
|
56
|
+
db_user = sa_email.replace(".gserviceaccount.com", "")
|
|
57
|
+
logger.info(
|
|
58
|
+
f"Auto-detected service account for IAM auth: {db_user}"
|
|
59
|
+
)
|
|
60
|
+
elif hasattr(credentials, "signer_email"):
|
|
61
|
+
# Fallback for some credential types
|
|
62
|
+
db_user = credentials.signer_email.replace(
|
|
63
|
+
".gserviceaccount.com", ""
|
|
64
|
+
)
|
|
65
|
+
logger.info(f"Auto-detected service account from signer: {db_user}")
|
|
66
|
+
except Exception as e:
|
|
67
|
+
logger.warning(f"Could not auto-detect service account email: {e}")
|
|
68
|
+
|
|
69
|
+
logger.info(
|
|
70
|
+
f"Connecting to Cloud SQL instance '{instance_connection_name}' using IAM auth as user '{db_user}'."
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# initialize Connector object
|
|
74
|
+
connector = Connector()
|
|
75
|
+
|
|
76
|
+
def getconn():
|
|
77
|
+
conn = connector.connect(
|
|
78
|
+
instance_connection_name,
|
|
79
|
+
"pg8000",
|
|
80
|
+
user=db_user,
|
|
81
|
+
db=db_name,
|
|
82
|
+
enable_iam_auth=True,
|
|
83
|
+
ip_type=IPTypes.PRIVATE,
|
|
84
|
+
)
|
|
85
|
+
return conn
|
|
86
|
+
|
|
87
|
+
pool = sqlalchemy.create_engine(
|
|
88
|
+
"postgresql+pg8000://",
|
|
89
|
+
creator=getconn,
|
|
90
|
+
)
|
|
91
|
+
logger.info("Successfully created Cloud SQL connector connection pool.")
|
|
92
|
+
return pool
|
|
93
|
+
|
|
26
94
|
logger.info(f"Connecting to database '{db_name}' at {db_host}:{db_port}")
|
|
27
|
-
|
|
95
|
+
|
|
28
96
|
try:
|
|
29
97
|
pool = sqlalchemy.create_engine(
|
|
30
98
|
# Equivalent URL:
|
|
@@ -38,32 +106,34 @@ def get_sql_engine(db_host, db_user, db_pass, db_name, db_port=5432) -> sqlalche
|
|
|
38
106
|
database=db_name,
|
|
39
107
|
),
|
|
40
108
|
)
|
|
41
|
-
logger.info("Successfully created database connection pool.")
|
|
109
|
+
logger.info("Successfully created TCP database connection pool.")
|
|
42
110
|
return pool
|
|
43
111
|
except Exception as e:
|
|
44
112
|
logger.exception(f"Error connecting to database: {str(e)}")
|
|
45
113
|
raise e
|
|
46
114
|
|
|
115
|
+
|
|
47
116
|
def get_session(engine):
|
|
48
117
|
"""Creates and returns a new session for the provided engine.
|
|
49
|
-
|
|
118
|
+
|
|
50
119
|
Args:
|
|
51
120
|
engine: SQLAlchemy Engine instance
|
|
52
|
-
|
|
121
|
+
|
|
53
122
|
Returns:
|
|
54
123
|
SQLAlchemy Session instance
|
|
55
124
|
"""
|
|
56
125
|
Session = sessionmaker(bind=engine)
|
|
57
126
|
return Session()
|
|
58
127
|
|
|
128
|
+
|
|
59
129
|
def execute_query(engine, query_str: str, params: dict = None):
|
|
60
130
|
"""Executes a raw SQL query.
|
|
61
|
-
|
|
131
|
+
|
|
62
132
|
Args:
|
|
63
133
|
engine: SQLAlchemy Engine instance
|
|
64
134
|
query_str: SQL query string
|
|
65
135
|
params: Dictionary of query parameters (optional)
|
|
66
|
-
|
|
136
|
+
|
|
67
137
|
Returns:
|
|
68
138
|
Query result
|
|
69
139
|
"""
|
|
@@ -81,9 +151,10 @@ def execute_query(engine, query_str: str, params: dict = None):
|
|
|
81
151
|
finally:
|
|
82
152
|
session.close()
|
|
83
153
|
|
|
154
|
+
|
|
84
155
|
def create_tables(engine, base_class=Base):
|
|
85
156
|
"""Creates all tables defined in the SQLAlchemy Base metadata.
|
|
86
|
-
|
|
157
|
+
|
|
87
158
|
Args:
|
|
88
159
|
engine: SQLAlchemy Engine instance
|
|
89
160
|
base_class: SQLAlchemy declarative base class (default: Base)
|
|
@@ -96,13 +167,14 @@ def create_tables(engine, base_class=Base):
|
|
|
96
167
|
logger.exception(f"Error creating tables: {str(e)}")
|
|
97
168
|
raise e
|
|
98
169
|
|
|
170
|
+
|
|
99
171
|
def save_model(engine, model_instance):
|
|
100
172
|
"""Saves a single model instance to the database.
|
|
101
|
-
|
|
173
|
+
|
|
102
174
|
Args:
|
|
103
175
|
engine: SQLAlchemy Engine instance
|
|
104
176
|
model_instance: SQLAlchemy model instance to save
|
|
105
|
-
|
|
177
|
+
|
|
106
178
|
Returns:
|
|
107
179
|
The saved model instance with refreshed data
|
|
108
180
|
"""
|
|
@@ -121,13 +193,14 @@ def save_model(engine, model_instance):
|
|
|
121
193
|
finally:
|
|
122
194
|
session.close()
|
|
123
195
|
|
|
196
|
+
|
|
124
197
|
def get_all(engine, model_class):
|
|
125
198
|
"""Retrieves all records for a given model class.
|
|
126
|
-
|
|
199
|
+
|
|
127
200
|
Args:
|
|
128
201
|
engine: SQLAlchemy Engine instance
|
|
129
202
|
model_class: SQLAlchemy model class
|
|
130
|
-
|
|
203
|
+
|
|
131
204
|
Returns:
|
|
132
205
|
List of all records for the model
|
|
133
206
|
"""
|
|
@@ -143,14 +216,15 @@ def get_all(engine, model_class):
|
|
|
143
216
|
finally:
|
|
144
217
|
session.close()
|
|
145
218
|
|
|
219
|
+
|
|
146
220
|
def get_by_id(engine, model_class, record_id):
|
|
147
221
|
"""Retrieves a single record by its primary key ID.
|
|
148
|
-
|
|
222
|
+
|
|
149
223
|
Args:
|
|
150
224
|
engine: SQLAlchemy Engine instance
|
|
151
225
|
model_class: SQLAlchemy model class
|
|
152
226
|
record_id: Primary key value to search for
|
|
153
|
-
|
|
227
|
+
|
|
154
228
|
Returns:
|
|
155
229
|
Model instance if found, None otherwise
|
|
156
230
|
"""
|
|
@@ -169,14 +243,15 @@ def get_by_id(engine, model_class, record_id):
|
|
|
169
243
|
finally:
|
|
170
244
|
session.close()
|
|
171
245
|
|
|
246
|
+
|
|
172
247
|
def get_by_filter(engine, model_class, **filters):
|
|
173
248
|
"""Retrieves records matching the provided filter criteria.
|
|
174
|
-
|
|
249
|
+
|
|
175
250
|
Args:
|
|
176
251
|
engine: SQLAlchemy Engine instance
|
|
177
252
|
model_class: SQLAlchemy model class
|
|
178
253
|
**filters: Keyword arguments for filtering (e.g., name="John", age=30)
|
|
179
|
-
|
|
254
|
+
|
|
180
255
|
Returns:
|
|
181
256
|
List of matching records
|
|
182
257
|
"""
|
|
@@ -192,20 +267,23 @@ def get_by_filter(engine, model_class, **filters):
|
|
|
192
267
|
finally:
|
|
193
268
|
session.close()
|
|
194
269
|
|
|
270
|
+
|
|
195
271
|
def get_one(engine, model_class, **filters):
|
|
196
272
|
"""Retrieves a single record matching the provided filter criteria.
|
|
197
|
-
|
|
273
|
+
|
|
198
274
|
Args:
|
|
199
275
|
engine: SQLAlchemy Engine instance
|
|
200
276
|
model_class: SQLAlchemy model class
|
|
201
277
|
**filters: Keyword arguments for filtering (e.g., email="user@example.com")
|
|
202
|
-
|
|
278
|
+
|
|
203
279
|
Returns:
|
|
204
280
|
First matching model instance if found, None otherwise
|
|
205
281
|
"""
|
|
206
282
|
session = get_session(engine)
|
|
207
283
|
try:
|
|
208
|
-
logger.info(
|
|
284
|
+
logger.info(
|
|
285
|
+
f"Retrieving single {model_class.__name__} with criteria: {filters}"
|
|
286
|
+
)
|
|
209
287
|
result = session.query(model_class).filter_by(**filters).first()
|
|
210
288
|
if result:
|
|
211
289
|
logger.info(f"Found matching record.")
|
|
@@ -218,13 +296,14 @@ def get_one(engine, model_class, **filters):
|
|
|
218
296
|
finally:
|
|
219
297
|
session.close()
|
|
220
298
|
|
|
299
|
+
|
|
221
300
|
def update_model(engine, model_instance):
|
|
222
301
|
"""Updates an existing model instance in the database.
|
|
223
|
-
|
|
302
|
+
|
|
224
303
|
Args:
|
|
225
304
|
engine: SQLAlchemy Engine instance
|
|
226
305
|
model_instance: SQLAlchemy model instance with updated values
|
|
227
|
-
|
|
306
|
+
|
|
228
307
|
Returns:
|
|
229
308
|
The updated model instance with refreshed data
|
|
230
309
|
"""
|
|
@@ -242,9 +321,10 @@ def update_model(engine, model_instance):
|
|
|
242
321
|
finally:
|
|
243
322
|
session.close()
|
|
244
323
|
|
|
324
|
+
|
|
245
325
|
def delete_model(engine, model_instance):
|
|
246
326
|
"""Deletes a model instance from the database.
|
|
247
|
-
|
|
327
|
+
|
|
248
328
|
Args:
|
|
249
329
|
engine: SQLAlchemy Engine instance
|
|
250
330
|
model_instance: SQLAlchemy model instance to delete
|
|
@@ -262,9 +342,10 @@ def delete_model(engine, model_instance):
|
|
|
262
342
|
finally:
|
|
263
343
|
session.close()
|
|
264
344
|
|
|
345
|
+
|
|
265
346
|
def cleanup_connector(engine):
|
|
266
347
|
"""Disposes the connection pool for the provided engine.
|
|
267
|
-
|
|
348
|
+
|
|
268
349
|
Args:
|
|
269
350
|
engine: SQLAlchemy Engine instance to dispose
|
|
270
351
|
"""
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: gcp_platforms_auto
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.5
|
|
4
4
|
Summary: A brief description of your package
|
|
5
5
|
Author-email: ofir4858 <ofirshasha10@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -17,5 +17,11 @@ Requires-Dist: gitpython
|
|
|
17
17
|
Requires-Dist: sqlalchemy
|
|
18
18
|
Requires-Dist: pg8000
|
|
19
19
|
Requires-Dist: pydantic
|
|
20
|
+
Requires-Dist: fastapi
|
|
21
|
+
Requires-Dist: uvicorn
|
|
22
|
+
Requires-Dist: pydantic-settings
|
|
23
|
+
Requires-Dist: google-cloud-storage
|
|
24
|
+
Requires-Dist: google-cloud-pubsub
|
|
25
|
+
Requires-Dist: cloud-sql-python-connector[pg8000]
|
|
20
26
|
|
|
21
27
|
# gcp_sdk
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
gcp_platforms_auto/__init__.py,sha256=pKouDuMclA8r93k5H9TjGbxNsFwqIYfymjO-owuGGws,526
|
|
2
|
-
gcp_platforms_auto/db.py,sha256=
|
|
2
|
+
gcp_platforms_auto/db.py,sha256=N9VNJgJklvbI1hkVjIt2oMlF-klvhfTqjURNOQgiKuk,11346
|
|
3
3
|
gcp_platforms_auto/git.py,sha256=NnLDfRzzrzbm9yekepc-qgu8ejYmjNxQ4VDlW46gG2o,5508
|
|
4
4
|
gcp_platforms_auto/iam.py,sha256=wnMZ_jl2YM5dLY8LqtPfEyh_0Osqs1ypsxakJGmwHVw,11614
|
|
5
5
|
gcp_platforms_auto/models.py,sha256=mVg8NKV25kqdTuazqenAp7Ay03N5D8GIh3F_TWP0zyI,853
|
|
6
|
-
gcp_platforms_auto-0.8.
|
|
7
|
-
gcp_platforms_auto-0.8.
|
|
8
|
-
gcp_platforms_auto-0.8.
|
|
9
|
-
gcp_platforms_auto-0.8.
|
|
6
|
+
gcp_platforms_auto-0.8.5.dist-info/METADATA,sha256=KlHmSF84Vk2FGVpYwupOdOWxAhOAvRs4yuWbg0xCP-w,800
|
|
7
|
+
gcp_platforms_auto-0.8.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
8
|
+
gcp_platforms_auto-0.8.5.dist-info/top_level.txt,sha256=4q-ofPMmvBaTnIbAzs-Wp_OwheAVxxmJ1fW9vl3-kyE,19
|
|
9
|
+
gcp_platforms_auto-0.8.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|