ordercloud-python 2026.10.1__tar.gz → 2026.10.2__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 (164) hide show
  1. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/ci.yml +4 -4
  2. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/codeql.yml +1 -1
  3. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/dependency-review.yml +1 -1
  4. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/release.yml +3 -3
  5. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/sbom.yml +2 -2
  6. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/scorecard.yml +2 -2
  7. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/CHANGELOG.md +21 -0
  8. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/CONTRIBUTING.md +24 -0
  9. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/PKG-INFO +8 -3
  10. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/README.md +7 -2
  11. ordercloud_python-2026.10.2/SECURITY.md +70 -0
  12. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/pyproject.toml +1 -1
  13. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/http.py +40 -14
  14. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/middleware.py +7 -0
  15. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/forgotten_credentials.py +4 -1
  16. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/group_orders.py +6 -1
  17. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/me.py +37 -8
  18. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/sync_client.py +1 -3
  19. ordercloud_python-2026.10.2/tests/test_codegen_sensitivity.py +250 -0
  20. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_http.py +88 -1
  21. ordercloud_python-2026.10.2/tests/test_log_redaction.py +287 -0
  22. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/cli.py +9 -0
  23. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/ir.py +7 -0
  24. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/renderer.py +26 -1
  25. ordercloud_python-2026.10.2/tools/codegen/sensitivity.py +172 -0
  26. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/resource_module.py.j2 +14 -14
  27. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/transformer.py +21 -1
  28. ordercloud_python-2026.10.1/SECURITY.md +0 -41
  29. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/dependabot.yml +0 -0
  30. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.gitignore +0 -0
  31. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/CODE_REVIEW.md +0 -0
  32. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/LICENSE +0 -0
  33. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/SECURITY_AUDIT.md +0 -0
  34. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/examples/basic_workflow.py +0 -0
  35. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/__init__.py +0 -0
  36. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/auth.py +0 -0
  37. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/client.py +0 -0
  38. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/config.py +0 -0
  39. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/errors.py +0 -0
  40. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/__init__.py +0 -0
  41. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/address.py +0 -0
  42. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/api_client.py +0 -0
  43. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/approval.py +0 -0
  44. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/assignments.py +0 -0
  45. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/auth_models.py +0 -0
  46. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/bundle.py +0 -0
  47. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/buyer.py +0 -0
  48. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/catalog.py +0 -0
  49. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/category.py +0 -0
  50. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/cost_center.py +0 -0
  51. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/credit_card.py +0 -0
  52. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/delivery.py +0 -0
  53. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/discount.py +0 -0
  54. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/integration.py +0 -0
  55. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/inventory_record.py +0 -0
  56. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/line_item.py +0 -0
  57. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/line_item_types.py +0 -0
  58. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/message_sender.py +0 -0
  59. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/misc.py +0 -0
  60. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/open_id_connect.py +0 -0
  61. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/order.py +0 -0
  62. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/order_return.py +0 -0
  63. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/payment.py +0 -0
  64. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/price_schedule.py +0 -0
  65. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/product.py +0 -0
  66. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/product_collection.py +0 -0
  67. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/promotion.py +0 -0
  68. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/security.py +0 -0
  69. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/shared.py +0 -0
  70. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/shipment.py +0 -0
  71. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/spec.py +0 -0
  72. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/spending_account.py +0 -0
  73. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/subscription.py +0 -0
  74. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/supplier.py +0 -0
  75. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/sync.py +0 -0
  76. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/user.py +0 -0
  77. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/user_group.py +0 -0
  78. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/webhook.py +0 -0
  79. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/py.typed +0 -0
  80. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/__init__.py +0 -0
  81. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/addresses.py +0 -0
  82. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/admin_addresses.py +0 -0
  83. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/admin_user_groups.py +0 -0
  84. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/admin_users.py +0 -0
  85. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/api_clients.py +0 -0
  86. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/approval_rules.py +0 -0
  87. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/base.py +0 -0
  88. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/bundle_line_items.py +0 -0
  89. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/bundle_subscription_items.py +0 -0
  90. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/bundles.py +0 -0
  91. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/buyer_groups.py +0 -0
  92. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/buyers.py +0 -0
  93. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/cart.py +0 -0
  94. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/catalogs.py +0 -0
  95. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/categories.py +0 -0
  96. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/cost_centers.py +0 -0
  97. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/credit_cards.py +0 -0
  98. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/delivery_configurations.py +0 -0
  99. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/discounts.py +0 -0
  100. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/entity_syncs.py +0 -0
  101. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/error_configs.py +0 -0
  102. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/impersonation_configs.py +0 -0
  103. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/incrementors.py +0 -0
  104. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/integration_events.py +0 -0
  105. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/inventory_integrations.py +0 -0
  106. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/inventory_records.py +0 -0
  107. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/line_items.py +0 -0
  108. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/locales.py +0 -0
  109. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/message_senders.py +0 -0
  110. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/open_id_connects.py +0 -0
  111. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/order_returns.py +0 -0
  112. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/order_syncs.py +0 -0
  113. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/orders.py +0 -0
  114. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/payments.py +0 -0
  115. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/price_schedules.py +0 -0
  116. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/product_collections.py +0 -0
  117. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/product_facets.py +0 -0
  118. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/product_syncs.py +0 -0
  119. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/products.py +0 -0
  120. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/promotion_integrations.py +0 -0
  121. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/promotions.py +0 -0
  122. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/security_profiles.py +0 -0
  123. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/seller_approval_rules.py +0 -0
  124. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/shipments.py +0 -0
  125. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/specs.py +0 -0
  126. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/spending_accounts.py +0 -0
  127. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/subscription_integrations.py +0 -0
  128. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/subscription_items.py +0 -0
  129. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/subscriptions.py +0 -0
  130. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/supplier_addresses.py +0 -0
  131. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/supplier_user_groups.py +0 -0
  132. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/supplier_users.py +0 -0
  133. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/suppliers.py +0 -0
  134. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/tracking_events.py +0 -0
  135. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/user_groups.py +0 -0
  136. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/users.py +0 -0
  137. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/webhooks.py +0 -0
  138. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/xp_indices.py +0 -0
  139. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/__init__.py +0 -0
  140. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/conftest.py +0 -0
  141. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/__init__.py +0 -0
  142. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/conftest.py +0 -0
  143. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_auth.py +0 -0
  144. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_crud.py +0 -0
  145. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_errors.py +0 -0
  146. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_pagination.py +0 -0
  147. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_query_params.py +0 -0
  148. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_sync_client.py +0 -0
  149. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_auth.py +0 -0
  150. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_models.py +0 -0
  151. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_resource_coverage.py +0 -0
  152. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_resources.py +0 -0
  153. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_sync_client.py +0 -0
  154. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/__init__.py +0 -0
  155. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/__init__.py +0 -0
  156. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/__main__.py +0 -0
  157. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/grouping.py +0 -0
  158. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/naming.py +0 -0
  159. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/parser.py +0 -0
  160. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/client.py.j2 +0 -0
  161. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/model_module.py.j2 +0 -0
  162. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/models_init.py.j2 +0 -0
  163. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/resources_init.py.j2 +0 -0
  164. {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/type_mapping.py +0 -0
@@ -14,7 +14,7 @@ jobs:
14
14
  name: Lint
15
15
  runs-on: ubuntu-latest
16
16
  steps:
17
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
17
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
18
18
  - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
19
19
  with:
20
20
  python-version: "3.13"
@@ -29,7 +29,7 @@ jobs:
29
29
  name: Type check
30
30
  runs-on: ubuntu-latest
31
31
  steps:
32
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
32
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
33
33
  - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
34
34
  with:
35
35
  python-version: "3.13"
@@ -46,7 +46,7 @@ jobs:
46
46
  matrix:
47
47
  python-version: ["3.10", "3.11", "3.12", "3.13"]
48
48
  steps:
49
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
49
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
50
50
  - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
51
51
  with:
52
52
  python-version: ${{ matrix.python-version }}
@@ -56,7 +56,7 @@ jobs:
56
56
  run: pytest --cov=ordercloud --cov-report=xml --cov-report=term-missing --cov-fail-under=90
57
57
  - name: Upload coverage to Codecov
58
58
  if: matrix.python-version == '3.13'
59
- uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
59
+ uses: codecov/codecov-action@303a32d7a59b442fa8d48b6a1cc6825c09c847a5 # v7.1.1
60
60
  with:
61
61
  files: ./coverage.xml
62
62
  fail_ci_if_error: false
@@ -18,7 +18,7 @@ jobs:
18
18
  name: Analyze (Python)
19
19
  runs-on: ubuntu-latest
20
20
  steps:
21
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
21
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
22
22
 
23
23
  - name: Initialize CodeQL
24
24
  uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
@@ -12,7 +12,7 @@ jobs:
12
12
  name: Review dependencies
13
13
  runs-on: ubuntu-latest
14
14
  steps:
15
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
15
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
16
16
  - name: Dependency Review
17
17
  uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5.0.0
18
18
  with:
@@ -14,7 +14,7 @@ jobs:
14
14
  permissions:
15
15
  contents: read
16
16
  steps:
17
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
17
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
18
18
  - uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
19
19
  with:
20
20
  python-version: "3.13"
@@ -71,7 +71,7 @@ jobs:
71
71
  path: dist/
72
72
  - name: Attest build provenance
73
73
  id: attest
74
- uses: actions/attest-build-provenance@0f67c3f4856b2e3261c31976d6725780e5e4c373 # v4.1.1
74
+ uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2
75
75
  with:
76
76
  subject-path: dist/*
77
77
  - name: Name provenance file
@@ -98,7 +98,7 @@ jobs:
98
98
  permissions:
99
99
  contents: write
100
100
  steps:
101
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
101
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
102
102
  - name: Download distributions
103
103
  uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
104
104
  with:
@@ -16,10 +16,10 @@ jobs:
16
16
  permissions:
17
17
  contents: read
18
18
  steps:
19
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
19
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
20
20
 
21
21
  - name: Generate SBOM
22
- uses: anchore/sbom-action@e22c389904149dbc22b58101806040fa8d37a610 # v0
22
+ uses: anchore/sbom-action@66cbf4bc1f1c0d2edc94016e65bc221b6bb0ad6c # v0.24.3
23
23
  with:
24
24
  format: spdx-json
25
25
  output-file: sbom.spdx.json
@@ -19,12 +19,12 @@ jobs:
19
19
  contents: read
20
20
  actions: read
21
21
  steps:
22
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
22
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
23
23
  with:
24
24
  persist-credentials: false
25
25
 
26
26
  - name: Run Scorecard
27
- uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3
27
+ uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4
28
28
  with:
29
29
  results_file: results.sarif
30
30
  results_format: sarif
@@ -4,6 +4,27 @@ 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.2 — 2026-10-05
8
+
9
+ ### Security
10
+
11
+ - **Credential-bearing path values are no longer written to the SDK's log lines.** The SDK logs requests on the `ordercloud` logger: a `Request:` and a `Response:` line at `DEBUG` for every request, and a `Retry` line at `WARNING` before each retry (retries are off unless `max_retries` is set). In 2026.4.1, 2026.6.1 and 2026.10.1 each of these lines contained the full request path. Two path parameters in the OrderCloud spec carry values that work as credentials, so those values reached the log:
12
+ - `verificationCode`, in `forgotten_credentials.reset_password_by_verification_code` (`PUT /password/reset/{verificationCode}`).
13
+ - `invitationID`, in `group_orders.get_token` (`POST /grouporders/{invitationID}/token`, which returns an `AccessToken`). The spec says of group-order invitations: "Contributors may request an access token with the invitation ID".
14
+
15
+ The SDK classifies path parameters by name, so every operation that takes an `invitationID` is covered: `group_orders.get_token`, and on `me` the three group-order invitation operations (`get_` / `delete_` / `patch_group_order_invitation`) and the five product-collection invitation operations (`get_` / `delete_` / `patch_` / `accept_` / `decline_product_collection_invitation`). On these ten operations the SDK's log lines now show `***` in place of the value, for example `Request: PUT /password/reset/***` and `Request: POST /me/productcollections/col-1/invitations/accept/***`. Other path values, and the log lines of every other operation, are unchanged.
16
+ - **What this release does not change:**
17
+ - `httpx` writes its own `HTTP Request: <METHOD> <URL> ...` line at `INFO` on the `httpx` logger, with the full URL including the query string. The SDK does not control that logger. If your application logs at `INFO` or below, set `logging.getLogger("httpx").setLevel(logging.WARNING)`. See [SECURITY.md](SECURITY.md#logging).
18
+ - Middleware hooks receive the concrete values: `RequestContext.path`, `url` and `params` are what is sent to the API.
19
+ - Exceptions that `httpx` raises for transport failures (timeouts, connection errors) carry the request, and with it the full URL, as their `request` attribute.
20
+ - The code generator now refuses to generate while any path-parameter name in the spec is unclassified. Every name is listed in `tools/codegen/sensitivity.py` as sensitive or not sensitive, and a name missing from the not-sensitive list is redacted. See [CONTRIBUTING.md](CONTRIBUTING.md#path-parameter-classification).
21
+
22
+ ### Changed
23
+
24
+ - Bumped package version to `2026.10.2` (CalVer).
25
+ - `HttpClient.request()` and its `get` / `post` / `put` / `patch` / `delete` helpers accept a `SensitivePath` (new, in `ordercloud.http`) as well as a plain string path. Generated resource method signatures are unchanged.
26
+ - Removed an unused module-level `TypeVar` from `ordercloud.sync_client`.
27
+
7
28
  ## 2026.10.1 — 2026-10-05
8
29
 
9
30
  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.
@@ -99,8 +99,32 @@ If you need to change how models or resources are structured, the relevant files
99
99
  - `tools/codegen/ir.py` — intermediate representation dataclasses
100
100
  - `tools/codegen/parser.py` — OpenAPI spec to IR
101
101
  - `tools/codegen/transformer.py` — IR enrichment (imports, grouping)
102
+ - `tools/codegen/sensitivity.py` — path-parameter sensitivity registry (see below)
102
103
  - `tools/codegen/templates/*.j2` — Jinja2 templates for output files
103
104
 
105
+ ### Path-parameter classification
106
+
107
+ The SDK logs the path of every request, and some path parameters carry values that work as credentials. Every path-parameter name in the spec is therefore classified in `tools/codegen/sensitivity.py`, in exactly one of two sets:
108
+
109
+ - `SENSITIVE_PATH_PARAMS` — anyone who reads the value from a log could use it to authenticate, obtain a token, or perform an account action without the account owner's own credentials.
110
+ - `NOT_SENSITIVE_PATH_PARAMS` — record identifiers that grant nothing without the caller's own authorisation.
111
+
112
+ When uncertain, choose sensitive. `NOT_SENSITIVE_PATH_PARAMS` is an allowlist: a parameter whose name is not on it is generated as a `SensitivePath`, and the SDK logs `***` in its place.
113
+
114
+ Generation stops with exit code 1, before any file is written, if a path-parameter name in the spec is unclassified, is in both sets, or is classified but no longer in the spec. A new, unclassified name fails like this (`shareToken` is an example, not a real parameter):
115
+
116
+ ```
117
+ Error: Path parameter 'shareToken' is not classified (used by Products.GetShareLink); add it to SENSITIVE_PATH_PARAMS or NOT_SENSITIVE_PATH_PARAMS in tools/codegen/sensitivity.py
118
+ ```
119
+
120
+ To resolve it:
121
+
122
+ 1. Read what the value is in the spec: the operations named in the error, their descriptions, and what they return.
123
+ 2. Apply the rule above and add the name to one of the two sets, with a comment giving the reason.
124
+ 3. Regenerate and run the tests. `tests/test_log_redaction.py` scans every generated request path and fails if one could log an unclassified or sensitive value.
125
+
126
+ The classification is by name, so the gate cannot tell when a new spec reuses an already-cleared name for a credential-bearing value. Review the spec diff at each spec bump for that.
127
+
104
128
  ## Conventions
105
129
 
106
130
  - **snake_case** for all Python identifiers. Model fields use snake_case with PascalCase aliases matching the API.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: ordercloud-python
3
- Version: 2026.10.1
3
+ Version: 2026.10.2
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
@@ -212,9 +212,8 @@ The SDK logs via Python's standard `logging` module under the `ordercloud` logge
212
212
 
213
213
  ```python
214
214
  import logging
215
- logging.basicConfig(level=logging.DEBUG)
216
215
 
217
- # Or configure just the SDK logger
216
+ logging.basicConfig() # adds a handler; the root logger stays at WARNING
218
217
  logging.getLogger("ordercloud").setLevel(logging.DEBUG)
219
218
  ```
220
219
 
@@ -223,6 +222,10 @@ logging.getLogger("ordercloud").setLevel(logging.DEBUG)
223
222
  | `DEBUG` | Every request (`Request: GET /products`) and response (`Response: GET /products 200`) |
224
223
  | `WARNING` | Retry attempts with status code and backoff delay |
225
224
 
225
+ Path values that work as credentials, a password-reset verification code and an invitation ID, are logged as `***`, for example `Request: PUT /password/reset/***`. Query parameters and request bodies are not logged.
226
+
227
+ Setting the root logger to `INFO` or `DEBUG` (for example `logging.basicConfig(level=logging.DEBUG)`) also turns on `httpx`'s own `HTTP Request:` line, which contains the full URL, unredacted, including the query string. The SDK does not control that logger. To keep it quiet, add `logging.getLogger("httpx").setLevel(logging.WARNING)`. See [SECURITY.md](SECURITY.md#logging).
228
+
226
229
  ## Middleware Hooks
227
230
 
228
231
  Register hooks to intercept requests and responses:
@@ -242,6 +245,8 @@ client.add_after_response(log_timing)
242
245
 
243
246
  Before-request hooks receive a mutable `RequestContext` — modify `headers`, `params`, or `json` before the request is sent. After-response hooks receive a `ResponseContext` with the request details and response. Hooks are called on every attempt, including retries.
244
247
 
248
+ Hooks see the concrete values sent to the API, including credential-bearing path values and the `Authorization` header; the redaction above applies only to the SDK's own log lines.
249
+
245
250
  ## API Coverage
246
251
 
247
252
  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).
@@ -172,9 +172,8 @@ The SDK logs via Python's standard `logging` module under the `ordercloud` logge
172
172
 
173
173
  ```python
174
174
  import logging
175
- logging.basicConfig(level=logging.DEBUG)
176
175
 
177
- # Or configure just the SDK logger
176
+ logging.basicConfig() # adds a handler; the root logger stays at WARNING
178
177
  logging.getLogger("ordercloud").setLevel(logging.DEBUG)
179
178
  ```
180
179
 
@@ -183,6 +182,10 @@ logging.getLogger("ordercloud").setLevel(logging.DEBUG)
183
182
  | `DEBUG` | Every request (`Request: GET /products`) and response (`Response: GET /products 200`) |
184
183
  | `WARNING` | Retry attempts with status code and backoff delay |
185
184
 
185
+ Path values that work as credentials, a password-reset verification code and an invitation ID, are logged as `***`, for example `Request: PUT /password/reset/***`. Query parameters and request bodies are not logged.
186
+
187
+ Setting the root logger to `INFO` or `DEBUG` (for example `logging.basicConfig(level=logging.DEBUG)`) also turns on `httpx`'s own `HTTP Request:` line, which contains the full URL, unredacted, including the query string. The SDK does not control that logger. To keep it quiet, add `logging.getLogger("httpx").setLevel(logging.WARNING)`. See [SECURITY.md](SECURITY.md#logging).
188
+
186
189
  ## Middleware Hooks
187
190
 
188
191
  Register hooks to intercept requests and responses:
@@ -202,6 +205,8 @@ client.add_after_response(log_timing)
202
205
 
203
206
  Before-request hooks receive a mutable `RequestContext` — modify `headers`, `params`, or `json` before the request is sent. After-response hooks receive a `ResponseContext` with the request details and response. Hooks are called on every attempt, including retries.
204
207
 
208
+ Hooks see the concrete values sent to the API, including credential-bearing path values and the `Authorization` header; the redaction above applies only to the SDK's own log lines.
209
+
205
210
  ## API Coverage
206
211
 
207
212
  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).
@@ -0,0 +1,70 @@
1
+ # Security Policy
2
+
3
+ ## Supported Versions
4
+
5
+ | Version | Supported |
6
+ |---------|--------------------|
7
+ | Latest | :white_check_mark: |
8
+
9
+ ## Reporting a Vulnerability
10
+
11
+ If you discover a security vulnerability in this project, please report it responsibly.
12
+
13
+ **Do not open a public GitHub issue for security vulnerabilities.**
14
+
15
+ Instead, email **mark.cassidy@markcassidyconsulting.com** with:
16
+
17
+ - A description of the vulnerability
18
+ - Steps to reproduce or a proof of concept
19
+ - The potential impact
20
+
21
+ You will receive an acknowledgement within 48 hours and a detailed response within 5 business days, including next steps and any planned fixes.
22
+
23
+ If the vulnerability is confirmed, a fix will be developed and released as a patch version. A security advisory will be published via [GitHub Security Advisories](https://github.com/markcassidyconsulting/ordercloud-python/security/advisories) once a fix is available.
24
+
25
+ ## Scope
26
+
27
+ This SDK is an HTTP client library. It does not run a server, store credentials persistently, or process untrusted input beyond what the OrderCloud API returns. Security concerns most likely relate to:
28
+
29
+ - Credential handling (OAuth tokens in memory)
30
+ - Dependency vulnerabilities (tracked via Dependabot and `dependency-review`)
31
+ - Injection via API response data (mitigated by Pydantic model validation)
32
+
33
+ ## Logging
34
+
35
+ The SDK writes request log lines to the `ordercloud` logger (see [Structured Logging](README.md#structured-logging)). Path values that work as credentials, a password-reset `verificationCode` and an `invitationID`, appear in those lines as `***`. Every path-parameter name in the OrderCloud spec is classified as sensitive or not sensitive when the SDK is generated, and an unclassified name stops generation. Query parameters and request bodies are not written to these lines.
36
+
37
+ The SDK does not control what other code logs:
38
+
39
+ - **`httpx` logs the full URL.** For every request, `httpx` writes `HTTP Request: <METHOD> <URL> "<HTTP version> <status> <reason>"` at `INFO` on the `httpx` logger, with the full URL including the path and the query string (checked against the httpx 0.28.1 source). The line appears whenever that logger is enabled for `INFO`, for example after `logging.basicConfig(level=logging.INFO)` or `level=logging.DEBUG`. To keep it out of your logs:
40
+
41
+ ```python
42
+ import logging
43
+
44
+ logging.getLogger("httpx").setLevel(logging.WARNING)
45
+ ```
46
+
47
+ - **`httpx` exceptions carry the request.** Exceptions that `httpx` raises for transport failures (timeouts, connection errors) carry the request as their `request` attribute, and `exc.request.url` is the full URL.
48
+ - **Middleware hooks receive the concrete values.** `RequestContext.path`, `url` and `params` are the values sent to the API, and `headers` holds the `Authorization` bearer token. A hook that logs them must redact them itself.
49
+
50
+ ## Security Measures
51
+
52
+ This project employs the following security practices:
53
+
54
+ - **Static analysis:** [CodeQL](https://github.com/markcassidyconsulting/ordercloud-python/actions/workflows/codeql.yml) runs on every push and weekly
55
+ - **Dependency scanning:** [Dependabot](https://github.com/markcassidyconsulting/ordercloud-python/security/dependabot) monitors for known vulnerabilities in dependencies
56
+ - **Dependency review:** Pull requests are checked for newly introduced vulnerable dependencies
57
+ - **Supply chain security:** All GitHub Actions are pinned to commit SHAs
58
+ - **Build provenance:** Every [GitHub release](https://github.com/markcassidyconsulting/ordercloud-python/releases) carries the build provenance attestation for its sdist and wheel as `ordercloud_python-<version>.intoto.jsonl`
59
+ - **Branch protection:** The `main` branch requires status checks to pass before merge
60
+
61
+ ### Verifying a release
62
+
63
+ With the [GitHub CLI](https://cli.github.com/), download a release's files and check a distribution against its provenance file:
64
+
65
+ ```bash
66
+ gh release download v<version> --repo markcassidyconsulting/ordercloud-python
67
+ gh attestation verify ordercloud_python-<version>-py3-none-any.whl --bundle ordercloud_python-<version>.intoto.jsonl --repo markcassidyconsulting/ordercloud-python
68
+ ```
69
+
70
+ The provenance file covers both the wheel and the sdist (`ordercloud_python-<version>.tar.gz`), and the same command checks a wheel fetched from PyPI with `pip download ordercloud-python==<version> --no-deps`. Exit code 0 means the file matches build provenance signed by a GitHub Actions workflow in this repository; a modified file fails verification.
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "ordercloud-python"
7
- version = "2026.10.1"
7
+ version = "2026.10.2"
8
8
  description = "Idiomatic Python SDK for Sitecore OrderCloud"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -2,7 +2,8 @@
2
2
 
3
3
  import asyncio
4
4
  import logging
5
- from typing import Any, Optional
5
+ from dataclasses import dataclass
6
+ from typing import Any, Optional, Union
6
7
 
7
8
  import httpx
8
9
 
@@ -11,13 +12,28 @@ from .config import OrderCloudConfig
11
12
  from .errors import ApiError, AuthenticationError, OrderCloudError
12
13
  from .middleware import AfterResponse, BeforeRequest, RequestContext, ResponseContext
13
14
 
14
- __all__ = ["HttpClient"]
15
+ __all__ = ["HttpClient", "SensitivePath"]
15
16
 
16
17
  logger = logging.getLogger("ordercloud")
17
18
 
18
19
  _RETRYABLE_STATUSES = frozenset({429, 500, 502, 503, 504})
19
20
 
20
21
 
22
+ @dataclass(frozen=True)
23
+ class SensitivePath:
24
+ """A request path that carries a sensitive value.
25
+
26
+ ``wire`` is sent to the API; ``log_form`` is the only form the SDK may log.
27
+ """
28
+
29
+ wire: str
30
+ log_form: str
31
+
32
+ def __repr__(self) -> str:
33
+ # str() falls back to this, so neither ever renders the wire form.
34
+ return f"SensitivePath({self.log_form!r})"
35
+
36
+
21
37
  class HttpClient:
22
38
  """Async HTTP client with automatic authentication and error handling.
23
39
 
@@ -60,7 +76,7 @@ class HttpClient:
60
76
  async def request(
61
77
  self,
62
78
  method: str,
63
- path: str,
79
+ path: Union[str, SensitivePath],
64
80
  *,
65
81
  params: Optional[dict[str, Any]] = None,
66
82
  json: Optional[dict[str, Any]] = None,
@@ -69,7 +85,9 @@ class HttpClient:
69
85
 
70
86
  Args:
71
87
  method: HTTP method (e.g. ``"GET"``, ``"POST"``).
72
- path: API path relative to the base URL (e.g. ``"/products"``).
88
+ path: API path relative to the base URL (e.g. ``"/products"``),
89
+ or a ``SensitivePath`` whose ``log_form`` is logged in place
90
+ of the path that is sent. A plain string is logged as given.
73
91
  params: Query parameters (``None`` values are stripped).
74
92
  json: JSON request body.
75
93
 
@@ -80,7 +98,8 @@ class HttpClient:
80
98
  AuthenticationError: On 401 or 403 responses.
81
99
  OrderCloudError: On any other 4xx/5xx response.
82
100
  """
83
- url = f"{self._config.base_url}{path}"
101
+ wire_path, log_path = self._split_path(path)
102
+ url = f"{self._config.base_url}{wire_path}"
84
103
 
85
104
  if params:
86
105
  params = {k: v for k, v in params.items() if v is not None}
@@ -93,7 +112,7 @@ class HttpClient:
93
112
 
94
113
  ctx = RequestContext(
95
114
  method=method,
96
- path=path,
115
+ path=wire_path,
97
116
  url=url,
98
117
  headers=dict(headers),
99
118
  params=dict(params) if params else None,
@@ -103,7 +122,7 @@ class HttpClient:
103
122
  for hook in self._before_request:
104
123
  await hook(ctx)
105
124
 
106
- logger.debug("Request: %s %s", method, path)
125
+ logger.debug("Request: %s %s", method, log_path)
107
126
 
108
127
  resp = await self._client.request(
109
128
  ctx.method,
@@ -113,7 +132,7 @@ class HttpClient:
113
132
  json=ctx.json,
114
133
  )
115
134
 
116
- logger.debug("Response: %s %s %d", method, path, resp.status_code)
135
+ logger.debug("Response: %s %s %d", method, log_path, resp.status_code)
117
136
 
118
137
  resp_ctx = ResponseContext(request=ctx, response=resp, attempt=attempt)
119
138
  for after_hook in self._after_response:
@@ -129,7 +148,7 @@ class HttpClient:
129
148
  attempt + 1,
130
149
  self._config.max_retries,
131
150
  method,
132
- path,
151
+ log_path,
133
152
  resp.status_code,
134
153
  delay,
135
154
  )
@@ -141,6 +160,13 @@ class HttpClient:
141
160
  # Unreachable — loop always returns or raises
142
161
  raise AssertionError("unreachable") # pragma: no cover
143
162
 
163
+ @staticmethod
164
+ def _split_path(path: Union[str, SensitivePath]) -> tuple[str, str]:
165
+ """Return ``(wire path, log path)``; a plain ``str`` is its own log form."""
166
+ if isinstance(path, SensitivePath):
167
+ return path.wire, path.log_form
168
+ return path, path
169
+
144
170
  def _retry_delay(self, resp: httpx.Response, attempt: int) -> float:
145
171
  """Calculate the delay before the next retry attempt.
146
172
 
@@ -157,13 +183,13 @@ class HttpClient:
157
183
  delay: float = self._config.retry_backoff * (2**attempt)
158
184
  return min(delay, max_delay)
159
185
 
160
- async def get(self, path: str, **params: Any) -> httpx.Response:
186
+ async def get(self, path: Union[str, SensitivePath], **params: Any) -> httpx.Response:
161
187
  """Send a GET request."""
162
188
  return await self.request("GET", path, params=params or None)
163
189
 
164
190
  async def post(
165
191
  self,
166
- path: str,
192
+ path: Union[str, SensitivePath],
167
193
  json: Optional[dict[str, Any]] = None,
168
194
  params: Optional[dict[str, Any]] = None,
169
195
  ) -> httpx.Response:
@@ -172,7 +198,7 @@ class HttpClient:
172
198
 
173
199
  async def put(
174
200
  self,
175
- path: str,
201
+ path: Union[str, SensitivePath],
176
202
  json: Optional[dict[str, Any]] = None,
177
203
  params: Optional[dict[str, Any]] = None,
178
204
  ) -> httpx.Response:
@@ -181,14 +207,14 @@ class HttpClient:
181
207
 
182
208
  async def patch(
183
209
  self,
184
- path: str,
210
+ path: Union[str, SensitivePath],
185
211
  json: Optional[dict[str, Any]] = None,
186
212
  params: Optional[dict[str, Any]] = None,
187
213
  ) -> httpx.Response:
188
214
  """Send a PATCH request."""
189
215
  return await self.request("PATCH", path, json=json, params=params)
190
216
 
191
- async def delete(self, path: str, **params: Any) -> httpx.Response:
217
+ async def delete(self, path: Union[str, SensitivePath], **params: Any) -> httpx.Response:
192
218
  """Send a DELETE request."""
193
219
  return await self.request("DELETE", path, params=params or None)
194
220
 
@@ -20,6 +20,13 @@ class RequestContext:
20
20
  Hooks can modify ``headers``, ``params``, or ``json`` in place
21
21
  before the request is sent.
22
22
 
23
+ ``path``, ``url`` and ``params`` are the concrete values sent on the
24
+ wire, and ``headers`` holds the ``Authorization`` bearer token. Any of
25
+ them may carry credentials — a password-reset verification code or a
26
+ group-order invitation ID in the path, for example — so redact them
27
+ before logging them from a hook. The SDK's own log lines redact such
28
+ path values; the context passed to hooks does not.
29
+
23
30
  Attributes:
24
31
  method: HTTP method (e.g. ``"GET"``).
25
32
  path: API path relative to the base URL.
@@ -5,6 +5,7 @@
5
5
  from __future__ import annotations
6
6
  from typing import Any, Optional, Union
7
7
 
8
+ from ..http import SensitivePath
8
9
  from ..models.auth_models import OneTimePasswordRequest, PasswordReset, PasswordResetRequest
9
10
  from .base import BaseResource
10
11
 
@@ -50,7 +51,9 @@ class ForgottenCredentialsResource(BaseResource):
50
51
  password_reset: A ``PasswordReset`` model or dict. Required fields: ClientID, Username.
51
52
  """
52
53
  await self._http.put(
53
- f"/password/reset/{verification_code}",
54
+ SensitivePath(
55
+ wire=f"/password/reset/{verification_code}", log_form="/password/reset/***"
56
+ ),
54
57
  json=self._serialize(password_reset),
55
58
  )
56
59
 
@@ -3,6 +3,7 @@
3
3
  """OrderCloud GroupOrders API resource."""
4
4
 
5
5
  from __future__ import annotations
6
+ from ..http import SensitivePath
6
7
  from ..models.auth_models import AccessToken
7
8
  from .base import BaseResource
8
9
 
@@ -24,5 +25,9 @@ class GroupOrdersResource(BaseResource):
24
25
  Returns:
25
26
  The AccessToken object.
26
27
  """
27
- resp = await self._http.post(f"/grouporders/{invitation_id}/token")
28
+ resp = await self._http.post(
29
+ SensitivePath(
30
+ wire=f"/grouporders/{invitation_id}/token", log_form="/grouporders/***/token"
31
+ )
32
+ )
28
33
  return AccessToken(**resp.json())
