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 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
- def get_sql_engine(db_host, db_user, db_pass, db_name, db_port=5432) -> sqlalchemy.engine.base.Engine:
14
- """Initializes a TCP connection pool for a Cloud SQL instance of Postgres.
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(f"Retrieving single {model_class.__name__} with criteria: {filters}")
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.4
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=jE5nwmqVHcxT4m6-meUgUz4V4DM8M_sMmeTpvKr2Z2Y,8768
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.4.dist-info/METADATA,sha256=UU0EYfRtyhRgQW_1867j05FBs16hAAan5w_UWKQn15w,600
7
- gcp_platforms_auto-0.8.4.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
8
- gcp_platforms_auto-0.8.4.dist-info/top_level.txt,sha256=4q-ofPMmvBaTnIbAzs-Wp_OwheAVxxmJ1fW9vl3-kyE,19
9
- gcp_platforms_auto-0.8.4.dist-info/RECORD,,
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,,