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.
- karrio/server/graph/__init__.py +1 -0
- karrio/server/graph/admin.py +3 -0
- karrio/server/graph/apps.py +5 -0
- karrio/server/graph/forms.py +59 -0
- karrio/server/graph/management/__init__.py +0 -0
- karrio/server/graph/management/commands/__init__.py +0 -0
- karrio/server/graph/management/commands/export_schema.py +9 -0
- karrio/server/graph/migrations/0001_initial.py +37 -0
- karrio/server/graph/migrations/0002_auto_20210512_1353.py +22 -0
- karrio/server/graph/migrations/__init__.py +0 -0
- karrio/server/graph/models.py +44 -0
- karrio/server/graph/schema.py +46 -0
- karrio/server/graph/schemas/__init__.py +2 -0
- karrio/server/graph/schemas/base/__init__.py +367 -0
- karrio/server/graph/schemas/base/inputs.py +582 -0
- karrio/server/graph/schemas/base/mutations.py +871 -0
- karrio/server/graph/schemas/base/types.py +1365 -0
- karrio/server/graph/serializers.py +388 -0
- karrio/server/graph/templates/graphql/graphiql.html +142 -0
- karrio/server/graph/templates/karrio/email_change_email.html +13 -0
- karrio/server/graph/templates/karrio/email_change_email.txt +13 -0
- karrio/server/graph/templates/karrio/password_reset_email.html +14 -0
- karrio/server/graph/tests/__init__.py +9 -0
- karrio/server/graph/tests/base.py +124 -0
- karrio/server/graph/tests/test_carrier_connections.py +219 -0
- karrio/server/graph/tests/test_metafield.py +404 -0
- karrio/server/graph/tests/test_rate_sheets.py +348 -0
- karrio/server/graph/tests/test_templates.py +677 -0
- karrio/server/graph/tests/test_user_info.py +71 -0
- karrio/server/graph/urls.py +10 -0
- karrio/server/graph/utils.py +304 -0
- karrio/server/graph/views.py +93 -0
- karrio/server/settings/graph.py +7 -0
- karrio_server_graph-2025.5rc1.dist-info/METADATA +29 -0
- karrio_server_graph-2025.5rc1.dist-info/RECORD +37 -0
- karrio_server_graph-2025.5rc1.dist-info/WHEEL +5 -0
- 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,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,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,,
|