@@ -5,6 +5,7 @@
5
5
  from __future__ import annotations
6
6
  from typing import Any, Optional, Union
7
7
 
8
+ from ..http import SensitivePath
8
9
  from ..models.auth_models import AccessTokenBasic, TokenPasswordReset
9
10
  from ..models.buyer import BuyerAddress, BuyerCreditCard, BuyerProduct, BuyerSupplier, ProductSeller
10
11
  from ..models.catalog import Catalog
@@ -513,7 +514,11 @@ class MeResource(BaseResource):
513
514
  Returns:
514
515
  The GroupOrderInvitation object.
515
516
  """
516
- resp = await self._http.get(f"/me/orderinvitations/{invitation_id}")
517
+ resp = await self._http.get(
518
+ SensitivePath(
519
+ wire=f"/me/orderinvitations/{invitation_id}", log_form="/me/orderinvitations/***"
520
+ )
521
+ )
517
522
  return GroupOrderInvitation(**resp.json())
518
523
 
519
524
  async def delete_group_order_invitation(
@@ -525,7 +530,11 @@ class MeResource(BaseResource):
525
530
  Args:
526
531
  invitation_id: ID of the invitation.
527
532
  """
528
- await self._http.delete(f"/me/orderinvitations/{invitation_id}")
533
+ await self._http.delete(
534
+ SensitivePath(
535
+ wire=f"/me/orderinvitations/{invitation_id}", log_form="/me/orderinvitations/***"
536
+ )
537
+ )
529
538
 
