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,677 @@
1
+ from unittest.mock import ANY
2
+ from karrio.server.graph.tests.base import GraphTestCase
3
+ import karrio.server.manager.models as manager
4
+ import karrio.server.graph.models as graph
5
+
6
+
7
+ class TestAddressTemplate(GraphTestCase):
8
+ def _create_address_template(self):
9
+ return self.query(
10
+ """
11
+ mutation create_address_template($data: CreateAddressTemplateInput!) {
12
+ create_address_template(input: $data) {
13
+ template {
14
+ id
15
+ is_default
16
+ label
17
+ address {
18
+ company_name
19
+ person_name
20
+ address_line1
21
+ address_line2
22
+ postal_code
23
+ residential
24
+ city
25
+ state_code
26
+ country_code
27
+ email
28
+ phone_number
29
+ validation
30
+ validate_location
31
+ }
32
+ }
33
+ errors {
34
+ field
35
+ messages
36
+ }
37
+ }
38
+ }
39
+ """,
40
+ operation_name="create_address_template",
41
+ variables=ADDRESS_TEMPLATE_DATA,
42
+ )
43
+
44
+ def test_create_address_template(self):
45
+ response = self._create_address_template()
46
+ self.assertResponseNoErrors(response)
47
+ self.assertDictEqual(response.data, ADDRESS_TEMPLATE_RESPONSE)
48
+
49
+ def test_update_address_template(self):
50
+ template = self._create_address_template().data
51
+ response = self.query(
52
+ """
53
+ mutation update_address_template($data: UpdateAddressTemplateInput!) {
54
+ update_address_template(input: $data) {
55
+ template {
56
+ id
57
+ is_default
58
+ label
59
+ address {
60
+ company_name
61
+ person_name
62
+ address_line1
63
+ address_line2
64
+ postal_code
65
+ residential
66
+ city
67
+ state_code
68
+ country_code
69
+ email
70
+ phone_number
71
+ validation
72
+ validate_location
73
+ }
74
+ }
75
+ errors {
76
+ field
77
+ messages
78
+ }
79
+ }
80
+ }
81
+ """,
82
+ operation_name="update_address_template",
83
+ variables={
84
+ "data": {
85
+ "id": template["data"]["create_address_template"]["template"]["id"],
86
+ **ADDRESS_TEMPLATE_UPDATE_DATA["data"],
87
+ },
88
+ },
89
+ )
90
+ response_data = response.data
91
+
92
+ self.assertResponseNoErrors(response)
93
+ self.assertDictEqual(response_data, ADDRESS_TEMPLATE_UPDATE_RESPONSE)
94
+
95
+ def test_delete_address_template(self):
96
+ template = self._create_address_template().data
97
+ template_id = template["data"]["create_address_template"]["template"]["id"]
98
+ delete_template_data = {"data": {"id": template_id}}
99
+ response = self.query(
100
+ """
101
+ mutation delete_template($data: DeleteMutationInput!) {
102
+ delete_template(input: $data) {
103
+ id
104
+ }
105
+ }
106
+ """,
107
+ operation_name="delete_template",
108
+ variables=delete_template_data,
109
+ )
110
+ response_data = response.data
111
+
112
+ self.assertResponseNoErrors(response)
113
+ self.assertEqual(response_data["data"]["delete_template"]["id"], template_id)
114
+ self.assertEqual(len(graph.Template.objects.all()), 0)
115
+ self.assertEqual(len(manager.Address.objects.all()), 0)
116
+
117
+
118
+ class TestParcelTemplate(GraphTestCase):
119
+ def _create_parcel_template(self):
120
+ return self.query(
121
+ """
122
+ mutation create_parcel_template($data: CreateParcelTemplateInput!) {
123
+ create_parcel_template(input: $data) {
124
+ template {
125
+ id
126
+ is_default
127
+ label
128
+ parcel {
129
+ width
130
+ height
131
+ length
132
+ dimension_unit
133
+ weight
134
+ weight_unit
135
+ packaging_type
136
+ package_preset
137
+ }
138
+ }
139
+ errors {
140
+ field
141
+ messages
142
+ }
143
+ }
144
+ }
145
+ """,
146
+ operation_name="create_parcel_template",
147
+ variables=PARCEL_TEMPLATE_DATA,
148
+ )
149
+
150
+ def test_create_parcel_template(self):
151
+ response = self._create_parcel_template()
152
+ response_data = response.data
153
+
154
+ self.assertResponseNoErrors(response)
155
+ self.assertDictEqual(response_data, PARCEL_TEMPLATE_RESPONSE)
156
+
157
+ def test_update_parcel_template(self):
158
+ template = self._create_parcel_template().data
159
+ response = self.query(
160
+ """
161
+ mutation update_parcel_template($data: UpdateParcelTemplateInput!) {
162
+ update_parcel_template(input: $data) {
163
+ template {
164
+ id
165
+ is_default
166
+ label
167
+ parcel {
168
+ width
169
+ height
170
+ length
171
+ dimension_unit
172
+ weight
173
+ weight_unit
174
+ packaging_type
175
+ package_preset
176
+ }
177
+ }
178
+ errors {
179
+ field
180
+ messages
181
+ }
182
+ }
183
+ }
184
+ """,
185
+ operation_name="update_parcel_template",
186
+ variables={
187
+ "data": {
188
+ "id": template["data"]["create_parcel_template"]["template"]["id"],
189
+ **PARCEL_TEMPLATE_UPDATE_DATA["data"],
190
+ },
191
+ },
192
+ )
193
+ response_data = response.data
194
+
195
+ self.assertResponseNoErrors(response)
196
+ self.assertDictEqual(response_data, PARCEL_TEMPLATE_UPDATE_RESPONSE)
197
+
198
+ def test_delete_parcel_template(self):
199
+ template = self._create_parcel_template().data
200
+ template_id = template["data"]["create_parcel_template"]["template"]["id"]
201
+ delete_template_data = {"data": {"id": template_id}}
202
+ response = self.query(
203
+ """
204
+ mutation delete_template($data: DeleteMutationInput!) {
205
+ delete_template(input: $data) {
206
+ id
207
+ }
208
+ }
209
+ """,
210
+ operation_name="delete_template",
211
+ variables=delete_template_data,
212
+ )
213
+ response_data = response.data
214
+
215
+ self.assertResponseNoErrors(response)
216
+ self.assertEqual(response_data["data"]["delete_template"]["id"], template_id)
217
+ self.assertEqual(len(graph.Template.objects.all()), 0)
218
+ self.assertEqual(len(manager.Parcel.objects.all()), 0)
219
+
220
+
221
+ class TestCustomsTemplate(GraphTestCase):
222
+ def _create_customs_info_template(self):
223
+ return self.query(
224
+ """
225
+ mutation create_customs_template($data: CreateCustomsTemplateInput!) {
226
+ create_customs_template(input: $data) {
227
+ template {
228
+ id
229
+ label
230
+ is_default
231
+ customs {
232
+ incoterm
233
+ content_type
234
+ commercial_invoice
235
+ content_description
236
+ duty {
237
+ paid_by
238
+ currency
239
+ account_number
240
+ declared_value
241
+ bill_to {
242
+ company_name
243
+ person_name
244
+ address_line1
245
+ address_line2
246
+ postal_code
247
+ residential
248
+ city
249
+ state_code
250
+ country_code
251
+ email
252
+ phone_number
253
+ validation
254
+ validate_location
255
+ }
256
+ }
257
+ invoice
258
+ signer
259
+ certify
260
+ commodities {
261
+ id
262
+ sku
263
+ weight
264
+ quantity
265
+ weight_unit
266
+ description
267
+ value_amount
268
+ value_currency
269
+ origin_country
270
+ }
271
+ }
272
+ }
273
+ errors {
274
+ field
275
+ messages
276
+ }
277
+ }
278
+ }
279
+ """,
280
+ operation_name="create_customs_template",
281
+ variables=CUSTOMS_TEMPLATE_DATA,
282
+ )
283
+
284
+ def test_create_customs_info_template(self):
285
+ response = self._create_customs_info_template()
286
+ response_data = response.data
287
+
288
+ self.assertResponseNoErrors(response)
289
+ self.assertDictEqual(response_data, CUSTOMS_TEMPLATE_RESPONSE)
290
+
291
+ def test_update_customs_info_template(self):
292
+ template = self._create_customs_info_template().data
293
+ CUSTOMS_TEMPLATE_UPDATE_DATA["data"]["id"] = template["data"][
294
+ "create_customs_template"
295
+ ]["template"]["id"]
296
+ CUSTOMS_TEMPLATE_UPDATE_DATA["data"]["customs"]["commodities"][0]["id"] = next(
297
+ c["id"]
298
+ for c in template["data"]["create_customs_template"]["template"]["customs"][
299
+ "commodities"
300
+ ]
301
+ if c["sku"]
302
+ == template["data"]["create_customs_template"]["template"]["customs"][
303
+ "commodities"
304
+ ][0]["sku"]
305
+ )
306
+
307
+ response = self.query(
308
+ """
309
+ mutation update_customs_template($data: UpdateCustomsTemplateInput!) {
310
+ update_customs_template(input: $data) {
311
+ template {
312
+ id
313
+ is_default
314
+ label
315
+ customs {
316
+ incoterm
317
+ content_type
318
+ commercial_invoice
319
+ content_description
320
+ duty {
321
+ paid_by
322
+ currency
323
+ account_number
324
+ declared_value
325
+ bill_to {
326
+ company_name
327
+ person_name
328
+ address_line1
329
+ address_line2
330
+ postal_code
331
+ residential
332
+ city
333
+ state_code
334
+ country_code
335
+ email
336
+ phone_number
337
+ validation
338
+ validate_location
339
+ }
340
+ }
341
+ invoice
342
+ signer
343
+ certify
344
+ commodities {
345
+ id
346
+ sku
347
+ weight
348
+ quantity
349
+ weight_unit
350
+ description
351
+ value_amount
352
+ value_currency
353
+ origin_country
354
+ }
355
+ }
356
+ }
357
+ errors {
358
+ field
359
+ messages
360
+ }
361
+ }
362
+ }
363
+ """,
364
+ operation_name="update_customs_template",
365
+ variables=CUSTOMS_TEMPLATE_UPDATE_DATA,
366
+ )
367
+ response_data = response.data
368
+
369
+ self.assertResponseNoErrors(response)
370
+ self.assertDictEqual(response_data, CUSTOMS_TEMPLATE_UPDATE_RESPONSE)
371
+
372
+ def test_delete_customs_info(self):
373
+ template = self._create_customs_info_template().data
374
+ template_id = template["data"]["create_customs_template"]["template"]["id"]
375
+ delete_template_data = {"data": {"id": template_id}}
376
+ response = self.query(
377
+ """
378
+ mutation delete_template($data: DeleteMutationInput!) {
379
+ delete_template(input: $data) {
380
+ id
381
+ }
382
+ }
383
+ """,
384
+ operation_name="delete_template",
385
+ variables=delete_template_data,
386
+ )
387
+ response_data = response.data
388
+
389
+ self.assertResponseNoErrors(response)
390
+ self.assertEqual(response_data["data"]["delete_template"]["id"], template_id)
391
+ self.assertEqual(len(graph.Template.objects.all()), 0)
392
+ self.assertEqual(len(manager.Customs.objects.all()), 0)
393
+
394
+
395
+ ADDRESS_TEMPLATE_DATA = {
396
+ "data": {
397
+ "label": "Warehouse",
398
+ "address": {
399
+ "address_line1": "125 Church St",
400
+ "person_name": "John Doe",
401
+ "company_name": "A corp.",
402
+ "phone_number": "514 000 0000",
403
+ "city": "Moncton",
404
+ "country_code": "CA",
405
+ "postal_code": "E1C4Z8",
406
+ "residential": False,
407
+ "state_code": "NB",
408
+ },
409
+ }
410
+ }
411
+
412
+ ADDRESS_TEMPLATE_RESPONSE = {
413
+ "data": {
414
+ "create_address_template": {
415
+ "template": {
416
+ "id": ANY,
417
+ "is_default": False,
418
+ "label": "Warehouse",
419
+ "address": {
420
+ "company_name": "A corp.",
421
+ "person_name": "John Doe",
422
+ "address_line1": "125 Church St",
423
+ "address_line2": None,
424
+ "postal_code": "E1C4Z8",
425
+ "residential": False,
426
+ "city": "Moncton",
427
+ "state_code": "NB",
428
+ "country_code": "CA",
429
+ "email": None,
430
+ "phone_number": "+1 514-000-0000",
431
+ "validation": None,
432
+ "validate_location": None,
433
+ },
434
+ },
435
+ "errors": None,
436
+ }
437
+ }
438
+ }
439
+
440
+ ADDRESS_TEMPLATE_UPDATE_DATA = {
441
+ "data": {
442
+ "label": "Warehouse Update",
443
+ "address": {
444
+ "city": "Moncton",
445
+ "email": "test@gmail.com",
446
+ "person_name": "John Moe",
447
+ },
448
+ }
449
+ }
450
+
451
+ ADDRESS_TEMPLATE_UPDATE_RESPONSE = {
452
+ "data": {
453
+ "update_address_template": {
454
+ "template": {
455
+ "id": ANY,
456
+ "address": {
457
+ "address_line1": "125 Church St",
458
+ "address_line2": None,
459
+ "city": "Moncton",
460
+ "company_name": "A corp.",
461
+ "country_code": "CA",
462
+ "email": "test@gmail.com",
463
+ "person_name": "John Moe",
464
+ "phone_number": "+1 514-000-0000",
465
+ "postal_code": "E1C4Z8",
466
+ "residential": False,
467
+ "state_code": "NB",
468
+ "validate_location": None,
469
+ "validation": None,
470
+ },
471
+ "id": ANY,
472
+ "is_default": False,
473
+ "label": "Warehouse Update",
474
+ },
475
+ "errors": None,
476
+ }
477
+ }
478
+ }
479
+
480
+ PARCEL_TEMPLATE_DATA = {
481
+ "data": {
482
+ "label": "Purple Pack",
483
+ "parcel": {
484
+ "weight": 1,
485
+ "weight_unit": "KG",
486
+ "package_preset": "canadapost_corrugated_small_box",
487
+ },
488
+ }
489
+ }
490
+
491
+ PARCEL_TEMPLATE_RESPONSE = {
492
+ "data": {
493
+ "create_parcel_template": {
494
+ "errors": None,
495
+ "template": {
496
+ "id": ANY,
497
+ "is_default": False,
498
+ "label": "Purple Pack",
499
+ "parcel": {
500
+ "dimension_unit": "CM",
501
+ "height": 32.0,
502
+ "length": 32.0,
503
+ "package_preset": "canadapost_corrugated_small_box",
504
+ "packaging_type": None,
505
+ "weight": 1.0,
506
+ "weight_unit": "KG",
507
+ "width": 42.0,
508
+ },
509
+ },
510
+ }
511
+ }
512
+ }
513
+
514
+ PARCEL_TEMPLATE_UPDATE_DATA = {
515
+ "data": {
516
+ "parcel": {
517
+ "weight": 0.45,
518
+ "weight_unit": "LB",
519
+ }
520
+ }
521
+ }
522
+
523
+ PARCEL_TEMPLATE_UPDATE_RESPONSE = {
524
+ "data": {
525
+ "update_parcel_template": {
526
+ "errors": None,
527
+ "template": {
528
+ "id": ANY,
529
+ "is_default": False,
530
+ "label": "Purple Pack",
531
+ "parcel": {
532
+ "dimension_unit": "CM",
533
+ "height": 32.0,
534
+ "length": 32.0,
535
+ "package_preset": "canadapost_corrugated_small_box",
536
+ "packaging_type": None,
537
+ "weight": 0.45,
538
+ "weight_unit": "LB",
539
+ "width": 42.0,
540
+ },
541
+ },
542
+ }
543
+ }
544
+ }
545
+
546
+ CUSTOMS_TEMPLATE_DATA = {
547
+ "data": {
548
+ "label": "Customs info template",
549
+ "customs": {
550
+ "content_type": "documents",
551
+ "incoterm": "DDU",
552
+ "commodities": [
553
+ {"weight": 1.15, "weight_unit": "KG", "sku": "6787L8K7J8L7J8L7K8"},
554
+ {
555
+ "weight": 0.75,
556
+ "weight_unit": "KG",
557
+ "sku": "3PO4I5J4PO5I4HI5OH",
558
+ "quantity": 4,
559
+ },
560
+ ],
561
+ },
562
+ }
563
+ }
564
+
565
+ CUSTOMS_TEMPLATE_RESPONSE = {
566
+ "data": {
567
+ "create_customs_template": {
568
+ "template": {
569
+ "id": ANY,
570
+ "label": "Customs info template",
571
+ "is_default": False,
572
+ "customs": {
573
+ "incoterm": "DDU",
574
+ "content_type": "documents",
575
+ "commercial_invoice": None,
576
+ "content_description": None,
577
+ "duty": None,
578
+ "invoice": None,
579
+ "signer": None,
580
+ "certify": None,
581
+ "commodities": [
582
+ {
583
+ "id": ANY,
584
+ "sku": "6787L8K7J8L7J8L7K8",
585
+ "weight": 1.15,
586
+ "quantity": 1,
587
+ "weight_unit": "KG",
588
+ "description": None,
589
+ "value_amount": None,
590
+ "value_currency": None,
591
+ "origin_country": None,
592
+ },
593
+ {
594
+ "id": ANY,
595
+ "sku": "3PO4I5J4PO5I4HI5OH",
596
+ "weight": 0.75,
597
+ "quantity": 4,
598
+ "weight_unit": "KG",
599
+ "description": None,
600
+ "value_amount": None,
601
+ "value_currency": None,
602
+ "origin_country": None,
603
+ },
604
+ ],
605
+ },
606
+ },
607
+ "errors": None,
608
+ }
609
+ }
610
+ }
611
+
612
+ CUSTOMS_TEMPLATE_UPDATE_DATA = {
613
+ "data": {
614
+ "customs": {
615
+ "commodities": [
616
+ {
617
+ "weight": 1,
618
+ "weight_unit": "LB",
619
+ }
620
+ ],
621
+ "duty": {"paid_by": "sender"},
622
+ }
623
+ }
624
+ }
625
+
626
+ CUSTOMS_TEMPLATE_UPDATE_RESPONSE = {
627
+ "data": {
628
+ "update_customs_template": {
629
+ "template": {
630
+ "id": ANY,
631
+ "is_default": False,
632
+ "label": "Customs info template",
633
+ "customs": {
634
+ "incoterm": "DDU",
635
+ "content_type": "documents",
636
+ "commercial_invoice": None,
637
+ "content_description": None,
638
+ "duty": {
639
+ "paid_by": "sender",
640
+ "currency": None,
641
+ "account_number": None,
642
+ "declared_value": None,
643
+ "bill_to": None,
644
+ },
645
+ "invoice": None,
646
+ "signer": None,
647
+ "certify": None,
648
+ "commodities": [
649
+ {
650
+ "id": ANY,
651
+ "sku": "6787L8K7J8L7J8L7K8",
652
+ "weight": 1.0,
653
+ "quantity": 1,
654
+ "weight_unit": "LB",
655
+ "description": None,
656
+ "value_amount": None,
657
+ "value_currency": None,
658
+ "origin_country": None,
659
+ },
660
+ {
661
+ "id": ANY,
662
+ "sku": "3PO4I5J4PO5I4HI5OH",
663
+ "weight": 0.75,
664
+ "quantity": 4,
665
+ "weight_unit": "KG",
666
+ "description": None,
667
+ "value_amount": None,
668
+ "value_currency": None,
669
+ "origin_country": None,
670
+ },
671
+ ],
672
+ },
673
+ },
674
+ "errors": None,
675
+ }
676
+ }
677
+ }