oplnk-python-utils 1.0.1__tar.gz → 1.0.2__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.
Files changed (22) hide show
  1. {oplnk_python_utils-1.0.1/oplnk_python_utils.egg-info → oplnk_python_utils-1.0.2}/PKG-INFO +163 -35
  2. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/README.md +162 -34
  3. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils/__init__.py +3 -8
  4. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils/cache/__init__.py +12 -4
  5. oplnk_python_utils-1.0.2/oplnk_python_utils/cache/cache_utils.py +226 -0
  6. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils/query/query_builder.py +295 -169
  7. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2/oplnk_python_utils.egg-info}/PKG-INFO +163 -35
  8. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/setup.py +2 -2
  9. oplnk_python_utils-1.0.1/oplnk_python_utils/cache/cache_utils.py +0 -88
  10. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/LICENSE +0 -0
  11. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/MANIFEST.in +0 -0
  12. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils/core/__init__.py +0 -0
  13. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils/core/data.py +0 -0
  14. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils/core/parsing.py +0 -0
  15. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils/core/serialization.py +0 -0
  16. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils/query/__init__.py +0 -0
  17. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils.egg-info/SOURCES.txt +0 -0
  18. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils.egg-info/dependency_links.txt +0 -0
  19. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils.egg-info/not-zip-safe +0 -0
  20. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils.egg-info/requires.txt +0 -0
  21. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/oplnk_python_utils.egg-info/top_level.txt +0 -0
  22. {oplnk_python_utils-1.0.1 → oplnk_python_utils-1.0.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oplnk-python-utils
3
- Version: 1.0.1
3
+ Version: 1.0.2
4
4
  Summary: Reusable utilities for caching, data manipulation, parsing, and MongoDB query building
5
5
  Home-page: https://github.com/openlinkspa/oplnk-python-utils
6
6
  Author: OpenLink SpA
@@ -189,7 +189,9 @@ serialized = datetime_serializer(data)
189
189
 
190
190
  ## Cache Module
191
191
 
192
- Advanced caching utilities with Redis integration and intelligent cache key management.
192
+ Advanced caching utilities with Redis integration and intelligent cache key management. The module provides both low-level caching utilities and high-level endpoint decorators for comprehensive caching strategies.
193
+
194
+ ### Low-Level Cache Utilities
193
195
 
194
196
  ```python
195
197
  from oplnk_python_utils.cache import generate_cache_id, get_or_set_cache, invalidate_cache_keys
@@ -199,7 +201,7 @@ cache_key = generate_cache_id({
199
201
  "filters": {"name": "John", "status": "active"},
200
202
  "sort": ["created_at:desc"]
201
203
  })
202
- # Automatically normalizes data internally and creates MD5 hash
204
+ # Automatically normalizes data internally and creates SHA256 hash
203
205
 
204
206
  # Get from cache or execute function and cache result
205
207
  async def expensive_operation(query):
@@ -207,37 +209,145 @@ async def expensive_operation(query):
207
209
  return await db.collection.find(query).to_list()
208
210
 
209
211
  result = await get_or_set_cache(
212
+ redis_client=redis,
210
213
  cache_key="user_query_abc123",
211
- fetch_function=expensive_operation,
212
- query={"status": "active"},
213
- expiration=3600 # 1 hour
214
+ fetch_func=expensive_operation,
215
+ ttl=3600, # 1 hour
216
+ serializer=datetime_serializer
214
217
  )
215
218
 
216
- # Cache with automatic serialization of datetime objects
217
- from datetime import datetime
218
- async def get_user_data():
219
- return {
220
- "user": "john",
221
- "last_login": datetime.now(),
222
- "preferences": {"theme": "dark"}
223
- }
219
+ # Invalidate multiple cache keys by pattern
220
+ invalidated_count = invalidate_cache_keys(redis, "user_*")
221
+ print(f"Invalidated {invalidated_count} cache entries")
222
+ ```
223
+
224
+ ### Endpoint-Level Caching (NEW)
225
+
226
+ High-level decorators for caching entire endpoint responses with automatic cache key generation and tag-based invalidation.
224
227
 
225
- cached_data = await get_or_set_cache(
226
- cache_key="user_john_data",
227
- fetch_function=get_user_data,
228
- expiration=1800
228
+ ```python
229
+ from oplnk_python_utils.cache import cache_response, cache_factory, build_cache_key, invalidate_tag
230
+ from fastapi import FastAPI, Request
231
+ import redis
232
+
233
+ app = FastAPI()
234
+ redis_client = redis.Redis(host='localhost', port=6379, db=0)
235
+
236
+ # Method 1: Direct decorator usage
237
+ @app.get("/users")
238
+ @cache_response(
239
+ redis_client=redis_client,
240
+ api_name="myapi",
241
+ collection="users",
242
+ endpoint_type="read",
243
+ tags=["users", "all_data"],
244
+ ttl=3600, # 1 hour
245
+ use_cache=True
229
246
  )
247
+ async def get_users(request: Request):
248
+ # Your endpoint logic here
249
+ return {"users": [...]}
250
+
251
+ # Method 2: Factory pattern for reusable decorators
252
+ users_cache = cache_factory(redis_client, "myapi", "users")
253
+
254
+ @app.get("/users")
255
+ @users_cache(
256
+ endpoint_type="read",
257
+ tags=["users", "public"],
258
+ ttl=1800 # 30 minutes
259
+ )
260
+ async def get_users(request: Request):
261
+ return {"users": [...]}
262
+
263
+ @app.post("/users")
264
+ @users_cache(
265
+ endpoint_type="create",
266
+ tags=["users"],
267
+ ttl=0 # No caching for write operations
268
+ )
269
+ async def create_user(request: Request):
270
+ # Automatically invalidates 'users' tag after creation
271
+ return {"created": True}
272
+
273
+ # Manual cache key building
274
+ @app.get("/custom")
275
+ async def custom_endpoint(request: Request):
276
+ cache_key = build_cache_key(request, "myapi", "custom", "read")
277
+ # Use cache_key for custom caching logic
278
+ return {"data": "custom"}
279
+
280
+ # Tag-based cache invalidation
281
+ @app.delete("/users/{user_id}")
282
+ async def delete_user(user_id: str):
283
+ # Delete user logic here
284
+ # ...
285
+
286
+ # Invalidate all cached responses tagged with 'users'
287
+ invalidate_tag(redis_client, "users")
288
+ return {"deleted": True}
289
+ ```
230
290
 
231
- # Invalidate multiple cache keys by pattern
232
- invalidated_count = await invalidate_cache_keys("user_*")
233
- print(f"Invalidated {invalidated_count} cache entries")
291
+ ### Cache Key Structure
292
+
293
+ The caching system generates structured cache keys based on:
294
+
295
+ - **API Name**: Namespace for your application
296
+ - **Collection**: The data collection being cached
297
+ - **Endpoint Type**: Type of operation (read, create, update, delete)
298
+ - **Query Hash**: SHA256 hash of normalized query parameters
299
+
300
+ Example cache key: `myapi:users:read:a1b2c3d4e5f6...`
301
+
302
+ ### Tag-Based Cache Management
303
+
304
+ ```python
305
+ # Cache responses with multiple tags
306
+ @cache_response(
307
+ redis_client=redis_client,
308
+ api_name="ecommerce",
309
+ collection="products",
310
+ endpoint_type="read",
311
+ tags=["products", "catalog", "public_data"],
312
+ ttl=7200 # 2 hours
313
+ )
314
+ async def get_products(request: Request):
315
+ return {"products": [...]}
316
+
317
+ # Invalidate specific tag groups
318
+ invalidate_tag(redis_client, "products") # Invalidates all product-related cache
319
+ invalidate_tag(redis_client, "catalog") # Invalidates all catalog cache
320
+ invalidate_tag(redis_client, "public_data") # Invalidates all public data cache
234
321
  ```
235
322
 
236
- ### Cache Functions
323
+ ### Cache Functions Reference
324
+
325
+ #### Low-Level Functions
326
+
327
+ - **`generate_cache_id(params)`**: Generate SHA256 hash from normalized data for consistent cache keys
328
+ - **`get_or_set_cache(redis_client, cache_key, fetch_func, clear_cache=False, ttl=None, serializer=None)`**: Get cached result or execute function and cache with automatic serialization
329
+ - **`invalidate_cache_keys(redis_client, pattern)`**: Delete cache keys matching pattern (supports wildcards)
330
+
331
+ #### Endpoint Caching Functions
332
+
333
+ - **`build_cache_key(request, api_name, collection, endpoint_type)`**: Build structured cache key from request parameters
334
+ - **`cache_response(redis_client, api_name, collection, endpoint_type, tags, ttl=None, use_cache=True)`**: Decorator for caching endpoint responses
335
+ - **`cache_factory(redis_client, api_name, collection)`**: Factory function to create reusable cache decorators
336
+ - **`invalidate_tag(redis_client, tag)`**: Invalidate all cache keys associated with a specific tag
337
+
338
+ ### Automatic Cache Invalidation
237
339
 
238
- - **`generate_cache_id(data, prefix="")`**: Generate MD5 hash from normalized data for consistent cache keys
239
- - **`get_or_set_cache(cache_key, fetch_function, expiration=3600, **kwargs)`\*\*: Get cached result or execute function and cache with automatic datetime serialization
240
- - **`invalidate_cache_keys(pattern)`**: Delete cache keys matching pattern (supports wildcards)
340
+ The caching system supports automatic cache clearing via query parameters:
341
+
342
+ ```python
343
+ # Clear cache before executing endpoint
344
+ GET /users?clear_cache=true
345
+
346
+ # This will:
347
+ # 1. Invalidate the collection tag (e.g., "users")
348
+ # 2. Execute the endpoint normally
349
+ # 3. Cache the fresh response
350
+ ```
241
351
 
242
352
  ## Query Module
243
353
 
@@ -313,7 +423,7 @@ results = query_builder.read_query(
313
423
  params=raw_params, # Will be parsed automatically using core.parsing
314
424
  cache_client=redis_client,
315
425
  cache_key_prefix="users",
316
- use_cache=True
426
+ use_cache=False # Note: Endpoint-level caching is now preferred over query-level caching
317
427
  )
318
428
 
319
429
  print(results)
@@ -441,30 +551,48 @@ class CRUD(QueryBuilder):
441
551
  self.db = db[self.collection]
442
552
  super().__init__(collection_prefix=PREFIX, relations=relations)
443
553
 
444
- def read_query(self, params=None, query=None, pipeline=None, pipeline_count=None, use_cache=False):
445
- # Just 6 lines! All complexity abstracted away
554
+ def read_query(self, params=None, query=None, pipeline=None, pipeline_count=None):
555
+ # Just 4 lines! All complexity abstracted away
446
556
  return super().read_query(
447
557
  db_collection=self.db, params=params, query=query,
448
- pipeline=pipeline, pipeline_count=pipeline_count,
449
- use_cache=use_cache, cache_client=redis,
450
- cache_key_prefix=f"app:{self.collection}"
558
+ pipeline=pipeline, pipeline_count=pipeline_count
451
559
  )
452
560
 
453
- # Endpoint - Even simpler with explicit imports!
561
+ # Endpoint - With new endpoint-level caching!
562
+ from oplnk_python_utils.cache import cache_factory
454
563
  from oplnk_python_utils.core.parsing import parse_params_to_dict
455
564
 
565
+ # Create reusable cache decorator for this collection
566
+ users_cache = cache_factory(redis_client, "myapp", "users")
567
+
456
568
  @router.get("/query")
569
+ @users_cache(
570
+ endpoint_type="read",
571
+ tags=["users", "queries"],
572
+ ttl=3600 # Cache for 1 hour
573
+ )
457
574
  async def get_data(request: Request):
458
575
  # Option 1: Manual parsing (explicit control)
459
576
  query = parse_params_to_dict(request.query_params)
460
- return crud.read_query(query=query, pipeline=base_pipeline, use_cache=True)
577
+ return crud.read_query(query=query, pipeline=base_pipeline)
461
578
 
462
579
  # Option 2: Automatic parsing (convenience)
463
580
  return crud.read_query(
464
581
  params=request.query_params, # Parsed automatically
465
- pipeline=base_pipeline,
466
- use_cache=True
582
+ pipeline=base_pipeline
467
583
  )
584
+
585
+ # Write operations with cache invalidation
586
+ @router.post("/users")
587
+ @users_cache(
588
+ endpoint_type="create",
589
+ tags=["users"],
590
+ ttl=0 # No caching for write operations
591
+ )
592
+ async def create_user(request: Request, user_data: dict):
593
+ result = crud.create(user_data)
594
+ # Cache is automatically invalidated due to 'users' tag
595
+ return result
468
596
  ```
469
597
 
470
598
  ## Advanced Features
@@ -147,7 +147,9 @@ serialized = datetime_serializer(data)
147
147
 
148
148
  ## Cache Module
149
149
 
150
- Advanced caching utilities with Redis integration and intelligent cache key management.
150
+ Advanced caching utilities with Redis integration and intelligent cache key management. The module provides both low-level caching utilities and high-level endpoint decorators for comprehensive caching strategies.
151
+
152
+ ### Low-Level Cache Utilities
151
153
 
152
154
  ```python
153
155
  from oplnk_python_utils.cache import generate_cache_id, get_or_set_cache, invalidate_cache_keys
@@ -157,7 +159,7 @@ cache_key = generate_cache_id({
157
159
  "filters": {"name": "John", "status": "active"},
158
160
  "sort": ["created_at:desc"]
159
161
  })
160
- # Automatically normalizes data internally and creates MD5 hash
162
+ # Automatically normalizes data internally and creates SHA256 hash
161
163
 
162
164
  # Get from cache or execute function and cache result
163
165
  async def expensive_operation(query):
@@ -165,37 +167,145 @@ async def expensive_operation(query):
165
167
  return await db.collection.find(query).to_list()
166
168
 
167
169
  result = await get_or_set_cache(
170
+ redis_client=redis,
168
171
  cache_key="user_query_abc123",
169
- fetch_function=expensive_operation,
170
- query={"status": "active"},
171
- expiration=3600 # 1 hour
172
+ fetch_func=expensive_operation,
173
+ ttl=3600, # 1 hour
174
+ serializer=datetime_serializer
172
175
  )
173
176
 
174
- # Cache with automatic serialization of datetime objects
175
- from datetime import datetime
176
- async def get_user_data():
177
- return {
178
- "user": "john",
179
- "last_login": datetime.now(),
180
- "preferences": {"theme": "dark"}
181
- }
177
+ # Invalidate multiple cache keys by pattern
178
+ invalidated_count = invalidate_cache_keys(redis, "user_*")
179
+ print(f"Invalidated {invalidated_count} cache entries")
180
+ ```
181
+
182
+ ### Endpoint-Level Caching (NEW)
183
+
184
+ High-level decorators for caching entire endpoint responses with automatic cache key generation and tag-based invalidation.
182
185
 
183
- cached_data = await get_or_set_cache(
184
- cache_key="user_john_data",
185
- fetch_function=get_user_data,
186
- expiration=1800
186
+ ```python
187
+ from oplnk_python_utils.cache import cache_response, cache_factory, build_cache_key, invalidate_tag
188
+ from fastapi import FastAPI, Request
189
+ import redis
190
+
191
+ app = FastAPI()
192
+ redis_client = redis.Redis(host='localhost', port=6379, db=0)
193
+
194
+ # Method 1: Direct decorator usage
195
+ @app.get("/users")
196
+ @cache_response(
197
+ redis_client=redis_client,
198
+ api_name="myapi",
199
+ collection="users",
200
+ endpoint_type="read",
201
+ tags=["users", "all_data"],
202
+ ttl=3600, # 1 hour
203
+ use_cache=True
187
204
  )
205
+ async def get_users(request: Request):
206
+ # Your endpoint logic here
207
+ return {"users": [...]}
208
+
209
+ # Method 2: Factory pattern for reusable decorators
210
+ users_cache = cache_factory(redis_client, "myapi", "users")
211
+
212
+ @app.get("/users")
213
+ @users_cache(
214
+ endpoint_type="read",
215
+ tags=["users", "public"],
216
+ ttl=1800 # 30 minutes
217
+ )
218
+ async def get_users(request: Request):
219
+ return {"users": [...]}
220
+
221
+ @app.post("/users")
222
+ @users_cache(
223
+ endpoint_type="create",
224
+ tags=["users"],
225
+ ttl=0 # No caching for write operations
226
+ )
227
+ async def create_user(request: Request):
228
+ # Automatically invalidates 'users' tag after creation
229
+ return {"created": True}
230
+
231
+ # Manual cache key building
232
+ @app.get("/custom")
233
+ async def custom_endpoint(request: Request):
234
+ cache_key = build_cache_key(request, "myapi", "custom", "read")
235
+ # Use cache_key for custom caching logic
236
+ return {"data": "custom"}
237
+
238
+ # Tag-based cache invalidation
239
+ @app.delete("/users/{user_id}")
240
+ async def delete_user(user_id: str):
241
+ # Delete user logic here
242
+ # ...
243
+
244
+ # Invalidate all cached responses tagged with 'users'
245
+ invalidate_tag(redis_client, "users")
246
+ return {"deleted": True}
247
+ ```
188
248
 
189
- # Invalidate multiple cache keys by pattern
190
- invalidated_count = await invalidate_cache_keys("user_*")
191
- print(f"Invalidated {invalidated_count} cache entries")
249
+ ### Cache Key Structure
250
+
251
+ The caching system generates structured cache keys based on:
252
+
253
+ - **API Name**: Namespace for your application
254
+ - **Collection**: The data collection being cached
255
+ - **Endpoint Type**: Type of operation (read, create, update, delete)
256
+ - **Query Hash**: SHA256 hash of normalized query parameters
257
+
258
+ Example cache key: `myapi:users:read:a1b2c3d4e5f6...`
259
+
260
+ ### Tag-Based Cache Management
261
+
262
+ ```python
263
+ # Cache responses with multiple tags
264
+ @cache_response(
265
+ redis_client=redis_client,
266
+ api_name="ecommerce",
267
+ collection="products",
268
+ endpoint_type="read",
269
+ tags=["products", "catalog", "public_data"],
270
+ ttl=7200 # 2 hours
271
+ )
272
+ async def get_products(request: Request):
273
+ return {"products": [...]}
274
+
275
+ # Invalidate specific tag groups
276
+ invalidate_tag(redis_client, "products") # Invalidates all product-related cache
277
+ invalidate_tag(redis_client, "catalog") # Invalidates all catalog cache
278
+ invalidate_tag(redis_client, "public_data") # Invalidates all public data cache
192
279
  ```
193
280
 
194
- ### Cache Functions
281
+ ### Cache Functions Reference
282
+
283
+ #### Low-Level Functions
284
+
285
+ - **`generate_cache_id(params)`**: Generate SHA256 hash from normalized data for consistent cache keys
286
+ - **`get_or_set_cache(redis_client, cache_key, fetch_func, clear_cache=False, ttl=None, serializer=None)`**: Get cached result or execute function and cache with automatic serialization
287
+ - **`invalidate_cache_keys(redis_client, pattern)`**: Delete cache keys matching pattern (supports wildcards)
288
+
289
+ #### Endpoint Caching Functions
290
+
291
+ - **`build_cache_key(request, api_name, collection, endpoint_type)`**: Build structured cache key from request parameters
292
+ - **`cache_response(redis_client, api_name, collection, endpoint_type, tags, ttl=None, use_cache=True)`**: Decorator for caching endpoint responses
293
+ - **`cache_factory(redis_client, api_name, collection)`**: Factory function to create reusable cache decorators
294
+ - **`invalidate_tag(redis_client, tag)`**: Invalidate all cache keys associated with a specific tag
295
+
296
+ ### Automatic Cache Invalidation
195
297
 
196
- - **`generate_cache_id(data, prefix="")`**: Generate MD5 hash from normalized data for consistent cache keys
197
- - **`get_or_set_cache(cache_key, fetch_function, expiration=3600, **kwargs)`\*\*: Get cached result or execute function and cache with automatic datetime serialization
198
- - **`invalidate_cache_keys(pattern)`**: Delete cache keys matching pattern (supports wildcards)
298
+ The caching system supports automatic cache clearing via query parameters:
299
+
300
+ ```python
301
+ # Clear cache before executing endpoint
302
+ GET /users?clear_cache=true
303
+
304
+ # This will:
305
+ # 1. Invalidate the collection tag (e.g., "users")
306
+ # 2. Execute the endpoint normally
307
+ # 3. Cache the fresh response
308
+ ```
199
309
 
200
310
  ## Query Module
201
311
 
@@ -271,7 +381,7 @@ results = query_builder.read_query(
271
381
  params=raw_params, # Will be parsed automatically using core.parsing
272
382
  cache_client=redis_client,
273
383
  cache_key_prefix="users",
274
- use_cache=True
384
+ use_cache=False # Note: Endpoint-level caching is now preferred over query-level caching
275
385
  )
276
386
 
277
387
  print(results)
@@ -399,30 +509,48 @@ class CRUD(QueryBuilder):
399
509
  self.db = db[self.collection]
400
510
  super().__init__(collection_prefix=PREFIX, relations=relations)
401
511
 
402
- def read_query(self, params=None, query=None, pipeline=None, pipeline_count=None, use_cache=False):
403
- # Just 6 lines! All complexity abstracted away
512
+ def read_query(self, params=None, query=None, pipeline=None, pipeline_count=None):
513
+ # Just 4 lines! All complexity abstracted away
404
514
  return super().read_query(
405
515
  db_collection=self.db, params=params, query=query,
406
- pipeline=pipeline, pipeline_count=pipeline_count,
407
- use_cache=use_cache, cache_client=redis,
408
- cache_key_prefix=f"app:{self.collection}"
516
+ pipeline=pipeline, pipeline_count=pipeline_count
409
517
  )
410
518
 
411
- # Endpoint - Even simpler with explicit imports!
519
+ # Endpoint - With new endpoint-level caching!
520
+ from oplnk_python_utils.cache import cache_factory
412
521
  from oplnk_python_utils.core.parsing import parse_params_to_dict
413
522
 
523
+ # Create reusable cache decorator for this collection
524
+ users_cache = cache_factory(redis_client, "myapp", "users")
525
+
414
526
  @router.get("/query")
527
+ @users_cache(
528
+ endpoint_type="read",
529
+ tags=["users", "queries"],
530
+ ttl=3600 # Cache for 1 hour
531
+ )
415
532
  async def get_data(request: Request):
416
533
  # Option 1: Manual parsing (explicit control)
417
534
  query = parse_params_to_dict(request.query_params)
418
- return crud.read_query(query=query, pipeline=base_pipeline, use_cache=True)
535
+ return crud.read_query(query=query, pipeline=base_pipeline)
419
536
 
420
537
  # Option 2: Automatic parsing (convenience)
421
538
  return crud.read_query(
422
539
  params=request.query_params, # Parsed automatically
423
- pipeline=base_pipeline,
424
- use_cache=True
540
+ pipeline=base_pipeline
425
541
  )
542
+
543
+ # Write operations with cache invalidation
544
+ @router.post("/users")
545
+ @users_cache(
546
+ endpoint_type="create",
547
+ tags=["users"],
548
+ ttl=0 # No caching for write operations
549
+ )
550
+ async def create_user(request: Request, user_data: dict):
551
+ result = crud.create(user_data)
552
+ # Cache is automatically invalidated due to 'users' tag
553
+ return result
426
554
  ```
427
555
 
428
556
  ## Advanced Features
@@ -2,17 +2,12 @@
2
2
  OpenLink Python Utils - Reusable utilities for caching, data manipulation, parsing, and MongoDB query building
3
3
  """
4
4
 
5
- # Import modules only - no function re-exports
5
+ # Import modules only - no function re-exports
6
6
  from . import core
7
7
  from . import cache
8
8
  from . import query
9
9
 
10
- __version__ = "1.0.1"
10
+ __version__ = "1.0.2"
11
11
  __author__ = "OpenLink SpA"
12
12
 
13
- __all__ = [
14
- "core",
15
- "cache",
16
- "query",
17
- "__version__"
18
- ]
13
+ __all__ = ["core", "cache", "query", "__version__"]
@@ -5,7 +5,11 @@ Cache utilities module - Reusable caching functionality
5
5
  from .cache_utils import (
6
6
  generate_cache_id,
7
7
  get_or_set_cache,
8
- invalidate_cache_keys
8
+ invalidate_cache_keys,
9
+ build_cache_key,
10
+ cache_response,
11
+ invalidate_tag,
12
+ cache_factory,
9
13
  )
10
14
 
11
15
  # Re-export core utilities for convenience in this domain
@@ -14,8 +18,12 @@ from ..core.serialization import datetime_serializer
14
18
 
15
19
  __all__ = [
16
20
  "generate_cache_id",
17
- "datetime_serializer",
21
+ "datetime_serializer",
18
22
  "get_or_set_cache",
19
23
  "invalidate_cache_keys",
20
- "normalize"
21
- ]
24
+ "normalize",
25
+ "build_cache_key",
26
+ "cache_response",
27
+ "invalidate_tag",
28
+ "cache_factory",
29
+ ]