530
539
  async def patch_group_order_invitation(
531
540
  self,
@@ -541,7 +550,12 @@ class MeResource(BaseResource):
541
550
  Returns:
542
551
  The GroupOrderInvitation object.
543
552
  """
544
- resp = await self._http.patch(f"/me/orderinvitations/{invitation_id}", json=partial)
553
+ resp = await self._http.patch(
554
+ SensitivePath(
555
+ wire=f"/me/orderinvitations/{invitation_id}", log_form="/me/orderinvitations/***"
556
+ ),
557
+ json=partial,
558
+ )
545
559
  return GroupOrderInvitation(**resp.json())
546
560
 
547
561
  async def list_orders(
@@ -877,7 +891,10 @@ class MeResource(BaseResource):
877
891
  The ProductCollectionInvitation object.
878
892
  """
879
893
  resp = await self._http.get(
880
- f"/me/productcollections/{product_collection_id}/invitations/{invitation_id}"
894
+ SensitivePath(
895
+ wire=f"/me/productcollections/{product_collection_id}/invitations/{invitation_id}",
896
+ log_form=f"/me/productcollections/{product_collection_id}/invitations/***",
897
+ )
881
898
  )
882
899
  return ProductCollectionInvitation(**resp.json())
883
900
 
@@ -893,7 +910,10 @@ class MeResource(BaseResource):
893
910
  invitation_id: ID of the invitation.
894
911
  """
895
912
  await self._http.delete(
896
- f"/me/productcollections/{product_collection_id}/invitations/{invitation_id}"
913
+ SensitivePath(
914
+ wire=f"/me/productcollections/{product_collection_id}/invitations/{invitation_id}",
915
+ log_form=f"/me/productcollections/{product_collection_id}/invitations/***",
916
+ )
897
917
  )
898
918
 
899
919
  async def patch_product_collection_invitation(
@@ -913,7 +933,10 @@ class MeResource(BaseResource):
913
933
  The ProductCollectionInvitation object.
914
934
  """
915
935
  resp = await self._http.patch(
916
- f"/me/productcollections/{product_collection_id}/invitations/{invitation_id}",
936
+ SensitivePath(
937
+ wire=f"/me/productcollections/{product_collection_id}/invitations/{invitation_id}",
938
+ log_form=f"/me/productcollections/{product_collection_id}/invitations/***",
939
+ ),
917
940
  json=partial,
918
941
  )
919
942
  return ProductCollectionInvitation(**resp.json())
@@ -930,7 +953,10 @@ class MeResource(BaseResource):
930
953
  invitation_id: ID of the invitation.
931
954
  """
932
955
  await self._http.post(
933
- f"/me/productcollections/{product_collection_id}/invitations/accept/{invitation_id}"
956
+ SensitivePath(
957
+ wire=f"/me/productcollections/{product_collection_id}/invitations/accept/{invitation_id}",
958
+ log_form=f"/me/productcollections/{product_collection_id}/invitations/accept/***",
959
+ )
934
960
  )
935
961
 
936
962
  async def decline_product_collection_invitation(
@@ -945,7 +971,10 @@ class MeResource(BaseResource):
945
971
  invitation_id: ID of the invitation.
946
972
  """
947
973
  await self._http.post(
948
- f"/me/productcollections/{product_collection_id}/invitations/decline/{invitation_id}"
974
+ SensitivePath(
975
+ wire=f"/me/productcollections/{product_collection_id}/invitations/decline/{invitation_id}",
976
+ log_form=f"/me/productcollections/{product_collection_id}/invitations/decline/***",
977
+ )
949
978
  )
950
979
 
951
980
  async def list_product_collection_entries(