karrio-server-graph 2025.5rc1__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 (37) hide show
  1. karrio/server/graph/__init__.py +1 -0
  2. karrio/server/graph/admin.py +3 -0
  3. karrio/server/graph/apps.py +5 -0
  4. karrio/server/graph/forms.py +59 -0
  5. karrio/server/graph/management/__init__.py +0 -0
  6. karrio/server/graph/management/commands/__init__.py +0 -0
  7. karrio/server/graph/management/commands/export_schema.py +9 -0
  8. karrio/server/graph/migrations/0001_initial.py +37 -0
  9. karrio/server/graph/migrations/0002_auto_20210512_1353.py +22 -0
  10. karrio/server/graph/migrations/__init__.py +0 -0
  11. karrio/server/graph/models.py +44 -0
  12. karrio/server/graph/schema.py +46 -0
  13. karrio/server/graph/schemas/__init__.py +2 -0
  14. karrio/server/graph/schemas/base/__init__.py +367 -0
  15. karrio/server/graph/schemas/base/inputs.py +582 -0
  16. karrio/server/graph/schemas/base/mutations.py +871 -0
  17. karrio/server/graph/schemas/base/types.py +1365 -0
  18. karrio/server/graph/serializers.py +388 -0
  19. karrio/server/graph/templates/graphql/graphiql.html +142 -0
  20. karrio/server/graph/templates/karrio/email_change_email.html +13 -0
  21. karrio/server/graph/templates/karrio/email_change_email.txt +13 -0
  22. karrio/server/graph/templates/karrio/password_reset_email.html +14 -0
  23. karrio/server/graph/tests/__init__.py +9 -0
  24. karrio/server/graph/tests/base.py +124 -0
  25. karrio/server/graph/tests/test_carrier_connections.py +219 -0
  26. karrio/server/graph/tests/test_metafield.py +404 -0
  27. karrio/server/graph/tests/test_rate_sheets.py +348 -0
  28. karrio/server/graph/tests/test_templates.py +677 -0
  29. karrio/server/graph/tests/test_user_info.py +71 -0
  30. karrio/server/graph/urls.py +10 -0
  31. karrio/server/graph/utils.py +304 -0
  32. karrio/server/graph/views.py +93 -0
  33. karrio/server/settings/graph.py +7 -0
  34. karrio_server_graph-2025.5rc1.dist-info/METADATA +29 -0
  35. karrio_server_graph-2025.5rc1.dist-info/RECORD +37 -0
  36. karrio_server_graph-2025.5rc1.dist-info/WHEEL +5 -0
  37. karrio_server_graph-2025.5rc1.dist-info/top_level.txt +2 -0
