base-api-utils 1.1.76__tar.gz → 1.1.78__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 (96) hide show
  1. {base_api_utils-1.1.76/src/base_api_utils.egg-info → base_api_utils-1.1.78}/PKG-INFO +1 -1
  2. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/pyproject.toml +1 -1
  3. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/abstract_oauth2_api_client.py +8 -4
  4. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/__init__.py +8 -1
  5. base_api_utils-1.1.78/src/base_api_utils/utils/fingerprint_helpers.py +99 -0
  6. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/http_helpers.py +3 -0
  7. {base_api_utils-1.1.76 → base_api_utils-1.1.78/src/base_api_utils.egg-info}/PKG-INFO +1 -1
  8. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils.egg-info/SOURCES.txt +1 -0
  9. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/LICENSE +0 -0
  10. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/README.md +0 -0
  11. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/setup.cfg +0 -0
  12. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/__init__.py +0 -0
  13. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/domain/__init__.py +0 -0
  14. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/domain/event_message.py +0 -0
  15. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/domain/event_message_factory.py +0 -0
  16. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/domain/events.py +0 -0
  17. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/dtos/__init__.py +0 -0
  18. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/dtos/authz_user.py +0 -0
  19. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/fields/__init__.py +0 -0
  20. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/fields/email_list_field.py +0 -0
  21. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/filters/__init__.py +0 -0
  22. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/filters/base_filter.py +0 -0
  23. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/filters/custom_ordering_filter.py +0 -0
  24. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/filters/filter_utils_mixin.py +0 -0
  25. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/handlers/__init__.py +0 -0
  26. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/handlers/base_event_handler.py +0 -0
  27. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/handlers/sponsor_event_handler.py +0 -0
  28. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/handlers/sponsorship_addon_event_handler.py +0 -0
  29. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/handlers/sponsorship_event_handler.py +0 -0
  30. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/handlers/summit_event_handler.py +0 -0
  31. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/ioc/__init__.py +0 -0
  32. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/ioc/container.py +0 -0
  33. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/models/__init__.py +0 -0
  34. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/models/item_image.py +0 -0
  35. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/__init__.py +0 -0
  36. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/abstract_access_token_service.py +0 -0
  37. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/access_token_service.py +0 -0
  38. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/check_sponsor_user_permissions.py +0 -0
  39. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/group_required.py +0 -0
  40. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/oauth2_authentication.py +0 -0
  41. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/oauth2_client_factory.py +0 -0
  42. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/oauth2_scope_required.py +0 -0
  43. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/permission_event_handler.py +0 -0
  44. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/shared.py +0 -0
  45. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/security/user_permissions_service.py +0 -0
  46. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/serializers/__init__.py +0 -0
  47. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/serializers/base_model_serializer.py +0 -0
  48. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/serializers/email_list_char_field.py +0 -0
  49. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/serializers/nullable_slug_related_field.py +0 -0
  50. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/serializers/serializers_registry.py +0 -0
  51. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/serializers/task_result_serializer.py +0 -0
  52. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/serializers/timestamp_field.py +0 -0
  53. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/__init__.py +0 -0
  54. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/abstract_domain_events_service.py +0 -0
  55. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/abstract_event_dispatcher.py +0 -0
  56. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/abstract_images_service.py +0 -0
  57. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/abstract_sponsor_metadata_service.py +0 -0
  58. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/abstract_storage_service.py +0 -0
  59. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/abstract_summit_metadata_service.py +0 -0
  60. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/amqp_service.py +0 -0
  61. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/celery_failed.py +0 -0
  62. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/event_dispatcher.py +0 -0
  63. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/images_service.py +0 -0
  64. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/s3_service.py +0 -0
  65. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/strategies/__init__.py +0 -0
  66. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/strategies/abstract_task_runner_strategy.py +0 -0
  67. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/services/strategies/publish_domain_event_strategy.py +0 -0
  68. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/tests/__init__.py +0 -0
  69. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/tests/test_failed_celery_task_management.py +0 -0
  70. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/urls.py +0 -0
  71. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/async_helpers.py +0 -0
  72. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/circuit_breaker.py +0 -0
  73. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/config.py +0 -0
  74. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/custom_auto_schema.py +0 -0
  75. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/db_connection_helpers.py +0 -0
  76. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/enums.py +0 -0
  77. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/exceptions.py +0 -0
  78. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/external_errors.py +0 -0
  79. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/file_lock.py +0 -0
  80. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/inject_job.py +0 -0
  81. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/oauth2_custom_schema_base.py +0 -0
  82. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/pagination.py +0 -0
  83. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/rate_utils.py +0 -0
  84. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/read_write_serializer_mixin.py +0 -0
  85. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/serializers.py +0 -0
  86. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/service_utils.py +0 -0
  87. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/string.py +0 -0
  88. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/utils/tests.py +0 -0
  89. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/views/__init__.py +0 -0
  90. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/views/base_view.py +0 -0
  91. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/views/parent_children_crud.py +0 -0
  92. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/views/root_entity_crud.py +0 -0
  93. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils/views/tasks_result_view.py +0 -0
  94. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils.egg-info/dependency_links.txt +0 -0
  95. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils.egg-info/requires.txt +0 -0
  96. {base_api_utils-1.1.76 → base_api_utils-1.1.78}/src/base_api_utils.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: base-api-utils
