ordercloud-python 2026.6.1__tar.gz → 2026.10.1__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 (160) hide show
  1. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/.github/dependabot.yml +6 -0
  2. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/.github/workflows/ci.yml +6 -6
  3. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/.github/workflows/codeql.yml +4 -4
  4. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/.github/workflows/dependency-review.yml +1 -1
  5. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/.github/workflows/release.yml +35 -7
  6. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/.github/workflows/sbom.yml +1 -1
  7. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/.github/workflows/scorecard.yml +2 -2
  8. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/CHANGELOG.md +35 -0
  9. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/PKG-INFO +13 -13
  10. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/README.md +11 -11
  11. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/pyproject.toml +6 -1
  12. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/api_client.py +0 -2
  13. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/discount.py +2 -0
  14. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/misc.py +1 -0
  15. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/test_models.py +51 -0
  16. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/test_resources.py +42 -0
  17. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/.gitignore +0 -0
  18. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/CODE_REVIEW.md +0 -0
  19. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/CONTRIBUTING.md +0 -0
  20. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/LICENSE +0 -0
  21. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/SECURITY.md +0 -0
  22. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/SECURITY_AUDIT.md +0 -0
  23. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/examples/basic_workflow.py +0 -0
  24. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/__init__.py +0 -0
  25. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/auth.py +0 -0
  26. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/client.py +0 -0
  27. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/config.py +0 -0
  28. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/errors.py +0 -0
  29. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/http.py +0 -0
  30. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/middleware.py +0 -0
  31. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/__init__.py +0 -0
  32. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/address.py +0 -0
  33. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/approval.py +0 -0
  34. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/assignments.py +0 -0
  35. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/auth_models.py +0 -0
  36. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/bundle.py +0 -0
  37. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/buyer.py +0 -0
  38. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/catalog.py +0 -0
  39. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/category.py +0 -0
  40. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/cost_center.py +0 -0
  41. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/credit_card.py +0 -0
  42. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/delivery.py +0 -0
  43. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/integration.py +0 -0
  44. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/inventory_record.py +0 -0
  45. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/line_item.py +0 -0
  46. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/line_item_types.py +0 -0
  47. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/message_sender.py +0 -0
  48. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/open_id_connect.py +0 -0
  49. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/order.py +0 -0
  50. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/order_return.py +0 -0
  51. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/payment.py +0 -0
  52. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/price_schedule.py +0 -0
  53. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/product.py +0 -0
  54. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/product_collection.py +0 -0
  55. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/promotion.py +0 -0
  56. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/security.py +0 -0
  57. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/shared.py +0 -0
  58. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/shipment.py +0 -0
  59. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/spec.py +0 -0
  60. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/spending_account.py +0 -0
  61. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/subscription.py +0 -0
  62. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/supplier.py +0 -0
  63. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/sync.py +0 -0
  64. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/user.py +0 -0
  65. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/user_group.py +0 -0
  66. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/models/webhook.py +0 -0
  67. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/py.typed +0 -0
  68. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/__init__.py +0 -0
  69. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/addresses.py +0 -0
  70. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/admin_addresses.py +0 -0
  71. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/admin_user_groups.py +0 -0
  72. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/admin_users.py +0 -0
  73. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/api_clients.py +0 -0
  74. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/approval_rules.py +0 -0
  75. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/base.py +0 -0
  76. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/bundle_line_items.py +0 -0
  77. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/bundle_subscription_items.py +0 -0
  78. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/bundles.py +0 -0
  79. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/buyer_groups.py +0 -0
  80. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/buyers.py +0 -0
  81. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/cart.py +0 -0
  82. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/catalogs.py +0 -0
  83. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/categories.py +0 -0
  84. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/cost_centers.py +0 -0
  85. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/credit_cards.py +0 -0
  86. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/delivery_configurations.py +0 -0
  87. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/discounts.py +0 -0
  88. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/entity_syncs.py +0 -0
  89. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/error_configs.py +0 -0
  90. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/forgotten_credentials.py +0 -0
  91. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/group_orders.py +0 -0
  92. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/impersonation_configs.py +0 -0
  93. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/incrementors.py +0 -0
  94. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/integration_events.py +0 -0
  95. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/inventory_integrations.py +0 -0
  96. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/inventory_records.py +0 -0
  97. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/line_items.py +0 -0
  98. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/locales.py +0 -0
  99. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/me.py +0 -0
  100. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/message_senders.py +0 -0
  101. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/open_id_connects.py +0 -0
  102. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/order_returns.py +0 -0
  103. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/order_syncs.py +0 -0
  104. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/orders.py +0 -0
  105. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/payments.py +0 -0
  106. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/price_schedules.py +0 -0
  107. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/product_collections.py +0 -0
  108. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/product_facets.py +0 -0
  109. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/product_syncs.py +0 -0
  110. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/products.py +0 -0
  111. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/promotion_integrations.py +0 -0
  112. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/promotions.py +0 -0
  113. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/security_profiles.py +0 -0
  114. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/seller_approval_rules.py +0 -0
  115. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/shipments.py +0 -0
  116. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/specs.py +0 -0
  117. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/spending_accounts.py +0 -0
  118. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/subscription_integrations.py +0 -0
  119. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/subscription_items.py +0 -0
  120. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/subscriptions.py +0 -0
  121. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/supplier_addresses.py +0 -0
  122. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/supplier_user_groups.py +0 -0
  123. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/supplier_users.py +0 -0
  124. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/suppliers.py +0 -0
  125. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/tracking_events.py +0 -0
  126. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/user_groups.py +0 -0
  127. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/users.py +0 -0
  128. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/webhooks.py +0 -0
  129. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/resources/xp_indices.py +0 -0
  130. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/src/ordercloud/sync_client.py +0 -0
  131. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/__init__.py +0 -0
  132. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/conftest.py +0 -0
  133. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/integration/__init__.py +0 -0
  134. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/integration/conftest.py +0 -0
  135. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/integration/test_auth.py +0 -0
  136. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/integration/test_crud.py +0 -0
  137. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/integration/test_errors.py +0 -0
  138. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/integration/test_pagination.py +0 -0
  139. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/integration/test_query_params.py +0 -0
  140. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/integration/test_sync_client.py +0 -0
  141. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/test_auth.py +0 -0
  142. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/test_http.py +0 -0
  143. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/test_resource_coverage.py +0 -0
  144. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tests/test_sync_client.py +0 -0
  145. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/__init__.py +0 -0
  146. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/__init__.py +0 -0
  147. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/__main__.py +0 -0
  148. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/cli.py +0 -0
  149. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/grouping.py +0 -0
  150. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/ir.py +0 -0
  151. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/naming.py +0 -0
  152. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/parser.py +0 -0
  153. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/renderer.py +0 -0
  154. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/templates/client.py.j2 +0 -0
  155. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/templates/model_module.py.j2 +0 -0
  156. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/templates/models_init.py.j2 +0 -0
  157. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/templates/resource_module.py.j2 +0 -0
  158. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/templates/resources_init.py.j2 +0 -0
  159. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/transformer.py +0 -0
  160. {ordercloud_python-2026.6.1 → ordercloud_python-2026.10.1}/tools/codegen/type_mapping.py +0 -0
