karrio-server-core 2025.5rc31__py3-none-any.whl → 2026.1.1__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.
Files changed (56) hide show
  1. karrio/server/core/authentication.py +38 -20
  2. karrio/server/core/config.py +31 -0
  3. karrio/server/core/datatypes.py +30 -4
  4. karrio/server/core/dataunits.py +26 -7
  5. karrio/server/core/exceptions.py +287 -17
  6. karrio/server/core/filters.py +14 -0
  7. karrio/server/core/gateway.py +284 -11
  8. karrio/server/core/logging.py +403 -0
  9. karrio/server/core/middleware.py +104 -2
  10. karrio/server/core/models/base.py +34 -1
  11. karrio/server/core/oauth_validators.py +2 -3
  12. karrio/server/core/permissions.py +1 -2
  13. karrio/server/core/serializers.py +154 -7
  14. karrio/server/core/signals.py +22 -28
  15. karrio/server/core/telemetry.py +573 -0
  16. karrio/server/core/tests/__init__.py +27 -0
  17. karrio/server/core/{tests.py → tests/base.py} +6 -7
  18. karrio/server/core/tests/test_exception_level.py +159 -0
  19. karrio/server/core/tests/test_resource_token.py +593 -0
  20. karrio/server/core/utils.py +688 -38
  21. karrio/server/core/validators.py +144 -222
  22. karrio/server/core/views/oauth.py +13 -12
  23. karrio/server/core/views/references.py +2 -2
  24. karrio/server/iam/apps.py +1 -4
  25. karrio/server/iam/migrations/0002_setup_carrier_permission_groups.py +103 -0
  26. karrio/server/iam/migrations/0003_remove_permission_groups.py +91 -0
  27. karrio/server/iam/permissions.py +7 -134
  28. karrio/server/iam/serializers.py +9 -3
  29. karrio/server/iam/signals.py +2 -4
  30. karrio/server/providers/admin.py +1 -1
  31. karrio/server/providers/migrations/0085_populate_dhl_parcel_de_oauth_credentials.py +82 -0
  32. karrio/server/providers/migrations/0086_rename_dhl_parcel_de_customer_number_to_billing_number.py +71 -0
  33. karrio/server/providers/migrations/0087_alter_carrier_capabilities.py +38 -0
  34. karrio/server/providers/migrations/0088_servicelevel_surcharges.py +24 -0
  35. karrio/server/providers/migrations/0089_servicelevel_cost_max_volume.py +31 -0
  36. karrio/server/providers/migrations/0090_ratesheet_surcharges_servicelevel_zone_surcharge_ids.py +47 -0
  37. karrio/server/providers/migrations/0091_migrate_legacy_zones_surcharges.py +154 -0
  38. karrio/server/providers/models/carrier.py +101 -29
  39. karrio/server/providers/models/service.py +182 -125
  40. karrio/server/providers/models/sheet.py +342 -198
  41. karrio/server/providers/serializers/base.py +263 -2
  42. karrio/server/providers/signals.py +2 -4
  43. karrio/server/providers/templates/providers/oauth_callback.html +105 -0
  44. karrio/server/providers/tests/__init__.py +5 -0
  45. karrio/server/providers/tests/test_connections.py +895 -0
  46. karrio/server/providers/views/carriers.py +1 -3
  47. karrio/server/providers/views/connections.py +322 -2
  48. karrio/server/serializers/abstract.py +112 -21
  49. karrio/server/tracing/utils.py +5 -8
  50. karrio/server/user/models.py +36 -34
  51. karrio/server/user/serializers.py +1 -0
  52. {karrio_server_core-2025.5rc31.dist-info → karrio_server_core-2026.1.1.dist-info}/METADATA +2 -2
  53. {karrio_server_core-2025.5rc31.dist-info → karrio_server_core-2026.1.1.dist-info}/RECORD +55 -38
  54. karrio/server/providers/tests.py +0 -3
  55. {karrio_server_core-2025.5rc31.dist-info → karrio_server_core-2026.1.1.dist-info}/WHEEL +0 -0
  56. {karrio_server_core-2025.5rc31.dist-info → karrio_server_core-2026.1.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,159 @@
1
+ """Tests for exception handler level classification.
2
+
3
+ These tests verify the level classification logic in isolation,
4
+ without requiring the full Django setup.
5
+ """
6
+
7
+ import unittest
8
+ from unittest.mock import MagicMock
9
+ from rest_framework import status
10
+
11
+
12
+ class TestGetDefaultLevel(unittest.TestCase):
13
+ """Tests for get_default_level function."""
14
+
15
+ def setUp(self):
16
+ # Import here to avoid Django setup issues
17
+ from karrio.server.core.exceptions import get_default_level, ERROR_LEVEL_DEFAULTS
18
+ self.get_default_level = get_default_level
19
+ self.ERROR_LEVEL_DEFAULTS = ERROR_LEVEL_DEFAULTS
20
+
21
+ def test_returns_exception_level_when_set(self):
22
+ exc = MagicMock()
23
+ exc.level = "warning"
24
+ result = self.get_default_level(400, exc)
25
+ self.assertEqual(result, "warning")
26
+
27
+ def test_returns_status_code_mapping_for_400(self):
28
+ result = self.get_default_level(400)
29
+ self.assertEqual(result, "error")
30
+
31
+ def test_returns_status_code_mapping_for_404(self):
32
+ result = self.get_default_level(404)
33
+ self.assertEqual(result, "warning")
34
+
35
+ def test_returns_status_code_mapping_for_429(self):
36
+ result = self.get_default_level(429)
37
+ self.assertEqual(result, "warning")
38
+
39
+ def test_returns_status_code_mapping_for_500(self):
40
+ result = self.get_default_level(500)
41
+ self.assertEqual(result, "error")
42
+
43
+ def test_returns_status_code_mapping_for_503(self):
44
+ result = self.get_default_level(503)
45
+ self.assertEqual(result, "warning")
46
+
47
+ def test_returns_error_for_unmapped_4xx(self):
48
+ result = self.get_default_level(418) # I'm a teapot
49
+ self.assertEqual(result, "error")
50
+
51
+ def test_returns_error_for_unmapped_5xx(self):
52
+ result = self.get_default_level(599)
53
+ self.assertEqual(result, "error")
54
+
55
+ def test_returns_info_for_2xx(self):
56
+ result = self.get_default_level(200)
57
+ self.assertEqual(result, "info")
58
+
59
+ def test_exception_level_overrides_status_code_default(self):
60
+ exc = MagicMock()
61
+ exc.level = "info"
62
+ result = self.get_default_level(500, exc) # 500 defaults to "error"
63
+ self.assertEqual(result, "info")
64
+
65
+ def test_ignores_none_level_on_exception(self):
66
+ exc = MagicMock()
67
+ exc.level = None
68
+ result = self.get_default_level(404)
69
+ self.assertEqual(result, "warning")
70
+
71
+
72
+ class TestAPIException(unittest.TestCase):
73
+ """Tests for APIException class with level support."""
74
+
75
+ def setUp(self):
76
+ from karrio.server.core.exceptions import APIException, IndexedAPIException
77
+ self.APIException = APIException
78
+ self.IndexedAPIException = IndexedAPIException
79
+
80
+ def test_default_level_is_none(self):
81
+ exc = self.APIException(detail="Test error")
82
+ self.assertIsNone(exc.level)
83
+
84
+ def test_level_can_be_set_in_constructor(self):
85
+ exc = self.APIException(detail="Test error", level="warning")
86
+ self.assertEqual(exc.level, "warning")
87
+
88
+ def test_status_code_default(self):
89
+ exc = self.APIException(detail="Test error")
90
+ self.assertEqual(exc.status_code, status.HTTP_400_BAD_REQUEST)
91
+
92
+ def test_custom_status_code(self):
93
+ exc = self.APIException(detail="Test error", status_code=500)
94
+ self.assertEqual(exc.status_code, 500)
95
+
96
+
97
+ class TestIndexedAPIException(unittest.TestCase):
98
+ """Tests for IndexedAPIException class."""
99
+
100
+ def setUp(self):
101
+ from karrio.server.core.exceptions import IndexedAPIException
102
+ self.IndexedAPIException = IndexedAPIException
103
+
104
+ def test_index_is_set(self):
105
+ exc = self.IndexedAPIException(index=5, detail="Test error")
106
+ self.assertEqual(exc.index, 5)
107
+
108
+ def test_level_can_be_set(self):
109
+ exc = self.IndexedAPIException(index=0, detail="Test error", level="warning")
110
+ self.assertEqual(exc.level, "warning")
111
+
112
+
113
+ class TestErrorLevelDefaults(unittest.TestCase):
114
+ """Tests for ERROR_LEVEL_DEFAULTS mapping."""
115
+
116
+ def setUp(self):
117
+ from karrio.server.core.exceptions import ERROR_LEVEL_DEFAULTS
118
+ self.ERROR_LEVEL_DEFAULTS = ERROR_LEVEL_DEFAULTS
119
+
120
+ def test_client_errors_mapped(self):
121
+ self.assertIn(400, self.ERROR_LEVEL_DEFAULTS)
122
+ self.assertIn(401, self.ERROR_LEVEL_DEFAULTS)
123
+ self.assertIn(403, self.ERROR_LEVEL_DEFAULTS)
124
+ self.assertIn(404, self.ERROR_LEVEL_DEFAULTS)
125
+ self.assertIn(422, self.ERROR_LEVEL_DEFAULTS)
126
+
127
+ def test_server_errors_mapped(self):
128
+ self.assertIn(500, self.ERROR_LEVEL_DEFAULTS)
129
+ self.assertIn(502, self.ERROR_LEVEL_DEFAULTS)
130
+ self.assertIn(503, self.ERROR_LEVEL_DEFAULTS)
131
+ self.assertIn(504, self.ERROR_LEVEL_DEFAULTS)
132
+
133
+ def test_404_is_warning(self):
134
+ self.assertEqual(self.ERROR_LEVEL_DEFAULTS[404], "warning")
135
+
136
+ def test_429_is_warning(self):
137
+ self.assertEqual(self.ERROR_LEVEL_DEFAULTS[429], "warning")
138
+
139
+ def test_503_is_warning(self):
140
+ self.assertEqual(self.ERROR_LEVEL_DEFAULTS[503], "warning")
141
+
142
+ def test_500_is_error(self):
143
+ self.assertEqual(self.ERROR_LEVEL_DEFAULTS[500], "error")
144
+
145
+
146
+ class TestErrorDatatype(unittest.TestCase):
147
+ """Tests for Error datatype with level field."""
148
+
149
+ def setUp(self):
150
+ from karrio.server.core.datatypes import Error
151
+ self.Error = Error
152
+
153
+ def test_error_has_level_field(self):
154
+ error = self.Error(code="test", message="Test error", level="warning")
155
+ self.assertEqual(error.level, "warning")
156
+
157
+ def test_error_level_defaults_to_none(self):
158
+ error = self.Error(code="test", message="Test error")
159
+ self.assertIsNone(error.level)