3
- Version: 1.1.76
3
+ Version: 1.1.78
4
4
  Summary: Django Rest Framework micro services common utilities
5
5
  Author-email: Román Gutierrez <roman@tipit.net>, Sebastian Marcet <sebastian@tipit.net>
6
6
  Project-URL: Homepage, https://github.com/fntechgit/base-api-utils
@@ -7,7 +7,7 @@ where = ["src"]
7
7
 
8
8
  [project]
9
9
  name = "base-api-utils"
10
- version = "1.1.76"
10
+ version = "1.1.78"
11
11
  dependencies = [
12
12
  "boto3",
13
13
  "celery",
@@ -6,7 +6,8 @@ from django.core.cache import cache
6
6
  from requests_oauthlib import OAuth2Session
7
7
  from requests.exceptions import RequestException
8
8
  from rest_framework.exceptions import ValidationError, NotFound
9
- from rest_framework.status import HTTP_404_NOT_FOUND, HTTP_401_UNAUTHORIZED, HTTP_412_PRECONDITION_FAILED
9
+ from rest_framework.status import HTTP_404_NOT_FOUND, HTTP_401_UNAUTHORIZED, HTTP_412_PRECONDITION_FAILED, \
10
+ HTTP_403_FORBIDDEN
10
11
 
11
12
  from .oauth2_client_factory import OAuth2ClientFactory
12
13
  from .shared import client_secret_basic_auth_method
@@ -58,12 +59,15 @@ class AbstractOAuth2APIClient(ABC):
58
59
  if ex.response.status_code == HTTP_404_NOT_FOUND:
59
60
  raise NotFound(ex.response.text)
60
61
 
61
- if ex.response.status_code == HTTP_401_UNAUTHORIZED:
62
- logging.warning(f"Access token revoked or expired. Cleaning cache and retrying... (attempt {retries + 1})")
62
+ if ex.response.status_code in (HTTP_401_UNAUTHORIZED, HTTP_403_FORBIDDEN):
63
+ logging.warning(
64
+ f"Access token revoked, expired or insufficient permissions (HTTP {ex.response.status_code}). "
65
+ f"Cleaning cache and retrying... (attempt {retries + 1})"
66
+ )
63
67
  self.clean_access_token()
64
68
  retries += 1
65
69
  if retries >= self.MAX_RETRIES:
66
- logging.error("Max retries reached after token revocation")
70
+ logging.error("Max retries reached after authentication/authorization failure")
67
71
  raise
68
72
  continue
69
73
 
@@ -6,7 +6,14 @@ from .enums import EnumChoices
6
6
  from .exceptions import S3Exception, custom_exception_handler
7
7
  from .external_errors import NamedCircuitBreakerError, handle_external_service_errors
8
8
  from .file_lock import FileLock
9
- from .http_helpers import raise_for_auth_errors, raise_for_status_codes
9
+ from .fingerprint_helpers import (
10
+ compute_sha256,
11
+ format_value,
12
+ build_fingerprint_string,
13
+ format_id_list,
14
+ combine_fingerprints,
15
+ )
16
+ from .http_helpers import raise_for_auth_errors, raise_for_external_catch, raise_for_status_codes
10
17
  from .inject_job import inject_job
11
18
  from .pagination import LargeResultsSetPagination
12
19
  from .rate_utils import RateUtils
@@ -0,0 +1,99 @@
1
+ """
2
+ Fingerprint utility functions for computing SHA-256 based identity hashes.
3
+
4
+ These functions enable change detection to determine when entities need to be
5
+ created or updated by computing deterministic fingerprints from their field values.
6
+ """
7
+
8
+ import hashlib
9
+ from datetime import datetime
10
+ from typing import Any, Dict, Iterable, Optional
11
+
12
+
13
+ def compute_sha256(data: str) -> str:
14
+ """
15
+ Compute SHA-256 hash of the input string.
16
+
17
+ Args:
18
+ data: The string to hash
19
+
20
+ Returns:
21
+ 64-character lowercase hex string
22
+ """
23
+ return hashlib.sha256(data.encode('utf-8')).hexdigest()
24
+
25
+
26
+ def format_value(value: Any) -> Optional[str]:
27
+ """
28
+ Format a field value for inclusion in a fingerprint string.
29
+
30
+ Args:
31
+ value: The value to format
32
+
33
+ Returns:
34
+ Formatted string representation, or None if the value should be skipped
35
+ """
36
+ if value is None:
37
+ return None
38
+
39
+ if isinstance(value, bool):
40
+ return "true" if value else "false"
41
+
42
+ if isinstance(value, datetime):
43
+ return value.isoformat()
44
+
45
+ return str(value)
46
+
47
+
48
+ def build_fingerprint_string(fields: Dict[str, Any]) -> str:
49
+ """
50
+ Build a deterministic string from field name-value pairs.
51
+
52
+ Fields are sorted by name for determinism. Fields with None values are skipped.
53
+
54
+ Args:
55
+ fields: Dictionary of field names to values
56
+
57
+ Returns:
58
+ Concatenated string of "name=value" pairs separated by "|"
59
+ """
60
+ parts = []
61
+ for key in sorted(fields.keys()):
62
+ formatted = format_value(fields[key])
63
+ if formatted is not None:
64
+ parts.append(f"{key}={formatted}")
65
+ return "|".join(parts)
66
+
67
+
68
+ def format_id_list(ids: Iterable[int]) -> str:
69
+ """
70
+ Format a list of IDs as a comma-separated string.
71
+
72
+ IDs are sorted numerically for determinism.
73
+
74
+ Args:
75
+ ids: Iterable of integer IDs
76
+
77
+ Returns:
78
+ Comma-separated string of sorted IDs
79
+ """
80
+ return ",".join(str(id_) for id_ in sorted(ids))
81
+
82
+
83
+ def combine_fingerprints(fingerprints: Iterable[str]) -> str:
84
+ """
85
+ Combine multiple fingerprints into a single hash.
86
+
87
+ Fingerprints are sorted for determinism before being concatenated and hashed.
88
+
89
+ Args:
90
+ fingerprints: Iterable of fingerprint hex strings
91
+
92
+ Returns:
93
+ 64-character lowercase hex string, or empty string if no fingerprints
94
+ """
95
+ sorted_fps = sorted(fingerprints)
96
+ if not sorted_fps:
97
+ return ""
98
+ combined = "|".join(sorted_fps)
99
+ return compute_sha256(combined)
@@ -23,3 +23,6 @@ def raise_for_auth_errors(response):
23
23
  HTTPError: If status code is 401 or 403
24
24
  """
25
25
  raise_for_status_codes(response, (401, 403))
26
+
27
+ def raise_for_external_catch(response):
28
+ response.raise_for_status()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: base-api-utils
3
- Version: 1.1.76
3
+ Version: 1.1.78
4
4
  Summary: Django Rest Framework micro services common utilities
5
5
  Author-email: Román Gutierrez <roman@tipit.net>, Sebastian Marcet <sebastian@tipit.net>
6
6
  Project-URL: Homepage, https://github.com/fntechgit/base-api-utils
@@ -76,6 +76,7 @@ src/base_api_utils/utils/enums.py
76
76
  src/base_api_utils/utils/exceptions.py
77
77
  src/base_api_utils/utils/external_errors.py
78
78
  src/base_api_utils/utils/file_lock.py
79
+ src/base_api_utils/utils/fingerprint_helpers.py
79
80
  src/base_api_utils/utils/http_helpers.py
80
81
  src/base_api_utils/utils/inject_job.py
81
82
  src/base_api_utils/utils/oauth2_custom_schema_base.py
File without changes