@@ -24,3 +24,9 @@ updates:
24
24
  labels:
25
25
  - "ci"
26
26
  open-pull-requests-limit: 5
27
+ groups:
28
+ # The init / autobuild / analyze / upload-sarif steps must share one
29
+ # version, so they are bumped together in a single PR.
30
+ codeql-action:
31
+ patterns:
32
+ - "github/codeql-action*"
@@ -14,8 +14,8 @@ jobs:
14
14
  name: Lint
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
18
- - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
17
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
18
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
19
19
  with:
20
20
  python-version: "3.13"
21
21
  - name: Install ruff
@@ -29,8 +29,8 @@ jobs:
29
29
  name: Type check
30
30
  runs-on: ubuntu-latest
31
31
  steps:
32
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
33
- - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
32
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
33
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
34
34
  with:
35
35
  python-version: "3.13"
36
36
  - name: Install dependencies
@@ -46,8 +46,8 @@ jobs:
46
46
  matrix:
47
47
  python-version: ["3.10", "3.11", "3.12", "3.13"]
48
48
  steps:
49
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
50
- - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
49
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
50
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
51
51
  with:
52
52
  python-version: ${{ matrix.python-version }}
53
53
  - name: Install dependencies
@@ -18,18 +18,18 @@ jobs:
18
18
  name: Analyze (Python)
