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.
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/ci.yml +4 -4
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/codeql.yml +1 -1
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/dependency-review.yml +1 -1
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/release.yml +3 -3
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/sbom.yml +2 -2
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/scorecard.yml +2 -2
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/CHANGELOG.md +21 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/CONTRIBUTING.md +24 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/PKG-INFO +8 -3
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/README.md +7 -2
- ordercloud_python-2026.10.2/SECURITY.md +70 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/pyproject.toml +1 -1
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/http.py +40 -14
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/middleware.py +7 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/forgotten_credentials.py +4 -1
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/group_orders.py +6 -1
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/me.py +37 -8
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/sync_client.py +1 -3
- ordercloud_python-2026.10.2/tests/test_codegen_sensitivity.py +250 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_http.py +88 -1
- ordercloud_python-2026.10.2/tests/test_log_redaction.py +287 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/cli.py +9 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/ir.py +7 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/renderer.py +26 -1
- ordercloud_python-2026.10.2/tools/codegen/sensitivity.py +172 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/resource_module.py.j2 +14 -14
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/transformer.py +21 -1
- ordercloud_python-2026.10.1/SECURITY.md +0 -41
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/dependabot.yml +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.gitignore +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/CODE_REVIEW.md +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/LICENSE +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/SECURITY_AUDIT.md +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/examples/basic_workflow.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/__init__.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/auth.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/client.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/config.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/errors.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/__init__.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/address.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/api_client.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/approval.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/assignments.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/auth_models.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/bundle.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/buyer.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/catalog.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/category.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/cost_center.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/credit_card.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/delivery.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/discount.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/integration.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/inventory_record.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/line_item.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/line_item_types.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/message_sender.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/misc.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/open_id_connect.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/order.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/order_return.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/payment.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/price_schedule.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/product.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/product_collection.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/promotion.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/security.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/shared.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/shipment.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/spec.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/spending_account.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/subscription.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/supplier.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/sync.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/user.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/user_group.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/models/webhook.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/py.typed +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/__init__.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/addresses.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/admin_addresses.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/admin_user_groups.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/admin_users.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/api_clients.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/approval_rules.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/base.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/bundle_line_items.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/bundle_subscription_items.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/bundles.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/buyer_groups.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/buyers.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/cart.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/catalogs.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/categories.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/cost_centers.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/credit_cards.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/delivery_configurations.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/discounts.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/entity_syncs.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/error_configs.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/impersonation_configs.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/incrementors.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/integration_events.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/inventory_integrations.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/inventory_records.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/line_items.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/locales.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/message_senders.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/open_id_connects.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/order_returns.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/order_syncs.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/orders.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/payments.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/price_schedules.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/product_collections.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/product_facets.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/product_syncs.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/products.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/promotion_integrations.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/promotions.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/security_profiles.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/seller_approval_rules.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/shipments.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/specs.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/spending_accounts.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/subscription_integrations.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/subscription_items.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/subscriptions.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/supplier_addresses.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/supplier_user_groups.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/supplier_users.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/suppliers.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/tracking_events.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/user_groups.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/users.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/webhooks.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/xp_indices.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/__init__.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/conftest.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/__init__.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/conftest.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_auth.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_crud.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_errors.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_pagination.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_query_params.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/integration/test_sync_client.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_auth.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_models.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_resource_coverage.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_resources.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tests/test_sync_client.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/__init__.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/__init__.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/__main__.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/grouping.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/naming.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/parser.py +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/client.py.j2 +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/model_module.py.j2 +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/models_init.py.j2 +0 -0
- {ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/tools/codegen/templates/resources_init.py.j2 +0 -0
- {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@
|
|
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@
|
|
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@
|
|
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@
|
|
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@
|
|
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
|
{ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/.github/workflows/dependency-review.yml
RENAMED
|
@@ -12,7 +12,7 @@ jobs:
|
|
|
12
12
|
name: Review dependencies
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
steps:
|
|
15
|
-
- uses: actions/checkout@
|
|
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@
|
|
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@
|
|
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@
|
|
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@
|
|
19
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
20
20
|
|
|
21
21
|
- name: Generate SBOM
|
|
22
|
-
uses: anchore/sbom-action@
|
|
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@
|
|
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@
|
|
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.
|
|
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
|
-
#
|
|
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
|
-
#
|
|
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.
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
import asyncio
|
|
4
4
|
import logging
|
|
5
|
-
from
|
|
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
|
-
|
|
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=
|
|
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,
|
|
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,
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
{ordercloud_python-2026.10.1 → ordercloud_python-2026.10.2}/src/ordercloud/resources/group_orders.py
RENAMED
|
@@ -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(
|
|
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(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|