@@ -0,0 +1,71 @@
1
+ from unittest.mock import ANY
2
+ from karrio.server.graph.tests.base import GraphTestCase
3
+
4
+
5
+ class TestUserUpdate(GraphTestCase):
6
+ def test_update_user_info(self):
7
+ response = self.query(
8
+ """
9
+ mutation update_user($data: UpdateUserInput!) {
10
+ update_user(input: $data) {
11
+ user {
12
+ email
13
+ full_name
14
+ is_staff
15
+ last_login
16
+ date_joined
17
+ }
18
+ }
19
+ }
20
+ """,
21
+ operation_name="update_user",
22
+ variables=USER_UPDATE_DATA,
23
+ )
24
+ response_data = response.data
25
+
26
+ self.assertResponseNoErrors(response)
27
+ self.assertDictEqual(response_data, USER_UPDATE_RESPONSE)
28
+
29
+
30
+ class TestTokenMutation(GraphTestCase):
31
+ def test_update_token(self):
32
+ current_token = self.token.key
33
+ response = self.query(
34
+ """
35
+ mutation mutate_token($data: TokenMutationInput!) {
36
+ mutate_token(input: $data) {
37
+ token {
38
+ key
39
+ created
40
+ }
41
+ }
42
+ }
43
+ """,
44
+ operation_name="mutate_token",
45
+ variables={
46
+ "data": {"refresh": True, "password": "test", "key": current_token}
47
+ },
48
+ )
49
+ response_data = response.data
50
+
51
+ self.assertResponseNoErrors(response)
52
+ self.assertFalse(
53
+ response_data["data"]["mutate_token"]["token"]["key"] == current_token
54
+ )
55
+
56
+
57
+ USER_UPDATE_DATA = {"data": {"full_name": "Marco"}}
58
+
59
+ USER_UPDATE_RESPONSE = {
60
+ "data": {
61
+ "update_user": {
62
+ "user": {
63
+ "date_joined": ANY,
64
+ "email": "admin@example.com",
65
+ "full_name": "Marco",
66
+ "is_staff": True,
67
+ "last_login": ANY,
68
+ }
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,10 @@
1
+ """
2
+ karrio server graph module urls
3
+ """
4
+ from django.urls import path, include
5
+
6
+
7
+ app_name = "karrio.server.graph"
8
+ urlpatterns = [
9
+ path("", include("karrio.server.graph.views")),
10
+ ]
@@ -0,0 +1,304 @@
1
+ from karrio.server.core.utils import *
2
+ import typing
3
+ import base64
4
+ import logging
5
+ import functools
6
+ import strawberry
7
+ import dataclasses
8
+ from rest_framework import exceptions
9
+ from django.utils.translation import gettext_lazy as _
10
+
11
+ import karrio.lib as lib
12
+ import karrio.server.core.utils as utils
13
+ import karrio.server.core.models as core
14
+ import karrio.server.orders.models as orders
15
+ import karrio.server.manager.models as manager
16
+ import karrio.server.providers.models as providers
17
+ import karrio.server.core.permissions as permissions
18
+ import karrio.server.core.serializers as serializers
19
+
20
+ Cursor = str
21
+ T = typing.TypeVar("T")
22
+ GenericType = typing.TypeVar("GenericType")
23
+ logger = logging.getLogger(__name__)
24
+
25
+ error_logger = utils.error_wrapper
26
+
27
+ JSON: typing.Any = strawberry.scalar(
28
+ typing.NewType("JSON", object),
29
+ description="The `JSON` scalar type represents JSON values as specified by ECMA-404",
30
+ )
31
+ MANUAL_SHIPMENT_STATUSES = [
32
+ (_.name, _.name)
33
+ for _ in [
34
+ serializers.ShipmentStatus.in_transit,
35
+ serializers.ShipmentStatus.needs_attention,
36
+ serializers.ShipmentStatus.delivery_failed,
37
+ serializers.ShipmentStatus.delivered,
38
+ ]
39
+ ]
40
+
41
+ CurrencyCodeEnum: typing.Any = strawberry.enum( # type: ignore
42
+ lib.Enum("CurrencyCodeEnum", serializers.CURRENCIES)
43
+ )
44
+ CountryCodeEnum: typing.Any = strawberry.enum( # type: ignore
45
+ lib.Enum("CountryCodeEnum", serializers.COUNTRIES)
46
+ )
47
+ DimensionUnitEnum: typing.Any = strawberry.enum( # type: ignore
48
+ lib.Enum("DimensionUnitEnum", serializers.DIMENSION_UNIT)
49
+ )
50
+ WeightUnitEnum: typing.Any = strawberry.enum( # type: ignore
51
+ lib.Enum("WeightUnitEnum", serializers.WEIGHT_UNIT)
52
+ )
53
+ CustomsContentTypeEnum: typing.Any = strawberry.enum( # type: ignore
54
+ lib.Enum("CustomsContentTypeEnum", serializers.CUSTOMS_CONTENT_TYPE)
55
+ )
56
+ IncotermCodeEnum: typing.Any = strawberry.enum( # type: ignore
57
+ lib.Enum("IncotermCodeEnum", serializers.INCOTERMS)
58
+ )
59
+ PaidByEnum: typing.Any = strawberry.enum( # type: ignore
60
+ lib.Enum("PaidByEnum", serializers.PAYMENT_TYPES)
61
+ )
62
+ LabelTypeEnum: typing.Any = strawberry.enum( # type: ignore
63
+ lib.Enum("LabelTypeEnum", serializers.LABEL_TYPES)
64
+ )
65
+ LabelTemplateTypeEnum: typing.Any = strawberry.enum( # type: ignore
66
+ lib.Enum("LabelTemplateTypeEnum", serializers.LABEL_TEMPLATE_TYPES)
67
+ )
68
+ ShipmentStatusEnum: typing.Any = strawberry.enum( # type: ignore
69
+ lib.Enum("ShipmentStatusEnum", serializers.SHIPMENT_STATUS)
70
+ )
71
+ ManualShipmentStatusEnum: typing.Any = strawberry.enum( # type: ignore
72
+ lib.Enum("ManualShipmentStatusEnum", MANUAL_SHIPMENT_STATUSES)
73
+ )
74
+ TrackerStatusEnum: typing.Any = strawberry.enum( # type: ignore
75
+ lib.Enum("TrackerStatusEnum", serializers.TRACKER_STATUS)
76
+ )
77
+ CarrierNameEnum: typing.Any = strawberry.enum( # type: ignore
78
+ lib.Enum("CarrierNameEnum", serializers.CARRIERS)
79
+ )
80
+ MetafieldTypeEnum: typing.Any = strawberry.enum( # type: ignore
81
+ lib.Enum("MetafieldTypeEnum", core.METAFIELD_TYPE)
82
+ )
83
+
84
+
85
+ class MetadataObjectType(lib.Enum):
86
+ carrier = providers.Carrier
87
+ commodity = manager.Commodity
88
+ shipment = manager.Shipment
89
+ tracker = manager.Tracking
90
+ order = orders.Order
91
+
92
+
93
+ MetadataObjectTypeEnum: typing.Any = strawberry.enum( # type: ignore
94
+ lib.StrEnum(
95
+ "MetadataObjectTypeEnum", [(c.name, c.name) for c in list(MetadataObjectType)]
96
+ )
97
+ )
98
+
99
+
100
+ def authentication_required(func):
101
+ @functools.wraps(func)
102
+ def wrapper(info, **kwargs):
103
+ user = getattr(info.context.request, 'user', None)
104
+
105
+ if user is None or user.is_anonymous:
106
+ raise exceptions.AuthenticationFailed(
107
+ _("You are not authenticated"), code="authentication_required"
108
+ )
109
+
110
+ if not user.is_verified():
111
+ raise exceptions.AuthenticationFailed(
112
+ _("Authentication Token not verified"), code="two_factor_required"
113
+ )
114
+
115
+ return func(info, **kwargs)
116
+
117
+ return wrapper
118
+
119
+
120
+ def password_required(func):
121
+ @functools.wraps(func)
122
+ def wrapper(info, **kwargs):
123
+ password = kwargs.get("password")
124
+
125
+ if not info.context.request.user.check_password(password):
126
+ raise exceptions.ValidationError({"password": "Invalid password"})
127
+
128
+ return func(info, **kwargs)
129
+
130
+ return wrapper
131
+
132
+
133
+ def authorization_required(keys: typing.List[str] = None):
134
+ def decorator(func):
135
+ @functools.wraps(func)
136
+ def wrapper(info, **kwargs):
137
+ permissions.check_permissions(
138
+ context=info.context.request,
139
+ keys=keys or [],
140
+ )
141
+
142
+ return func(info, **kwargs)
143
+
144
+ return wrapper
145
+
146
+ return decorator
147
+
148
+
149
+ @strawberry.type
150
+ class ErrorType:
151
+ field: str
152
+ messages: typing.List[str]
153
+
154
+ @staticmethod
155
+ def from_errors(errors):
156
+ return []
157
+
158
+
159
+ @strawberry.input
160
+ class BaseInput:
161
+ def pagination(self) -> typing.Dict[str, typing.Any]:
162
+ return {
163
+ k: v
164
+ for k, v in dataclass_to_dict(self).items()
165
+ if k in ["offset", "before", "after", "first", "last"]
166
+ }
167
+
168
+ def to_dict(self) -> typing.Dict[str, typing.Any]:
169
+ return dataclass_to_dict(self)
170
+
171
+
172
+ @strawberry.type
173
+ class BaseMutation:
174
+ errors: typing.Optional[typing.List[ErrorType]] = None
175
+
176
+
177
+ @strawberry.type
178
+ class UsageStatType:
179
+ date: typing.Optional[str] = None
180
+ label: typing.Optional[str] = None
181
+ count: typing.Optional[float] = None
182
+
183
+ @staticmethod
184
+ def parse(value: dict) -> "UsageStatType":
185
+ return UsageStatType(
186
+ **{k: v for k, v in value.items() if k in UsageStatType.__annotations__}
187
+ )
188
+
189
+
190
+ @strawberry.input
191
+ class UsageFilter(BaseInput):
192
+ date_after: typing.Optional[str] = strawberry.UNSET
193
+ date_before: typing.Optional[str] = strawberry.UNSET
194
+ omit: typing.Optional[typing.List[str]] = strawberry.UNSET
195
+
196
+
197
+ @dataclasses.dataclass
198
+ @strawberry.type
199
+ class Connection(typing.Generic[GenericType]):
200
+ """Represents a paginated relationship between two entities
201
+ This pattern is used when the relationship itself has attributes.
202
+ In a Facebook-based domain example, a friendship between two people
203
+ would be a connection that might have a `friendshipStartTime`
204
+ """
205
+
206
+ page_info: "PageInfo"
207
+ edges: typing.List["Edge[GenericType]"]
208
+
209
+
210
+ @dataclasses.dataclass
211
+ @strawberry.type
212
+ class PageInfo:
213
+ """Pagination context to navigate objects with cursor-based pagination
214
+ Instead of classic offset pagination via `page` and `limit` parameters,
215
+ here we have a cursor of the last object and we fetch items starting from that one
216
+ Read more at:
217
+ - https://graphql.org/learn/pagination/#pagination-and-edges
218
+ - https://relay.dev/graphql/connections.htm
219
+ """
220
+
221
+ count: int
222
+ has_next_page: bool
223
+ has_previous_page: bool
224
+ start_cursor: typing.Optional[str]
225
+ end_cursor: typing.Optional[str]
226
+
227
+
228
+ @dataclasses.dataclass
229
+ @strawberry.type
230
+ class Edge(typing.Generic[GenericType]):
231
+ """An edge may contain additional information of the relationship. This is the trivial case"""
232
+
233
+ node: GenericType
234
+ cursor: str
235
+
236
+
237
+ @strawberry.input
238
+ class Paginated(BaseInput):
239
+ offset: typing.Optional[int] = strawberry.UNSET
240
+ first: typing.Optional[int] = strawberry.UNSET
241
+
242
+
243
+ def build_entity_cursor(entity: T):
244
+ """Adapt this method to build an *opaque* ID from an instance"""
245
+ entityid = f"{getattr(entity, 'id', id(entity))}".encode("utf-8")
246
+ return base64.b64encode(entityid).decode()
247
+
248
+
249
+ def paginated_connection(
250
+ queryset,
251
+ first: int = 25,
252
+ offset: int = 0,
253
+ ) -> Connection[T]:
254
+ """A non-trivial implementation should efficiently fetch only
255
+ the necessary books after the offset.
256
+ For simplicity, here we build the list and then slice it accordingly
257
+ """
258
+
259
+ # Fetch the requested results plus one, just to calculate `has_next_page`
260
+ # fmt: off
261
+ results = queryset[offset:offset+first+1]
262
+ # fmt: on
263
+
264
+ edges: typing.List[typing.Any] = [
265
+ Edge(node=typing.cast(T, entity), cursor=build_entity_cursor(entity))
266
+ for entity in results
267
+ ]
268
+ return Connection(
269
+ page_info=PageInfo(
270
+ count=queryset.count(),
271
+ has_previous_page=False,
272
+ has_next_page=len(results) > first,
273
+ start_cursor=edges[0].cursor if edges else None,
274
+ end_cursor=edges[-2].cursor if len(edges) > 1 else None,
275
+ ),
276
+ edges=(edges[:-1] if len(edges) > first else edges),
277
+ )
278
+
279
+
280
+ def is_unset(v: typing.Any) -> bool:
281
+ return isinstance(v, type(strawberry.UNSET)) or v == strawberry.UNSET
282
+
283
+
284
+ def _dict_factory(items):
285
+ if isinstance(next(iter(items), None), tuple):
286
+ return dict(
287
+ [
288
+ (key, _dict_factory(value) if isinstance(value, list) else value)
289
+ for key, value in items
290
+ if not is_unset(value)
291
+ ]
292
+ )
293
+
294
+ return items
295
+
296
+
297
+ def dataclass_to_dict(data):
298
+ return lib.to_dict(
299
+ dataclasses.asdict(
300
+ data,
301
+ dict_factory=_dict_factory,
302
+ ),
303
+ clear_empty=False,
304
+ )
@@ -0,0 +1,93 @@
1
+ """
2
+ karrio server graph module urls
3
+ """
4
+
5
+ import pydoc
6
+ import typing
7
+ import logging
8
+ from django.urls import path
9
+ from django.conf import settings
10
+ from rest_framework import exceptions
11
+ from django.views.decorators.csrf import csrf_exempt
12
+ import graphql.error.graphql_error as graphql
13
+ import strawberry.django.views as views
14
+ import strawberry.types as types
15
+ import strawberry.http as http
16
+
17
+ import karrio.lib as lib
18
+ import karrio.server.conf as conf
19
+ import karrio.server.graph.schema as schema
20
+
21
+ logger = logging.getLogger(__name__)
22
+ ACCESS_METHOD = getattr(
23
+ settings,
24
+ "SESSION_ACCESS_MIXIN",
25
+ "karrio.server.core.authentication.AccessMixin",
26
+ )
27
+ AccessMixin: typing.Any = pydoc.locate(ACCESS_METHOD)
28
+
29
+
30
+ class GraphQLView(AccessMixin, views.GraphQLView):
31
+ def dispatch(self, request, *args, **kwargs):
32
+ should_render_graphiql = lib.failsafe(
33
+ lambda: self.should_render_graphiql(request)
34
+ )
35
+
36
+ if should_render_graphiql:
37
+ context = dict(APP_NAME=conf.settings.APP_NAME)
38
+
39
+ return self._render_graphiql(request, context=context)
40
+
41
+ return super().dispatch(request, *args, **kwargs)
42
+
43
+ def process_result(
44
+ self, request, result: types.ExecutionResult
45
+ ) -> http.GraphQLHTTPResponse:
46
+ data: http.GraphQLHTTPResponse = {"data": result.data}
47
+
48
+ if result.errors:
49
+ data["errors"] = [self.format_graphql_error(err) for err in result.errors]
50
+ if result.extensions:
51
+ data["extensions"] = result.extensions
52
+
53
+ return data
54
+
55
+ def format_graphql_error(self, error: graphql.GraphQLError):
56
+ formatted_error: dict = (
57
+ graphql.format_error(error) # type: ignore
58
+ if isinstance(error, graphql.GraphQLError)
59
+ else {}
60
+ )
61
+
62
+ if isinstance(error.original_error, exceptions.APIException):
63
+ formatted_error["message"] = str(error.original_error.detail)
64
+ formatted_error["code"] = (
65
+ error.original_error.get_codes()
66
+ if hasattr(error.original_error, "get_codes")
67
+ else getattr(
68
+ error.original_error,
69
+ "code",
70
+ getattr(error.original_error, "default_code", None),
71
+ )
72
+ )
73
+ formatted_error["status_code"] = error.original_error.status_code
74
+
75
+ if isinstance(error.original_error, exceptions.ValidationError):
76
+ formatted_error["message"] = str(error.original_error.default_detail)
77
+ formatted_error["validation"] = error.original_error.detail
78
+
79
+ return formatted_error
80
+
81
+
82
+ urlpatterns = [
83
+ path(
84
+ "graphql/",
85
+ csrf_exempt(GraphQLView.as_view(schema=schema.schema)),
86
+ name="graphql",
87
+ ),
88
+ path(
89
+ "graphql",
90
+ csrf_exempt(GraphQLView.as_view(schema=schema.schema)),
91
+ name="graphql-api",
92
+ ),
93
+ ]
@@ -0,0 +1,7 @@
1
+ # type: ignore
2
+ from karrio.server.settings.base import *
3
+
4
+
5
+ INSTALLED_APPS += [
6
+ "strawberry.django",
7
+ ]
@@ -0,0 +1,29 @@
1
+ Metadata-Version: 2.4
2
+ Name: karrio_server_graph
3
+ Version: 2025.5rc1
4
+ Summary: Multi-carrier shipping API Graph module
5
+ Author-email: karrio <hello@karrio.io>
6
+ License-Expression: Apache-2.0
7
+ Project-URL: Homepage, https://github.com/karrioapi/karrio
8
+ Classifier: Programming Language :: Python :: 3
9
+ Requires-Python: >=3.11
10
+ Description-Content-Type: text/markdown
11
+ Requires-Dist: karrio_server_core
12
+ Requires-Dist: django-filter
13
+ Requires-Dist: strawberry-graphql
14
+
15
+ # karrio.server.graph
16
+
17
+ This package is a module of the [karrio](https://pypi.org/project/karrio.server) universal shipping API.
18
+
19
+ ## Requirements
20
+
21
+ `Python 3.11+`
22
+
23
+ ## Installation
24
+
25
+ ```bash
26
+ pip install karrio.server.graph
27
+ ```
28
+
29
+ Check the [karrio docs](https://docs.karrio.io) to get started.
@@ -0,0 +1,37 @@
1
+ karrio/server/graph/__init__.py,sha256=iOEMwnlORWezdO8-2vxBIPSR37D7JGjluZ8f55vzxls,81
2
+ karrio/server/graph/admin.py,sha256=suMo4x8I3JBxAFBVIdE-5qnqZ6JAZV0FESABHOSc-vg,63
3
+ karrio/server/graph/apps.py,sha256=grL5ySDnW2amzj0nTXFm-WCoTWViVYvKqGlsi4k5epI,99
4
+ karrio/server/graph/forms.py,sha256=5iGC3hj6dsAsupYz4QubyygTeTKDRR56Q5mYq0vbGhI,1962
5
+ karrio/server/graph/models.py,sha256=CEnE4AsVyjBufyK6ebWmUH3s8DwA0HvZg0fUoZb5Pn4,1321
6
+ karrio/server/graph/schema.py,sha256=2dXM8nD1usOc1S6QSalajoFmgwYuXxsrwj20AJ5HtT4,1151
7
+ karrio/server/graph/serializers.py,sha256=vwP_U59aLnsnYVjwn3jrpz7SxOFzT_vplzSK-2FcsrU,13692
8
+ karrio/server/graph/urls.py,sha256=HKo0gkx5TgoWDV0ap2QCtueNTmaAqvX6qVDe3c2UT1E,183
9
+ karrio/server/graph/utils.py,sha256=eeIKeTfGP-8U6MkdMdTyobnk6wbNEE75Bm0cFu85RfU,9031
10
+ karrio/server/graph/views.py,sha256=qWfa-wteB-Mb7glAYz6SVlZSwHPw_ImMm7XVmewdmTQ,2889
11
+ karrio/server/graph/management/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ karrio/server/graph/management/commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ karrio/server/graph/management/commands/export_schema.py,sha256=WMwiDISpTwtA6MRqSxKjX9LOa3caWEHA_OKRxPIgcsQ,294
14
+ karrio/server/graph/migrations/0001_initial.py,sha256=oeaS5JSkQP6w9ulYar3mdn695ybkog-1QoyKUIW1SCg,1609
15
+ karrio/server/graph/migrations/0002_auto_20210512_1353.py,sha256=TnUwR9EP0qp3gJ38f9w0PYawK2VheDtqXEgyRhYZS2M,538
16
+ karrio/server/graph/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
+ karrio/server/graph/schemas/__init__.py,sha256=Kn-I1j3HP3jwZccpz6FL9r1k6b3UEAMVh2kFPCKNS0w,80
18
+ karrio/server/graph/schemas/base/__init__.py,sha256=aarmPw7csVZCeRxRaU6FNZ6tbN8SOla-nbYqlXFn3lw,14272
19
+ karrio/server/graph/schemas/base/inputs.py,sha256=Lptv7JfyXP6EKMSQ838oZ4Qyr9GckbrJzXiLg1CCJMc,20762
20
+ karrio/server/graph/schemas/base/mutations.py,sha256=a1_OxbhRV89PrOZ-T0dMEfiGJAzEWSYosGDrTkStUO0,28731
21
+ karrio/server/graph/schemas/base/types.py,sha256=KPN154yeFiIyFU0pfkhPjcP-h5Nr2hrzdQkDX--ufeU,46371
22
+ karrio/server/graph/templates/graphql/graphiql.html,sha256=MQjQbBqoRE0QLsOUck8SaXo6B2oJO8dT6YZzUqbDan0,3786
23
+ karrio/server/graph/templates/karrio/email_change_email.html,sha256=YHqTy9VGV_s7kio57Tg3v7TCIN3QlnPHi2ioTOcHJLE,467
24
+ karrio/server/graph/templates/karrio/email_change_email.txt,sha256=NXXuzLR63hn2F1fVAjzmuguptuuTvujwqI7YLSrQoio,431
25
+ karrio/server/graph/templates/karrio/password_reset_email.html,sha256=dttqYVL73cQNuTFsVdn2GV4Ckee8PTY8oEF53GbDRcg,553
26
+ karrio/server/graph/tests/__init__.py,sha256=dPzsYY5hoO5gmY6fhL8tiz7Bfst8RB3JzsBVHZazHRE,338
27
+ karrio/server/graph/tests/base.py,sha256=m3k1CE-d9JHDsqfyXX0Fwksmkel33S7sh9-zkypp8wc,4004
28
+ karrio/server/graph/tests/test_carrier_connections.py,sha256=PoXxJB53jBIz8j1GYCzTiaTW14Q0D1McdOkDYgcOqNM,6140
29
+ karrio/server/graph/tests/test_metafield.py,sha256=K7Oon0CLEm_MUMbmcu0t2iAZvFN8Wl7Kp4QAWeUXo_Y,12783
30
+ karrio/server/graph/tests/test_rate_sheets.py,sha256=iNdN0GR26wv8lZd7slUbHTs1HbasKlMk8gk-A-XRZ5c,10079
31
+ karrio/server/graph/tests/test_templates.py,sha256=WVU6vcfr6tEk917uSn1dECU8bkQtgD7FNuE-GJuFido,21626
32
+ karrio/server/graph/tests/test_user_info.py,sha256=K91BL7SgxLWflCZriSVI8Vt5M5RIqmSCHKrgN2w8GmM,1928
33
+ karrio/server/settings/graph.py,sha256=cz2yQHbp3xCfyFKuUkPEFfkI2fFVggExIY49wGz7mt0,106
34
+ karrio_server_graph-2025.5rc1.dist-info/METADATA,sha256=XrVtgey_oDUgkKf7ELTbOV4Yj1XDr4ZYvOx4nWdYwq4,743
35
+ karrio_server_graph-2025.5rc1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ karrio_server_graph-2025.5rc1.dist-info/top_level.txt,sha256=D1D7x8R3cTfjF_15mfiO7wCQ5QMtuM4x8GaPr7z5i78,12
37
+ karrio_server_graph-2025.5rc1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ dist
2
+ karrio