19
19
  runs-on: ubuntu-latest
20
20
  steps:
21
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
21
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
22
22
 
23
23
  - name: Initialize CodeQL
24
- uses: github/codeql-action/init@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
24
+ uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
25
25
  with:
26
26
  languages: python
27
27
  queries: security-and-quality
28
28
 
29
29
  - name: Autobuild
30
- uses: github/codeql-action/autobuild@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
30
+ uses: github/codeql-action/autobuild@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
31
31
 
32
32
  - name: Perform CodeQL Analysis
33
- uses: github/codeql-action/analyze@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
33
+ uses: github/codeql-action/analyze@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
34
34
  with:
35
35
  category: "/language:python"
@@ -12,7 +12,7 @@ jobs:
12
12
  name: Review dependencies
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
15
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
16
16
  - name: Dependency Review
17
17
  uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
18
18
  with:
@@ -14,8 +14,8 @@ jobs:
14
14
  permissions:
15
15
  contents: read
16
16
  steps:
17
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
18
- - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
17
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
18
+ - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
19
19
  with:
20
20
  python-version: "3.13"
21
21
  - name: Install build
@@ -38,7 +38,8 @@ jobs:
38
38
 
39
39
  publish:
40
40
  name: Publish to PyPI
41
- needs: build
41
+ # The PyPI upload cannot be undone, so it runs only after attestation.
42
+ needs: attestation
42
43
  runs-on: ubuntu-latest
43
44
  permissions:
44
45
  id-token: write
@@ -52,7 +53,7 @@ jobs:
52
53
  name: dist
53
54
  path: dist/
54
55
  - name: Publish to PyPI
55
- uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0
56
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
56
57
 
57
58
  attestation:
58
59
  name: Attest build provenance
@@ -69,9 +70,26 @@ jobs:
69
70
  name: dist
70
71
  path: dist/
71
72
  - name: Attest build provenance
72
- uses: actions/attest-build-provenance@a2bbfa25375fe432b6a289bc6b6cd05ecd0c4c32 # v4.1.0
73
+ id: attest
74
+ uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
73
75
  with:
74
76
  subject-path: dist/*
77
+ - name: Name provenance file
78
+ env:
79
+ BUNDLE_PATH: ${{ steps.attest.outputs.bundle-path }}
80
+ run: |
81
+ if [ ! -s "$BUNDLE_PATH" ]; then
82
+ echo "::error::Attestation bundle is missing or empty: $BUNDLE_PATH"
83
+ exit 1
84
+ fi
85
+ mkdir provenance
86
+ cp "$BUNDLE_PATH" "provenance/ordercloud_python-${GITHUB_REF_NAME#v}.intoto.jsonl"
87
+ - name: Upload provenance
88
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
89
+ with:
90
+ name: provenance
91
+ path: provenance/
92
+ if-no-files-found: error
75
93
 
76
94
  github-release:
77
95
  name: Create GitHub Release
@@ -80,16 +98,26 @@ jobs:
80
98
  permissions:
81
99
  contents: write
82
100
  steps:
83
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
101
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
84
102
  - name: Download distributions
85
103
  uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
86
104
  with:
87
105
  name: dist
88
106
  path: dist/
107
+ - name: Download provenance
108
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
109
+ with:
110
+ name: provenance
111
+ path: provenance/
89
112
  - name: Create GitHub Release
90
113
  env:
91
114
  GH_TOKEN: ${{ github.token }}
92
115
  run: |
93
- gh release create "$GITHUB_REF_NAME" dist/* \
116
+ PROVENANCE="provenance/ordercloud_python-${GITHUB_REF_NAME#v}.intoto.jsonl"
117
+ if [ ! -s "$PROVENANCE" ]; then
118
+ echo "::error::Provenance file is missing or empty: $PROVENANCE"
119
+ exit 1
120
+ fi
121
+ gh release create "$GITHUB_REF_NAME" dist/* "$PROVENANCE" \
94
122
  --title "$GITHUB_REF_NAME" \
95
123
  --generate-notes
@@ -16,7 +16,7 @@ jobs:
16
16
  permissions:
17
17
  contents: read
18
18
  steps:
19
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
19
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
20
20
 
21
21
  - name: Generate SBOM
22
22
  uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
@@ -19,7 +19,7 @@ jobs:
19
19
  contents: read
20
20
  actions: read
21
21
  steps:
22
- - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3
22
+ - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
23
23
  with:
24
24
  persist-credentials: false
25
25
 
@@ -31,6 +31,6 @@ jobs:
31
31
  publish_results: true
32
32
 
33
33
  - name: Upload SARIF results
34
- uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4.36.2
34
+ uses: github/codeql-action/upload-sarif@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
35
35
  with:
36
36
  sarif_file: results.sarif
@@ -4,6 +4,41 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  This project uses [Calendar Versioning](https://calver.org/) — `YYYY.MM.N` where `N` is the release number within that month.
6
6
 
7
+ ## 2026.10.1 — 2026-10-05
8
+
9
+ Regenerated from the OrderCloud OpenAPI v3 spec, **version 1.0.454 → 1.0.470**. This release contains **one breaking change** — `ApiClient.client_secret` is removed — which the CalVer version number does not signal. Coverage is unchanged: **639 operations** across 60 resources, with 173 models and 17 enums.
10
+
11
+ ### Breaking
12
+
13
+ - **`ApiClient.client_secret` (`ClientSecret`) removed**, following its removal from the `ApiClient` schema in the spec. `ApiClient` keeps the models' `extra="allow"` configuration, so code that still uses the field now behaves as follows:
14
+ - Reading `.client_secret` on an `ApiClient` that was not given one raises `AttributeError` (it previously returned `None`).
15
+ - mypy reports any `.client_secret` access as an `attr-defined` error.
16
+ - A `ClientSecret` value in an API response is no longer available as `.client_secret`; it is kept as an undeclared extra under its API name, in `model_extra["ClientSecret"]`.
17
+ - `ApiClient(ClientSecret="...")` is accepted, and the value is still sent to the API under the key `ClientSecret`.
18
+ - `ApiClient(client_secret="...")` is accepted, but the value is now sent to the API under the snake_case key `client_secret` instead of `ClientSecret`.
19
+ - Request bodies passed as plain dicts are sent unchanged.
20
+
21
+ The five API-client secret operations (`ApiClients.ListSecrets` / `CreateSecret` / `GetSecret` / `PatchSecret` / `DeleteSecret`) and the `ApiClientSecret` / `ApiClientSecretCreateResponse` models are unchanged in this release.
22
+
23
+ ### Added
24
+
25
+ - **`Discount.priority`** (`Priority`, integer). The spec describes it as: "Controls precedence when multiple discounts apply to the same user and product. Lower number = higher precedence (1 beats 2). Null is applied last."
26
+ - **`OrderEditAfterSubmit`** added to the `ApiRole` enum. Role lists that contain it (for example `SecurityProfile.roles` or `ApiClient.maximum_granted_roles`) now validate; on 2026.6.1 they raise a pydantic `ValidationError`.
27
+
28
+ ### Spec changes with no SDK code change
29
+
30
+ - The `x-lifecycle: Beta` marker is removed from the Discounts tag and its nine operations. The code generator does not read this marker.
31
+ - `GET /discounts` now lists `Priority` and `!Priority` as `sortBy` values. `Discounts.list` takes `sort_by` as a plain string and passes it through unvalidated, so no SDK change was needed.
32
+ - `OrderEditAfterSubmit` is also added to the spec's OAuth2 scopes. The SDK's `scopes` setting takes plain strings.
33
+
34
+ ### Release artefacts
35
+
36
+ - GitHub releases now also attach the build provenance attestation for the sdist and wheel, as `ordercloud_python-<version>.intoto.jsonl`, alongside the distributions themselves.
37
+
38
+ ### Changed
39
+
40
+ - Bumped package version to `2026.10.1` (CalVer).
41
+
7
42
  ## 2026.6.1 — 2026-06-14
8
43
 
9
44
  Regenerated from the OrderCloud OpenAPI v3 spec, **version 1.0.445 → 1.0.454**. All changes are additive — no breaking changes to existing models or operations. Coverage now spans **639 operations** (was 632) across the same 60 resources, with **173 models** and 17 enums.
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.5
2
2
  Name: ordercloud-python
3
- Version: 2026.6.1
3
+ Version: 2026.10.1
4
4
  Summary: Idiomatic Python SDK for Sitecore OrderCloud
5
5
  Project-URL: Homepage, https://github.com/markcassidyconsulting/ordercloud-python
6
6
  Project-URL: Repository, https://github.com/markcassidyconsulting/ordercloud-python
@@ -60,7 +60,7 @@ A fully typed, async-first Python SDK for [Sitecore OrderCloud](https://orderclo
60
60
  - **Middleware hooks** — intercept requests and responses for logging, metrics, or header injection.
61
61
  - **Structured logging** — standard Python `logging` module, DEBUG/WARNING levels.
62
62
  - **Full type annotations** — `py.typed` marker for downstream type checking with mypy, pyright, etc.
63
- - **791 tests, 97% coverage** — 766 unit tests (mocked HTTP) + 25 integration tests (live sandbox).
63
+ - **803 tests, 97% coverage** — 778 unit tests (mocked HTTP) + 25 integration tests (live sandbox).
64
64
 
65
65
  ## Installation
66
66
 
@@ -244,7 +244,7 @@ Before-request hooks receive a mutable `RequestContext` — modify `headers`, `p
244
244
 
245
245
  ## API Coverage
246
246
 
247
- The SDK covers **all 60 resources** and **639 operations** in the OrderCloud API. Models and resource clients are generated from the official OpenAPI v3 spec (version 1.0.454).
247
+ The SDK covers **all 60 resources** and **639 operations** in the OrderCloud API. Models and resource clients are generated from the official OpenAPI v3 spec (version 1.0.470).
248
248
 
249
249
  ### Core Commerce
250
250
 
@@ -504,18 +504,18 @@ The test suite is self-bootstrapping — it uses the SDK itself to create all te
504
504
 
505
505
  ### Test Suite
506
506
 
507
- 791 tests across 12 modules.
507
+ 803 tests across 12 modules.
508
508
 
509
- **Unit tests (766)** — mocked HTTP via [respx](https://lundberg.github.io/respx/), no network calls:
509
+ **Unit tests (778)** — mocked HTTP via [respx](https://lundberg.github.io/respx/), no network calls:
510
510
 
511
511
  | Module | Tests | Purpose |
512
512
  |--------|-------|---------|
513
513
  | `test_auth.py` | 13 | OAuth2 token management |
514
- | `test_http.py` | 16 | HTTP client, error parsing, retries |
515
- | `test_models.py` | 28 | Model round-trips, enums, xp, ListPage |
516
- | `test_resources.py` | 22 | Representative resource operations |
514
+ | `test_http.py` | 37 | HTTP client, error parsing, retries |
515
+ | `test_models.py` | 44 | Model round-trips, enums, xp, ListPage |
516
+ | `test_resources.py` | 33 | Representative resource operations |
517
517
  | `test_resource_coverage.py` | 639 | All 60 resources, all 639 operations |
518
- | `test_sync_client.py` | 48 | Sync wrapper, pagination |
518
+ | `test_sync_client.py` | 12 | Sync wrapper, pagination |
519
519
 
520
520
  **Integration tests (25)** — live sandbox, skipped when credentials are absent:
521
521
 
@@ -532,11 +532,11 @@ The test suite is self-bootstrapping — it uses the SDK itself to create all te
532
532
 
533
533
  | Module | Coverage |
534
534
  |--------|----------|
535
- | `auth.py` | 100% |
535
+ | `auth.py` | 93% |
536
536
  | `client.py` | 100% |
537
- | `config.py` | 100% |
537
+ | `config.py` | 94% |
538
538
  | `errors.py` | 100% |
539
- | `http.py` | 97% |
539
+ | `http.py` | 98% |
540
540
  | `middleware.py` | 100% |
541
541
  | `sync_client.py` | 100% |
542
542
  | `resources/base.py` | 100% |
@@ -20,7 +20,7 @@ A fully typed, async-first Python SDK for [Sitecore OrderCloud](https://orderclo
20
20
  - **Middleware hooks** — intercept requests and responses for logging, metrics, or header injection.
21
21
  - **Structured logging** — standard Python `logging` module, DEBUG/WARNING levels.
22
22
  - **Full type annotations** — `py.typed` marker for downstream type checking with mypy, pyright, etc.
23
- - **791 tests, 97% coverage** — 766 unit tests (mocked HTTP) + 25 integration tests (live sandbox).
23
+ - **803 tests, 97% coverage** — 778 unit tests (mocked HTTP) + 25 integration tests (live sandbox).
24
24
 
25
25
  ## Installation
26
26
 
@@ -204,7 +204,7 @@ Before-request hooks receive a mutable `RequestContext` — modify `headers`, `p
204
204
 
205
205
  ## API Coverage
206
206
 
207
- The SDK covers **all 60 resources** and **639 operations** in the OrderCloud API. Models and resource clients are generated from the official OpenAPI v3 spec (version 1.0.454).
207
+ The SDK covers **all 60 resources** and **639 operations** in the OrderCloud API. Models and resource clients are generated from the official OpenAPI v3 spec (version 1.0.470).
208
208
 
209
209
  ### Core Commerce
210
210
 
@@ -464,18 +464,18 @@ The test suite is self-bootstrapping — it uses the SDK itself to create all te
464
464
 
465
465
  ### Test Suite
466
466
 
467
- 791 tests across 12 modules.
467
+ 803 tests across 12 modules.
468
468
 
469
- **Unit tests (766)** — mocked HTTP via [respx](https://lundberg.github.io/respx/), no network calls:
469
+ **Unit tests (778)** — mocked HTTP via [respx](https://lundberg.github.io/respx/), no network calls:
470
470
 
471
471
  | Module | Tests | Purpose |
472
472
  |--------|-------|---------|
473
473
  | `test_auth.py` | 13 | OAuth2 token management |
474
- | `test_http.py` | 16 | HTTP client, error parsing, retries |
475
- | `test_models.py` | 28 | Model round-trips, enums, xp, ListPage |
476
- | `test_resources.py` | 22 | Representative resource operations |
474
+ | `test_http.py` | 37 | HTTP client, error parsing, retries |
475
+ | `test_models.py` | 44 | Model round-trips, enums, xp, ListPage |
476
+ | `test_resources.py` | 33 | Representative resource operations |
477
477
  | `test_resource_coverage.py` | 639 | All 60 resources, all 639 operations |
478
- | `test_sync_client.py` | 48 | Sync wrapper, pagination |
478
+ | `test_sync_client.py` | 12 | Sync wrapper, pagination |
479
479
 
480
480
  **Integration tests (25)** — live sandbox, skipped when credentials are absent:
481
481
 
@@ -492,11 +492,11 @@ The test suite is self-bootstrapping — it uses the SDK itself to create all te
492
492
 
493
493
  | Module | Coverage |
494
494
  |--------|----------|
495
- | `auth.py` | 100% |
495
+ | `auth.py` | 93% |
496
496
  | `client.py` | 100% |
497
- | `config.py` | 100% |
497
+ | `config.py` | 94% |
498
498
  | `errors.py` | 100% |
499
- | `http.py` | 97% |
499
+ | `http.py` | 98% |
500
500
  | `middleware.py` | 100% |
501
501
  | `sync_client.py` | 100% |
502
502
  | `resources/base.py` | 100% |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ordercloud-python"
7
- version = "2026.6.1"
7
+ version = "2026.10.1"
8
8
  description = "Idiomatic Python SDK for Sitecore OrderCloud"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -70,6 +70,11 @@ packages = ["src/ordercloud"]
70
70
  target-version = "py310"
71
71
  line-length = 100
72
72
 
73
+ [tool.ruff.lint]
74
+ # Explicit rule selection: the set this codebase is linted against. Stated
75
+ # here so the gate does not change when Ruff changes its default rules.
76
+ select = ["E4", "E7", "E9", "F"]
77
+
73
78
  [tool.pytest.ini_options]
74
79
  testpaths = ["tests"]
75
80
  asyncio_mode = "auto"
@@ -47,7 +47,6 @@ class ApiClient(OrderCloudModel, Generic[XP]):
47
47
 
48
48
  Attributes:
49
49
  id: Used for OAuth 2.0 workflows and impersonation. (read-only)
50
- client_secret: Enables the OAuth 2.0 client credentials grant type. Required on all OAuth workflows when present.
51
50
  access_token_duration:
52
51
  active: All user authentication is prohibited if false.
53
52
  app_name:
@@ -74,7 +73,6 @@ class ApiClient(OrderCloudModel, Generic[XP]):
74
73
  """
75
74
 
76
75
  id: Optional[str] = Field(None, alias="ID")
77
- client_secret: Optional[str] = Field(None, alias="ClientSecret")
78
76
  access_token_duration: Optional[int] = Field(None, alias="AccessTokenDuration")
79
77
  active: Optional[bool] = Field(None, alias="Active")
80
78
  app_name: Optional[str] = Field(None, alias="AppName")
@@ -52,6 +52,7 @@ class Discount(OrderCloudModel, Generic[XP]):
52
52
  catalog_id:
53
53
  category_id:
54
54
  product_id:
55
+ priority: Controls precedence when multiple discounts apply to the same user and product. Lower number = higher precedence (1 beats 2). Null is applied last.
55
56
  xp:
56
57
  """
57
58
 
@@ -62,4 +63,5 @@ class Discount(OrderCloudModel, Generic[XP]):
62
63
  catalog_id: Optional[str] = Field(None, alias="CatalogID")
63
64
  category_id: Optional[str] = Field(None, alias="CategoryID")
64
65
  product_id: Optional[str] = Field(None, alias="ProductID")
66
+ priority: Optional[int] = Field(None, alias="Priority")
65
67
  xp: Optional[XP] = Field(None, alias="xp")
@@ -78,6 +78,7 @@ class ApiRole(OrderCloudEnum):
78
78
  MeSubscriptionAdmin = "MeSubscriptionAdmin"
79
79
  MeXpAdmin = "MeXpAdmin"
80
80
  OrderAdmin = "OrderAdmin"
81
+ OrderEditAfterSubmit = "OrderEditAfterSubmit"
81
82
  OrderReader = "OrderReader"
82
83
  OverrideShipping = "OverrideShipping"
83
84
  OverrideTax = "OverrideTax"
@@ -2,6 +2,7 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import pytest
5
6
 
6
7
  from ordercloud.models import (
7
8
  Address,
@@ -12,6 +13,9 @@ from ordercloud.models import (
12
13
  MetaWithFacets,
13
14
  Product,
14
15
  )
16
+ from ordercloud.models.api_client import ApiClient
17
+ from ordercloud.models.discount import Discount
18
+ from ordercloud.models.misc import ApiRole
15
19
  from ordercloud.models.order import Order, OrderDirection, OrderStatus
16
20
  from ordercloud.models.line_item import LineItem
17
21
  from ordercloud.models.payment import Payment, PaymentType
@@ -241,6 +245,46 @@ class TestPhase2Models:
241
245
  assert "BuyerAdmin" in sp.roles
242
246
 
243
247
 
248
+ class TestDiscountModel:
249
+ def test_priority_round_trip(self):
250
+ d = Discount.model_validate({"ID": "disc-1", "Priority": 1})
251
+ assert d.priority == 1
252
+ assert d.model_dump(by_alias=True, exclude_none=True)["Priority"] == 1
253
+
254
+ def test_priority_defaults_to_none(self):
255
+ assert Discount().priority is None
256
+
257
+
258
+ class TestApiClientModel:
259
+ """``ClientSecret`` is not a declared ``ApiClient`` field (the spec dropped it in
260
+ 1.0.470), so ``extra="allow"`` decides where a supplied value lands."""
261
+
262
+ def test_client_secret_not_a_declared_field(self):
263
+ assert "client_secret" not in ApiClient.model_fields
264
+
265
+ def test_client_secret_attribute_absent_when_not_supplied(self):
266
+ c = ApiClient(AppName="app")
267
+ with pytest.raises(AttributeError):
268
+ c.client_secret
269
+
270
+ def test_client_secret_in_response_kept_as_extra_under_api_name(self):
271
+ c = ApiClient.model_validate({"ID": "c1", "ClientSecret": "s"})
272
+ assert c.model_extra == {"ClientSecret": "s"}
273
+ with pytest.raises(AttributeError):
274
+ c.client_secret
275
+
276
+ def test_snake_case_client_secret_kwarg_kept_as_extra(self):
277
+ c = ApiClient(client_secret="s")
278
+ assert c.model_extra == {"client_secret": "s"}
279
+ assert c.client_secret == "s"
280
+
281
+ def test_pascal_case_client_secret_kwarg_kept_as_extra(self):
282
+ c = ApiClient(ClientSecret="s")
283
+ assert c.model_extra == {"ClientSecret": "s"}
284
+ with pytest.raises(AttributeError):
285
+ c.client_secret
286
+
287
+
244
288
  # ---------------------------------------------------------------------------
245
289
  # Enum serialisation
246
290
  # ---------------------------------------------------------------------------
@@ -266,6 +310,13 @@ class TestEnums:
266
310
  assert PaymentType.CreditCard.value == "CreditCard"
267
311
  assert PaymentType.PurchaseOrder.value == "PurchaseOrder"
268
312
 
313
+ def test_api_role_order_edit_after_submit(self):
314
+ assert ApiRole.OrderEditAfterSubmit.value == "OrderEditAfterSubmit"
315
+
316
+ def test_role_list_accepts_order_edit_after_submit(self):
317
+ sp = SecurityProfile.model_validate({"ID": "sp-1", "Roles": ["OrderEditAfterSubmit"]})
318
+ assert sp.roles == [ApiRole.OrderEditAfterSubmit]
319
+
269
320
 
270
321
  # ---------------------------------------------------------------------------
271
322
  # Typed xp generics
@@ -2,15 +2,20 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import json
6
+
5
7
  import respx
6
8
  from httpx import Response
7
9
 
8
10
  from ordercloud.http import HttpClient
9
11
  from ordercloud.models import Product, ListPage
12
+ from ordercloud.models.api_client import ApiClient
10
13
  from ordercloud.models.order import Order, OrderDirection, OrderStatus
11
14
  from ordercloud.models.line_item import LineItem
12
15
  from ordercloud.models.promotion import Promotion
16
+ from ordercloud.resources.api_clients import ApiClientsResource
13
17
  from ordercloud.resources.base import paginate
18
+ from ordercloud.resources.discounts import DiscountsResource
14
19
  from ordercloud.resources.products import ProductsResource
15
20
  from ordercloud.resources.orders import OrdersResource
16
21
  from ordercloud.resources.line_items import LineItemsResource
@@ -293,6 +298,43 @@ class TestPhase2Resources:
293
298
  shipment = await resource.get("ship-1")
294
299
  assert shipment.buyer_id == "b1"
295
300
 
301
+ @respx.mock
302
+ async def test_api_clients_create_sends_client_secret_under_the_key_given(
303
+ self, http_client: HttpClient
304
+ ):
305
+ """With no declared field, an extra is serialised under the exact name supplied."""
306
+ route = respx.post(f"{TEST_BASE_URL}/apiclients").mock(
307
+ return_value=Response(201, json={"ID": "c1", "AppName": "app"})
308
+ )
309
+ resource = ApiClientsResource(http_client)
310
+ await resource.create(ApiClient(AppName="app", client_secret="s"))
311
+ await resource.create(ApiClient(AppName="app", ClientSecret="s"))
312
+ snake_body = json.loads(route.calls[0].request.content)
313
+ pascal_body = json.loads(route.calls[1].request.content)
314
+ assert snake_body["client_secret"] == "s"
315
+ assert "ClientSecret" not in snake_body
316
+ assert pascal_body["ClientSecret"] == "s"
317
+ assert "client_secret" not in pascal_body
318
+
319
+ @respx.mock
320
+ async def test_api_clients_get_keeps_client_secret_as_extra(self, http_client: HttpClient):
321
+ respx.get(f"{TEST_BASE_URL}/apiclients/c1").mock(
322
+ return_value=Response(200, json={"ID": "c1", "ClientSecret": "s"})
323
+ )
324
+ resource = ApiClientsResource(http_client)
325
+ api_client = await resource.get("c1")
326
+ assert api_client.model_extra == {"ClientSecret": "s"}
327
+
328
+ @respx.mock
329
+ async def test_discounts_list_passes_sort_by_through(self, http_client: HttpClient):
330
+ route = respx.get(f"{TEST_BASE_URL}/discounts").mock(
331
+ return_value=Response(200, json=list_response([{"ID": "d1", "Priority": 1}]))
332
+ )
333
+ resource = DiscountsResource(http_client)
334
+ page = await resource.list(sort_by="!Priority")
335
+ assert route.calls[0].request.url.params["sortBy"] == "!Priority"
336
+ assert page.items[0].priority == 1
337
+
296
338
 
297
339
  # ---------------------------------------------------------------------------
298
340
  # Auto-pagination