diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2ee47b6d..4ddd2d14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,12 +1,14 @@ name: CI on: push: - branches-ignore: - - 'generated' - - 'codegen/**' - - 'integrated/**' - - 'stl-preview-head/**' - - 'stl-preview-base/**' + branches: + - '**' + - '!integrated/**' + - '!stl-preview-head/**' + - '!stl-preview-base/**' + - '!generated' + - '!codegen/**' + - 'codegen/stl/**' pull_request: branches-ignore: - 'stl-preview-head/**' @@ -17,7 +19,7 @@ jobs: timeout-minutes: 10 name: lint runs-on: ${{ github.repository == 'stainless-sdks/orb-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') steps: - uses: actions/checkout@v6 @@ -36,7 +38,7 @@ jobs: run: ./scripts/lint build: - if: github.event_name == 'push' || github.event.pull_request.head.repo.fork + if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata') timeout-minutes: 10 name: build permissions: @@ -61,14 +63,18 @@ jobs: run: rye build - name: Get GitHub OIDC Token - if: github.repository == 'stainless-sdks/orb-python' + if: |- + github.repository == 'stainless-sdks/orb-python' && + !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc uses: actions/github-script@v8 with: script: core.setOutput('github_token', await core.getIDToken()); - name: Upload tarball - if: github.repository == 'stainless-sdks/orb-python' + if: |- + github.repository == 'stainless-sdks/orb-python' && + !startsWith(github.ref, 'refs/heads/stl/') env: URL: https://pkg.stainless.com/s AUTH: ${{ steps.github-oidc.outputs.github_token }} diff --git a/.gitignore b/.gitignore index 95ceb189..3824f4c4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .prism.log +.stdy.log _dev __pycache__ diff --git a/.release-please-manifest.json b/.release-please-manifest.json index c3d8acd5..331e8361 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "4.55.0" + ".": "4.56.0" } \ No newline at end of file diff --git a/.stats.yml b/.stats.yml index c80eee48..345cd65b 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 139 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb%2Forb-c92fb451e13f157b3735f188acc8d57aa3adfbaac1683645e1ba4f432dd7a4f8.yml -openapi_spec_hash: dbcd87ecfbd3976eb3b99ec6f9fbc606 -config_hash: 3279841440b02d4e8303c961d6983492 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/orb/orb-7f97a69f22372b818e8d24a72f6020bcf2f0c6d6b3ad07abb8395c87ec4a72ee.yml +openapi_spec_hash: a6261e730c54d08ad36f1b946e663fc3 +config_hash: c01c1191b1cd696c7ca855ff6d28a8df diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c03365a..42415419 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,79 @@ # Changelog +## 4.56.0 (2026-05-08) + +Full Changelog: [v4.55.0...v4.56.0](https://github.com/orbcorp/orb-python/compare/v4.55.0...v4.56.0) + +### Features + +* **api:** api update ([420a0ee](https://github.com/orbcorp/orb-python/commit/420a0ee33bf090fbbab838ce83f0a84f9c043407)) +* **api:** api update ([71340f6](https://github.com/orbcorp/orb-python/commit/71340f6025d41322701c9aa59ae9ff52d783428e)) +* **api:** api update ([6450ca8](https://github.com/orbcorp/orb-python/commit/6450ca894f18893714bde57ed1b2841deef556ab)) +* **api:** api update ([d11987d](https://github.com/orbcorp/orb-python/commit/d11987d6a51e91b50bbd1a5b551fed1747bd42b6)) +* **api:** api update ([f5bfd3c](https://github.com/orbcorp/orb-python/commit/f5bfd3c2c346af87b457b62726d8e117ba3e0f23)) +* **api:** api update ([3b0e75f](https://github.com/orbcorp/orb-python/commit/3b0e75fcfff160299f9229116ed1a267f2d0b44c)) +* **api:** api update ([de414ba](https://github.com/orbcorp/orb-python/commit/de414ba5ad025dd149eb8fde15767aca928b289d)) +* **api:** api update ([5a1e185](https://github.com/orbcorp/orb-python/commit/5a1e185f488ee90547d11f4178d25e6382447823)) +* **api:** api update ([3c67098](https://github.com/orbcorp/orb-python/commit/3c67098dbbfb806a14bfe65f83930b1f594d873a)) +* **api:** api update ([4e3652d](https://github.com/orbcorp/orb-python/commit/4e3652dfaec77221b4907179784aaf88e1ff2d67)) +* **api:** api update ([931c0f4](https://github.com/orbcorp/orb-python/commit/931c0f421c553981b7c0326a0c721e7cc5fa53f3)) +* **api:** api update ([8c1ffda](https://github.com/orbcorp/orb-python/commit/8c1ffda41e28f9f3dad2b64363dc3e6d8b8e57b2)) +* **api:** api update ([deda5e1](https://github.com/orbcorp/orb-python/commit/deda5e1a4c4412e91ad61f038b5c9e17d3072a22)) +* **api:** api update ([425bac4](https://github.com/orbcorp/orb-python/commit/425bac4cc3a9df0085fad215bf5c66d108008d67)) +* **api:** api update ([cd1cd23](https://github.com/orbcorp/orb-python/commit/cd1cd2396264bbc01c71c6ba7e2dc937ee1b5928)) +* **api:** api update ([6b4583e](https://github.com/orbcorp/orb-python/commit/6b4583ede27c6245a828b670301e1bf37cf708a7)) +* **api:** api update ([b676980](https://github.com/orbcorp/orb-python/commit/b6769806d684451bd19d037fd80132e4a0de5930)) +* **api:** api update ([19af3f7](https://github.com/orbcorp/orb-python/commit/19af3f7d6238fb95b66cf2229e5095a0f2bb0df1)) +* **api:** api update ([3686dc6](https://github.com/orbcorp/orb-python/commit/3686dc6ec326e564578f5ab13c7d258d41de8def)) +* **api:** api update ([2fbc2b1](https://github.com/orbcorp/orb-python/commit/2fbc2b14d46648f4ef4ea57ead9cd08b444211b1)) +* **internal:** implement indices array format for query and form serialization ([b78355c](https://github.com/orbcorp/orb-python/commit/b78355cef870adb25c8c6995362c1bef24ca78d1)) +* support setting headers via env ([2366135](https://github.com/orbcorp/orb-python/commit/2366135b23351d39f2da94f68e2649436ab6c4b7)) + + +### Bug Fixes + +* **client:** add missing f-string prefix in file type error message ([67ca8a4](https://github.com/orbcorp/orb-python/commit/67ca8a4ce63933b112cd3188b720bfa13cf177b5)) +* **client:** preserve hardcoded query params when merging with user params ([a87a1f8](https://github.com/orbcorp/orb-python/commit/a87a1f812952e2aaf62d34539abe40d3fe3b8778)) +* **deps:** bump minimum typing-extensions version ([93f9afe](https://github.com/orbcorp/orb-python/commit/93f9afed7c1b6c5aa432e78bac43107a4b7f2df1)) +* ensure file data are only sent as 1 parameter ([e478a4e](https://github.com/orbcorp/orb-python/commit/e478a4e612a3cf3f4ac6162cb0f965938fe06856)) +* **pydantic:** do not pass `by_alias` unless set ([06cedae](https://github.com/orbcorp/orb-python/commit/06cedaebd513685daa42d203346112a8ac4c792a)) +* sanitize endpoint path params ([a7e8834](https://github.com/orbcorp/orb-python/commit/a7e88347c865c3daa237efaedbb824cf04a5d3b2)) +* use correct field name format for multipart file arrays ([62c31ad](https://github.com/orbcorp/orb-python/commit/62c31ad3426daf3df4ac7c1af8813f6ac4b30103)) + + +### Performance Improvements + +* **client:** optimize file structure copying in multipart requests ([b9feeba](https://github.com/orbcorp/orb-python/commit/b9feeba9110ee8bc347368a1f277b2f2c75f5955)) + + +### Chores + +* **ci:** skip lint on metadata-only changes ([16d7746](https://github.com/orbcorp/orb-python/commit/16d77465cdd71d193002e3d977f926a856b8a693)) +* **ci:** skip uploading artifacts on stainless-internal branches ([a2813c8](https://github.com/orbcorp/orb-python/commit/a2813c8575727ecbd2582b251095ed70a9b75613)) +* **docs:** add missing descriptions ([1bb4db8](https://github.com/orbcorp/orb-python/commit/1bb4db815a18a056aa19366c6622a66935d59577)) +* **internal:** add request options to SSE classes ([9110978](https://github.com/orbcorp/orb-python/commit/9110978adab2229b2eff3f3dfeb3ce6df2f5305e)) +* **internal:** make `test_proxy_environment_variables` more resilient ([e567054](https://github.com/orbcorp/orb-python/commit/e56705443f6920ad4b6eb750a5cd0488d8a119f3)) +* **internal:** make `test_proxy_environment_variables` more resilient to env ([a2da977](https://github.com/orbcorp/orb-python/commit/a2da97718dd34551d38475738e3fd701cc8e2fbe)) +* **internal:** more robust bootstrap script ([eaaf738](https://github.com/orbcorp/orb-python/commit/eaaf7380236110ea5ddacd9bc49eaaff895b3fab)) +* **internal:** reformat pyproject.toml ([e022f45](https://github.com/orbcorp/orb-python/commit/e022f4563ac868f55d5f7a3baa66773295446a97)) +* **internal:** tweak CI branches ([c5ab3b4](https://github.com/orbcorp/orb-python/commit/c5ab3b47348d6b75866c9e3e3ff6cc185e3cd1c8)) +* **internal:** update gitignore ([e2316ae](https://github.com/orbcorp/orb-python/commit/e2316aee6077ef333f4ae35fd759e3ef0a8d4343)) +* **test:** do not count install time for mock server timeout ([888e2ca](https://github.com/orbcorp/orb-python/commit/888e2ca02a4fd7cb2294bf0ce1af4b8cee014809)) +* **tests:** bump steady to v0.19.4 ([dba860e](https://github.com/orbcorp/orb-python/commit/dba860e1955d0964daee83d2f4eae6030df1e802)) +* **tests:** bump steady to v0.19.5 ([0918804](https://github.com/orbcorp/orb-python/commit/09188043849d27914198fd370b525ab9353cb174)) +* **tests:** bump steady to v0.19.6 ([f37e5be](https://github.com/orbcorp/orb-python/commit/f37e5be1bdad7d3f627f2a5f1a7e97fa590dc3c3)) +* **tests:** bump steady to v0.19.7 ([403afe2](https://github.com/orbcorp/orb-python/commit/403afe25fbed8a923ec2bbcf5b7ab4deb16bfaef)) +* **tests:** bump steady to v0.20.1 ([a15890c](https://github.com/orbcorp/orb-python/commit/a15890cea8a677e9f3fa1d63cb13411ec39896d4)) +* **tests:** bump steady to v0.20.2 ([1b0c906](https://github.com/orbcorp/orb-python/commit/1b0c906c63dd56858f8c45293e6089abcb6f0060)) +* **tests:** bump steady to v0.22.1 ([cbbf427](https://github.com/orbcorp/orb-python/commit/cbbf42715e0f53d2e9fd5f720b11c0333738ae3e)) +* **tests:** update mock server to steady ([97a3d8f](https://github.com/orbcorp/orb-python/commit/97a3d8ff2b9ffe7810864b7146fbe9dab0909aa5)) +* update mock server docs ([0f016de](https://github.com/orbcorp/orb-python/commit/0f016de052ebb429752c7ffdc4ca061c19562469)) + + +### Documentation + +* improve examples ([de8f18b](https://github.com/orbcorp/orb-python/commit/de8f18b0efd5c11bb5a62078ada084dc8b81bd46)) + ## 4.55.0 (2026-02-12) Full Changelog: [v4.54.0...v4.55.0](https://github.com/orbcorp/orb-python/compare/v4.54.0...v4.55.0) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ea9f566f..13aed782 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,11 +85,10 @@ $ pip install ./path-to-wheel-file.whl ## Running tests -Most tests require you to [set up a mock server](https://github.com/stoplightio/prism) against the OpenAPI spec to run the tests. +Most tests require you to [set up a mock server](https://github.com/dgellow/steady) against the OpenAPI spec to run the tests. ```sh -# you will need npm installed -$ npx prism mock path/to/your/openapi.yml +$ ./scripts/mock ``` ```sh diff --git a/api.md b/api.md index b50a14f4..37c8c3c8 100644 --- a/api.md +++ b/api.md @@ -408,7 +408,7 @@ Methods: - client.invoices.issue_summary(invoice_id, \*\*params) -> InvoiceIssueSummaryResponse - client.invoices.list_summary(\*\*params) -> SyncPage[InvoiceListSummaryResponse] - client.invoices.mark_paid(invoice_id, \*\*params) -> Invoice -- client.invoices.pay(invoice_id) -> Invoice +- client.invoices.pay(invoice_id, \*\*params) -> Invoice - client.invoices.void(invoice_id) -> Invoice # Items diff --git a/pyproject.toml b/pyproject.toml index 0f0bbaa4..9135d7d3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "orb-billing" -version = "4.55.0" +version = "4.56.0" description = "The official Python library for the orb API" dynamic = ["readme"] license = "Apache-2.0" @@ -11,7 +11,7 @@ authors = [ dependencies = [ "httpx>=0.23.0, <1", "pydantic>=1.9.0, <3", - "typing-extensions>=4.10, <5", + "typing-extensions>=4.14, <5", "anyio>=3.5.0, <5", "distro>=1.7.0, <2", "sniffio", @@ -168,7 +168,7 @@ show_error_codes = true # # We also exclude our `tests` as mypy doesn't always infer # types correctly and Pyright will still catch any type errors. -exclude = ['src/orb/_files.py', '_dev/.*.py', 'tests/.*'] +exclude = ["src/orb/_files.py", "_dev/.*.py", "tests/.*"] strict_equality = true implicit_reexport = true diff --git a/scripts/bootstrap b/scripts/bootstrap index b430fee3..fe8451e4 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,7 +4,7 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "${SKIP_BREW:-}" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { echo -n "==> Install Homebrew dependencies? (y/N): " read -r response diff --git a/scripts/mock b/scripts/mock index 0b28f6ea..04d29019 100755 --- a/scripts/mock +++ b/scripts/mock @@ -19,23 +19,34 @@ fi echo "==> Starting mock server with URL ${URL}" -# Run prism mock on the given spec +# Run steady mock on the given spec if [ "$1" == "--daemon" ]; then - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + # Pre-install the package so the download doesn't eat into the startup timeout + npm exec --package=@stdy/cli@0.22.1 -- steady --version - # Wait for server to come online + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + + # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + attempts=0 + while ! curl --silent --fail "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1; do + if ! kill -0 $! 2>/dev/null; then + echo + cat .stdy.log + exit 1 + fi + attempts=$((attempts + 1)) + if [ "$attempts" -ge 300 ]; then + echo + echo "Timed out waiting for Steady server to start" + cat .stdy.log + exit 1 + fi echo -n "." sleep 0.1 done - if grep -q "✖ fatal" ".prism.log"; then - cat .prism.log - exit 1 - fi - echo else - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index dbeda2d2..7b05e44f 100755 --- a/scripts/test +++ b/scripts/test @@ -9,8 +9,8 @@ GREEN='\033[0;32m' YELLOW='\033[0;33m' NC='\033[0m' # No Color -function prism_is_running() { - curl --silent "http://localhost:4010" >/dev/null 2>&1 +function steady_is_running() { + curl --silent "http://127.0.0.1:4010/_x-steady/health" >/dev/null 2>&1 } kill_server_on_port() { @@ -25,7 +25,7 @@ function is_overriding_api_base_url() { [ -n "$TEST_API_BASE_URL" ] } -if ! is_overriding_api_base_url && ! prism_is_running ; then +if ! is_overriding_api_base_url && ! steady_is_running ; then # When we exit this script, make sure to kill the background mock server process trap 'kill_server_on_port 4010' EXIT @@ -36,19 +36,19 @@ fi if is_overriding_api_base_url ; then echo -e "${GREEN}✔ Running tests against ${TEST_API_BASE_URL}${NC}" echo -elif ! prism_is_running ; then - echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Prism server" +elif ! steady_is_running ; then + echo -e "${RED}ERROR:${NC} The test suite will not run without a mock Steady server" echo -e "running against your OpenAPI spec." echo echo -e "To run the server, pass in the path or url of your OpenAPI" - echo -e "spec to the prism command:" + echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock path/to/your.openapi.yml${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.22.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=brackets --validator-form-array-format=brackets --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 else - echo -e "${GREEN}✔ Mock prism server is running with your OpenAPI spec${NC}" + echo -e "${GREEN}✔ Mock steady server is running with your OpenAPI spec${NC}" echo fi diff --git a/src/orb/_base_client.py b/src/orb/_base_client.py index 80ec2d78..86fbf71c 100644 --- a/src/orb/_base_client.py +++ b/src/orb/_base_client.py @@ -541,6 +541,10 @@ def _build_request( files = cast(HttpxRequestFiles, ForceMultipartDict()) prepared_url = self._prepare_url(options.url) + # preserve hard-coded query params from the url + if params and prepared_url.query: + params = {**dict(prepared_url.params.items()), **params} + prepared_url = prepared_url.copy_with(raw_path=prepared_url.raw_path.split(b"?", 1)[0]) if "_" in prepared_url.host: # work around https://github.com/encode/httpx/discussions/2880 kwargs["extensions"] = {"sni_hostname": prepared_url.host.replace("_", "-")} diff --git a/src/orb/_client.py b/src/orb/_client.py index d1f4078c..441fec66 100644 --- a/src/orb/_client.py +++ b/src/orb/_client.py @@ -22,6 +22,7 @@ from ._utils import ( is_given, is_mapping, + is_mapping_t, get_async_library, ) from ._compat import cached_property @@ -135,6 +136,15 @@ def __init__( if base_url is None: base_url = f"https://api.withorb.com/v1" + custom_headers_env = os.environ.get("ORB_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, @@ -156,66 +166,134 @@ def top_level(self) -> TopLevel: @cached_property def beta(self) -> Beta: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.beta import Beta return Beta(self) @cached_property def coupons(self) -> Coupons: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ from .resources.coupons import Coupons return Coupons(self) @cached_property def credit_notes(self) -> CreditNotes: + """ + The [Credit Note](/invoicing/credit-notes) resource represents a credit that has been applied to a + particular invoice. + """ from .resources.credit_notes import CreditNotes return CreditNotes(self) @cached_property def customers(self) -> Customers: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ from .resources.customers import Customers return Customers(self) @cached_property def events(self) -> Events: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ from .resources.events import Events return Events(self) @cached_property def invoice_line_items(self) -> InvoiceLineItems: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoice_line_items import InvoiceLineItems return InvoiceLineItems(self) @cached_property def invoices(self) -> Invoices: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoices import Invoices return Invoices(self) @cached_property def items(self) -> Items: + """The Item resource represents a sellable product or good. + + Items are associated with all line items, billable metrics, + and prices and are used for defining external sync behavior for invoices and tax calculation purposes. + """ from .resources.items import Items return Items(self) @cached_property def metrics(self) -> Metrics: + """ + The Metric resource represents a calculation of a quantity based on events. + Metrics are defined by the query that transforms raw usage events into meaningful values for your customers. + """ from .resources.metrics import Metrics return Metrics(self) @cached_property def plans(self) -> Plans: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.plans import Plans return Plans(self) @cached_property def prices(self) -> Prices: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ from .resources.prices import Prices return Prices(self) @@ -228,6 +306,12 @@ def subscriptions(self) -> Subscriptions: @cached_property def alerts(self) -> Alerts: + """ + [Alerts within Orb](/product-catalog/configuring-alerts) monitor spending, + usage, or credit balance and trigger webhooks when a threshold is exceeded. + + Alerts created through the API can be scoped to either customers or subscriptions. + """ from .resources.alerts import Alerts return Alerts(self) @@ -252,12 +336,19 @@ def webhooks(self) -> webhooks.Webhooks: @cached_property def credit_blocks(self) -> CreditBlocks: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ from .resources.credit_blocks import CreditBlocks return CreditBlocks(self) @cached_property def license_types(self) -> LicenseTypes: + """ + The LicenseType resource represents a type of license that can be assigned to users. + License types are used during billing by grouping metrics on the configured grouping key. + """ from .resources.license_types import LicenseTypes return LicenseTypes(self) @@ -483,6 +574,15 @@ def __init__( if base_url is None: base_url = f"https://api.withorb.com/v1" + custom_headers_env = os.environ.get("ORB_CUSTOM_HEADERS") + if custom_headers_env is not None: + parsed: dict[str, str] = {} + for line in custom_headers_env.split("\n"): + colon = line.find(":") + if colon >= 0: + parsed[line[:colon].strip()] = line[colon + 1 :].strip() + default_headers = {**parsed, **(default_headers if is_mapping_t(default_headers) else {})} + super().__init__( version=__version__, base_url=base_url, @@ -504,66 +604,134 @@ def top_level(self) -> AsyncTopLevel: @cached_property def beta(self) -> AsyncBeta: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.beta import AsyncBeta return AsyncBeta(self) @cached_property def coupons(self) -> AsyncCoupons: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ from .resources.coupons import AsyncCoupons return AsyncCoupons(self) @cached_property def credit_notes(self) -> AsyncCreditNotes: + """ + The [Credit Note](/invoicing/credit-notes) resource represents a credit that has been applied to a + particular invoice. + """ from .resources.credit_notes import AsyncCreditNotes return AsyncCreditNotes(self) @cached_property def customers(self) -> AsyncCustomers: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ from .resources.customers import AsyncCustomers return AsyncCustomers(self) @cached_property def events(self) -> AsyncEvents: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ from .resources.events import AsyncEvents return AsyncEvents(self) @cached_property def invoice_line_items(self) -> AsyncInvoiceLineItems: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoice_line_items import AsyncInvoiceLineItems return AsyncInvoiceLineItems(self) @cached_property def invoices(self) -> AsyncInvoices: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoices import AsyncInvoices return AsyncInvoices(self) @cached_property def items(self) -> AsyncItems: + """The Item resource represents a sellable product or good. + + Items are associated with all line items, billable metrics, + and prices and are used for defining external sync behavior for invoices and tax calculation purposes. + """ from .resources.items import AsyncItems return AsyncItems(self) @cached_property def metrics(self) -> AsyncMetrics: + """ + The Metric resource represents a calculation of a quantity based on events. + Metrics are defined by the query that transforms raw usage events into meaningful values for your customers. + """ from .resources.metrics import AsyncMetrics return AsyncMetrics(self) @cached_property def plans(self) -> AsyncPlans: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.plans import AsyncPlans return AsyncPlans(self) @cached_property def prices(self) -> AsyncPrices: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ from .resources.prices import AsyncPrices return AsyncPrices(self) @@ -576,6 +744,12 @@ def subscriptions(self) -> AsyncSubscriptions: @cached_property def alerts(self) -> AsyncAlerts: + """ + [Alerts within Orb](/product-catalog/configuring-alerts) monitor spending, + usage, or credit balance and trigger webhooks when a threshold is exceeded. + + Alerts created through the API can be scoped to either customers or subscriptions. + """ from .resources.alerts import AsyncAlerts return AsyncAlerts(self) @@ -600,12 +774,19 @@ def webhooks(self) -> webhooks.AsyncWebhooks: @cached_property def credit_blocks(self) -> AsyncCreditBlocks: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ from .resources.credit_blocks import AsyncCreditBlocks return AsyncCreditBlocks(self) @cached_property def license_types(self) -> AsyncLicenseTypes: + """ + The LicenseType resource represents a type of license that can be assigned to users. + License types are used during billing by grouping metrics on the configured grouping key. + """ from .resources.license_types import AsyncLicenseTypes return AsyncLicenseTypes(self) @@ -793,66 +974,134 @@ def top_level(self) -> top_level.TopLevelWithRawResponse: @cached_property def beta(self) -> beta.BetaWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.beta import BetaWithRawResponse return BetaWithRawResponse(self._client.beta) @cached_property def coupons(self) -> coupons.CouponsWithRawResponse: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ from .resources.coupons import CouponsWithRawResponse return CouponsWithRawResponse(self._client.coupons) @cached_property def credit_notes(self) -> credit_notes.CreditNotesWithRawResponse: + """ + The [Credit Note](/invoicing/credit-notes) resource represents a credit that has been applied to a + particular invoice. + """ from .resources.credit_notes import CreditNotesWithRawResponse return CreditNotesWithRawResponse(self._client.credit_notes) @cached_property def customers(self) -> customers.CustomersWithRawResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ from .resources.customers import CustomersWithRawResponse return CustomersWithRawResponse(self._client.customers) @cached_property def events(self) -> events.EventsWithRawResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ from .resources.events import EventsWithRawResponse return EventsWithRawResponse(self._client.events) @cached_property def invoice_line_items(self) -> invoice_line_items.InvoiceLineItemsWithRawResponse: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoice_line_items import InvoiceLineItemsWithRawResponse return InvoiceLineItemsWithRawResponse(self._client.invoice_line_items) @cached_property def invoices(self) -> invoices.InvoicesWithRawResponse: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoices import InvoicesWithRawResponse return InvoicesWithRawResponse(self._client.invoices) @cached_property def items(self) -> items.ItemsWithRawResponse: + """The Item resource represents a sellable product or good. + + Items are associated with all line items, billable metrics, + and prices and are used for defining external sync behavior for invoices and tax calculation purposes. + """ from .resources.items import ItemsWithRawResponse return ItemsWithRawResponse(self._client.items) @cached_property def metrics(self) -> metrics.MetricsWithRawResponse: + """ + The Metric resource represents a calculation of a quantity based on events. + Metrics are defined by the query that transforms raw usage events into meaningful values for your customers. + """ from .resources.metrics import MetricsWithRawResponse return MetricsWithRawResponse(self._client.metrics) @cached_property def plans(self) -> plans.PlansWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.plans import PlansWithRawResponse return PlansWithRawResponse(self._client.plans) @cached_property def prices(self) -> prices.PricesWithRawResponse: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ from .resources.prices import PricesWithRawResponse return PricesWithRawResponse(self._client.prices) @@ -865,6 +1114,12 @@ def subscriptions(self) -> subscriptions.SubscriptionsWithRawResponse: @cached_property def alerts(self) -> alerts.AlertsWithRawResponse: + """ + [Alerts within Orb](/product-catalog/configuring-alerts) monitor spending, + usage, or credit balance and trigger webhooks when a threshold is exceeded. + + Alerts created through the API can be scoped to either customers or subscriptions. + """ from .resources.alerts import AlertsWithRawResponse return AlertsWithRawResponse(self._client.alerts) @@ -883,12 +1138,19 @@ def subscription_changes(self) -> subscription_changes.SubscriptionChangesWithRa @cached_property def credit_blocks(self) -> credit_blocks.CreditBlocksWithRawResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ from .resources.credit_blocks import CreditBlocksWithRawResponse return CreditBlocksWithRawResponse(self._client.credit_blocks) @cached_property def license_types(self) -> license_types.LicenseTypesWithRawResponse: + """ + The LicenseType resource represents a type of license that can be assigned to users. + License types are used during billing by grouping metrics on the configured grouping key. + """ from .resources.license_types import LicenseTypesWithRawResponse return LicenseTypesWithRawResponse(self._client.license_types) @@ -914,66 +1176,134 @@ def top_level(self) -> top_level.AsyncTopLevelWithRawResponse: @cached_property def beta(self) -> beta.AsyncBetaWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.beta import AsyncBetaWithRawResponse return AsyncBetaWithRawResponse(self._client.beta) @cached_property def coupons(self) -> coupons.AsyncCouponsWithRawResponse: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ from .resources.coupons import AsyncCouponsWithRawResponse return AsyncCouponsWithRawResponse(self._client.coupons) @cached_property def credit_notes(self) -> credit_notes.AsyncCreditNotesWithRawResponse: + """ + The [Credit Note](/invoicing/credit-notes) resource represents a credit that has been applied to a + particular invoice. + """ from .resources.credit_notes import AsyncCreditNotesWithRawResponse return AsyncCreditNotesWithRawResponse(self._client.credit_notes) @cached_property def customers(self) -> customers.AsyncCustomersWithRawResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ from .resources.customers import AsyncCustomersWithRawResponse return AsyncCustomersWithRawResponse(self._client.customers) @cached_property def events(self) -> events.AsyncEventsWithRawResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ from .resources.events import AsyncEventsWithRawResponse return AsyncEventsWithRawResponse(self._client.events) @cached_property def invoice_line_items(self) -> invoice_line_items.AsyncInvoiceLineItemsWithRawResponse: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoice_line_items import AsyncInvoiceLineItemsWithRawResponse return AsyncInvoiceLineItemsWithRawResponse(self._client.invoice_line_items) @cached_property def invoices(self) -> invoices.AsyncInvoicesWithRawResponse: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoices import AsyncInvoicesWithRawResponse return AsyncInvoicesWithRawResponse(self._client.invoices) @cached_property def items(self) -> items.AsyncItemsWithRawResponse: + """The Item resource represents a sellable product or good. + + Items are associated with all line items, billable metrics, + and prices and are used for defining external sync behavior for invoices and tax calculation purposes. + """ from .resources.items import AsyncItemsWithRawResponse return AsyncItemsWithRawResponse(self._client.items) @cached_property def metrics(self) -> metrics.AsyncMetricsWithRawResponse: + """ + The Metric resource represents a calculation of a quantity based on events. + Metrics are defined by the query that transforms raw usage events into meaningful values for your customers. + """ from .resources.metrics import AsyncMetricsWithRawResponse return AsyncMetricsWithRawResponse(self._client.metrics) @cached_property def plans(self) -> plans.AsyncPlansWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.plans import AsyncPlansWithRawResponse return AsyncPlansWithRawResponse(self._client.plans) @cached_property def prices(self) -> prices.AsyncPricesWithRawResponse: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ from .resources.prices import AsyncPricesWithRawResponse return AsyncPricesWithRawResponse(self._client.prices) @@ -986,6 +1316,12 @@ def subscriptions(self) -> subscriptions.AsyncSubscriptionsWithRawResponse: @cached_property def alerts(self) -> alerts.AsyncAlertsWithRawResponse: + """ + [Alerts within Orb](/product-catalog/configuring-alerts) monitor spending, + usage, or credit balance and trigger webhooks when a threshold is exceeded. + + Alerts created through the API can be scoped to either customers or subscriptions. + """ from .resources.alerts import AsyncAlertsWithRawResponse return AsyncAlertsWithRawResponse(self._client.alerts) @@ -1004,12 +1340,19 @@ def subscription_changes(self) -> subscription_changes.AsyncSubscriptionChangesW @cached_property def credit_blocks(self) -> credit_blocks.AsyncCreditBlocksWithRawResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ from .resources.credit_blocks import AsyncCreditBlocksWithRawResponse return AsyncCreditBlocksWithRawResponse(self._client.credit_blocks) @cached_property def license_types(self) -> license_types.AsyncLicenseTypesWithRawResponse: + """ + The LicenseType resource represents a type of license that can be assigned to users. + License types are used during billing by grouping metrics on the configured grouping key. + """ from .resources.license_types import AsyncLicenseTypesWithRawResponse return AsyncLicenseTypesWithRawResponse(self._client.license_types) @@ -1035,66 +1378,134 @@ def top_level(self) -> top_level.TopLevelWithStreamingResponse: @cached_property def beta(self) -> beta.BetaWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.beta import BetaWithStreamingResponse return BetaWithStreamingResponse(self._client.beta) @cached_property def coupons(self) -> coupons.CouponsWithStreamingResponse: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ from .resources.coupons import CouponsWithStreamingResponse return CouponsWithStreamingResponse(self._client.coupons) @cached_property def credit_notes(self) -> credit_notes.CreditNotesWithStreamingResponse: + """ + The [Credit Note](/invoicing/credit-notes) resource represents a credit that has been applied to a + particular invoice. + """ from .resources.credit_notes import CreditNotesWithStreamingResponse return CreditNotesWithStreamingResponse(self._client.credit_notes) @cached_property def customers(self) -> customers.CustomersWithStreamingResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ from .resources.customers import CustomersWithStreamingResponse return CustomersWithStreamingResponse(self._client.customers) @cached_property def events(self) -> events.EventsWithStreamingResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ from .resources.events import EventsWithStreamingResponse return EventsWithStreamingResponse(self._client.events) @cached_property def invoice_line_items(self) -> invoice_line_items.InvoiceLineItemsWithStreamingResponse: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoice_line_items import InvoiceLineItemsWithStreamingResponse return InvoiceLineItemsWithStreamingResponse(self._client.invoice_line_items) @cached_property def invoices(self) -> invoices.InvoicesWithStreamingResponse: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoices import InvoicesWithStreamingResponse return InvoicesWithStreamingResponse(self._client.invoices) @cached_property def items(self) -> items.ItemsWithStreamingResponse: + """The Item resource represents a sellable product or good. + + Items are associated with all line items, billable metrics, + and prices and are used for defining external sync behavior for invoices and tax calculation purposes. + """ from .resources.items import ItemsWithStreamingResponse return ItemsWithStreamingResponse(self._client.items) @cached_property def metrics(self) -> metrics.MetricsWithStreamingResponse: + """ + The Metric resource represents a calculation of a quantity based on events. + Metrics are defined by the query that transforms raw usage events into meaningful values for your customers. + """ from .resources.metrics import MetricsWithStreamingResponse return MetricsWithStreamingResponse(self._client.metrics) @cached_property def plans(self) -> plans.PlansWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.plans import PlansWithStreamingResponse return PlansWithStreamingResponse(self._client.plans) @cached_property def prices(self) -> prices.PricesWithStreamingResponse: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ from .resources.prices import PricesWithStreamingResponse return PricesWithStreamingResponse(self._client.prices) @@ -1107,6 +1518,12 @@ def subscriptions(self) -> subscriptions.SubscriptionsWithStreamingResponse: @cached_property def alerts(self) -> alerts.AlertsWithStreamingResponse: + """ + [Alerts within Orb](/product-catalog/configuring-alerts) monitor spending, + usage, or credit balance and trigger webhooks when a threshold is exceeded. + + Alerts created through the API can be scoped to either customers or subscriptions. + """ from .resources.alerts import AlertsWithStreamingResponse return AlertsWithStreamingResponse(self._client.alerts) @@ -1125,12 +1542,19 @@ def subscription_changes(self) -> subscription_changes.SubscriptionChangesWithSt @cached_property def credit_blocks(self) -> credit_blocks.CreditBlocksWithStreamingResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ from .resources.credit_blocks import CreditBlocksWithStreamingResponse return CreditBlocksWithStreamingResponse(self._client.credit_blocks) @cached_property def license_types(self) -> license_types.LicenseTypesWithStreamingResponse: + """ + The LicenseType resource represents a type of license that can be assigned to users. + License types are used during billing by grouping metrics on the configured grouping key. + """ from .resources.license_types import LicenseTypesWithStreamingResponse return LicenseTypesWithStreamingResponse(self._client.license_types) @@ -1156,66 +1580,134 @@ def top_level(self) -> top_level.AsyncTopLevelWithStreamingResponse: @cached_property def beta(self) -> beta.AsyncBetaWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.beta import AsyncBetaWithStreamingResponse return AsyncBetaWithStreamingResponse(self._client.beta) @cached_property def coupons(self) -> coupons.AsyncCouponsWithStreamingResponse: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ from .resources.coupons import AsyncCouponsWithStreamingResponse return AsyncCouponsWithStreamingResponse(self._client.coupons) @cached_property def credit_notes(self) -> credit_notes.AsyncCreditNotesWithStreamingResponse: + """ + The [Credit Note](/invoicing/credit-notes) resource represents a credit that has been applied to a + particular invoice. + """ from .resources.credit_notes import AsyncCreditNotesWithStreamingResponse return AsyncCreditNotesWithStreamingResponse(self._client.credit_notes) @cached_property def customers(self) -> customers.AsyncCustomersWithStreamingResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ from .resources.customers import AsyncCustomersWithStreamingResponse return AsyncCustomersWithStreamingResponse(self._client.customers) @cached_property def events(self) -> events.AsyncEventsWithStreamingResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ from .resources.events import AsyncEventsWithStreamingResponse return AsyncEventsWithStreamingResponse(self._client.events) @cached_property def invoice_line_items(self) -> invoice_line_items.AsyncInvoiceLineItemsWithStreamingResponse: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoice_line_items import AsyncInvoiceLineItemsWithStreamingResponse return AsyncInvoiceLineItemsWithStreamingResponse(self._client.invoice_line_items) @cached_property def invoices(self) -> invoices.AsyncInvoicesWithStreamingResponse: + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ from .resources.invoices import AsyncInvoicesWithStreamingResponse return AsyncInvoicesWithStreamingResponse(self._client.invoices) @cached_property def items(self) -> items.AsyncItemsWithStreamingResponse: + """The Item resource represents a sellable product or good. + + Items are associated with all line items, billable metrics, + and prices and are used for defining external sync behavior for invoices and tax calculation purposes. + """ from .resources.items import AsyncItemsWithStreamingResponse return AsyncItemsWithStreamingResponse(self._client.items) @cached_property def metrics(self) -> metrics.AsyncMetricsWithStreamingResponse: + """ + The Metric resource represents a calculation of a quantity based on events. + Metrics are defined by the query that transforms raw usage events into meaningful values for your customers. + """ from .resources.metrics import AsyncMetricsWithStreamingResponse return AsyncMetricsWithStreamingResponse(self._client.metrics) @cached_property def plans(self) -> plans.AsyncPlansWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ from .resources.plans import AsyncPlansWithStreamingResponse return AsyncPlansWithStreamingResponse(self._client.plans) @cached_property def prices(self) -> prices.AsyncPricesWithStreamingResponse: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ from .resources.prices import AsyncPricesWithStreamingResponse return AsyncPricesWithStreamingResponse(self._client.prices) @@ -1228,6 +1720,12 @@ def subscriptions(self) -> subscriptions.AsyncSubscriptionsWithStreamingResponse @cached_property def alerts(self) -> alerts.AsyncAlertsWithStreamingResponse: + """ + [Alerts within Orb](/product-catalog/configuring-alerts) monitor spending, + usage, or credit balance and trigger webhooks when a threshold is exceeded. + + Alerts created through the API can be scoped to either customers or subscriptions. + """ from .resources.alerts import AsyncAlertsWithStreamingResponse return AsyncAlertsWithStreamingResponse(self._client.alerts) @@ -1246,12 +1744,19 @@ def subscription_changes(self) -> subscription_changes.AsyncSubscriptionChangesW @cached_property def credit_blocks(self) -> credit_blocks.AsyncCreditBlocksWithStreamingResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ from .resources.credit_blocks import AsyncCreditBlocksWithStreamingResponse return AsyncCreditBlocksWithStreamingResponse(self._client.credit_blocks) @cached_property def license_types(self) -> license_types.AsyncLicenseTypesWithStreamingResponse: + """ + The LicenseType resource represents a type of license that can be assigned to users. + License types are used during billing by grouping metrics on the configured grouping key. + """ from .resources.license_types import AsyncLicenseTypesWithStreamingResponse return AsyncLicenseTypesWithStreamingResponse(self._client.license_types) diff --git a/src/orb/_compat.py b/src/orb/_compat.py index 786ff42a..e6690a4f 100644 --- a/src/orb/_compat.py +++ b/src/orb/_compat.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING, Any, Union, Generic, TypeVar, Callable, cast, overload from datetime import date, datetime -from typing_extensions import Self, Literal +from typing_extensions import Self, Literal, TypedDict import pydantic from pydantic.fields import FieldInfo @@ -131,6 +131,10 @@ def model_json(model: pydantic.BaseModel, *, indent: int | None = None) -> str: return model.model_dump_json(indent=indent) +class _ModelDumpKwargs(TypedDict, total=False): + by_alias: bool + + def model_dump( model: pydantic.BaseModel, *, @@ -142,6 +146,9 @@ def model_dump( by_alias: bool | None = None, ) -> dict[str, Any]: if (not PYDANTIC_V1) or hasattr(model, "model_dump"): + kwargs: _ModelDumpKwargs = {} + if by_alias is not None: + kwargs["by_alias"] = by_alias return model.model_dump( mode=mode, exclude=exclude, @@ -149,7 +156,7 @@ def model_dump( exclude_defaults=exclude_defaults, # warnings are not supported in Pydantic v1 warnings=True if PYDANTIC_V1 else warnings, - by_alias=by_alias, + **kwargs, ) return cast( "dict[str, Any]", diff --git a/src/orb/_files.py b/src/orb/_files.py index cc14c14f..76da9e08 100644 --- a/src/orb/_files.py +++ b/src/orb/_files.py @@ -3,8 +3,8 @@ import io import os import pathlib -from typing import overload -from typing_extensions import TypeGuard +from typing import Sequence, cast, overload +from typing_extensions import TypeVar, TypeGuard import anyio @@ -17,7 +17,9 @@ HttpxFileContent, HttpxRequestFiles, ) -from ._utils import is_tuple_t, is_mapping_t, is_sequence_t +from ._utils import is_list, is_mapping, is_tuple_t, is_mapping_t, is_sequence_t + +_T = TypeVar("_T") def is_base64_file_input(obj: object) -> TypeGuard[Base64FileInput]: @@ -97,7 +99,7 @@ async def async_to_httpx_files(files: RequestFiles | None) -> HttpxRequestFiles elif is_sequence_t(files): files = [(key, await _async_transform_file(file)) for key, file in files] else: - raise TypeError("Unexpected file type input {type(files)}, expected mapping or sequence") + raise TypeError(f"Unexpected file type input {type(files)}, expected mapping or sequence") return files @@ -121,3 +123,51 @@ async def async_read_file_content(file: FileContent) -> HttpxFileContent: return await anyio.Path(file).read_bytes() return file + + +def deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]]) -> _T: + """Copy only the containers along the given paths. + + Used to guard against mutation by extract_files without copying the entire structure. + Only dicts and lists that lie on a path are copied; everything else + is returned by reference. + + For example, given paths=[["foo", "files", "file"]] and the structure: + { + "foo": { + "bar": {"baz": {}}, + "files": {"file": } + } + } + The root dict, "foo", and "files" are copied (they lie on the path). + "bar" and "baz" are returned by reference (off the path). + """ + return _deepcopy_with_paths(item, paths, 0) + + +def _deepcopy_with_paths(item: _T, paths: Sequence[Sequence[str]], index: int) -> _T: + if not paths: + return item + if is_mapping(item): + key_to_paths: dict[str, list[Sequence[str]]] = {} + for path in paths: + if index < len(path): + key_to_paths.setdefault(path[index], []).append(path) + + # if no path continues through this mapping, it won't be mutated and copying it is redundant + if not key_to_paths: + return item + + result = dict(item) + for key, subpaths in key_to_paths.items(): + if key in result: + result[key] = _deepcopy_with_paths(result[key], subpaths, index + 1) + return cast(_T, result) + if is_list(item): + array_paths = [path for path in paths if index < len(path) and path[index] == ""] + + # if no path expects a list here, nothing will be mutated inside it - return by reference + if not array_paths: + return cast(_T, item) + return cast(_T, [_deepcopy_with_paths(entry, array_paths, index + 1) for entry in item]) + return item diff --git a/src/orb/_legacy_response.py b/src/orb/_legacy_response.py index 96cbe8bd..a78dda22 100644 --- a/src/orb/_legacy_response.py +++ b/src/orb/_legacy_response.py @@ -214,6 +214,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -224,6 +225,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -237,6 +239,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) diff --git a/src/orb/_qs.py b/src/orb/_qs.py index ada6fd3f..4127c19c 100644 --- a/src/orb/_qs.py +++ b/src/orb/_qs.py @@ -2,17 +2,13 @@ from typing import Any, List, Tuple, Union, Mapping, TypeVar from urllib.parse import parse_qs, urlencode -from typing_extensions import Literal, get_args +from typing_extensions import get_args -from ._types import NotGiven, not_given +from ._types import NotGiven, ArrayFormat, NestedFormat, not_given from ._utils import flatten _T = TypeVar("_T") - -ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] -NestedFormat = Literal["dots", "brackets"] - PrimitiveData = Union[str, int, float, bool, None] # this should be Data = Union[PrimitiveData, "List[Data]", "Tuple[Data]", "Mapping[str, Data]"] # https://github.com/microsoft/pyright/issues/3555 @@ -101,7 +97,10 @@ def _stringify_item( items.extend(self._stringify_item(key, item, opts)) return items elif array_format == "indices": - raise NotImplementedError("The array indices format is not supported yet") + items = [] + for i, item in enumerate(value): + items.extend(self._stringify_item(f"{key}[{i}]", item, opts)) + return items elif array_format == "brackets": items = [] key = key + "[]" diff --git a/src/orb/_response.py b/src/orb/_response.py index 2e125354..d8a81963 100644 --- a/src/orb/_response.py +++ b/src/orb/_response.py @@ -152,6 +152,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: ), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -162,6 +163,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=extract_stream_chunk_type(self._stream_cls), response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) @@ -175,6 +177,7 @@ def _parse(self, *, to: type[_T] | None = None) -> R | _T: cast_to=cast_to, response=self.http_response, client=cast(Any, self._client), + options=self._options, ), ) diff --git a/src/orb/_streaming.py b/src/orb/_streaming.py index 943d03ab..321fe6aa 100644 --- a/src/orb/_streaming.py +++ b/src/orb/_streaming.py @@ -4,7 +4,7 @@ import json import inspect from types import TracebackType -from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, AsyncIterator, cast +from typing import TYPE_CHECKING, Any, Generic, TypeVar, Iterator, Optional, AsyncIterator, cast from typing_extensions import Self, Protocol, TypeGuard, override, get_origin, runtime_checkable import httpx @@ -13,6 +13,7 @@ if TYPE_CHECKING: from ._client import Orb, AsyncOrb + from ._models import FinalRequestOptions _T = TypeVar("_T") @@ -22,7 +23,7 @@ class Stream(Generic[_T]): """Provides the core interface to iterate over a synchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEBytesDecoder def __init__( @@ -31,10 +32,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: Orb, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() @@ -85,7 +88,7 @@ class AsyncStream(Generic[_T]): """Provides the core interface to iterate over an asynchronous stream response.""" response: httpx.Response - + _options: Optional[FinalRequestOptions] = None _decoder: SSEDecoder | SSEBytesDecoder def __init__( @@ -94,10 +97,12 @@ def __init__( cast_to: type[_T], response: httpx.Response, client: AsyncOrb, + options: Optional[FinalRequestOptions] = None, ) -> None: self.response = response self._cast_to = cast_to self._client = client + self._options = options self._decoder = client._make_sse_decoder() self._iterator = self.__stream__() diff --git a/src/orb/_types.py b/src/orb/_types.py index 76df2189..514f1d7a 100644 --- a/src/orb/_types.py +++ b/src/orb/_types.py @@ -48,6 +48,9 @@ ModelT = TypeVar("ModelT", bound=pydantic.BaseModel) _T = TypeVar("_T") +ArrayFormat = Literal["comma", "repeat", "indices", "brackets"] +NestedFormat = Literal["dots", "brackets"] + # Approximates httpx internal ProxiesTypes and RequestFiles types # while adding support for `PathLike` instances diff --git a/src/orb/_utils/__init__.py b/src/orb/_utils/__init__.py index dc64e29a..1c090e51 100644 --- a/src/orb/_utils/__init__.py +++ b/src/orb/_utils/__init__.py @@ -1,3 +1,4 @@ +from ._path import path_template as path_template from ._sync import asyncify as asyncify from ._proxy import LazyProxy as LazyProxy from ._utils import ( @@ -23,7 +24,6 @@ coerce_integer as coerce_integer, file_from_path as file_from_path, strip_not_given as strip_not_given, - deepcopy_minimal as deepcopy_minimal, get_async_library as get_async_library, maybe_coerce_float as maybe_coerce_float, get_required_header as get_required_header, diff --git a/src/orb/_utils/_path.py b/src/orb/_utils/_path.py new file mode 100644 index 00000000..4d6e1e4c --- /dev/null +++ b/src/orb/_utils/_path.py @@ -0,0 +1,127 @@ +from __future__ import annotations + +import re +from typing import ( + Any, + Mapping, + Callable, +) +from urllib.parse import quote + +# Matches '.' or '..' where each dot is either literal or percent-encoded (%2e / %2E). +_DOT_SEGMENT_RE = re.compile(r"^(?:\.|%2[eE]){1,2}$") + +_PLACEHOLDER_RE = re.compile(r"\{(\w+)\}") + + +def _quote_path_segment_part(value: str) -> str: + """Percent-encode `value` for use in a URI path segment. + + Considers characters not in `pchar` set from RFC 3986 §3.3 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 + """ + # quote() already treats unreserved characters (letters, digits, and -._~) + # as safe, so we only need to add sub-delims, ':', and '@'. + # Notably, unlike the default `safe` for quote(), / is unsafe and must be quoted. + return quote(value, safe="!$&'()*+,;=:@") + + +def _quote_query_part(value: str) -> str: + """Percent-encode `value` for use in a URI query string. + + Considers &, = and characters not in `query` set from RFC 3986 §3.4 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.4 + """ + return quote(value, safe="!$'()*+,;:@/?") + + +def _quote_fragment_part(value: str) -> str: + """Percent-encode `value` for use in a URI fragment. + + Considers characters not in `fragment` set from RFC 3986 §3.5 to be unsafe. + https://datatracker.ietf.org/doc/html/rfc3986#section-3.5 + """ + return quote(value, safe="!$&'()*+,;=:@/?") + + +def _interpolate( + template: str, + values: Mapping[str, Any], + quoter: Callable[[str], str], +) -> str: + """Replace {name} placeholders in `template`, quoting each value with `quoter`. + + Placeholder names are looked up in `values`. + + Raises: + KeyError: If a placeholder is not found in `values`. + """ + # re.split with a capturing group returns alternating + # [text, name, text, name, ..., text] elements. + parts = _PLACEHOLDER_RE.split(template) + + for i in range(1, len(parts), 2): + name = parts[i] + if name not in values: + raise KeyError(f"a value for placeholder {{{name}}} was not provided") + val = values[name] + if val is None: + parts[i] = "null" + elif isinstance(val, bool): + parts[i] = "true" if val else "false" + else: + parts[i] = quoter(str(values[name])) + + return "".join(parts) + + +def path_template(template: str, /, **kwargs: Any) -> str: + """Interpolate {name} placeholders in `template` from keyword arguments. + + Args: + template: The template string containing {name} placeholders. + **kwargs: Keyword arguments to interpolate into the template. + + Returns: + The template with placeholders interpolated and percent-encoded. + + Safe characters for percent-encoding are dependent on the URI component. + Placeholders in path and fragment portions are percent-encoded where the `segment` + and `fragment` sets from RFC 3986 respectively are considered safe. + Placeholders in the query portion are percent-encoded where the `query` set from + RFC 3986 §3.3 is considered safe except for = and & characters. + + Raises: + KeyError: If a placeholder is not found in `kwargs`. + ValueError: If resulting path contains /./ or /../ segments (including percent-encoded dot-segments). + """ + # Split the template into path, query, and fragment portions. + fragment_template: str | None = None + query_template: str | None = None + + rest = template + if "#" in rest: + rest, fragment_template = rest.split("#", 1) + if "?" in rest: + rest, query_template = rest.split("?", 1) + path_template = rest + + # Interpolate each portion with the appropriate quoting rules. + path_result = _interpolate(path_template, kwargs, _quote_path_segment_part) + + # Reject dot-segments (. and ..) in the final assembled path. The check + # runs after interpolation so that adjacent placeholders or a mix of static + # text and placeholders that together form a dot-segment are caught. + # Also reject percent-encoded dot-segments to protect against incorrectly + # implemented normalization in servers/proxies. + for segment in path_result.split("/"): + if _DOT_SEGMENT_RE.match(segment): + raise ValueError(f"Constructed path {path_result!r} contains dot-segment {segment!r} which is not allowed") + + result = path_result + if query_template is not None: + result += "?" + _interpolate(query_template, kwargs, _quote_query_part) + if fragment_template is not None: + result += "#" + _interpolate(fragment_template, kwargs, _quote_fragment_part) + + return result diff --git a/src/orb/_utils/_utils.py b/src/orb/_utils/_utils.py index eec7f4a1..199cd231 100644 --- a/src/orb/_utils/_utils.py +++ b/src/orb/_utils/_utils.py @@ -17,11 +17,11 @@ ) from pathlib import Path from datetime import date, datetime -from typing_extensions import TypeGuard +from typing_extensions import TypeGuard, get_args import sniffio -from .._types import Omit, NotGiven, FileTypes, HeadersLike +from .._types import Omit, NotGiven, FileTypes, ArrayFormat, HeadersLike _T = TypeVar("_T") _TupleT = TypeVar("_TupleT", bound=Tuple[object, ...]) @@ -40,25 +40,45 @@ def extract_files( query: Mapping[str, object], *, paths: Sequence[Sequence[str]], + array_format: ArrayFormat = "brackets", ) -> list[tuple[str, FileTypes]]: """Recursively extract files from the given dictionary based on specified paths. A path may look like this ['foo', 'files', '', 'data']. + ``array_format`` controls how ```` segments contribute to the emitted + field name. Supported values: ``"brackets"`` (``foo[]``), ``"repeat"`` and + ``"comma"`` (``foo``), ``"indices"`` (``foo[0]``, ``foo[1]``). + Note: this mutates the given dictionary. """ files: list[tuple[str, FileTypes]] = [] for path in paths: - files.extend(_extract_items(query, path, index=0, flattened_key=None)) + files.extend(_extract_items(query, path, index=0, flattened_key=None, array_format=array_format)) return files +def _array_suffix(array_format: ArrayFormat, array_index: int) -> str: + if array_format == "brackets": + return "[]" + if array_format == "indices": + return f"[{array_index}]" + if array_format == "repeat" or array_format == "comma": + # Both repeat the bare field name for each file part; there is no + # meaningful way to comma-join binary parts. + return "" + raise NotImplementedError( + f"Unknown array_format value: {array_format}, choose from {', '.join(get_args(ArrayFormat))}" + ) + + def _extract_items( obj: object, path: Sequence[str], *, index: int, flattened_key: str | None, + array_format: ArrayFormat, ) -> list[tuple[str, FileTypes]]: try: key = path[index] @@ -75,9 +95,11 @@ def _extract_items( if is_list(obj): files: list[tuple[str, FileTypes]] = [] - for entry in obj: - assert_is_file_content(entry, key=flattened_key + "[]" if flattened_key else "") - files.append((flattened_key + "[]", cast(FileTypes, entry))) + for array_index, entry in enumerate(obj): + suffix = _array_suffix(array_format, array_index) + emitted_key = (flattened_key + suffix) if flattened_key else suffix + assert_is_file_content(entry, key=emitted_key) + files.append((emitted_key, cast(FileTypes, entry))) return files assert_is_file_content(obj, key=flattened_key) @@ -86,8 +108,9 @@ def _extract_items( index += 1 if is_dict(obj): try: - # We are at the last entry in the path so we must remove the field - if (len(path)) == index: + # Remove the field if there are no more dict keys in the path, + # only "" traversal markers or end. + if all(p == "" for p in path[index:]): item = obj.pop(key) else: item = obj[key] @@ -105,6 +128,7 @@ def _extract_items( path, index=index, flattened_key=flattened_key, + array_format=array_format, ) elif is_list(obj): if key != "": @@ -116,9 +140,12 @@ def _extract_items( item, path, index=index, - flattened_key=flattened_key + "[]" if flattened_key is not None else "[]", + flattened_key=( + (flattened_key if flattened_key is not None else "") + _array_suffix(array_format, array_index) + ), + array_format=array_format, ) - for item in obj + for array_index, item in enumerate(obj) ] ) @@ -176,21 +203,6 @@ def is_iterable(obj: object) -> TypeGuard[Iterable[object]]: return isinstance(obj, Iterable) -def deepcopy_minimal(item: _T) -> _T: - """Minimal reimplementation of copy.deepcopy() that will only copy certain object types: - - - mappings, e.g. `dict` - - list - - This is done for performance reasons. - """ - if is_mapping(item): - return cast(_T, {k: deepcopy_minimal(v) for k, v in item.items()}) - if is_list(item): - return cast(_T, [deepcopy_minimal(entry) for entry in item]) - return item - - # copied from https://github.com/Rapptz/RoboDanny def human_join(seq: Sequence[str], *, delim: str = ", ", final: str = "or") -> str: size = len(seq) diff --git a/src/orb/_version.py b/src/orb/_version.py index 11898a79..c5a82e2e 100644 --- a/src/orb/_version.py +++ b/src/orb/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "orb" -__version__ = "4.55.0" # x-release-please-version +__version__ = "4.56.0" # x-release-please-version diff --git a/src/orb/resources/alerts.py b/src/orb/resources/alerts.py index 84f485f9..cb54e826 100644 --- a/src/orb/resources/alerts.py +++ b/src/orb/resources/alerts.py @@ -18,8 +18,8 @@ alert_create_for_subscription_params, alert_create_for_external_customer_params, ) -from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -32,6 +32,13 @@ class Alerts(SyncAPIResource): + """ + [Alerts within Orb](/product-catalog/configuring-alerts) monitor spending, + usage, or credit balance and trigger webhooks when a threshold is exceeded. + + Alerts created through the API can be scoped to either customers or subscriptions. + """ + @cached_property def with_raw_response(self) -> AlertsWithRawResponse: """ @@ -77,7 +84,7 @@ def retrieve( if not alert_id: raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") return self._get( - f"/alerts/{alert_id}", + path_template("/alerts/{alert_id}", alert_id=alert_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -118,7 +125,7 @@ def update( f"Expected a non-empty value for `alert_configuration_id` but received {alert_configuration_id!r}" ) return self._put( - f"/alerts/{alert_configuration_id}", + path_template("/alerts/{alert_configuration_id}", alert_configuration_id=alert_configuration_id), body=maybe_transform({"thresholds": thresholds}, alert_update_params.AlertUpdateParams), options=make_request_options( extra_headers=extra_headers, @@ -254,7 +261,7 @@ def create_for_customer( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._post( - f"/alerts/customer_id/{customer_id}", + path_template("/alerts/customer_id/{customer_id}", customer_id=customer_id), body=maybe_transform( { "currency": currency, @@ -321,7 +328,9 @@ def create_for_external_customer( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._post( - f"/alerts/external_customer_id/{external_customer_id}", + path_template( + "/alerts/external_customer_id/{external_customer_id}", external_customer_id=external_customer_id + ), body=maybe_transform( { "currency": currency, @@ -346,7 +355,11 @@ def create_for_subscription( *, thresholds: Iterable[ThresholdParam], type: Literal["usage_exceeded", "cost_exceeded"], + grouping_keys: Optional[SequenceNotStr[str]] | Omit = omit, metric_id: Optional[str] | Omit = omit, + price_filters: Optional[Iterable[alert_create_for_subscription_params.PriceFilter]] | Omit = omit, + pricing_unit_id: Optional[str] | Omit = omit, + threshold_overrides: Optional[Iterable[alert_create_for_subscription_params.ThresholdOverride]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -373,8 +386,24 @@ def create_for_subscription( type: The type of alert to create. This must be a valid alert type. + grouping_keys: The property keys to group cost alerts by. Only applicable for cost_exceeded + alerts. + metric_id: The metric to track usage for. + price_filters: Filters to scope which prices are included in grouped cost alert evaluation. + Supports filtering by price_id, item_id, or price_type with includes/excludes + operators. Only applicable when grouping_keys is set. + + pricing_unit_id: The pricing unit to use for grouped cost alerts. Required when grouping_keys is + set. + + threshold_overrides: Per-group threshold overrides. Each override maps a specific combination of + grouping_keys values to a list of thresholds that fully replaces the default + thresholds for that group. An empty thresholds list silences the group. Groups + without an override use the default thresholds. Only applicable when + grouping_keys is set. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -388,12 +417,16 @@ def create_for_subscription( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/alerts/subscription_id/{subscription_id}", + path_template("/alerts/subscription_id/{subscription_id}", subscription_id=subscription_id), body=maybe_transform( { "thresholds": thresholds, "type": type, + "grouping_keys": grouping_keys, "metric_id": metric_id, + "price_filters": price_filters, + "pricing_unit_id": pricing_unit_id, + "threshold_overrides": threshold_overrides, }, alert_create_for_subscription_params.AlertCreateForSubscriptionParams, ), @@ -444,7 +477,7 @@ def disable( f"Expected a non-empty value for `alert_configuration_id` but received {alert_configuration_id!r}" ) return self._post( - f"/alerts/{alert_configuration_id}/disable", + path_template("/alerts/{alert_configuration_id}/disable", alert_configuration_id=alert_configuration_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -493,7 +526,7 @@ def enable( f"Expected a non-empty value for `alert_configuration_id` but received {alert_configuration_id!r}" ) return self._post( - f"/alerts/{alert_configuration_id}/enable", + path_template("/alerts/{alert_configuration_id}/enable", alert_configuration_id=alert_configuration_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -507,6 +540,13 @@ def enable( class AsyncAlerts(AsyncAPIResource): + """ + [Alerts within Orb](/product-catalog/configuring-alerts) monitor spending, + usage, or credit balance and trigger webhooks when a threshold is exceeded. + + Alerts created through the API can be scoped to either customers or subscriptions. + """ + @cached_property def with_raw_response(self) -> AsyncAlertsWithRawResponse: """ @@ -552,7 +592,7 @@ async def retrieve( if not alert_id: raise ValueError(f"Expected a non-empty value for `alert_id` but received {alert_id!r}") return await self._get( - f"/alerts/{alert_id}", + path_template("/alerts/{alert_id}", alert_id=alert_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -593,7 +633,7 @@ async def update( f"Expected a non-empty value for `alert_configuration_id` but received {alert_configuration_id!r}" ) return await self._put( - f"/alerts/{alert_configuration_id}", + path_template("/alerts/{alert_configuration_id}", alert_configuration_id=alert_configuration_id), body=await async_maybe_transform({"thresholds": thresholds}, alert_update_params.AlertUpdateParams), options=make_request_options( extra_headers=extra_headers, @@ -729,7 +769,7 @@ async def create_for_customer( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return await self._post( - f"/alerts/customer_id/{customer_id}", + path_template("/alerts/customer_id/{customer_id}", customer_id=customer_id), body=await async_maybe_transform( { "currency": currency, @@ -796,7 +836,9 @@ async def create_for_external_customer( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return await self._post( - f"/alerts/external_customer_id/{external_customer_id}", + path_template( + "/alerts/external_customer_id/{external_customer_id}", external_customer_id=external_customer_id + ), body=await async_maybe_transform( { "currency": currency, @@ -821,7 +863,11 @@ async def create_for_subscription( *, thresholds: Iterable[ThresholdParam], type: Literal["usage_exceeded", "cost_exceeded"], + grouping_keys: Optional[SequenceNotStr[str]] | Omit = omit, metric_id: Optional[str] | Omit = omit, + price_filters: Optional[Iterable[alert_create_for_subscription_params.PriceFilter]] | Omit = omit, + pricing_unit_id: Optional[str] | Omit = omit, + threshold_overrides: Optional[Iterable[alert_create_for_subscription_params.ThresholdOverride]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -848,8 +894,24 @@ async def create_for_subscription( type: The type of alert to create. This must be a valid alert type. + grouping_keys: The property keys to group cost alerts by. Only applicable for cost_exceeded + alerts. + metric_id: The metric to track usage for. + price_filters: Filters to scope which prices are included in grouped cost alert evaluation. + Supports filtering by price_id, item_id, or price_type with includes/excludes + operators. Only applicable when grouping_keys is set. + + pricing_unit_id: The pricing unit to use for grouped cost alerts. Required when grouping_keys is + set. + + threshold_overrides: Per-group threshold overrides. Each override maps a specific combination of + grouping_keys values to a list of thresholds that fully replaces the default + thresholds for that group. An empty thresholds list silences the group. Groups + without an override use the default thresholds. Only applicable when + grouping_keys is set. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -863,12 +925,16 @@ async def create_for_subscription( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/alerts/subscription_id/{subscription_id}", + path_template("/alerts/subscription_id/{subscription_id}", subscription_id=subscription_id), body=await async_maybe_transform( { "thresholds": thresholds, "type": type, + "grouping_keys": grouping_keys, "metric_id": metric_id, + "price_filters": price_filters, + "pricing_unit_id": pricing_unit_id, + "threshold_overrides": threshold_overrides, }, alert_create_for_subscription_params.AlertCreateForSubscriptionParams, ), @@ -919,7 +985,7 @@ async def disable( f"Expected a non-empty value for `alert_configuration_id` but received {alert_configuration_id!r}" ) return await self._post( - f"/alerts/{alert_configuration_id}/disable", + path_template("/alerts/{alert_configuration_id}/disable", alert_configuration_id=alert_configuration_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -970,7 +1036,7 @@ async def enable( f"Expected a non-empty value for `alert_configuration_id` but received {alert_configuration_id!r}" ) return await self._post( - f"/alerts/{alert_configuration_id}/enable", + path_template("/alerts/{alert_configuration_id}/enable", alert_configuration_id=alert_configuration_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/orb/resources/beta/beta.py b/src/orb/resources/beta/beta.py index eb85e15f..4a49637c 100644 --- a/src/orb/resources/beta/beta.py +++ b/src/orb/resources/beta/beta.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ...types import beta_create_plan_version_params, beta_set_default_plan_version_params from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -29,8 +29,19 @@ class Beta(SyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def external_plan_id(self) -> ExternalPlanID: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return ExternalPlanID(self._client) @cached_property @@ -105,7 +116,7 @@ def create_plan_version( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return self._post( - f"/plans/{plan_id}/versions", + path_template("/plans/{plan_id}/versions", plan_id=plan_id), body=maybe_transform( { "version": version, @@ -160,7 +171,7 @@ def fetch_plan_version( if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") return self._get( - f"/plans/{plan_id}/versions/{version}", + path_template("/plans/{plan_id}/versions/{version}", plan_id=plan_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -199,7 +210,7 @@ def set_default_plan_version( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return self._post( - f"/plans/{plan_id}/set_default_version", + path_template("/plans/{plan_id}/set_default_version", plan_id=plan_id), body=maybe_transform( {"version": version}, beta_set_default_plan_version_params.BetaSetDefaultPlanVersionParams ), @@ -215,8 +226,19 @@ def set_default_plan_version( class AsyncBeta(AsyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def external_plan_id(self) -> AsyncExternalPlanID: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return AsyncExternalPlanID(self._client) @cached_property @@ -291,7 +313,7 @@ async def create_plan_version( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return await self._post( - f"/plans/{plan_id}/versions", + path_template("/plans/{plan_id}/versions", plan_id=plan_id), body=await async_maybe_transform( { "version": version, @@ -346,7 +368,7 @@ async def fetch_plan_version( if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") return await self._get( - f"/plans/{plan_id}/versions/{version}", + path_template("/plans/{plan_id}/versions/{version}", plan_id=plan_id, version=version), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -385,7 +407,7 @@ async def set_default_plan_version( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return await self._post( - f"/plans/{plan_id}/set_default_version", + path_template("/plans/{plan_id}/set_default_version", plan_id=plan_id), body=await async_maybe_transform( {"version": version}, beta_set_default_plan_version_params.BetaSetDefaultPlanVersionParams ), @@ -416,6 +438,11 @@ def __init__(self, beta: Beta) -> None: @cached_property def external_plan_id(self) -> ExternalPlanIDWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return ExternalPlanIDWithRawResponse(self._beta.external_plan_id) @@ -435,6 +462,11 @@ def __init__(self, beta: AsyncBeta) -> None: @cached_property def external_plan_id(self) -> AsyncExternalPlanIDWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return AsyncExternalPlanIDWithRawResponse(self._beta.external_plan_id) @@ -454,6 +486,11 @@ def __init__(self, beta: Beta) -> None: @cached_property def external_plan_id(self) -> ExternalPlanIDWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return ExternalPlanIDWithStreamingResponse(self._beta.external_plan_id) @@ -473,4 +510,9 @@ def __init__(self, beta: AsyncBeta) -> None: @cached_property def external_plan_id(self) -> AsyncExternalPlanIDWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return AsyncExternalPlanIDWithStreamingResponse(self._beta.external_plan_id) diff --git a/src/orb/resources/beta/external_plan_id.py b/src/orb/resources/beta/external_plan_id.py index d76ecb35..48aa0a7d 100644 --- a/src/orb/resources/beta/external_plan_id.py +++ b/src/orb/resources/beta/external_plan_id.py @@ -8,7 +8,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -21,6 +21,12 @@ class ExternalPlanID(SyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def with_raw_response(self) -> ExternalPlanIDWithRawResponse: """ @@ -95,7 +101,7 @@ def create_plan_version( if not external_plan_id: raise ValueError(f"Expected a non-empty value for `external_plan_id` but received {external_plan_id!r}") return self._post( - f"/plans/external_plan_id/{external_plan_id}/versions", + path_template("/plans/external_plan_id/{external_plan_id}/versions", external_plan_id=external_plan_id), body=maybe_transform( { "version": version, @@ -150,7 +156,11 @@ def fetch_plan_version( if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") return self._get( - f"/plans/external_plan_id/{external_plan_id}/versions/{version}", + path_template( + "/plans/external_plan_id/{external_plan_id}/versions/{version}", + external_plan_id=external_plan_id, + version=version, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -189,7 +199,9 @@ def set_default_plan_version( if not external_plan_id: raise ValueError(f"Expected a non-empty value for `external_plan_id` but received {external_plan_id!r}") return self._post( - f"/plans/external_plan_id/{external_plan_id}/set_default_version", + path_template( + "/plans/external_plan_id/{external_plan_id}/set_default_version", external_plan_id=external_plan_id + ), body=maybe_transform( {"version": version}, external_plan_id_set_default_plan_version_params.ExternalPlanIDSetDefaultPlanVersionParams, @@ -206,6 +218,12 @@ def set_default_plan_version( class AsyncExternalPlanID(AsyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def with_raw_response(self) -> AsyncExternalPlanIDWithRawResponse: """ @@ -280,7 +298,7 @@ async def create_plan_version( if not external_plan_id: raise ValueError(f"Expected a non-empty value for `external_plan_id` but received {external_plan_id!r}") return await self._post( - f"/plans/external_plan_id/{external_plan_id}/versions", + path_template("/plans/external_plan_id/{external_plan_id}/versions", external_plan_id=external_plan_id), body=await async_maybe_transform( { "version": version, @@ -335,7 +353,11 @@ async def fetch_plan_version( if not version: raise ValueError(f"Expected a non-empty value for `version` but received {version!r}") return await self._get( - f"/plans/external_plan_id/{external_plan_id}/versions/{version}", + path_template( + "/plans/external_plan_id/{external_plan_id}/versions/{version}", + external_plan_id=external_plan_id, + version=version, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -374,7 +396,9 @@ async def set_default_plan_version( if not external_plan_id: raise ValueError(f"Expected a non-empty value for `external_plan_id` but received {external_plan_id!r}") return await self._post( - f"/plans/external_plan_id/{external_plan_id}/set_default_version", + path_template( + "/plans/external_plan_id/{external_plan_id}/set_default_version", external_plan_id=external_plan_id + ), body=await async_maybe_transform( {"version": version}, external_plan_id_set_default_plan_version_params.ExternalPlanIDSetDefaultPlanVersionParams, diff --git a/src/orb/resources/coupons/coupons.py b/src/orb/resources/coupons/coupons.py index b89662c6..7b3a9dad 100644 --- a/src/orb/resources/coupons/coupons.py +++ b/src/orb/resources/coupons/coupons.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ...types import coupon_list_params, coupon_create_params from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -29,8 +29,15 @@ class Coupons(SyncAPIResource): + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ + @cached_property def subscriptions(self) -> Subscriptions: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ return Subscriptions(self._client) @cached_property @@ -130,8 +137,7 @@ def list( The list of coupons is ordered starting from the most recently created coupon. The response also includes `pagination_metadata`, which lets the caller retrieve - the next page of results if they exist. More information about pagination can be - found in the Pagination-metadata schema. + the next page of results if they exist. Args: cursor: Cursor for pagination. This can be populated by the `next_cursor` value returned @@ -205,7 +211,7 @@ def archive( if not coupon_id: raise ValueError(f"Expected a non-empty value for `coupon_id` but received {coupon_id!r}") return self._post( - f"/coupons/{coupon_id}/archive", + path_template("/coupons/{coupon_id}/archive", coupon_id=coupon_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -245,7 +251,7 @@ def fetch( if not coupon_id: raise ValueError(f"Expected a non-empty value for `coupon_id` but received {coupon_id!r}") return self._get( - f"/coupons/{coupon_id}", + path_template("/coupons/{coupon_id}", coupon_id=coupon_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -254,8 +260,15 @@ def fetch( class AsyncCoupons(AsyncAPIResource): + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ + @cached_property def subscriptions(self) -> AsyncSubscriptions: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ return AsyncSubscriptions(self._client) @cached_property @@ -355,8 +368,7 @@ def list( The list of coupons is ordered starting from the most recently created coupon. The response also includes `pagination_metadata`, which lets the caller retrieve - the next page of results if they exist. More information about pagination can be - found in the Pagination-metadata schema. + the next page of results if they exist. Args: cursor: Cursor for pagination. This can be populated by the `next_cursor` value returned @@ -430,7 +442,7 @@ async def archive( if not coupon_id: raise ValueError(f"Expected a non-empty value for `coupon_id` but received {coupon_id!r}") return await self._post( - f"/coupons/{coupon_id}/archive", + path_template("/coupons/{coupon_id}/archive", coupon_id=coupon_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -470,7 +482,7 @@ async def fetch( if not coupon_id: raise ValueError(f"Expected a non-empty value for `coupon_id` but received {coupon_id!r}") return await self._get( - f"/coupons/{coupon_id}", + path_template("/coupons/{coupon_id}", coupon_id=coupon_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -497,6 +509,9 @@ def __init__(self, coupons: Coupons) -> None: @cached_property def subscriptions(self) -> SubscriptionsWithRawResponse: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ return SubscriptionsWithRawResponse(self._coupons.subscriptions) @@ -519,6 +534,9 @@ def __init__(self, coupons: AsyncCoupons) -> None: @cached_property def subscriptions(self) -> AsyncSubscriptionsWithRawResponse: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ return AsyncSubscriptionsWithRawResponse(self._coupons.subscriptions) @@ -541,6 +559,9 @@ def __init__(self, coupons: Coupons) -> None: @cached_property def subscriptions(self) -> SubscriptionsWithStreamingResponse: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ return SubscriptionsWithStreamingResponse(self._coupons.subscriptions) @@ -563,4 +584,7 @@ def __init__(self, coupons: AsyncCoupons) -> None: @cached_property def subscriptions(self) -> AsyncSubscriptionsWithStreamingResponse: + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ return AsyncSubscriptionsWithStreamingResponse(self._coupons.subscriptions) diff --git a/src/orb/resources/coupons/subscriptions.py b/src/orb/resources/coupons/subscriptions.py index f3770de4..b8ea4181 100644 --- a/src/orb/resources/coupons/subscriptions.py +++ b/src/orb/resources/coupons/subscriptions.py @@ -8,7 +8,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -21,6 +21,10 @@ class Subscriptions(SyncAPIResource): + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ + @cached_property def with_raw_response(self) -> SubscriptionsWithRawResponse: """ @@ -76,7 +80,7 @@ def list( if not coupon_id: raise ValueError(f"Expected a non-empty value for `coupon_id` but received {coupon_id!r}") return self._get_api_list( - f"/coupons/{coupon_id}/subscriptions", + path_template("/coupons/{coupon_id}/subscriptions", coupon_id=coupon_id), page=SyncPage[Subscription], options=make_request_options( extra_headers=extra_headers, @@ -96,6 +100,10 @@ def list( class AsyncSubscriptions(AsyncAPIResource): + """ + A coupon represents a reusable discount configuration that can be applied either as a fixed or percentage amount to an invoice or subscription. Coupons are activated using a redemption code, which applies the discount to a subscription or invoice. The duration of a coupon determines how long it remains available for use by end users. + """ + @cached_property def with_raw_response(self) -> AsyncSubscriptionsWithRawResponse: """ @@ -151,7 +159,7 @@ def list( if not coupon_id: raise ValueError(f"Expected a non-empty value for `coupon_id` but received {coupon_id!r}") return self._get_api_list( - f"/coupons/{coupon_id}/subscriptions", + path_template("/coupons/{coupon_id}/subscriptions", coupon_id=coupon_id), page=AsyncPage[Subscription], options=make_request_options( extra_headers=extra_headers, diff --git a/src/orb/resources/credit_blocks.py b/src/orb/resources/credit_blocks.py index dbba3823..8e30f760 100644 --- a/src/orb/resources/credit_blocks.py +++ b/src/orb/resources/credit_blocks.py @@ -6,6 +6,7 @@ from .. import _legacy_response from .._types import Body, Query, Headers, NoneType, NotGiven, not_given +from .._utils import path_template from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -17,6 +18,10 @@ class CreditBlocks(SyncAPIResource): + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ + @cached_property def with_raw_response(self) -> CreditBlocksWithRawResponse: """ @@ -62,7 +67,7 @@ def retrieve( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return self._get( - f"/credit_blocks/{block_id}", + path_template("/credit_blocks/{block_id}", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -113,7 +118,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/credit_blocks/{block_id}", + path_template("/credit_blocks/{block_id}", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -138,14 +143,17 @@ def list_invoices( """ This endpoint returns the credit block and its associated purchasing invoices. - If a credit block was purchased (as opposed to being manually added or allocated - from a subscription), this endpoint returns the invoices that were created to - charge the customer for the credit block. For credit blocks with payment - schedules spanning multiple periods (e.g., monthly payments over 12 months), - multiple invoices will be returned. + If a credit block was purchased (as opposed to being manually added), this + endpoint returns the invoices that were created to charge the customer for the + credit block. For credit blocks with payment schedules spanning multiple periods + (e.g., monthly payments over 12 months), multiple invoices will be returned. + + For credit blocks created by subscription allocation prices, this endpoint + returns the subscription invoice containing the allocation line item that + created the block. - If the credit block was not purchased (e.g., manual increment, allocation), an - empty invoices list is returned. + If the credit block was not purchased (e.g., manual increment), an empty + invoices list is returned. **Note: This endpoint is currently experimental and its interface may change in future releases. Please contact support before building production integrations @@ -163,7 +171,7 @@ def list_invoices( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return self._get( - f"/credit_blocks/{block_id}/invoices", + path_template("/credit_blocks/{block_id}/invoices", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -172,6 +180,10 @@ def list_invoices( class AsyncCreditBlocks(AsyncAPIResource): + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ + @cached_property def with_raw_response(self) -> AsyncCreditBlocksWithRawResponse: """ @@ -217,7 +229,7 @@ async def retrieve( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return await self._get( - f"/credit_blocks/{block_id}", + path_template("/credit_blocks/{block_id}", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -268,7 +280,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/credit_blocks/{block_id}", + path_template("/credit_blocks/{block_id}", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -293,14 +305,17 @@ async def list_invoices( """ This endpoint returns the credit block and its associated purchasing invoices. - If a credit block was purchased (as opposed to being manually added or allocated - from a subscription), this endpoint returns the invoices that were created to - charge the customer for the credit block. For credit blocks with payment - schedules spanning multiple periods (e.g., monthly payments over 12 months), - multiple invoices will be returned. + If a credit block was purchased (as opposed to being manually added), this + endpoint returns the invoices that were created to charge the customer for the + credit block. For credit blocks with payment schedules spanning multiple periods + (e.g., monthly payments over 12 months), multiple invoices will be returned. + + For credit blocks created by subscription allocation prices, this endpoint + returns the subscription invoice containing the allocation line item that + created the block. - If the credit block was not purchased (e.g., manual increment, allocation), an - empty invoices list is returned. + If the credit block was not purchased (e.g., manual increment), an empty + invoices list is returned. **Note: This endpoint is currently experimental and its interface may change in future releases. Please contact support before building production integrations @@ -318,7 +333,7 @@ async def list_invoices( if not block_id: raise ValueError(f"Expected a non-empty value for `block_id` but received {block_id!r}") return await self._get( - f"/credit_blocks/{block_id}/invoices", + path_template("/credit_blocks/{block_id}/invoices", block_id=block_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/orb/resources/credit_notes.py b/src/orb/resources/credit_notes.py index 0a862b32..98307829 100644 --- a/src/orb/resources/credit_notes.py +++ b/src/orb/resources/credit_notes.py @@ -11,7 +11,7 @@ from .. import _legacy_response from ..types import credit_note_list_params, credit_note_create_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -23,6 +23,11 @@ class CreditNotes(SyncAPIResource): + """ + The [Credit Note](/invoicing/credit-notes) resource represents a credit that has been applied to a + particular invoice. + """ + @cached_property def with_raw_response(self) -> CreditNotesWithRawResponse: """ @@ -220,7 +225,7 @@ def fetch( if not credit_note_id: raise ValueError(f"Expected a non-empty value for `credit_note_id` but received {credit_note_id!r}") return self._get( - f"/credit_notes/{credit_note_id}", + path_template("/credit_notes/{credit_note_id}", credit_note_id=credit_note_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -229,6 +234,11 @@ def fetch( class AsyncCreditNotes(AsyncAPIResource): + """ + The [Credit Note](/invoicing/credit-notes) resource represents a credit that has been applied to a + particular invoice. + """ + @cached_property def with_raw_response(self) -> AsyncCreditNotesWithRawResponse: """ @@ -426,7 +436,7 @@ async def fetch( if not credit_note_id: raise ValueError(f"Expected a non-empty value for `credit_note_id` but received {credit_note_id!r}") return await self._get( - f"/credit_notes/{credit_note_id}", + path_template("/credit_notes/{credit_note_id}", credit_note_id=credit_note_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/orb/resources/customers/balance_transactions.py b/src/orb/resources/customers/balance_transactions.py index cf122136..a35733cb 100644 --- a/src/orb/resources/customers/balance_transactions.py +++ b/src/orb/resources/customers/balance_transactions.py @@ -10,7 +10,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -24,6 +24,23 @@ class BalanceTransactions(SyncAPIResource): + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ + @cached_property def with_raw_response(self) -> BalanceTransactionsWithRawResponse: """ @@ -78,7 +95,7 @@ def create( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._post( - f"/customers/{customer_id}/balance_transactions", + path_template("/customers/{customer_id}/balance_transactions", customer_id=customer_id), body=maybe_transform( { "amount": amount, @@ -154,7 +171,7 @@ def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get_api_list( - f"/customers/{customer_id}/balance_transactions", + path_template("/customers/{customer_id}/balance_transactions", customer_id=customer_id), page=SyncPage[BalanceTransactionListResponse], options=make_request_options( extra_headers=extra_headers, @@ -178,6 +195,23 @@ def list( class AsyncBalanceTransactions(AsyncAPIResource): + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ + @cached_property def with_raw_response(self) -> AsyncBalanceTransactionsWithRawResponse: """ @@ -232,7 +266,7 @@ async def create( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return await self._post( - f"/customers/{customer_id}/balance_transactions", + path_template("/customers/{customer_id}/balance_transactions", customer_id=customer_id), body=await async_maybe_transform( { "amount": amount, @@ -308,7 +342,7 @@ def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get_api_list( - f"/customers/{customer_id}/balance_transactions", + path_template("/customers/{customer_id}/balance_transactions", customer_id=customer_id), page=AsyncPage[BalanceTransactionListResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/orb/resources/customers/costs.py b/src/orb/resources/customers/costs.py index 6b5f040e..0ccf347b 100644 --- a/src/orb/resources/customers/costs.py +++ b/src/orb/resources/customers/costs.py @@ -10,7 +10,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -23,6 +23,23 @@ class Costs(SyncAPIResource): + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ + @cached_property def with_raw_response(self) -> CostsWithRawResponse: """ @@ -199,7 +216,7 @@ def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get( - f"/customers/{customer_id}/costs", + path_template("/customers/{customer_id}/costs", customer_id=customer_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -377,7 +394,10 @@ def list_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._get( - f"/customers/external_customer_id/{external_customer_id}/costs", + path_template( + "/customers/external_customer_id/{external_customer_id}/costs", + external_customer_id=external_customer_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -398,6 +418,23 @@ def list_by_external_id( class AsyncCosts(AsyncAPIResource): + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ + @cached_property def with_raw_response(self) -> AsyncCostsWithRawResponse: """ @@ -574,7 +611,7 @@ async def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return await self._get( - f"/customers/{customer_id}/costs", + path_template("/customers/{customer_id}/costs", customer_id=customer_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -752,7 +789,10 @@ async def list_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return await self._get( - f"/customers/external_customer_id/{external_customer_id}/costs", + path_template( + "/customers/external_customer_id/{external_customer_id}/costs", + external_customer_id=external_customer_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/orb/resources/customers/credits/credits.py b/src/orb/resources/customers/credits/credits.py index bd4f7c3a..21eee7d2 100644 --- a/src/orb/resources/customers/credits/credits.py +++ b/src/orb/resources/customers/credits/credits.py @@ -25,7 +25,7 @@ AsyncTopUpsWithStreamingResponse, ) from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import maybe_transform +from ...._utils import path_template, maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -39,12 +39,22 @@ class Credits(SyncAPIResource): + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ + @cached_property def ledger(self) -> Ledger: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return Ledger(self._client) @cached_property def top_ups(self) -> TopUps: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return TopUps(self._client) @cached_property @@ -121,7 +131,7 @@ def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get_api_list( - f"/customers/{customer_id}/credits", + path_template("/customers/{customer_id}/credits", customer_id=customer_id), page=SyncPage[CreditListResponse], options=make_request_options( extra_headers=extra_headers, @@ -202,7 +212,10 @@ def list_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._get_api_list( - f"/customers/external_customer_id/{external_customer_id}/credits", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits", + external_customer_id=external_customer_id, + ), page=SyncPage[CreditListByExternalIDResponse], options=make_request_options( extra_headers=extra_headers, @@ -228,12 +241,22 @@ def list_by_external_id( class AsyncCredits(AsyncAPIResource): + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ + @cached_property def ledger(self) -> AsyncLedger: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return AsyncLedger(self._client) @cached_property def top_ups(self) -> AsyncTopUps: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return AsyncTopUps(self._client) @cached_property @@ -310,7 +333,7 @@ def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get_api_list( - f"/customers/{customer_id}/credits", + path_template("/customers/{customer_id}/credits", customer_id=customer_id), page=AsyncPage[CreditListResponse], options=make_request_options( extra_headers=extra_headers, @@ -391,7 +414,10 @@ def list_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._get_api_list( - f"/customers/external_customer_id/{external_customer_id}/credits", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits", + external_customer_id=external_customer_id, + ), page=AsyncPage[CreditListByExternalIDResponse], options=make_request_options( extra_headers=extra_headers, @@ -429,10 +455,16 @@ def __init__(self, credits: Credits) -> None: @cached_property def ledger(self) -> LedgerWithRawResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return LedgerWithRawResponse(self._credits.ledger) @cached_property def top_ups(self) -> TopUpsWithRawResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return TopUpsWithRawResponse(self._credits.top_ups) @@ -449,10 +481,16 @@ def __init__(self, credits: AsyncCredits) -> None: @cached_property def ledger(self) -> AsyncLedgerWithRawResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return AsyncLedgerWithRawResponse(self._credits.ledger) @cached_property def top_ups(self) -> AsyncTopUpsWithRawResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return AsyncTopUpsWithRawResponse(self._credits.top_ups) @@ -469,10 +507,16 @@ def __init__(self, credits: Credits) -> None: @cached_property def ledger(self) -> LedgerWithStreamingResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return LedgerWithStreamingResponse(self._credits.ledger) @cached_property def top_ups(self) -> TopUpsWithStreamingResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return TopUpsWithStreamingResponse(self._credits.top_ups) @@ -489,8 +533,14 @@ def __init__(self, credits: AsyncCredits) -> None: @cached_property def ledger(self) -> AsyncLedgerWithStreamingResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return AsyncLedgerWithStreamingResponse(self._credits.ledger) @cached_property def top_ups(self) -> AsyncTopUpsWithStreamingResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return AsyncTopUpsWithStreamingResponse(self._credits.top_ups) diff --git a/src/orb/resources/customers/credits/ledger.py b/src/orb/resources/customers/credits/ledger.py index 4ae2e04d..c208d5a2 100644 --- a/src/orb/resources/customers/credits/ledger.py +++ b/src/orb/resources/customers/credits/ledger.py @@ -10,7 +10,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ...._utils import required_args, maybe_transform, async_maybe_transform +from ...._utils import path_template, required_args, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -33,6 +33,10 @@ class Ledger(SyncAPIResource): + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ + @cached_property def with_raw_response(self) -> LedgerWithRawResponse: """ @@ -184,7 +188,7 @@ def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get_api_list( - f"/customers/{customer_id}/credits/ledger", + path_template("/customers/{customer_id}/credits/ledger", customer_id=customer_id), page=SyncPage[LedgerListResponse], options=make_request_options( extra_headers=extra_headers, @@ -286,8 +290,14 @@ def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -460,8 +470,14 @@ def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -621,8 +637,14 @@ def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -684,9 +706,9 @@ def create_entry( return to the customer, up to the block's initial balance. Args: - target_expiry_date: A future date (specified in YYYY-MM-DD format) used for expiration change, - denoting when credits transferred (as part of a partial block expiration) should - expire. + target_expiry_date: A date (specified in YYYY-MM-DD format) used for expiration change, denoting + when credits transferred (as part of a partial block expiration) should expire. + This date must be on or after the effective date of the credit block. amount: The number of credits to effect. Note that this is required for increment, decrement, void, or undo operations. @@ -790,8 +812,14 @@ def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -953,8 +981,14 @@ def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -1081,7 +1115,7 @@ def create_entry( return cast( LedgerCreateEntryResponse, self._post( - f"/customers/{customer_id}/credits/ledger_entry", + path_template("/customers/{customer_id}/credits/ledger_entry", customer_id=customer_id), body=maybe_transform( { "amount": amount, @@ -1193,8 +1227,14 @@ def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -1367,8 +1407,14 @@ def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -1528,8 +1574,14 @@ def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -1591,9 +1643,9 @@ def create_entry_by_external_id( return to the customer, up to the block's initial balance. Args: - target_expiry_date: A future date (specified in YYYY-MM-DD format) used for expiration change, - denoting when credits transferred (as part of a partial block expiration) should - expire. + target_expiry_date: A date (specified in YYYY-MM-DD format) used for expiration change, denoting + when credits transferred (as part of a partial block expiration) should expire. + This date must be on or after the effective date of the credit block. amount: The number of credits to effect. Note that this is required for increment, decrement, void, or undo operations. @@ -1697,8 +1749,14 @@ def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -1860,8 +1918,14 @@ def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -1994,7 +2058,10 @@ def create_entry_by_external_id( return cast( LedgerCreateEntryByExternalIDResponse, self._post( - f"/customers/external_customer_id/{external_customer_id}/credits/ledger_entry", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/ledger_entry", + external_customer_id=external_customer_id, + ), body=maybe_transform( { "amount": amount, @@ -2160,7 +2227,10 @@ def list_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._get_api_list( - f"/customers/external_customer_id/{external_customer_id}/credits/ledger", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/ledger", + external_customer_id=external_customer_id, + ), page=SyncPage[LedgerListByExternalIDResponse], options=make_request_options( extra_headers=extra_headers, @@ -2190,6 +2260,10 @@ def list_by_external_id( class AsyncLedger(AsyncAPIResource): + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ + @cached_property def with_raw_response(self) -> AsyncLedgerWithRawResponse: """ @@ -2341,7 +2415,7 @@ def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get_api_list( - f"/customers/{customer_id}/credits/ledger", + path_template("/customers/{customer_id}/credits/ledger", customer_id=customer_id), page=AsyncPage[LedgerListResponse], options=make_request_options( extra_headers=extra_headers, @@ -2443,8 +2517,14 @@ async def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -2617,8 +2697,14 @@ async def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -2778,8 +2864,14 @@ async def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -2841,9 +2933,9 @@ async def create_entry( return to the customer, up to the block's initial balance. Args: - target_expiry_date: A future date (specified in YYYY-MM-DD format) used for expiration change, - denoting when credits transferred (as part of a partial block expiration) should - expire. + target_expiry_date: A date (specified in YYYY-MM-DD format) used for expiration change, denoting + when credits transferred (as part of a partial block expiration) should expire. + This date must be on or after the effective date of the credit block. amount: The number of credits to effect. Note that this is required for increment, decrement, void, or undo operations. @@ -2947,8 +3039,14 @@ async def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -3110,8 +3208,14 @@ async def create_entry( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -3238,7 +3342,7 @@ async def create_entry( return cast( LedgerCreateEntryResponse, await self._post( - f"/customers/{customer_id}/credits/ledger_entry", + path_template("/customers/{customer_id}/credits/ledger_entry", customer_id=customer_id), body=await async_maybe_transform( { "amount": amount, @@ -3350,8 +3454,14 @@ async def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -3524,8 +3634,14 @@ async def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -3685,8 +3801,14 @@ async def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -3748,9 +3870,9 @@ async def create_entry_by_external_id( return to the customer, up to the block's initial balance. Args: - target_expiry_date: A future date (specified in YYYY-MM-DD format) used for expiration change, - denoting when credits transferred (as part of a partial block expiration) should - expire. + target_expiry_date: A date (specified in YYYY-MM-DD format) used for expiration change, denoting + when credits transferred (as part of a partial block expiration) should expire. + This date must be on or after the effective date of the credit block. amount: The number of credits to effect. Note that this is required for increment, decrement, void, or undo operations. @@ -3854,8 +3976,14 @@ async def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -4017,8 +4145,14 @@ async def create_entry_by_external_id( also generate a one-off invoice for the customer for the credits pre-purchase. Note that you _must_ provide the `per_unit_cost_basis`, since the total charges on the invoice are calculated by multiplying the cost basis with the number of - credit units added. Additionally, Orb also enforces invoice generation when a - non-zero `per_unit_cost_basis` value is provided. + credit units added. If you invoice or handle payment of credits outside of Orb + (i.e. marketplace customers), set `mark_as_paid` in the `invoice_settings` to + `true` to prevent duplicate invoicing effects. + + - if `per_unit_cost_basis` is greater than zero, an invoice will be generated + and `invoice_settings` must be included + - if `invoice_settings` is passed, one of either `custom_due_date` or + `net_terms` is required to determine the due date ## Deducting Credits @@ -4151,7 +4285,10 @@ async def create_entry_by_external_id( return cast( LedgerCreateEntryByExternalIDResponse, await self._post( - f"/customers/external_customer_id/{external_customer_id}/credits/ledger_entry", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/ledger_entry", + external_customer_id=external_customer_id, + ), body=await async_maybe_transform( { "amount": amount, @@ -4317,7 +4454,10 @@ def list_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._get_api_list( - f"/customers/external_customer_id/{external_customer_id}/credits/ledger", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/ledger", + external_customer_id=external_customer_id, + ), page=AsyncPage[LedgerListByExternalIDResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/orb/resources/customers/credits/top_ups.py b/src/orb/resources/customers/credits/top_ups.py index 6dc08792..52d2ac9f 100644 --- a/src/orb/resources/customers/credits/top_ups.py +++ b/src/orb/resources/customers/credits/top_ups.py @@ -10,7 +10,7 @@ from .... import _legacy_response from ...._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given -from ...._utils import maybe_transform, async_maybe_transform +from ...._utils import path_template, maybe_transform, async_maybe_transform from ...._compat import cached_property from ...._resource import SyncAPIResource, AsyncAPIResource from ...._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -31,6 +31,10 @@ class TopUps(SyncAPIResource): + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ + @cached_property def with_raw_response(self) -> TopUpsWithRawResponse: """ @@ -113,7 +117,7 @@ def create( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._post( - f"/customers/{customer_id}/credits/top_ups", + path_template("/customers/{customer_id}/credits/top_ups", customer_id=customer_id), body=maybe_transform( { "amount": amount, @@ -171,7 +175,7 @@ def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get_api_list( - f"/customers/{customer_id}/credits/top_ups", + path_template("/customers/{customer_id}/credits/top_ups", customer_id=customer_id), page=SyncPage[TopUpListResponse], options=make_request_options( extra_headers=extra_headers, @@ -223,7 +227,9 @@ def delete( raise ValueError(f"Expected a non-empty value for `top_up_id` but received {top_up_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/customers/{customer_id}/credits/top_ups/{top_up_id}", + path_template( + "/customers/{customer_id}/credits/top_ups/{top_up_id}", customer_id=customer_id, top_up_id=top_up_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -299,7 +305,10 @@ def create_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._post( - f"/customers/external_customer_id/{external_customer_id}/credits/top_ups", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/top_ups", + external_customer_id=external_customer_id, + ), body=maybe_transform( { "amount": amount, @@ -359,7 +368,11 @@ def delete_by_external_id( raise ValueError(f"Expected a non-empty value for `top_up_id` but received {top_up_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/customers/external_customer_id/{external_customer_id}/credits/top_ups/{top_up_id}", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/top_ups/{top_up_id}", + external_customer_id=external_customer_id, + top_up_id=top_up_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -406,7 +419,10 @@ def list_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._get_api_list( - f"/customers/external_customer_id/{external_customer_id}/credits/top_ups", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/top_ups", + external_customer_id=external_customer_id, + ), page=SyncPage[TopUpListByExternalIDResponse], options=make_request_options( extra_headers=extra_headers, @@ -426,6 +442,10 @@ def list_by_external_id( class AsyncTopUps(AsyncAPIResource): + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ + @cached_property def with_raw_response(self) -> AsyncTopUpsWithRawResponse: """ @@ -508,7 +528,7 @@ async def create( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return await self._post( - f"/customers/{customer_id}/credits/top_ups", + path_template("/customers/{customer_id}/credits/top_ups", customer_id=customer_id), body=await async_maybe_transform( { "amount": amount, @@ -566,7 +586,7 @@ def list( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get_api_list( - f"/customers/{customer_id}/credits/top_ups", + path_template("/customers/{customer_id}/credits/top_ups", customer_id=customer_id), page=AsyncPage[TopUpListResponse], options=make_request_options( extra_headers=extra_headers, @@ -618,7 +638,9 @@ async def delete( raise ValueError(f"Expected a non-empty value for `top_up_id` but received {top_up_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/customers/{customer_id}/credits/top_ups/{top_up_id}", + path_template( + "/customers/{customer_id}/credits/top_ups/{top_up_id}", customer_id=customer_id, top_up_id=top_up_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -694,7 +716,10 @@ async def create_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return await self._post( - f"/customers/external_customer_id/{external_customer_id}/credits/top_ups", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/top_ups", + external_customer_id=external_customer_id, + ), body=await async_maybe_transform( { "amount": amount, @@ -754,7 +779,11 @@ async def delete_by_external_id( raise ValueError(f"Expected a non-empty value for `top_up_id` but received {top_up_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/customers/external_customer_id/{external_customer_id}/credits/top_ups/{top_up_id}", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/top_ups/{top_up_id}", + external_customer_id=external_customer_id, + top_up_id=top_up_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -801,7 +830,10 @@ def list_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._get_api_list( - f"/customers/external_customer_id/{external_customer_id}/credits/top_ups", + path_template( + "/customers/external_customer_id/{external_customer_id}/credits/top_ups", + external_customer_id=external_customer_id, + ), page=AsyncPage[TopUpListByExternalIDResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/orb/resources/customers/customers.py b/src/orb/resources/customers/customers.py index 20ae377a..94d424ab 100644 --- a/src/orb/resources/customers/customers.py +++ b/src/orb/resources/customers/customers.py @@ -24,7 +24,7 @@ customer_update_by_external_id_params, ) from ..._types import Body, Omit, Query, Headers, NoneType, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -57,16 +57,68 @@ class Customers(SyncAPIResource): + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ + @cached_property def costs(self) -> Costs: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return Costs(self._client) @cached_property def credits(self) -> Credits: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return Credits(self._client) @cached_property def balance_transactions(self) -> BalanceTransactions: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return BalanceTransactions(self._client) @cached_property @@ -104,7 +156,9 @@ def create( hierarchy: Optional[CustomerHierarchyConfigParam] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, payment_configuration: Optional[customer_create_params.PaymentConfiguration] | Omit = omit, - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] | Omit = omit, payment_provider_id: Optional[str] | Omit = omit, reporting_configuration: Optional[NewReportingConfigurationParam] | Omit = omit, @@ -391,7 +445,9 @@ def update( metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, name: Optional[str] | Omit = omit, payment_configuration: Optional[customer_update_params.PaymentConfiguration] | Omit = omit, - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] | Omit = omit, payment_provider_id: Optional[str] | Omit = omit, reporting_configuration: Optional[NewReportingConfigurationParam] | Omit = omit, @@ -409,9 +465,10 @@ def update( """ This endpoint can be used to update the `payment_provider`, `payment_provider_id`, `name`, `email`, `email_delivery`, `tax_id`, - `auto_collection`, `metadata`, `shipping_address`, `billing_address`, and - `additional_emails` of an existing customer. Other fields on a customer are - currently immutable. + `auto_collection`, `metadata`, `shipping_address`, `billing_address`, + `additional_emails`, and `currency` of an existing customer. `currency` can only + be set if it has not already been set on the customer. Other fields on a + customer are currently immutable. Args: additional_emails: Additional email addresses for this customer. If populated, these email @@ -427,8 +484,10 @@ def update( manual approval.If `null` is specified, the customer's auto issuance setting will be inherited from the account-level setting. - currency: An ISO 4217 currency string used for the customer's invoices and balance. If not - set at creation time, will be set at subscription creation time. + currency: An ISO 4217 currency string used for the customer's invoices and balance. This + can only be set if the customer does not already have a currency configured. If + not set at creation or update time, it will be set at subscription creation + time. email: A valid customer email, to be used for invoicing and notifications. @@ -619,7 +678,7 @@ def update( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._put( - f"/customers/{customer_id}", + path_template("/customers/{customer_id}", customer_id=customer_id), body=maybe_transform( { "accounting_sync_configuration": accounting_sync_configuration, @@ -757,7 +816,7 @@ def delete( raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/customers/{customer_id}", + path_template("/customers/{customer_id}", customer_id=customer_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -800,7 +859,7 @@ def fetch( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return self._get( - f"/customers/{customer_id}", + path_template("/customers/{customer_id}", customer_id=customer_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -839,7 +898,9 @@ def fetch_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return self._get( - f"/customers/external_customer_id/{external_customer_id}", + path_template( + "/customers/external_customer_id/{external_customer_id}", external_customer_id=external_customer_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -881,7 +942,7 @@ def sync_payment_methods_from_gateway( raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._post( - f"/customers/{customer_id}/sync_payment_methods_from_gateway", + path_template("/customers/{customer_id}/sync_payment_methods_from_gateway", customer_id=customer_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -929,7 +990,10 @@ def sync_payment_methods_from_gateway_by_external_customer_id( ) extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._post( - f"/customers/external_customer_id/{external_customer_id}/sync_payment_methods_from_gateway", + path_template( + "/customers/external_customer_id/{external_customer_id}/sync_payment_methods_from_gateway", + external_customer_id=external_customer_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -957,7 +1021,9 @@ def update_by_external_id( metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, name: Optional[str] | Omit = omit, payment_configuration: Optional[customer_update_by_external_id_params.PaymentConfiguration] | Omit = omit, - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] | Omit = omit, payment_provider_id: Optional[str] | Omit = omit, reporting_configuration: Optional[NewReportingConfigurationParam] | Omit = omit, @@ -992,8 +1058,10 @@ def update_by_external_id( manual approval.If `null` is specified, the customer's auto issuance setting will be inherited from the account-level setting. - currency: An ISO 4217 currency string used for the customer's invoices and balance. If not - set at creation time, will be set at subscription creation time. + currency: An ISO 4217 currency string used for the customer's invoices and balance. This + can only be set if the customer does not already have a currency configured. If + not set at creation or update time, it will be set at subscription creation + time. email: A valid customer email, to be used for invoicing and notifications. @@ -1184,7 +1252,7 @@ def update_by_external_id( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/customers/external_customer_id/{id}", + path_template("/customers/external_customer_id/{id}", id=id), body=maybe_transform( { "accounting_sync_configuration": accounting_sync_configuration, @@ -1221,16 +1289,68 @@ def update_by_external_id( class AsyncCustomers(AsyncAPIResource): + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ + @cached_property def costs(self) -> AsyncCosts: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return AsyncCosts(self._client) @cached_property def credits(self) -> AsyncCredits: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return AsyncCredits(self._client) @cached_property def balance_transactions(self) -> AsyncBalanceTransactions: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return AsyncBalanceTransactions(self._client) @cached_property @@ -1268,7 +1388,9 @@ async def create( hierarchy: Optional[CustomerHierarchyConfigParam] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, payment_configuration: Optional[customer_create_params.PaymentConfiguration] | Omit = omit, - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] | Omit = omit, payment_provider_id: Optional[str] | Omit = omit, reporting_configuration: Optional[NewReportingConfigurationParam] | Omit = omit, @@ -1555,7 +1677,9 @@ async def update( metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, name: Optional[str] | Omit = omit, payment_configuration: Optional[customer_update_params.PaymentConfiguration] | Omit = omit, - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] | Omit = omit, payment_provider_id: Optional[str] | Omit = omit, reporting_configuration: Optional[NewReportingConfigurationParam] | Omit = omit, @@ -1573,9 +1697,10 @@ async def update( """ This endpoint can be used to update the `payment_provider`, `payment_provider_id`, `name`, `email`, `email_delivery`, `tax_id`, - `auto_collection`, `metadata`, `shipping_address`, `billing_address`, and - `additional_emails` of an existing customer. Other fields on a customer are - currently immutable. + `auto_collection`, `metadata`, `shipping_address`, `billing_address`, + `additional_emails`, and `currency` of an existing customer. `currency` can only + be set if it has not already been set on the customer. Other fields on a + customer are currently immutable. Args: additional_emails: Additional email addresses for this customer. If populated, these email @@ -1591,8 +1716,10 @@ async def update( manual approval.If `null` is specified, the customer's auto issuance setting will be inherited from the account-level setting. - currency: An ISO 4217 currency string used for the customer's invoices and balance. If not - set at creation time, will be set at subscription creation time. + currency: An ISO 4217 currency string used for the customer's invoices and balance. This + can only be set if the customer does not already have a currency configured. If + not set at creation or update time, it will be set at subscription creation + time. email: A valid customer email, to be used for invoicing and notifications. @@ -1783,7 +1910,7 @@ async def update( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return await self._put( - f"/customers/{customer_id}", + path_template("/customers/{customer_id}", customer_id=customer_id), body=await async_maybe_transform( { "accounting_sync_configuration": accounting_sync_configuration, @@ -1921,7 +2048,7 @@ async def delete( raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/customers/{customer_id}", + path_template("/customers/{customer_id}", customer_id=customer_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1964,7 +2091,7 @@ async def fetch( if not customer_id: raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") return await self._get( - f"/customers/{customer_id}", + path_template("/customers/{customer_id}", customer_id=customer_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -2003,7 +2130,9 @@ async def fetch_by_external_id( f"Expected a non-empty value for `external_customer_id` but received {external_customer_id!r}" ) return await self._get( - f"/customers/external_customer_id/{external_customer_id}", + path_template( + "/customers/external_customer_id/{external_customer_id}", external_customer_id=external_customer_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -2045,7 +2174,7 @@ async def sync_payment_methods_from_gateway( raise ValueError(f"Expected a non-empty value for `customer_id` but received {customer_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._post( - f"/customers/{customer_id}/sync_payment_methods_from_gateway", + path_template("/customers/{customer_id}/sync_payment_methods_from_gateway", customer_id=customer_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -2093,7 +2222,10 @@ async def sync_payment_methods_from_gateway_by_external_customer_id( ) extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._post( - f"/customers/external_customer_id/{external_customer_id}/sync_payment_methods_from_gateway", + path_template( + "/customers/external_customer_id/{external_customer_id}/sync_payment_methods_from_gateway", + external_customer_id=external_customer_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -2121,7 +2253,9 @@ async def update_by_external_id( metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, name: Optional[str] | Omit = omit, payment_configuration: Optional[customer_update_by_external_id_params.PaymentConfiguration] | Omit = omit, - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] | Omit = omit, payment_provider_id: Optional[str] | Omit = omit, reporting_configuration: Optional[NewReportingConfigurationParam] | Omit = omit, @@ -2156,8 +2290,10 @@ async def update_by_external_id( manual approval.If `null` is specified, the customer's auto issuance setting will be inherited from the account-level setting. - currency: An ISO 4217 currency string used for the customer's invoices and balance. If not - set at creation time, will be set at subscription creation time. + currency: An ISO 4217 currency string used for the customer's invoices and balance. This + can only be set if the customer does not already have a currency configured. If + not set at creation or update time, it will be set at subscription creation + time. email: A valid customer email, to be used for invoicing and notifications. @@ -2348,7 +2484,7 @@ async def update_by_external_id( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/customers/external_customer_id/{id}", + path_template("/customers/external_customer_id/{id}", id=id), body=await async_maybe_transform( { "accounting_sync_configuration": accounting_sync_configuration, @@ -2418,14 +2554,49 @@ def __init__(self, customers: Customers) -> None: @cached_property def costs(self) -> CostsWithRawResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return CostsWithRawResponse(self._customers.costs) @cached_property def credits(self) -> CreditsWithRawResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return CreditsWithRawResponse(self._customers.credits) @cached_property def balance_transactions(self) -> BalanceTransactionsWithRawResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return BalanceTransactionsWithRawResponse(self._customers.balance_transactions) @@ -2463,14 +2634,49 @@ def __init__(self, customers: AsyncCustomers) -> None: @cached_property def costs(self) -> AsyncCostsWithRawResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return AsyncCostsWithRawResponse(self._customers.costs) @cached_property def credits(self) -> AsyncCreditsWithRawResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return AsyncCreditsWithRawResponse(self._customers.credits) @cached_property def balance_transactions(self) -> AsyncBalanceTransactionsWithRawResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return AsyncBalanceTransactionsWithRawResponse(self._customers.balance_transactions) @@ -2508,14 +2714,49 @@ def __init__(self, customers: Customers) -> None: @cached_property def costs(self) -> CostsWithStreamingResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return CostsWithStreamingResponse(self._customers.costs) @cached_property def credits(self) -> CreditsWithStreamingResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return CreditsWithStreamingResponse(self._customers.credits) @cached_property def balance_transactions(self) -> BalanceTransactionsWithStreamingResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return BalanceTransactionsWithStreamingResponse(self._customers.balance_transactions) @@ -2553,12 +2794,47 @@ def __init__(self, customers: AsyncCustomers) -> None: @cached_property def costs(self) -> AsyncCostsWithStreamingResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return AsyncCostsWithStreamingResponse(self._customers.costs) @cached_property def credits(self) -> AsyncCreditsWithStreamingResponse: + """ + The [Credit Ledger Entry resource](/product-catalog/prepurchase) models prepaid credits within Orb. + """ return AsyncCreditsWithStreamingResponse(self._customers.credits) @cached_property def balance_transactions(self) -> AsyncBalanceTransactionsWithStreamingResponse: + """ + A customer is a buyer of your products, and the other party to the billing relationship. + + In Orb, customers are assigned system generated identifiers automatically, but it's often desirable to have these + match existing identifiers in your system. To avoid having to denormalize Orb ID information, you can pass in an + `external_customer_id` with your own identifier. See + [Customer ID Aliases](/events-and-metrics/customer-aliases) for further information about how these + aliases work in Orb. + + In addition to having an identifier in your system, a customer may exist in a payment provider solution like + Stripe. Use the `payment_provider_id` and the `payment_provider` enum field to express this mapping. + + A customer also has a timezone (from the standard [IANA timezone database](https://www.iana.org/time-zones)), which + defaults to your account's timezone. See [Timezone localization](/essentials/timezones) for + information on what this timezone parameter influences within Orb. + """ return AsyncBalanceTransactionsWithStreamingResponse(self._customers.balance_transactions) diff --git a/src/orb/resources/dimensional_price_groups/dimensional_price_groups.py b/src/orb/resources/dimensional_price_groups/dimensional_price_groups.py index 8c1367ab..1f0691df 100644 --- a/src/orb/resources/dimensional_price_groups/dimensional_price_groups.py +++ b/src/orb/resources/dimensional_price_groups/dimensional_price_groups.py @@ -13,7 +13,7 @@ dimensional_price_group_update_params, ) from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -149,7 +149,10 @@ def retrieve( f"Expected a non-empty value for `dimensional_price_group_id` but received {dimensional_price_group_id!r}" ) return self._get( - f"/dimensional_price_groups/{dimensional_price_group_id}", + path_template( + "/dimensional_price_groups/{dimensional_price_group_id}", + dimensional_price_group_id=dimensional_price_group_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -200,7 +203,10 @@ def update( f"Expected a non-empty value for `dimensional_price_group_id` but received {dimensional_price_group_id!r}" ) return self._put( - f"/dimensional_price_groups/{dimensional_price_group_id}", + path_template( + "/dimensional_price_groups/{dimensional_price_group_id}", + dimensional_price_group_id=dimensional_price_group_id, + ), body=maybe_transform( { "external_dimensional_price_group_id": external_dimensional_price_group_id, @@ -385,7 +391,10 @@ async def retrieve( f"Expected a non-empty value for `dimensional_price_group_id` but received {dimensional_price_group_id!r}" ) return await self._get( - f"/dimensional_price_groups/{dimensional_price_group_id}", + path_template( + "/dimensional_price_groups/{dimensional_price_group_id}", + dimensional_price_group_id=dimensional_price_group_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -436,7 +445,10 @@ async def update( f"Expected a non-empty value for `dimensional_price_group_id` but received {dimensional_price_group_id!r}" ) return await self._put( - f"/dimensional_price_groups/{dimensional_price_group_id}", + path_template( + "/dimensional_price_groups/{dimensional_price_group_id}", + dimensional_price_group_id=dimensional_price_group_id, + ), body=await async_maybe_transform( { "external_dimensional_price_group_id": external_dimensional_price_group_id, diff --git a/src/orb/resources/dimensional_price_groups/external_dimensional_price_group_id.py b/src/orb/resources/dimensional_price_groups/external_dimensional_price_group_id.py index dcd0e4bb..1e3bf12d 100644 --- a/src/orb/resources/dimensional_price_groups/external_dimensional_price_group_id.py +++ b/src/orb/resources/dimensional_price_groups/external_dimensional_price_group_id.py @@ -8,7 +8,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -67,7 +67,10 @@ def retrieve( f"Expected a non-empty value for `external_dimensional_price_group_id` but received {external_dimensional_price_group_id!r}" ) return self._get( - f"/dimensional_price_groups/external_dimensional_price_group_id/{external_dimensional_price_group_id}", + path_template( + "/dimensional_price_groups/external_dimensional_price_group_id/{external_dimensional_price_group_id}", + external_dimensional_price_group_id=external_dimensional_price_group_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -118,7 +121,10 @@ def update( f"Expected a non-empty value for `path_external_dimensional_price_group_id` but received {path_external_dimensional_price_group_id!r}" ) return self._put( - f"/dimensional_price_groups/external_dimensional_price_group_id/{path_external_dimensional_price_group_id}", + path_template( + "/dimensional_price_groups/external_dimensional_price_group_id/{path_external_dimensional_price_group_id}", + path_external_dimensional_price_group_id=path_external_dimensional_price_group_id, + ), body=maybe_transform( { "body_external_dimensional_price_group_id": body_external_dimensional_price_group_id, @@ -185,7 +191,10 @@ async def retrieve( f"Expected a non-empty value for `external_dimensional_price_group_id` but received {external_dimensional_price_group_id!r}" ) return await self._get( - f"/dimensional_price_groups/external_dimensional_price_group_id/{external_dimensional_price_group_id}", + path_template( + "/dimensional_price_groups/external_dimensional_price_group_id/{external_dimensional_price_group_id}", + external_dimensional_price_group_id=external_dimensional_price_group_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -236,7 +245,10 @@ async def update( f"Expected a non-empty value for `path_external_dimensional_price_group_id` but received {path_external_dimensional_price_group_id!r}" ) return await self._put( - f"/dimensional_price_groups/external_dimensional_price_group_id/{path_external_dimensional_price_group_id}", + path_template( + "/dimensional_price_groups/external_dimensional_price_group_id/{path_external_dimensional_price_group_id}", + path_external_dimensional_price_group_id=path_external_dimensional_price_group_id, + ), body=await async_maybe_transform( { "body_external_dimensional_price_group_id": body_external_dimensional_price_group_id, diff --git a/src/orb/resources/events/backfills.py b/src/orb/resources/events/backfills.py index 7839e27c..1bfd66e7 100644 --- a/src/orb/resources/events/backfills.py +++ b/src/orb/resources/events/backfills.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -26,6 +26,12 @@ class Backfills(SyncAPIResource): + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ + @cached_property def with_raw_response(self) -> BackfillsWithRawResponse: """ @@ -69,8 +75,8 @@ def create( 3 steps: 1. Create the backfill, specifying its parameters. - 2. [Ingest](ingest) usage events, referencing the backfill (query parameter - `backfill_id`). + 2. [Ingest](/api-reference/event/ingest-events) usage events, referencing the + backfill (query parameter `backfill_id`). 3. [Close](close-backfill) the backfill, propagating the update in past usage throughout Orb. @@ -183,8 +189,7 @@ def list( The list of backfills is ordered starting from the most recently created backfill. The response also includes [`pagination_metadata`](/api-reference/pagination), which lets the caller - retrieve the next page of results if they exist. More information about - pagination can be found in the [Pagination-metadata schema](pagination). + retrieve the next page of results if they exist. Args: cursor: Cursor for pagination. This can be populated by the `next_cursor` value returned @@ -252,7 +257,7 @@ def close( if not backfill_id: raise ValueError(f"Expected a non-empty value for `backfill_id` but received {backfill_id!r}") return self._post( - f"/events/backfills/{backfill_id}/close", + path_template("/events/backfills/{backfill_id}/close", backfill_id=backfill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -289,7 +294,7 @@ def fetch( if not backfill_id: raise ValueError(f"Expected a non-empty value for `backfill_id` but received {backfill_id!r}") return self._get( - f"/events/backfills/{backfill_id}", + path_template("/events/backfills/{backfill_id}", backfill_id=backfill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -332,7 +337,7 @@ def revert( if not backfill_id: raise ValueError(f"Expected a non-empty value for `backfill_id` but received {backfill_id!r}") return self._post( - f"/events/backfills/{backfill_id}/revert", + path_template("/events/backfills/{backfill_id}/revert", backfill_id=backfill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -345,6 +350,12 @@ def revert( class AsyncBackfills(AsyncAPIResource): + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ + @cached_property def with_raw_response(self) -> AsyncBackfillsWithRawResponse: """ @@ -388,8 +399,8 @@ async def create( 3 steps: 1. Create the backfill, specifying its parameters. - 2. [Ingest](ingest) usage events, referencing the backfill (query parameter - `backfill_id`). + 2. [Ingest](/api-reference/event/ingest-events) usage events, referencing the + backfill (query parameter `backfill_id`). 3. [Close](close-backfill) the backfill, propagating the update in past usage throughout Orb. @@ -502,8 +513,7 @@ def list( The list of backfills is ordered starting from the most recently created backfill. The response also includes [`pagination_metadata`](/api-reference/pagination), which lets the caller - retrieve the next page of results if they exist. More information about - pagination can be found in the [Pagination-metadata schema](pagination). + retrieve the next page of results if they exist. Args: cursor: Cursor for pagination. This can be populated by the `next_cursor` value returned @@ -571,7 +581,7 @@ async def close( if not backfill_id: raise ValueError(f"Expected a non-empty value for `backfill_id` but received {backfill_id!r}") return await self._post( - f"/events/backfills/{backfill_id}/close", + path_template("/events/backfills/{backfill_id}/close", backfill_id=backfill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -608,7 +618,7 @@ async def fetch( if not backfill_id: raise ValueError(f"Expected a non-empty value for `backfill_id` but received {backfill_id!r}") return await self._get( - f"/events/backfills/{backfill_id}", + path_template("/events/backfills/{backfill_id}", backfill_id=backfill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -651,7 +661,7 @@ async def revert( if not backfill_id: raise ValueError(f"Expected a non-empty value for `backfill_id` but received {backfill_id!r}") return await self._post( - f"/events/backfills/{backfill_id}/revert", + path_template("/events/backfills/{backfill_id}/revert", backfill_id=backfill_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/orb/resources/events/events.py b/src/orb/resources/events/events.py index 0d372f1a..9deeff57 100644 --- a/src/orb/resources/events/events.py +++ b/src/orb/resources/events/events.py @@ -18,7 +18,7 @@ ) from ...types import event_ingest_params, event_search_params, event_update_params from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from .backfills import ( Backfills, @@ -40,12 +40,28 @@ class Events(SyncAPIResource): + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ + @cached_property def backfills(self) -> Backfills: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return Backfills(self._client) @cached_property def volume(self) -> Volume: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return Volume(self._client) @cached_property @@ -159,7 +175,7 @@ def update( if not event_id: raise ValueError(f"Expected a non-empty value for `event_id` but received {event_id!r}") return self._put( - f"/events/{event_id}", + path_template("/events/{event_id}", event_id=event_id), body=maybe_transform( { "event_name": event_name, @@ -248,7 +264,7 @@ def deprecate( if not event_id: raise ValueError(f"Expected a non-empty value for `event_id` but received {event_id!r}") return self._put( - f"/events/{event_id}/deprecate", + path_template("/events/{event_id}/deprecate", event_id=event_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -591,12 +607,28 @@ def search( class AsyncEvents(AsyncAPIResource): + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ + @cached_property def backfills(self) -> AsyncBackfills: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return AsyncBackfills(self._client) @cached_property def volume(self) -> AsyncVolume: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return AsyncVolume(self._client) @cached_property @@ -710,7 +742,7 @@ async def update( if not event_id: raise ValueError(f"Expected a non-empty value for `event_id` but received {event_id!r}") return await self._put( - f"/events/{event_id}", + path_template("/events/{event_id}", event_id=event_id), body=await async_maybe_transform( { "event_name": event_name, @@ -799,7 +831,7 @@ async def deprecate( if not event_id: raise ValueError(f"Expected a non-empty value for `event_id` but received {event_id!r}") return await self._put( - f"/events/{event_id}/deprecate", + path_template("/events/{event_id}/deprecate", event_id=event_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1160,10 +1192,20 @@ def __init__(self, events: Events) -> None: @cached_property def backfills(self) -> BackfillsWithRawResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return BackfillsWithRawResponse(self._events.backfills) @cached_property def volume(self) -> VolumeWithRawResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return VolumeWithRawResponse(self._events.volume) @@ -1186,10 +1228,20 @@ def __init__(self, events: AsyncEvents) -> None: @cached_property def backfills(self) -> AsyncBackfillsWithRawResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return AsyncBackfillsWithRawResponse(self._events.backfills) @cached_property def volume(self) -> AsyncVolumeWithRawResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return AsyncVolumeWithRawResponse(self._events.volume) @@ -1212,10 +1264,20 @@ def __init__(self, events: Events) -> None: @cached_property def backfills(self) -> BackfillsWithStreamingResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return BackfillsWithStreamingResponse(self._events.backfills) @cached_property def volume(self) -> VolumeWithStreamingResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return VolumeWithStreamingResponse(self._events.volume) @@ -1238,8 +1300,18 @@ def __init__(self, events: AsyncEvents) -> None: @cached_property def backfills(self) -> AsyncBackfillsWithStreamingResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return AsyncBackfillsWithStreamingResponse(self._events.backfills) @cached_property def volume(self) -> AsyncVolumeWithStreamingResponse: + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ return AsyncVolumeWithStreamingResponse(self._events.volume) diff --git a/src/orb/resources/events/volume.py b/src/orb/resources/events/volume.py index b2be9b8e..1342b46c 100644 --- a/src/orb/resources/events/volume.py +++ b/src/orb/resources/events/volume.py @@ -21,6 +21,12 @@ class Volume(SyncAPIResource): + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ + @cached_property def with_raw_response(self) -> VolumeWithRawResponse: """ @@ -115,6 +121,12 @@ def list( class AsyncVolume(AsyncAPIResource): + """ + The [Event](/core-concepts#event) resource represents a usage event that has been created for a + customer. Events are the core of Orb's usage-based billing model, and are used to calculate the usage charges for + a given billing period. + """ + @cached_property def with_raw_response(self) -> AsyncVolumeWithRawResponse: """ diff --git a/src/orb/resources/invoice_line_items.py b/src/orb/resources/invoice_line_items.py index 0bc3c106..0c2d31dc 100644 --- a/src/orb/resources/invoice_line_items.py +++ b/src/orb/resources/invoice_line_items.py @@ -21,6 +21,13 @@ class InvoiceLineItems(SyncAPIResource): + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ + @cached_property def with_raw_response(self) -> InvoiceLineItemsWithRawResponse: """ @@ -133,6 +140,13 @@ def create( class AsyncInvoiceLineItems(AsyncAPIResource): + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ + @cached_property def with_raw_response(self) -> AsyncInvoiceLineItemsWithRawResponse: """ diff --git a/src/orb/resources/invoices.py b/src/orb/resources/invoices.py index 6660d1f6..22888133 100644 --- a/src/orb/resources/invoices.py +++ b/src/orb/resources/invoices.py @@ -10,6 +10,7 @@ from .. import _legacy_response from ..types import ( + invoice_pay_params, invoice_list_params, invoice_issue_params, invoice_create_params, @@ -20,7 +21,7 @@ invoice_fetch_upcoming_params, ) from .._types import Body, Omit, Query, Headers, NoneType, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -36,6 +37,13 @@ class Invoices(SyncAPIResource): + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ + @cached_property def with_raw_response(self) -> InvoicesWithRawResponse: """ @@ -61,6 +69,7 @@ def create( currency: str, invoice_date: Union[str, datetime], line_items: Iterable[invoice_create_params.LineItem], + auto_collection: Optional[bool] | Omit = omit, customer_id: Optional[str] | Omit = omit, discount: Optional[Discount] | Omit = omit, due_date: Union[Union[str, date], Union[str, datetime], None] | Omit = omit, @@ -87,6 +96,10 @@ def create( invoice_date: Optional invoice date to set. Must be in the past, if not set, `invoice_date` is set to the current time in the customer's timezone. + auto_collection: Determines whether this invoice will automatically attempt to charge a saved + payment method, if any. If not specified, the invoice inherits the customer's + auto_collection setting. + customer_id: The id of the `Customer` to create this invoice for. One of `customer_id` and `external_customer_id` are required. @@ -132,6 +145,7 @@ def create( "currency": currency, "invoice_date": invoice_date, "line_items": line_items, + "auto_collection": auto_collection, "customer_id": customer_id, "discount": discount, "due_date": due_date, @@ -157,6 +171,7 @@ def update( self, invoice_id: str, *, + auto_collection: Optional[bool] | Omit = omit, due_date: Union[Union[str, date], Union[str, datetime], None] | Omit = omit, invoice_date: Union[Union[str, date], Union[str, datetime], None] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, @@ -170,15 +185,20 @@ def update( idempotency_key: str | None = None, ) -> Invoice: """ - This endpoint allows you to update the `metadata`, `net_terms`, `due_date`, and - `invoice_date` properties on an invoice. If you pass null for the metadata - value, it will clear any existing metadata for that invoice. + This endpoint allows you to update the `metadata`, `net_terms`, `due_date`, + `invoice_date`, and `auto_collection` properties on an invoice. If you pass null + for the metadata value, it will clear any existing metadata for that invoice. `metadata` can be modified regardless of invoice state. `net_terms`, `due_date`, - and `invoice_date` can only be modified if the invoice is in a `draft` state. - `invoice_date` can only be modified for non-subscription invoices. + `invoice_date`, and `auto_collection` can only be modified if the invoice is in + a `draft` state. `invoice_date` can only be modified for non-subscription + invoices. Args: + auto_collection: Determines whether this invoice will automatically attempt to charge a saved + payment method, if any. Can only be modified on draft invoices. If not + specified, the invoice's existing setting is unchanged. + due_date: An optional custom due date for the invoice. If not set, the due date will be calculated based on the `net_terms` value. @@ -207,9 +227,10 @@ def update( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return self._put( - f"/invoices/{invoice_id}", + path_template("/invoices/{invoice_id}", invoice_id=invoice_id), body=maybe_transform( { + "auto_collection": auto_collection, "due_date": due_date, "invoice_date": invoice_date, "metadata": metadata, @@ -271,6 +292,10 @@ def list( values for each draft invoice, which may not always be up-to-date since Orb regularly refreshes invoices asynchronously. + If you don't need line item details, minimums, maximums, or discounts, prefer + the [list invoices summary](/api-reference/invoice/list-invoices-summary) + endpoint for better performance. + Args: cursor: Cursor for pagination. This can be populated by the `next_cursor` value returned from the initial request. @@ -363,7 +388,11 @@ def delete_line_item( raise ValueError(f"Expected a non-empty value for `line_item_id` but received {line_item_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._delete( - f"/invoices/{invoice_id}/invoice_line_items/{line_item_id}", + path_template( + "/invoices/{invoice_id}/invoice_line_items/{line_item_id}", + invoice_id=invoice_id, + line_item_id=line_item_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -401,7 +430,7 @@ def fetch( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return self._get( - f"/invoices/{invoice_id}", + path_template("/invoices/{invoice_id}", invoice_id=invoice_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -489,7 +518,7 @@ def issue( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return self._post( - f"/invoices/{invoice_id}/issue", + path_template("/invoices/{invoice_id}/issue", invoice_id=invoice_id), body=maybe_transform({"synchronous": synchronous}, invoice_issue_params.InvoiceIssueParams), options=make_request_options( extra_headers=extra_headers, @@ -546,7 +575,7 @@ def issue_summary( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return self._post( - f"/invoices/summary/{invoice_id}/issue", + path_template("/invoices/summary/{invoice_id}/issue", invoice_id=invoice_id), body=maybe_transform({"synchronous": synchronous}, invoice_issue_summary_params.InvoiceIssueSummaryParams), options=make_request_options( extra_headers=extra_headers, @@ -700,7 +729,7 @@ def mark_paid( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return self._post( - f"/invoices/{invoice_id}/mark_paid", + path_template("/invoices/{invoice_id}/mark_paid", invoice_id=invoice_id), body=maybe_transform( { "payment_received_date": payment_received_date, @@ -723,6 +752,7 @@ def pay( self, invoice_id: str, *, + shared_payment_token_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -731,11 +761,16 @@ def pay( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> Invoice: - """ - This endpoint collects payment for an invoice using the customer's default - payment method. This action can only be taken on invoices with status "issued". + """This endpoint collects payment for an invoice. + + By default, it uses the + customer's default payment method. Optionally, a shared payment token (SPT) can + be provided to pay using agent-granted credentials instead. This action can only + be taken on invoices with status "issued". Args: + shared_payment_token_id: The ID of a shared payment token granted by an agent to use for this payment. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -749,7 +784,10 @@ def pay( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return self._post( - f"/invoices/{invoice_id}/pay", + path_template("/invoices/{invoice_id}/pay", invoice_id=invoice_id), + body=maybe_transform( + {"shared_payment_token_id": shared_payment_token_id}, invoice_pay_params.InvoicePayParams + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -800,7 +838,7 @@ def void( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return self._post( - f"/invoices/{invoice_id}/void", + path_template("/invoices/{invoice_id}/void", invoice_id=invoice_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -813,6 +851,13 @@ def void( class AsyncInvoices(AsyncAPIResource): + """ + An [`Invoice`](/core-concepts#invoice) is a fundamental billing entity, representing the request for payment for + a single subscription. This includes a set of line items, which correspond to prices in the subscription's plan and + can represent fixed recurring fees or usage-based fees. They are generated at the end of a billing period, or as + the result of an action, such as a cancellation. + """ + @cached_property def with_raw_response(self) -> AsyncInvoicesWithRawResponse: """ @@ -838,6 +883,7 @@ async def create( currency: str, invoice_date: Union[str, datetime], line_items: Iterable[invoice_create_params.LineItem], + auto_collection: Optional[bool] | Omit = omit, customer_id: Optional[str] | Omit = omit, discount: Optional[Discount] | Omit = omit, due_date: Union[Union[str, date], Union[str, datetime], None] | Omit = omit, @@ -864,6 +910,10 @@ async def create( invoice_date: Optional invoice date to set. Must be in the past, if not set, `invoice_date` is set to the current time in the customer's timezone. + auto_collection: Determines whether this invoice will automatically attempt to charge a saved + payment method, if any. If not specified, the invoice inherits the customer's + auto_collection setting. + customer_id: The id of the `Customer` to create this invoice for. One of `customer_id` and `external_customer_id` are required. @@ -909,6 +959,7 @@ async def create( "currency": currency, "invoice_date": invoice_date, "line_items": line_items, + "auto_collection": auto_collection, "customer_id": customer_id, "discount": discount, "due_date": due_date, @@ -934,6 +985,7 @@ async def update( self, invoice_id: str, *, + auto_collection: Optional[bool] | Omit = omit, due_date: Union[Union[str, date], Union[str, datetime], None] | Omit = omit, invoice_date: Union[Union[str, date], Union[str, datetime], None] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, @@ -947,15 +999,20 @@ async def update( idempotency_key: str | None = None, ) -> Invoice: """ - This endpoint allows you to update the `metadata`, `net_terms`, `due_date`, and - `invoice_date` properties on an invoice. If you pass null for the metadata - value, it will clear any existing metadata for that invoice. + This endpoint allows you to update the `metadata`, `net_terms`, `due_date`, + `invoice_date`, and `auto_collection` properties on an invoice. If you pass null + for the metadata value, it will clear any existing metadata for that invoice. `metadata` can be modified regardless of invoice state. `net_terms`, `due_date`, - and `invoice_date` can only be modified if the invoice is in a `draft` state. - `invoice_date` can only be modified for non-subscription invoices. + `invoice_date`, and `auto_collection` can only be modified if the invoice is in + a `draft` state. `invoice_date` can only be modified for non-subscription + invoices. Args: + auto_collection: Determines whether this invoice will automatically attempt to charge a saved + payment method, if any. Can only be modified on draft invoices. If not + specified, the invoice's existing setting is unchanged. + due_date: An optional custom due date for the invoice. If not set, the due date will be calculated based on the `net_terms` value. @@ -984,9 +1041,10 @@ async def update( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return await self._put( - f"/invoices/{invoice_id}", + path_template("/invoices/{invoice_id}", invoice_id=invoice_id), body=await async_maybe_transform( { + "auto_collection": auto_collection, "due_date": due_date, "invoice_date": invoice_date, "metadata": metadata, @@ -1048,6 +1106,10 @@ def list( values for each draft invoice, which may not always be up-to-date since Orb regularly refreshes invoices asynchronously. + If you don't need line item details, minimums, maximums, or discounts, prefer + the [list invoices summary](/api-reference/invoice/list-invoices-summary) + endpoint for better performance. + Args: cursor: Cursor for pagination. This can be populated by the `next_cursor` value returned from the initial request. @@ -1140,7 +1202,11 @@ async def delete_line_item( raise ValueError(f"Expected a non-empty value for `line_item_id` but received {line_item_id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._delete( - f"/invoices/{invoice_id}/invoice_line_items/{line_item_id}", + path_template( + "/invoices/{invoice_id}/invoice_line_items/{line_item_id}", + invoice_id=invoice_id, + line_item_id=line_item_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1178,7 +1244,7 @@ async def fetch( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return await self._get( - f"/invoices/{invoice_id}", + path_template("/invoices/{invoice_id}", invoice_id=invoice_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1266,7 +1332,7 @@ async def issue( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return await self._post( - f"/invoices/{invoice_id}/issue", + path_template("/invoices/{invoice_id}/issue", invoice_id=invoice_id), body=await async_maybe_transform({"synchronous": synchronous}, invoice_issue_params.InvoiceIssueParams), options=make_request_options( extra_headers=extra_headers, @@ -1323,7 +1389,7 @@ async def issue_summary( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return await self._post( - f"/invoices/summary/{invoice_id}/issue", + path_template("/invoices/summary/{invoice_id}/issue", invoice_id=invoice_id), body=await async_maybe_transform( {"synchronous": synchronous}, invoice_issue_summary_params.InvoiceIssueSummaryParams ), @@ -1479,7 +1545,7 @@ async def mark_paid( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return await self._post( - f"/invoices/{invoice_id}/mark_paid", + path_template("/invoices/{invoice_id}/mark_paid", invoice_id=invoice_id), body=await async_maybe_transform( { "payment_received_date": payment_received_date, @@ -1502,6 +1568,7 @@ async def pay( self, invoice_id: str, *, + shared_payment_token_id: str, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -1510,11 +1577,16 @@ async def pay( timeout: float | httpx.Timeout | None | NotGiven = not_given, idempotency_key: str | None = None, ) -> Invoice: - """ - This endpoint collects payment for an invoice using the customer's default - payment method. This action can only be taken on invoices with status "issued". + """This endpoint collects payment for an invoice. + + By default, it uses the + customer's default payment method. Optionally, a shared payment token (SPT) can + be provided to pay using agent-granted credentials instead. This action can only + be taken on invoices with status "issued". Args: + shared_payment_token_id: The ID of a shared payment token granted by an agent to use for this payment. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -1528,7 +1600,10 @@ async def pay( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return await self._post( - f"/invoices/{invoice_id}/pay", + path_template("/invoices/{invoice_id}/pay", invoice_id=invoice_id), + body=await async_maybe_transform( + {"shared_payment_token_id": shared_payment_token_id}, invoice_pay_params.InvoicePayParams + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1579,7 +1654,7 @@ async def void( if not invoice_id: raise ValueError(f"Expected a non-empty value for `invoice_id` but received {invoice_id!r}") return await self._post( - f"/invoices/{invoice_id}/void", + path_template("/invoices/{invoice_id}/void", invoice_id=invoice_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/orb/resources/items.py b/src/orb/resources/items.py index d1cd7d7d..09589686 100644 --- a/src/orb/resources/items.py +++ b/src/orb/resources/items.py @@ -9,7 +9,7 @@ from .. import _legacy_response from ..types import item_list_params, item_create_params, item_update_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -21,6 +21,12 @@ class Items(SyncAPIResource): + """The Item resource represents a sellable product or good. + + Items are associated with all line items, billable metrics, + and prices and are used for defining external sync behavior for invoices and tax calculation purposes. + """ + @cached_property def with_raw_response(self) -> ItemsWithRawResponse: """ @@ -128,7 +134,7 @@ def update( if not item_id: raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") return self._put( - f"/items/{item_id}", + path_template("/items/{item_id}", item_id=item_id), body=maybe_transform( { "external_connections": external_connections, @@ -225,7 +231,7 @@ def archive( if not item_id: raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") return self._post( - f"/items/{item_id}/archive", + path_template("/items/{item_id}/archive", item_id=item_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -262,7 +268,7 @@ def fetch( if not item_id: raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") return self._get( - f"/items/{item_id}", + path_template("/items/{item_id}", item_id=item_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -271,6 +277,12 @@ def fetch( class AsyncItems(AsyncAPIResource): + """The Item resource represents a sellable product or good. + + Items are associated with all line items, billable metrics, + and prices and are used for defining external sync behavior for invoices and tax calculation purposes. + """ + @cached_property def with_raw_response(self) -> AsyncItemsWithRawResponse: """ @@ -378,7 +390,7 @@ async def update( if not item_id: raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") return await self._put( - f"/items/{item_id}", + path_template("/items/{item_id}", item_id=item_id), body=await async_maybe_transform( { "external_connections": external_connections, @@ -475,7 +487,7 @@ async def archive( if not item_id: raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") return await self._post( - f"/items/{item_id}/archive", + path_template("/items/{item_id}/archive", item_id=item_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -512,7 +524,7 @@ async def fetch( if not item_id: raise ValueError(f"Expected a non-empty value for `item_id` but received {item_id!r}") return await self._get( - f"/items/{item_id}", + path_template("/items/{item_id}", item_id=item_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/orb/resources/license_types.py b/src/orb/resources/license_types.py index 0809c648..f95b8e07 100644 --- a/src/orb/resources/license_types.py +++ b/src/orb/resources/license_types.py @@ -9,7 +9,7 @@ from .. import _legacy_response from ..types import license_type_list_params, license_type_create_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -23,6 +23,11 @@ class LicenseTypes(SyncAPIResource): + """ + The LicenseType resource represents a type of license that can be assigned to users. + License types are used during billing by grouping metrics on the configured grouping key. + """ + @cached_property def with_raw_response(self) -> LicenseTypesWithRawResponse: """ @@ -126,7 +131,7 @@ def retrieve( if not license_type_id: raise ValueError(f"Expected a non-empty value for `license_type_id` but received {license_type_id!r}") return self._get( - f"/license_types/{license_type_id}", + path_template("/license_types/{license_type_id}", license_type_id=license_type_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -188,6 +193,11 @@ def list( class AsyncLicenseTypes(AsyncAPIResource): + """ + The LicenseType resource represents a type of license that can be assigned to users. + License types are used during billing by grouping metrics on the configured grouping key. + """ + @cached_property def with_raw_response(self) -> AsyncLicenseTypesWithRawResponse: """ @@ -291,7 +301,7 @@ async def retrieve( if not license_type_id: raise ValueError(f"Expected a non-empty value for `license_type_id` but received {license_type_id!r}") return await self._get( - f"/license_types/{license_type_id}", + path_template("/license_types/{license_type_id}", license_type_id=license_type_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/orb/resources/licenses/external_licenses.py b/src/orb/resources/licenses/external_licenses.py index 5d5468ff..66b98fc8 100644 --- a/src/orb/resources/licenses/external_licenses.py +++ b/src/orb/resources/licenses/external_licenses.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -95,7 +95,9 @@ def get_usage( f"Expected a non-empty value for `external_license_id` but received {external_license_id!r}" ) return self._get( - f"/licenses/external_licenses/{external_license_id}/usage", + path_template( + "/licenses/external_licenses/{external_license_id}/usage", external_license_id=external_license_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -193,7 +195,9 @@ async def get_usage( f"Expected a non-empty value for `external_license_id` but received {external_license_id!r}" ) return await self._get( - f"/licenses/external_licenses/{external_license_id}/usage", + path_template( + "/licenses/external_licenses/{external_license_id}/usage", external_license_id=external_license_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/orb/resources/licenses/licenses.py b/src/orb/resources/licenses/licenses.py index 14a70f77..ea56b1c4 100644 --- a/src/orb/resources/licenses/licenses.py +++ b/src/orb/resources/licenses/licenses.py @@ -24,7 +24,7 @@ license_retrieve_by_external_id_params, ) from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -165,7 +165,7 @@ def retrieve( if not license_id: raise ValueError(f"Expected a non-empty value for `license_id` but received {license_id!r}") return self._get( - f"/licenses/{license_id}", + path_template("/licenses/{license_id}", license_id=license_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -265,7 +265,7 @@ def deactivate( if not license_id: raise ValueError(f"Expected a non-empty value for `license_id` but received {license_id!r}") return self._post( - f"/licenses/{license_id}/deactivate", + path_template("/licenses/{license_id}/deactivate", license_id=license_id), body=maybe_transform({"end_date": end_date}, license_deactivate_params.LicenseDeactivateParams), options=make_request_options( extra_headers=extra_headers, @@ -311,7 +311,9 @@ def retrieve_by_external_id( f"Expected a non-empty value for `external_license_id` but received {external_license_id!r}" ) return self._get( - f"/licenses/external_license_id/{external_license_id}", + path_template( + "/licenses/external_license_id/{external_license_id}", external_license_id=external_license_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -447,7 +449,7 @@ async def retrieve( if not license_id: raise ValueError(f"Expected a non-empty value for `license_id` but received {license_id!r}") return await self._get( - f"/licenses/{license_id}", + path_template("/licenses/{license_id}", license_id=license_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -547,7 +549,7 @@ async def deactivate( if not license_id: raise ValueError(f"Expected a non-empty value for `license_id` but received {license_id!r}") return await self._post( - f"/licenses/{license_id}/deactivate", + path_template("/licenses/{license_id}/deactivate", license_id=license_id), body=await async_maybe_transform({"end_date": end_date}, license_deactivate_params.LicenseDeactivateParams), options=make_request_options( extra_headers=extra_headers, @@ -593,7 +595,9 @@ async def retrieve_by_external_id( f"Expected a non-empty value for `external_license_id` but received {external_license_id!r}" ) return await self._get( - f"/licenses/external_license_id/{external_license_id}", + path_template( + "/licenses/external_license_id/{external_license_id}", external_license_id=external_license_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/orb/resources/licenses/usage.py b/src/orb/resources/licenses/usage.py index c58214f7..d78b6e4f 100644 --- a/src/orb/resources/licenses/usage.py +++ b/src/orb/resources/licenses/usage.py @@ -9,7 +9,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -159,7 +159,7 @@ def get_usage( if not license_id: raise ValueError(f"Expected a non-empty value for `license_id` but received {license_id!r}") return self._get( - f"/licenses/{license_id}/usage", + path_template("/licenses/{license_id}/usage", license_id=license_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -318,7 +318,7 @@ async def get_usage( if not license_id: raise ValueError(f"Expected a non-empty value for `license_id` but received {license_id!r}") return await self._get( - f"/licenses/{license_id}/usage", + path_template("/licenses/{license_id}/usage", license_id=license_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/orb/resources/metrics.py b/src/orb/resources/metrics.py index 6f62951d..08492c02 100644 --- a/src/orb/resources/metrics.py +++ b/src/orb/resources/metrics.py @@ -10,7 +10,7 @@ from .. import _legacy_response from ..types import metric_list_params, metric_create_params, metric_update_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -22,6 +22,11 @@ class Metrics(SyncAPIResource): + """ + The Metric resource represents a calculation of a quantity based on events. + Metrics are defined by the query that transforms raw usage events into meaningful values for your customers. + """ + @cached_property def with_raw_response(self) -> MetricsWithRawResponse: """ @@ -144,7 +149,7 @@ def update( if not metric_id: raise ValueError(f"Expected a non-empty value for `metric_id` but received {metric_id!r}") return self._put( - f"/metrics/{metric_id}", + path_template("/metrics/{metric_id}", metric_id=metric_id), body=maybe_transform({"metadata": metadata}, metric_update_params.MetricUpdateParams), options=make_request_options( extra_headers=extra_headers, @@ -242,7 +247,7 @@ def fetch( if not metric_id: raise ValueError(f"Expected a non-empty value for `metric_id` but received {metric_id!r}") return self._get( - f"/metrics/{metric_id}", + path_template("/metrics/{metric_id}", metric_id=metric_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -251,6 +256,11 @@ def fetch( class AsyncMetrics(AsyncAPIResource): + """ + The Metric resource represents a calculation of a quantity based on events. + Metrics are defined by the query that transforms raw usage events into meaningful values for your customers. + """ + @cached_property def with_raw_response(self) -> AsyncMetricsWithRawResponse: """ @@ -373,7 +383,7 @@ async def update( if not metric_id: raise ValueError(f"Expected a non-empty value for `metric_id` but received {metric_id!r}") return await self._put( - f"/metrics/{metric_id}", + path_template("/metrics/{metric_id}", metric_id=metric_id), body=await async_maybe_transform({"metadata": metadata}, metric_update_params.MetricUpdateParams), options=make_request_options( extra_headers=extra_headers, @@ -471,7 +481,7 @@ async def fetch( if not metric_id: raise ValueError(f"Expected a non-empty value for `metric_id` but received {metric_id!r}") return await self._get( - f"/metrics/{metric_id}", + path_template("/metrics/{metric_id}", metric_id=metric_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/orb/resources/plans/external_plan_id.py b/src/orb/resources/plans/external_plan_id.py index 1017afcc..3a0ea6cc 100644 --- a/src/orb/resources/plans/external_plan_id.py +++ b/src/orb/resources/plans/external_plan_id.py @@ -8,7 +8,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -20,6 +20,12 @@ class ExternalPlanID(SyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def with_raw_response(self) -> ExternalPlanIDWithRawResponse: """ @@ -43,6 +49,7 @@ def update( self, other_external_plan_id: str, *, + description: Optional[str] | Omit = omit, external_plan_id: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -54,12 +61,14 @@ def update( idempotency_key: str | None = None, ) -> Plan: """ - This endpoint can be used to update the `external_plan_id`, and `metadata` of an - existing plan. + This endpoint can be used to update the `external_plan_id`, `description`, and + `metadata` of an existing plan. Other fields on a plan are currently immutable. Args: + description: An optional user-defined description of the plan. + external_plan_id: An optional user-defined ID for this plan resource, used throughout the system as an alias for this Plan. Use this field to identify a plan by an existing identifier in your system. @@ -83,9 +92,12 @@ def update( f"Expected a non-empty value for `other_external_plan_id` but received {other_external_plan_id!r}" ) return self._put( - f"/plans/external_plan_id/{other_external_plan_id}", + path_template( + "/plans/external_plan_id/{other_external_plan_id}", other_external_plan_id=other_external_plan_id + ), body=maybe_transform( { + "description": description, "external_plan_id": external_plan_id, "metadata": metadata, }, @@ -142,7 +154,7 @@ def fetch( if not external_plan_id: raise ValueError(f"Expected a non-empty value for `external_plan_id` but received {external_plan_id!r}") return self._get( - f"/plans/external_plan_id/{external_plan_id}", + path_template("/plans/external_plan_id/{external_plan_id}", external_plan_id=external_plan_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -151,6 +163,12 @@ def fetch( class AsyncExternalPlanID(AsyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def with_raw_response(self) -> AsyncExternalPlanIDWithRawResponse: """ @@ -174,6 +192,7 @@ async def update( self, other_external_plan_id: str, *, + description: Optional[str] | Omit = omit, external_plan_id: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -185,12 +204,14 @@ async def update( idempotency_key: str | None = None, ) -> Plan: """ - This endpoint can be used to update the `external_plan_id`, and `metadata` of an - existing plan. + This endpoint can be used to update the `external_plan_id`, `description`, and + `metadata` of an existing plan. Other fields on a plan are currently immutable. Args: + description: An optional user-defined description of the plan. + external_plan_id: An optional user-defined ID for this plan resource, used throughout the system as an alias for this Plan. Use this field to identify a plan by an existing identifier in your system. @@ -214,9 +235,12 @@ async def update( f"Expected a non-empty value for `other_external_plan_id` but received {other_external_plan_id!r}" ) return await self._put( - f"/plans/external_plan_id/{other_external_plan_id}", + path_template( + "/plans/external_plan_id/{other_external_plan_id}", other_external_plan_id=other_external_plan_id + ), body=await async_maybe_transform( { + "description": description, "external_plan_id": external_plan_id, "metadata": metadata, }, @@ -273,7 +297,7 @@ async def fetch( if not external_plan_id: raise ValueError(f"Expected a non-empty value for `external_plan_id` but received {external_plan_id!r}") return await self._get( - f"/plans/external_plan_id/{external_plan_id}", + path_template("/plans/external_plan_id/{external_plan_id}", external_plan_id=external_plan_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/orb/resources/plans/migrations.py b/src/orb/resources/plans/migrations.py index 593a7e10..8655180b 100644 --- a/src/orb/resources/plans/migrations.py +++ b/src/orb/resources/plans/migrations.py @@ -8,7 +8,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform +from ..._utils import path_template, maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -23,6 +23,12 @@ class Migrations(SyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def with_raw_response(self) -> MigrationsWithRawResponse: """ @@ -71,7 +77,7 @@ def retrieve( if not migration_id: raise ValueError(f"Expected a non-empty value for `migration_id` but received {migration_id!r}") return self._get( - f"/plans/{plan_id}/migrations/{migration_id}", + path_template("/plans/{plan_id}/migrations/{migration_id}", plan_id=plan_id, migration_id=migration_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -115,7 +121,7 @@ def list( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return self._get_api_list( - f"/plans/{plan_id}/migrations", + path_template("/plans/{plan_id}/migrations", plan_id=plan_id), page=SyncPage[MigrationListResponse], options=make_request_options( extra_headers=extra_headers, @@ -165,7 +171,9 @@ def cancel( if not migration_id: raise ValueError(f"Expected a non-empty value for `migration_id` but received {migration_id!r}") return self._post( - f"/plans/{plan_id}/migrations/{migration_id}/cancel", + path_template( + "/plans/{plan_id}/migrations/{migration_id}/cancel", plan_id=plan_id, migration_id=migration_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -178,6 +186,12 @@ def cancel( class AsyncMigrations(AsyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def with_raw_response(self) -> AsyncMigrationsWithRawResponse: """ @@ -226,7 +240,7 @@ async def retrieve( if not migration_id: raise ValueError(f"Expected a non-empty value for `migration_id` but received {migration_id!r}") return await self._get( - f"/plans/{plan_id}/migrations/{migration_id}", + path_template("/plans/{plan_id}/migrations/{migration_id}", plan_id=plan_id, migration_id=migration_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -270,7 +284,7 @@ def list( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return self._get_api_list( - f"/plans/{plan_id}/migrations", + path_template("/plans/{plan_id}/migrations", plan_id=plan_id), page=AsyncPage[MigrationListResponse], options=make_request_options( extra_headers=extra_headers, @@ -320,7 +334,9 @@ async def cancel( if not migration_id: raise ValueError(f"Expected a non-empty value for `migration_id` but received {migration_id!r}") return await self._post( - f"/plans/{plan_id}/migrations/{migration_id}/cancel", + path_template( + "/plans/{plan_id}/migrations/{migration_id}/cancel", plan_id=plan_id, migration_id=migration_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/orb/resources/plans/plans.py b/src/orb/resources/plans/plans.py index 64d0bad8..58f58f68 100644 --- a/src/orb/resources/plans/plans.py +++ b/src/orb/resources/plans/plans.py @@ -11,7 +11,7 @@ from ... import _legacy_response from ...types import plan_list_params, plan_create_params, plan_update_params from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from .migrations import ( Migrations, @@ -39,12 +39,28 @@ class Plans(SyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def external_plan_id(self) -> ExternalPlanID: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return ExternalPlanID(self._client) @cached_property def migrations(self) -> Migrations: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return Migrations(self._client) @cached_property @@ -74,6 +90,7 @@ def create( prices: Iterable[plan_create_params.Price], adjustments: Optional[Iterable[plan_create_params.Adjustment]] | Omit = omit, default_invoice_memo: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, external_plan_id: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, net_terms: Optional[int] | Omit = omit, @@ -102,6 +119,8 @@ def create( default_invoice_memo: Free-form text which is available on the invoice PDF and the Orb invoice portal. + description: An optional user-defined description of the plan. + metadata: User-specified key/value pairs for the resource. Individual keys can be removed by setting the value to `null`, and the entire metadata mapping can be cleared by setting `metadata` to `null`. @@ -135,6 +154,7 @@ def create( "prices": prices, "adjustments": adjustments, "default_invoice_memo": default_invoice_memo, + "description": description, "external_plan_id": external_plan_id, "metadata": metadata, "net_terms": net_terms, @@ -157,6 +177,7 @@ def update( self, plan_id: str, *, + description: Optional[str] | Omit = omit, external_plan_id: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -168,12 +189,14 @@ def update( idempotency_key: str | None = None, ) -> Plan: """ - This endpoint can be used to update the `external_plan_id`, and `metadata` of an - existing plan. + This endpoint can be used to update the `external_plan_id`, `description`, and + `metadata` of an existing plan. Other fields on a plan are currently immutable. Args: + description: An optional user-defined description of the plan. + external_plan_id: An optional user-defined ID for this plan resource, used throughout the system as an alias for this Plan. Use this field to identify a plan by an existing identifier in your system. @@ -195,9 +218,10 @@ def update( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return self._put( - f"/plans/{plan_id}", + path_template("/plans/{plan_id}", plan_id=plan_id), body=maybe_transform( { + "description": description, "external_plan_id": external_plan_id, "metadata": metadata, }, @@ -319,7 +343,7 @@ def fetch( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return self._get( - f"/plans/{plan_id}", + path_template("/plans/{plan_id}", plan_id=plan_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -328,12 +352,28 @@ def fetch( class AsyncPlans(AsyncAPIResource): + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ + @cached_property def external_plan_id(self) -> AsyncExternalPlanID: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return AsyncExternalPlanID(self._client) @cached_property def migrations(self) -> AsyncMigrations: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return AsyncMigrations(self._client) @cached_property @@ -363,6 +403,7 @@ async def create( prices: Iterable[plan_create_params.Price], adjustments: Optional[Iterable[plan_create_params.Adjustment]] | Omit = omit, default_invoice_memo: Optional[str] | Omit = omit, + description: Optional[str] | Omit = omit, external_plan_id: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, net_terms: Optional[int] | Omit = omit, @@ -391,6 +432,8 @@ async def create( default_invoice_memo: Free-form text which is available on the invoice PDF and the Orb invoice portal. + description: An optional user-defined description of the plan. + metadata: User-specified key/value pairs for the resource. Individual keys can be removed by setting the value to `null`, and the entire metadata mapping can be cleared by setting `metadata` to `null`. @@ -424,6 +467,7 @@ async def create( "prices": prices, "adjustments": adjustments, "default_invoice_memo": default_invoice_memo, + "description": description, "external_plan_id": external_plan_id, "metadata": metadata, "net_terms": net_terms, @@ -446,6 +490,7 @@ async def update( self, plan_id: str, *, + description: Optional[str] | Omit = omit, external_plan_id: Optional[str] | Omit = omit, metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. @@ -457,12 +502,14 @@ async def update( idempotency_key: str | None = None, ) -> Plan: """ - This endpoint can be used to update the `external_plan_id`, and `metadata` of an - existing plan. + This endpoint can be used to update the `external_plan_id`, `description`, and + `metadata` of an existing plan. Other fields on a plan are currently immutable. Args: + description: An optional user-defined description of the plan. + external_plan_id: An optional user-defined ID for this plan resource, used throughout the system as an alias for this Plan. Use this field to identify a plan by an existing identifier in your system. @@ -484,9 +531,10 @@ async def update( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return await self._put( - f"/plans/{plan_id}", + path_template("/plans/{plan_id}", plan_id=plan_id), body=await async_maybe_transform( { + "description": description, "external_plan_id": external_plan_id, "metadata": metadata, }, @@ -608,7 +656,7 @@ async def fetch( if not plan_id: raise ValueError(f"Expected a non-empty value for `plan_id` but received {plan_id!r}") return await self._get( - f"/plans/{plan_id}", + path_template("/plans/{plan_id}", plan_id=plan_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -635,10 +683,20 @@ def __init__(self, plans: Plans) -> None: @cached_property def external_plan_id(self) -> ExternalPlanIDWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return ExternalPlanIDWithRawResponse(self._plans.external_plan_id) @cached_property def migrations(self) -> MigrationsWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return MigrationsWithRawResponse(self._plans.migrations) @@ -661,10 +719,20 @@ def __init__(self, plans: AsyncPlans) -> None: @cached_property def external_plan_id(self) -> AsyncExternalPlanIDWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return AsyncExternalPlanIDWithRawResponse(self._plans.external_plan_id) @cached_property def migrations(self) -> AsyncMigrationsWithRawResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return AsyncMigrationsWithRawResponse(self._plans.migrations) @@ -687,10 +755,20 @@ def __init__(self, plans: Plans) -> None: @cached_property def external_plan_id(self) -> ExternalPlanIDWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return ExternalPlanIDWithStreamingResponse(self._plans.external_plan_id) @cached_property def migrations(self) -> MigrationsWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return MigrationsWithStreamingResponse(self._plans.migrations) @@ -713,8 +791,18 @@ def __init__(self, plans: AsyncPlans) -> None: @cached_property def external_plan_id(self) -> AsyncExternalPlanIDWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return AsyncExternalPlanIDWithStreamingResponse(self._plans.external_plan_id) @cached_property def migrations(self) -> AsyncMigrationsWithStreamingResponse: + """ + The [Plan](/core-concepts#plan-and-price) resource represents a plan that can be subscribed to by a + customer. Plans define the billing behavior of the subscription. You can see more about how to configure prices + in the [Price resource](/reference/price). + """ return AsyncMigrationsWithStreamingResponse(self._plans.migrations) diff --git a/src/orb/resources/prices/external_price_id.py b/src/orb/resources/prices/external_price_id.py index 8d023ee9..541874e3 100644 --- a/src/orb/resources/prices/external_price_id.py +++ b/src/orb/resources/prices/external_price_id.py @@ -8,7 +8,7 @@ from ... import _legacy_response from ..._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from ..._utils import maybe_transform, async_maybe_transform +from ..._utils import path_template, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -20,6 +20,16 @@ class ExternalPriceID(SyncAPIResource): + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ + @cached_property def with_raw_response(self) -> ExternalPriceIDWithRawResponse: """ @@ -78,7 +88,7 @@ def update( return cast( Price, self._put( - f"/prices/external_price_id/{external_price_id}", + path_template("/prices/external_price_id/{external_price_id}", external_price_id=external_price_id), body=maybe_transform( {"metadata": metadata}, external_price_id_update_params.ExternalPriceIDUpdateParams ), @@ -124,7 +134,7 @@ def fetch( return cast( Price, self._get( - f"/prices/external_price_id/{external_price_id}", + path_template("/prices/external_price_id/{external_price_id}", external_price_id=external_price_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -134,6 +144,16 @@ def fetch( class AsyncExternalPriceID(AsyncAPIResource): + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ + @cached_property def with_raw_response(self) -> AsyncExternalPriceIDWithRawResponse: """ @@ -192,7 +212,7 @@ async def update( return cast( Price, await self._put( - f"/prices/external_price_id/{external_price_id}", + path_template("/prices/external_price_id/{external_price_id}", external_price_id=external_price_id), body=await async_maybe_transform( {"metadata": metadata}, external_price_id_update_params.ExternalPriceIDUpdateParams ), @@ -238,7 +258,7 @@ async def fetch( return cast( Price, await self._get( - f"/prices/external_price_id/{external_price_id}", + path_template("/prices/external_price_id/{external_price_id}", external_price_id=external_price_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/orb/resources/prices/prices.py b/src/orb/resources/prices/prices.py index 1c568a13..25b204cf 100644 --- a/src/orb/resources/prices/prices.py +++ b/src/orb/resources/prices/prices.py @@ -18,7 +18,7 @@ price_evaluate_preview_events_params, ) from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from ..._utils import required_args, maybe_transform, async_maybe_transform +from ..._utils import path_template, required_args, maybe_transform, async_maybe_transform from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -49,8 +49,27 @@ class Prices(SyncAPIResource): + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ + @cached_property def external_price_id(self) -> ExternalPriceID: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ return ExternalPriceID(self._client) @cached_property @@ -2853,6 +2872,105 @@ def create( """ ... + @overload + def create( + self, + *, + cadence: Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"], + currency: str, + daily_credit_allowance_config: price_create_params.NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig, + item_id: str, + model_type: Literal["daily_credit_allowance"], + name: str, + billable_metric_id: Optional[str] | Omit = omit, + billed_in_advance: Optional[bool] | Omit = omit, + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] | Omit = omit, + conversion_rate: Optional[float] | Omit = omit, + conversion_rate_config: Optional[price_create_params.NewFloatingDailyCreditAllowancePriceConversionRateConfig] + | Omit = omit, + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] | Omit = omit, + external_price_id: Optional[str] | Omit = omit, + fixed_price_quantity: Optional[float] | Omit = omit, + invoice_grouping_key: Optional[str] | Omit = omit, + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] | Omit = omit, + license_type_id: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + idempotency_key: str | None = None, + ) -> Price: + """ + This endpoint is used to create a [price](/product-catalog/price-configuration). + A price created using this endpoint is always an add-on, meaning that it's not + associated with a specific plan and can instead be individually added to + subscriptions, including subscriptions on different plans. + + An `external_price_id` can be optionally specified as an alias to allow + ergonomic interaction with prices in the Orb API. + + See the [Price resource](/product-catalog/price-configuration) for the + specification of different price model configurations possible in this endpoint. + + Args: + cadence: The cadence to bill for this price on. + + currency: An ISO 4217 currency string for which this price is billed in. + + daily_credit_allowance_config: Configuration for daily_credit_allowance pricing + + item_id: The id of the item the price will be associated with. + + model_type: The pricing model type + + name: The name of the price. + + billable_metric_id: The id of the billable metric for the price. Only needed if the price is + usage-based. + + billed_in_advance: If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + + billing_cycle_configuration: For custom cadence: specifies the duration of the billing period in days or + months. + + conversion_rate: The per unit conversion rate of the price currency to the invoicing currency. + + conversion_rate_config: The configuration for the rate of the price currency to the invoicing currency. + + dimensional_price_configuration: For dimensional price: specifies a price group and dimension values + + external_price_id: An alias for the price. + + fixed_price_quantity: If the Price represents a fixed cost, this represents the quantity of units + applied. + + invoice_grouping_key: The property used to group this price on an invoice + + invoicing_cycle_configuration: Within each billing cycle, specifies the cadence at which invoices are produced. + If unspecified, a single invoice is produced per billing cycle. + + license_type_id: The ID of the license type to associate with this price. + + metadata: User-specified key/value pairs for the resource. Individual keys can be removed + by setting the value to `null`, and the entire metadata mapping can be cleared + by setting `metadata` to `null`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + ... + @overload def create( self, @@ -3179,6 +3297,7 @@ def create( ["cadence", "currency", "item_id", "model_type", "name", "scalable_matrix_with_tiered_pricing_config"], ["cadence", "cumulative_grouped_bulk_config", "currency", "item_id", "model_type", "name"], ["cadence", "cumulative_grouped_allocation_config", "currency", "item_id", "model_type", "name"], + ["cadence", "currency", "daily_credit_allowance_config", "item_id", "model_type", "name"], ["cadence", "currency", "item_id", "minimum_composite_config", "model_type", "name"], ["cadence", "currency", "item_id", "model_type", "name", "percent_config"], ["cadence", "currency", "event_output_config", "item_id", "model_type", "name"], @@ -3217,6 +3336,7 @@ def create( | Literal["scalable_matrix_with_tiered_pricing"] | Literal["cumulative_grouped_bulk"] | Literal["cumulative_grouped_allocation"] + | Literal["daily_credit_allowance"] | Literal["minimum_composite"] | Literal["percent"] | Literal["event_output"], @@ -3254,6 +3374,7 @@ def create( | Optional[price_create_params.NewFloatingScalableMatrixWithTieredPricingPriceConversionRateConfig] | Optional[price_create_params.NewFloatingCumulativeGroupedBulkPriceConversionRateConfig] | Optional[price_create_params.NewFloatingCumulativeGroupedAllocationPriceConversionRateConfig] + | Optional[price_create_params.NewFloatingDailyCreditAllowancePriceConversionRateConfig] | Optional[price_create_params.NewFloatingMinimumCompositePriceConversionRateConfig] | Optional[price_create_params.NewFloatingPercentCompositePriceConversionRateConfig] | Optional[price_create_params.NewFloatingEventOutputPriceConversionRateConfig] @@ -3312,6 +3433,8 @@ def create( | Omit = omit, cumulative_grouped_allocation_config: price_create_params.NewFloatingCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig | Omit = omit, + daily_credit_allowance_config: price_create_params.NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig + | Omit = omit, minimum_composite_config: price_create_params.NewFloatingMinimumCompositePriceMinimumCompositeConfig | Omit = omit, percent_config: price_create_params.NewFloatingPercentCompositePricePercentConfig | Omit = omit, @@ -3375,6 +3498,7 @@ def create( "scalable_matrix_with_tiered_pricing_config": scalable_matrix_with_tiered_pricing_config, "cumulative_grouped_bulk_config": cumulative_grouped_bulk_config, "cumulative_grouped_allocation_config": cumulative_grouped_allocation_config, + "daily_credit_allowance_config": daily_credit_allowance_config, "minimum_composite_config": minimum_composite_config, "percent_config": percent_config, "event_output_config": event_output_config, @@ -3431,7 +3555,7 @@ def update( return cast( Price, self._put( - f"/prices/{price_id}", + path_template("/prices/{price_id}", price_id=price_id), body=maybe_transform({"metadata": metadata}, price_update_params.PriceUpdateParams), options=make_request_options( extra_headers=extra_headers, @@ -3503,6 +3627,7 @@ def evaluate( external_customer_id: Optional[str] | Omit = omit, filter: Optional[str] | Omit = omit, grouping_keys: SequenceNotStr[str] | Omit = omit, + metric_parameter_overrides: Optional[Dict[str, object]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -3555,6 +3680,9 @@ def evaluate( [computed properties](/extensibility/advanced-metrics#computed-properties)) used to group the underlying billable metric + metric_parameter_overrides: Optional overrides for parameterized billable metric parameters. If the metric + has parameter definitions and no overrides are provided, defaults will be used. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -3568,7 +3696,7 @@ def evaluate( if not price_id: raise ValueError(f"Expected a non-empty value for `price_id` but received {price_id!r}") return self._post( - f"/prices/{price_id}/evaluate", + path_template("/prices/{price_id}/evaluate", price_id=price_id), body=maybe_transform( { "timeframe_end": timeframe_end, @@ -3577,6 +3705,7 @@ def evaluate( "external_customer_id": external_customer_id, "filter": filter, "grouping_keys": grouping_keys, + "metric_parameter_overrides": metric_parameter_overrides, }, price_evaluate_params.PriceEvaluateParams, ), @@ -3791,7 +3920,7 @@ def fetch( return cast( Price, self._get( - f"/prices/{price_id}", + path_template("/prices/{price_id}", price_id=price_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -3801,8 +3930,27 @@ def fetch( class AsyncPrices(AsyncAPIResource): + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ + @cached_property def external_price_id(self) -> AsyncExternalPriceID: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ return AsyncExternalPriceID(self._client) @cached_property @@ -6605,6 +6753,105 @@ async def create( """ ... + @overload + async def create( + self, + *, + cadence: Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"], + currency: str, + daily_credit_allowance_config: price_create_params.NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig, + item_id: str, + model_type: Literal["daily_credit_allowance"], + name: str, + billable_metric_id: Optional[str] | Omit = omit, + billed_in_advance: Optional[bool] | Omit = omit, + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] | Omit = omit, + conversion_rate: Optional[float] | Omit = omit, + conversion_rate_config: Optional[price_create_params.NewFloatingDailyCreditAllowancePriceConversionRateConfig] + | Omit = omit, + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] | Omit = omit, + external_price_id: Optional[str] | Omit = omit, + fixed_price_quantity: Optional[float] | Omit = omit, + invoice_grouping_key: Optional[str] | Omit = omit, + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] | Omit = omit, + license_type_id: Optional[str] | Omit = omit, + metadata: Optional[Dict[str, Optional[str]]] | Omit = omit, + # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. + # The extra values given here take precedence over values defined on the client or passed to this method. + extra_headers: Headers | None = None, + extra_query: Query | None = None, + extra_body: Body | None = None, + timeout: float | httpx.Timeout | None | NotGiven = not_given, + idempotency_key: str | None = None, + ) -> Price: + """ + This endpoint is used to create a [price](/product-catalog/price-configuration). + A price created using this endpoint is always an add-on, meaning that it's not + associated with a specific plan and can instead be individually added to + subscriptions, including subscriptions on different plans. + + An `external_price_id` can be optionally specified as an alias to allow + ergonomic interaction with prices in the Orb API. + + See the [Price resource](/product-catalog/price-configuration) for the + specification of different price model configurations possible in this endpoint. + + Args: + cadence: The cadence to bill for this price on. + + currency: An ISO 4217 currency string for which this price is billed in. + + daily_credit_allowance_config: Configuration for daily_credit_allowance pricing + + item_id: The id of the item the price will be associated with. + + model_type: The pricing model type + + name: The name of the price. + + billable_metric_id: The id of the billable metric for the price. Only needed if the price is + usage-based. + + billed_in_advance: If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + + billing_cycle_configuration: For custom cadence: specifies the duration of the billing period in days or + months. + + conversion_rate: The per unit conversion rate of the price currency to the invoicing currency. + + conversion_rate_config: The configuration for the rate of the price currency to the invoicing currency. + + dimensional_price_configuration: For dimensional price: specifies a price group and dimension values + + external_price_id: An alias for the price. + + fixed_price_quantity: If the Price represents a fixed cost, this represents the quantity of units + applied. + + invoice_grouping_key: The property used to group this price on an invoice + + invoicing_cycle_configuration: Within each billing cycle, specifies the cadence at which invoices are produced. + If unspecified, a single invoice is produced per billing cycle. + + license_type_id: The ID of the license type to associate with this price. + + metadata: User-specified key/value pairs for the resource. Individual keys can be removed + by setting the value to `null`, and the entire metadata mapping can be cleared + by setting `metadata` to `null`. + + extra_headers: Send extra headers + + extra_query: Add additional query parameters to the request + + extra_body: Add additional JSON properties to the request + + timeout: Override the client-level default timeout for this request, in seconds + + idempotency_key: Specify a custom idempotency key for this request + """ + ... + @overload async def create( self, @@ -6931,6 +7178,7 @@ async def create( ["cadence", "currency", "item_id", "model_type", "name", "scalable_matrix_with_tiered_pricing_config"], ["cadence", "cumulative_grouped_bulk_config", "currency", "item_id", "model_type", "name"], ["cadence", "cumulative_grouped_allocation_config", "currency", "item_id", "model_type", "name"], + ["cadence", "currency", "daily_credit_allowance_config", "item_id", "model_type", "name"], ["cadence", "currency", "item_id", "minimum_composite_config", "model_type", "name"], ["cadence", "currency", "item_id", "model_type", "name", "percent_config"], ["cadence", "currency", "event_output_config", "item_id", "model_type", "name"], @@ -6969,6 +7217,7 @@ async def create( | Literal["scalable_matrix_with_tiered_pricing"] | Literal["cumulative_grouped_bulk"] | Literal["cumulative_grouped_allocation"] + | Literal["daily_credit_allowance"] | Literal["minimum_composite"] | Literal["percent"] | Literal["event_output"], @@ -7006,6 +7255,7 @@ async def create( | Optional[price_create_params.NewFloatingScalableMatrixWithTieredPricingPriceConversionRateConfig] | Optional[price_create_params.NewFloatingCumulativeGroupedBulkPriceConversionRateConfig] | Optional[price_create_params.NewFloatingCumulativeGroupedAllocationPriceConversionRateConfig] + | Optional[price_create_params.NewFloatingDailyCreditAllowancePriceConversionRateConfig] | Optional[price_create_params.NewFloatingMinimumCompositePriceConversionRateConfig] | Optional[price_create_params.NewFloatingPercentCompositePriceConversionRateConfig] | Optional[price_create_params.NewFloatingEventOutputPriceConversionRateConfig] @@ -7064,6 +7314,8 @@ async def create( | Omit = omit, cumulative_grouped_allocation_config: price_create_params.NewFloatingCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig | Omit = omit, + daily_credit_allowance_config: price_create_params.NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig + | Omit = omit, minimum_composite_config: price_create_params.NewFloatingMinimumCompositePriceMinimumCompositeConfig | Omit = omit, percent_config: price_create_params.NewFloatingPercentCompositePricePercentConfig | Omit = omit, @@ -7127,6 +7379,7 @@ async def create( "scalable_matrix_with_tiered_pricing_config": scalable_matrix_with_tiered_pricing_config, "cumulative_grouped_bulk_config": cumulative_grouped_bulk_config, "cumulative_grouped_allocation_config": cumulative_grouped_allocation_config, + "daily_credit_allowance_config": daily_credit_allowance_config, "minimum_composite_config": minimum_composite_config, "percent_config": percent_config, "event_output_config": event_output_config, @@ -7183,7 +7436,7 @@ async def update( return cast( Price, await self._put( - f"/prices/{price_id}", + path_template("/prices/{price_id}", price_id=price_id), body=await async_maybe_transform({"metadata": metadata}, price_update_params.PriceUpdateParams), options=make_request_options( extra_headers=extra_headers, @@ -7255,6 +7508,7 @@ async def evaluate( external_customer_id: Optional[str] | Omit = omit, filter: Optional[str] | Omit = omit, grouping_keys: SequenceNotStr[str] | Omit = omit, + metric_parameter_overrides: Optional[Dict[str, object]] | Omit = omit, # Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs. # The extra values given here take precedence over values defined on the client or passed to this method. extra_headers: Headers | None = None, @@ -7307,6 +7561,9 @@ async def evaluate( [computed properties](/extensibility/advanced-metrics#computed-properties)) used to group the underlying billable metric + metric_parameter_overrides: Optional overrides for parameterized billable metric parameters. If the metric + has parameter definitions and no overrides are provided, defaults will be used. + extra_headers: Send extra headers extra_query: Add additional query parameters to the request @@ -7320,7 +7577,7 @@ async def evaluate( if not price_id: raise ValueError(f"Expected a non-empty value for `price_id` but received {price_id!r}") return await self._post( - f"/prices/{price_id}/evaluate", + path_template("/prices/{price_id}/evaluate", price_id=price_id), body=await async_maybe_transform( { "timeframe_end": timeframe_end, @@ -7329,6 +7586,7 @@ async def evaluate( "external_customer_id": external_customer_id, "filter": filter, "grouping_keys": grouping_keys, + "metric_parameter_overrides": metric_parameter_overrides, }, price_evaluate_params.PriceEvaluateParams, ), @@ -7543,7 +7801,7 @@ async def fetch( return cast( Price, await self._get( - f"/prices/{price_id}", + path_template("/prices/{price_id}", price_id=price_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -7580,6 +7838,15 @@ def __init__(self, prices: Prices) -> None: @cached_property def external_price_id(self) -> ExternalPriceIDWithRawResponse: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ return ExternalPriceIDWithRawResponse(self._prices.external_price_id) @@ -7611,6 +7878,15 @@ def __init__(self, prices: AsyncPrices) -> None: @cached_property def external_price_id(self) -> AsyncExternalPriceIDWithRawResponse: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ return AsyncExternalPriceIDWithRawResponse(self._prices.external_price_id) @@ -7642,6 +7918,15 @@ def __init__(self, prices: Prices) -> None: @cached_property def external_price_id(self) -> ExternalPriceIDWithStreamingResponse: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ return ExternalPriceIDWithStreamingResponse(self._prices.external_price_id) @@ -7673,4 +7958,13 @@ def __init__(self, prices: AsyncPrices) -> None: @cached_property def external_price_id(self) -> AsyncExternalPriceIDWithStreamingResponse: + """ + The Price resource represents a price that can be billed on a subscription, resulting in a charge on an invoice in + the form of an invoice line item. Prices take a quantity and determine an amount to bill. + + Orb supports a few different pricing models out of the box. Each of these models is serialized differently in a + given Price object. The model_type field determines the key for the configuration object that is present. + + For more on the types of prices, see [the core concepts documentation](/core-concepts#plan-and-price) + """ return AsyncExternalPriceIDWithStreamingResponse(self._prices.external_price_id) diff --git a/src/orb/resources/subscription_changes.py b/src/orb/resources/subscription_changes.py index 5adbb9e3..65d3309d 100644 --- a/src/orb/resources/subscription_changes.py +++ b/src/orb/resources/subscription_changes.py @@ -11,7 +11,7 @@ from .. import _legacy_response from ..types import subscription_change_list_params, subscription_change_apply_params from .._types import Body, Omit, Query, Headers, NotGiven, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -81,7 +81,9 @@ def retrieve( f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" ) return self._get( - f"/subscription_changes/{subscription_change_id}", + path_template( + "/subscription_changes/{subscription_change_id}", subscription_change_id=subscription_change_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -201,7 +203,9 @@ def apply( f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" ) return self._post( - f"/subscription_changes/{subscription_change_id}/apply", + path_template( + "/subscription_changes/{subscription_change_id}/apply", subscription_change_id=subscription_change_id + ), body=maybe_transform( { "description": description, @@ -257,7 +261,9 @@ def cancel( f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" ) return self._post( - f"/subscription_changes/{subscription_change_id}/cancel", + path_template( + "/subscription_changes/{subscription_change_id}/cancel", subscription_change_id=subscription_change_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -325,7 +331,9 @@ async def retrieve( f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" ) return await self._get( - f"/subscription_changes/{subscription_change_id}", + path_template( + "/subscription_changes/{subscription_change_id}", subscription_change_id=subscription_change_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -445,7 +453,9 @@ async def apply( f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" ) return await self._post( - f"/subscription_changes/{subscription_change_id}/apply", + path_template( + "/subscription_changes/{subscription_change_id}/apply", subscription_change_id=subscription_change_id + ), body=await async_maybe_transform( { "description": description, @@ -501,7 +511,9 @@ async def cancel( f"Expected a non-empty value for `subscription_change_id` but received {subscription_change_id!r}" ) return await self._post( - f"/subscription_changes/{subscription_change_id}/cancel", + path_template( + "/subscription_changes/{subscription_change_id}/cancel", subscription_change_id=subscription_change_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/orb/resources/subscriptions.py b/src/orb/resources/subscriptions.py index 0f41dd97..40b908cf 100644 --- a/src/orb/resources/subscriptions.py +++ b/src/orb/resources/subscriptions.py @@ -26,7 +26,7 @@ subscription_unschedule_fixed_fee_quantity_updates_params, ) from .._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, omit, not_given -from .._utils import maybe_transform, async_maybe_transform +from .._utils import path_template, maybe_transform, async_maybe_transform from .._compat import cached_property from .._resource import SyncAPIResource, AsyncAPIResource from .._response import to_streamed_response_wrapper, async_to_streamed_response_wrapper @@ -563,7 +563,7 @@ def update( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._put( - f"/subscriptions/{subscription_id}", + path_template("/subscriptions/{subscription_id}", subscription_id=subscription_id), body=maybe_transform( { "auto_collection": auto_collection, @@ -760,7 +760,7 @@ def cancel( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/cancel", + path_template("/subscriptions/{subscription_id}/cancel", subscription_id=subscription_id), body=maybe_transform( { "cancel_option": cancel_option, @@ -806,7 +806,7 @@ def fetch( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._get( - f"/subscriptions/{subscription_id}", + path_template("/subscriptions/{subscription_id}", subscription_id=subscription_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -835,10 +835,10 @@ def fetch_costs( metric, in usage units rather than a currency). The semantics of this endpoint exactly mirror those of - [fetching a customer's costs](fetch-customer-costs). Use this endpoint to limit - your analysis of costs to a specific subscription for the customer (e.g. to - de-aggregate costs when a customer's subscription has started and stopped on the - same day). + [fetching a customer's costs](/api-reference/customer/fetch-customer-costs). Use + this endpoint to limit your analysis of costs to a specific subscription for the + customer (e.g. to de-aggregate costs when a customer's subscription has started + and stopped on the same day). Args: currency: The currency or custom pricing unit to use. @@ -863,7 +863,7 @@ def fetch_costs( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._get( - f"/subscriptions/{subscription_id}/costs", + path_template("/subscriptions/{subscription_id}/costs", subscription_id=subscription_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -922,7 +922,7 @@ def fetch_schedule( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._get_api_list( - f"/subscriptions/{subscription_id}/schedule", + path_template("/subscriptions/{subscription_id}/schedule", subscription_id=subscription_id), page=SyncPage[SubscriptionFetchScheduleResponse], options=make_request_options( extra_headers=extra_headers, @@ -1193,7 +1193,7 @@ def fetch_usage( return cast( SubscriptionUsage, self._get( - f"/subscriptions/{subscription_id}/usage", + path_template("/subscriptions/{subscription_id}/usage", subscription_id=subscription_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1340,7 +1340,7 @@ def price_intervals( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/price_intervals", + path_template("/subscriptions/{subscription_id}/price_intervals", subscription_id=subscription_id), body=maybe_transform( { "add": add, @@ -1407,7 +1407,7 @@ def redeem_coupon( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/redeem_coupon", + path_template("/subscriptions/{subscription_id}/redeem_coupon", subscription_id=subscription_id), body=maybe_transform( { "change_option": change_option, @@ -1745,7 +1745,7 @@ def schedule_plan_change( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/schedule_plan_change", + path_template("/subscriptions/{subscription_id}/schedule_plan_change", subscription_id=subscription_id), body=maybe_transform( { "change_option": change_option, @@ -1826,7 +1826,7 @@ def trigger_phase( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/trigger_phase", + path_template("/subscriptions/{subscription_id}/trigger_phase", subscription_id=subscription_id), body=maybe_transform( { "allow_invoice_credit_or_void": allow_invoice_credit_or_void, @@ -1864,6 +1864,12 @@ def unschedule_cancellation( cancellation. This operation will turn on auto-renew, ensuring that the subscription does not end at the currently scheduled cancellation time. + Note: uncancellation is a lossy operation. Price intervals that were cut short + by the cancellation are extended to infinity (original end dates are lost), and + future intervals or phases scheduled after the cancellation time are permanently + deleted. For complex subscriptions with phases or scheduled plan changes, + consider creating a new plan change instead of uncancelling. + Args: extra_headers: Send extra headers @@ -1878,7 +1884,7 @@ def unschedule_cancellation( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/unschedule_cancellation", + path_template("/subscriptions/{subscription_id}/unschedule_cancellation", subscription_id=subscription_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1925,7 +1931,10 @@ def unschedule_fixed_fee_quantity_updates( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/unschedule_fixed_fee_quantity_updates", + path_template( + "/subscriptions/{subscription_id}/unschedule_fixed_fee_quantity_updates", + subscription_id=subscription_id, + ), body=maybe_transform( {"price_id": price_id}, subscription_unschedule_fixed_fee_quantity_updates_params.SubscriptionUnscheduleFixedFeeQuantityUpdatesParams, @@ -1970,7 +1979,9 @@ def unschedule_pending_plan_changes( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/unschedule_pending_plan_changes", + path_template( + "/subscriptions/{subscription_id}/unschedule_pending_plan_changes", subscription_id=subscription_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -2042,7 +2053,9 @@ def update_fixed_fee_quantity( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/update_fixed_fee_quantity", + path_template( + "/subscriptions/{subscription_id}/update_fixed_fee_quantity", subscription_id=subscription_id + ), body=maybe_transform( { "price_id": price_id, @@ -2118,7 +2131,7 @@ def update_trial( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._post( - f"/subscriptions/{subscription_id}/update_trial", + path_template("/subscriptions/{subscription_id}/update_trial", subscription_id=subscription_id), body=maybe_transform( { "trial_end_date": trial_end_date, @@ -2658,7 +2671,7 @@ async def update( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._put( - f"/subscriptions/{subscription_id}", + path_template("/subscriptions/{subscription_id}", subscription_id=subscription_id), body=await async_maybe_transform( { "auto_collection": auto_collection, @@ -2855,7 +2868,7 @@ async def cancel( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/cancel", + path_template("/subscriptions/{subscription_id}/cancel", subscription_id=subscription_id), body=await async_maybe_transform( { "cancel_option": cancel_option, @@ -2901,7 +2914,7 @@ async def fetch( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._get( - f"/subscriptions/{subscription_id}", + path_template("/subscriptions/{subscription_id}", subscription_id=subscription_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -2930,10 +2943,10 @@ async def fetch_costs( metric, in usage units rather than a currency). The semantics of this endpoint exactly mirror those of - [fetching a customer's costs](fetch-customer-costs). Use this endpoint to limit - your analysis of costs to a specific subscription for the customer (e.g. to - de-aggregate costs when a customer's subscription has started and stopped on the - same day). + [fetching a customer's costs](/api-reference/customer/fetch-customer-costs). Use + this endpoint to limit your analysis of costs to a specific subscription for the + customer (e.g. to de-aggregate costs when a customer's subscription has started + and stopped on the same day). Args: currency: The currency or custom pricing unit to use. @@ -2958,7 +2971,7 @@ async def fetch_costs( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._get( - f"/subscriptions/{subscription_id}/costs", + path_template("/subscriptions/{subscription_id}/costs", subscription_id=subscription_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -3017,7 +3030,7 @@ def fetch_schedule( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return self._get_api_list( - f"/subscriptions/{subscription_id}/schedule", + path_template("/subscriptions/{subscription_id}/schedule", subscription_id=subscription_id), page=AsyncPage[SubscriptionFetchScheduleResponse], options=make_request_options( extra_headers=extra_headers, @@ -3288,7 +3301,7 @@ async def fetch_usage( return cast( SubscriptionUsage, await self._get( - f"/subscriptions/{subscription_id}/usage", + path_template("/subscriptions/{subscription_id}/usage", subscription_id=subscription_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -3435,7 +3448,7 @@ async def price_intervals( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/price_intervals", + path_template("/subscriptions/{subscription_id}/price_intervals", subscription_id=subscription_id), body=await async_maybe_transform( { "add": add, @@ -3502,7 +3515,7 @@ async def redeem_coupon( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/redeem_coupon", + path_template("/subscriptions/{subscription_id}/redeem_coupon", subscription_id=subscription_id), body=await async_maybe_transform( { "change_option": change_option, @@ -3840,7 +3853,7 @@ async def schedule_plan_change( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/schedule_plan_change", + path_template("/subscriptions/{subscription_id}/schedule_plan_change", subscription_id=subscription_id), body=await async_maybe_transform( { "change_option": change_option, @@ -3921,7 +3934,7 @@ async def trigger_phase( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/trigger_phase", + path_template("/subscriptions/{subscription_id}/trigger_phase", subscription_id=subscription_id), body=await async_maybe_transform( { "allow_invoice_credit_or_void": allow_invoice_credit_or_void, @@ -3959,6 +3972,12 @@ async def unschedule_cancellation( cancellation. This operation will turn on auto-renew, ensuring that the subscription does not end at the currently scheduled cancellation time. + Note: uncancellation is a lossy operation. Price intervals that were cut short + by the cancellation are extended to infinity (original end dates are lost), and + future intervals or phases scheduled after the cancellation time are permanently + deleted. For complex subscriptions with phases or scheduled plan changes, + consider creating a new plan change instead of uncancelling. + Args: extra_headers: Send extra headers @@ -3973,7 +3992,7 @@ async def unschedule_cancellation( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/unschedule_cancellation", + path_template("/subscriptions/{subscription_id}/unschedule_cancellation", subscription_id=subscription_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -4020,7 +4039,10 @@ async def unschedule_fixed_fee_quantity_updates( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/unschedule_fixed_fee_quantity_updates", + path_template( + "/subscriptions/{subscription_id}/unschedule_fixed_fee_quantity_updates", + subscription_id=subscription_id, + ), body=await async_maybe_transform( {"price_id": price_id}, subscription_unschedule_fixed_fee_quantity_updates_params.SubscriptionUnscheduleFixedFeeQuantityUpdatesParams, @@ -4065,7 +4087,9 @@ async def unschedule_pending_plan_changes( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/unschedule_pending_plan_changes", + path_template( + "/subscriptions/{subscription_id}/unschedule_pending_plan_changes", subscription_id=subscription_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -4137,7 +4161,9 @@ async def update_fixed_fee_quantity( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/update_fixed_fee_quantity", + path_template( + "/subscriptions/{subscription_id}/update_fixed_fee_quantity", subscription_id=subscription_id + ), body=await async_maybe_transform( { "price_id": price_id, @@ -4213,7 +4239,7 @@ async def update_trial( if not subscription_id: raise ValueError(f"Expected a non-empty value for `subscription_id` but received {subscription_id!r}") return await self._post( - f"/subscriptions/{subscription_id}/update_trial", + path_template("/subscriptions/{subscription_id}/update_trial", subscription_id=subscription_id), body=await async_maybe_transform( { "trial_end_date": trial_end_date, diff --git a/src/orb/types/__init__.py b/src/orb/types/__init__.py index fb4d0969..888766fa 100644 --- a/src/orb/types/__init__.py +++ b/src/orb/types/__init__.py @@ -150,6 +150,7 @@ from .alert_list_params import AlertListParams as AlertListParams from .price_list_params import PriceListParams as PriceListParams from .coupon_list_params import CouponListParams as CouponListParams +from .invoice_pay_params import InvoicePayParams as InvoicePayParams from .item_create_params import ItemCreateParams as ItemCreateParams from .item_update_params import ItemUpdateParams as ItemUpdateParams from .metric_list_params import MetricListParams as MetricListParams diff --git a/src/orb/types/alert.py b/src/orb/types/alert.py index 433daf36..9381a4a3 100644 --- a/src/orb/types/alert.py +++ b/src/orb/types/alert.py @@ -9,7 +9,7 @@ from .shared.customer_minified import CustomerMinified from .shared.subscription_minified import SubscriptionMinified -__all__ = ["Alert", "Metric", "Plan", "BalanceAlertStatus", "LicenseType"] +__all__ = ["Alert", "Metric", "Plan", "BalanceAlertStatus", "LicenseType", "PriceFilter", "ThresholdOverride"] class Metric(BaseModel): @@ -51,6 +51,37 @@ class LicenseType(BaseModel): id: str +class PriceFilter(BaseModel): + field: Literal["price_id", "item_id", "price_type", "currency", "pricing_unit_id"] + """The property of the price to filter on.""" + + operator: Literal["includes", "excludes"] + """Should prices that match the filter be included or excluded.""" + + values: List[str] + """The IDs or values that match this filter.""" + + +class ThresholdOverride(BaseModel): + """A per-group threshold override on a grouped cost alert. + + An empty `thresholds` list means the group is silenced (never fires). + A non-empty list fully replaces the default thresholds for that group. + """ + + group_values: List[str] + """The values of the grouping keys that identify this group. + + The list length matches the alert's grouping_keys. + """ + + thresholds: List[Threshold] + """The thresholds applied to this group. + + An empty list means the group is silenced. + """ + + class Alert(BaseModel): """ [Alerts within Orb](/product-catalog/configuring-alerts) monitor spending, @@ -105,5 +136,22 @@ class Alert(BaseModel): This field is only present for credit balance alerts. """ + grouping_keys: Optional[List[str]] = None + """The property keys to group cost alerts by. + + Only present for cost alerts with grouping enabled. + """ + license_type: Optional[LicenseType] = None """Minified license type for alert serialization.""" + + price_filters: Optional[List[PriceFilter]] = None + """Filters scoping which prices are included in grouped cost alert evaluation.""" + + threshold_overrides: Optional[List[ThresholdOverride]] = None + """Per-group threshold overrides. + + Each override maps a specific combination of grouping_keys values to a + replacement threshold list. Only present for grouped cost alerts that have at + least one override. + """ diff --git a/src/orb/types/alert_create_for_subscription_params.py b/src/orb/types/alert_create_for_subscription_params.py index 9b8f2208..5777336d 100644 --- a/src/orb/types/alert_create_for_subscription_params.py +++ b/src/orb/types/alert_create_for_subscription_params.py @@ -5,9 +5,10 @@ from typing import Iterable, Optional from typing_extensions import Literal, Required, TypedDict +from .._types import SequenceNotStr from .threshold_param import ThresholdParam -__all__ = ["AlertCreateForSubscriptionParams"] +__all__ = ["AlertCreateForSubscriptionParams", "PriceFilter", "ThresholdOverride"] class AlertCreateForSubscriptionParams(TypedDict, total=False): @@ -17,5 +18,66 @@ class AlertCreateForSubscriptionParams(TypedDict, total=False): type: Required[Literal["usage_exceeded", "cost_exceeded"]] """The type of alert to create. This must be a valid alert type.""" + grouping_keys: Optional[SequenceNotStr[str]] + """The property keys to group cost alerts by. + + Only applicable for cost_exceeded alerts. + """ + metric_id: Optional[str] """The metric to track usage for.""" + + price_filters: Optional[Iterable[PriceFilter]] + """Filters to scope which prices are included in grouped cost alert evaluation. + + Supports filtering by price_id, item_id, or price_type with includes/excludes + operators. Only applicable when grouping_keys is set. + """ + + pricing_unit_id: Optional[str] + """The pricing unit to use for grouped cost alerts. + + Required when grouping_keys is set. + """ + + threshold_overrides: Optional[Iterable[ThresholdOverride]] + """Per-group threshold overrides. + + Each override maps a specific combination of grouping_keys values to a list of + thresholds that fully replaces the default thresholds for that group. An empty + thresholds list silences the group. Groups without an override use the default + thresholds. Only applicable when grouping_keys is set. + """ + + +class PriceFilter(TypedDict, total=False): + field: Required[Literal["price_id", "item_id", "price_type", "currency", "pricing_unit_id"]] + """The property of the price to filter on.""" + + operator: Required[Literal["includes", "excludes"]] + """Should prices that match the filter be included or excluded.""" + + values: Required[SequenceNotStr[str]] + """The IDs or values that match this filter.""" + + +class ThresholdOverride(TypedDict, total=False): + """Per-group threshold override on a grouped cost alert. + + - An empty `thresholds` list silences alerts for this group (never fires). + - A non-empty list fully replaces the default thresholds for this group. + """ + + group_values: Required[SequenceNotStr[str]] + """The values of the grouping keys that identify this group. + + The list length must match the alert's grouping_keys, and values appear in the + same order as grouping_keys. + """ + + thresholds: Required[Iterable[ThresholdParam]] + """The thresholds to apply to this group. + + An empty list silences alerts for this group. A non-empty list fully replaces + the default thresholds for this group. + """ diff --git a/src/orb/types/beta/external_plan_id_create_plan_version_params.py b/src/orb/types/beta/external_plan_id_create_plan_version_params.py index 6b31bed1..025db34b 100644 --- a/src/orb/types/beta/external_plan_id_create_plan_version_params.py +++ b/src/orb/types/beta/external_plan_id_create_plan_version_params.py @@ -5,8 +5,10 @@ from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from ..._types import SequenceNotStr from ..shared_params.new_maximum import NewMaximum from ..shared_params.new_minimum import NewMinimum +from ..shared_params.unit_config import UnitConfig from ..shared_params.new_usage_discount import NewUsageDiscount from ..shared_params.new_amount_discount import NewAmountDiscount from ..shared_params.new_plan_bulk_price import NewPlanBulkPrice @@ -48,6 +50,9 @@ "AddAdjustment", "AddAdjustmentAdjustment", "AddPrice", + "AddPriceLicenseAllocationPrice", + "AddPriceLicenseAllocationPriceLicenseAllocation", + "AddPriceLicenseAllocationPriceConversionRateConfig", "AddPricePrice", "AddPricePriceNewPlanBulkWithFiltersPrice", "AddPricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfig", @@ -64,6 +69,10 @@ "AddPricePriceNewPlanCumulativeGroupedAllocationPrice", "AddPricePriceNewPlanCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "AddPricePriceNewPlanCumulativeGroupedAllocationPriceConversionRateConfig", + "AddPricePriceNewPlanDailyCreditAllowancePrice", + "AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "AddPricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig", "AddPricePriceNewPlanPercentCompositePrice", "AddPricePriceNewPlanPercentCompositePricePercentConfig", "AddPricePriceNewPlanPercentCompositePriceConversionRateConfig", @@ -75,6 +84,9 @@ "ReplaceAdjustment", "ReplaceAdjustmentAdjustment", "ReplacePrice", + "ReplacePriceLicenseAllocationPrice", + "ReplacePriceLicenseAllocationPriceLicenseAllocation", + "ReplacePriceLicenseAllocationPriceConversionRateConfig", "ReplacePricePrice", "ReplacePricePriceNewPlanBulkWithFiltersPrice", "ReplacePricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfig", @@ -91,6 +103,10 @@ "ReplacePricePriceNewPlanCumulativeGroupedAllocationPrice", "ReplacePricePriceNewPlanCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "ReplacePricePriceNewPlanCumulativeGroupedAllocationPriceConversionRateConfig", + "ReplacePricePriceNewPlanDailyCreditAllowancePrice", + "ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "ReplacePricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig", "ReplacePricePriceNewPlanPercentCompositePrice", "ReplacePricePriceNewPlanPercentCompositePricePercentConfig", "ReplacePricePriceNewPlanPercentCompositePriceConversionRateConfig", @@ -139,6 +155,115 @@ class AddAdjustment(TypedDict, total=False): """The phase to add this adjustment to.""" +class AddPriceLicenseAllocationPriceLicenseAllocation(TypedDict, total=False): + amount: Required[str] + """The amount of credits granted per active license per cadence.""" + + currency: Required[str] + """The currency of the license allocation.""" + + write_off_overage: Optional[bool] + """When True, overage beyond the allocation is written off.""" + + +AddPriceLicenseAllocationPriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class AddPriceLicenseAllocationPrice(TypedDict, total=False): + """The license allocation price to add to the plan.""" + + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + license_allocations: Required[Iterable[AddPriceLicenseAllocationPriceLicenseAllocation]] + """License allocations to associate with this price. + + Each entry defines a per-license credit pool granted each cadence. Requires + license_type_id or license_type_configuration to be set. + """ + + model_type: Required[Literal["unit"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + unit_config: Required[UnitConfig] + """Configuration for unit pricing""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[AddPriceLicenseAllocationPriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class AddPricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfigFilter(TypedDict, total=False): """Configuration for a single property filter""" @@ -581,6 +706,137 @@ class AddPricePriceNewPlanCumulativeGroupedAllocationPrice(TypedDict, total=Fals """ +class AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue(TypedDict, total=False): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +AddPricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class AddPricePriceNewPlanDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + daily_credit_allowance_config: Required[AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[AddPricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class AddPricePriceNewPlanPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -815,6 +1071,7 @@ class AddPricePriceNewPlanEventOutputPrice(TypedDict, total=False): NewPlanScalableMatrixWithTieredPricingPrice, NewPlanCumulativeGroupedBulkPrice, AddPricePriceNewPlanCumulativeGroupedAllocationPrice, + AddPricePriceNewPlanDailyCreditAllowancePrice, NewPlanMinimumCompositePrice, AddPricePriceNewPlanPercentCompositePrice, AddPricePriceNewPlanEventOutputPrice, @@ -825,6 +1082,9 @@ class AddPrice(TypedDict, total=False): allocation_price: Optional[NewAllocationPrice] """The allocation price to add to the plan.""" + license_allocation_price: Optional[AddPriceLicenseAllocationPrice] + """The license allocation price to add to the plan.""" + plan_phase_order: Optional[int] """The phase to add this price to.""" @@ -864,6 +1124,115 @@ class ReplaceAdjustment(TypedDict, total=False): """The phase to replace this adjustment from.""" +class ReplacePriceLicenseAllocationPriceLicenseAllocation(TypedDict, total=False): + amount: Required[str] + """The amount of credits granted per active license per cadence.""" + + currency: Required[str] + """The currency of the license allocation.""" + + write_off_overage: Optional[bool] + """When True, overage beyond the allocation is written off.""" + + +ReplacePriceLicenseAllocationPriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class ReplacePriceLicenseAllocationPrice(TypedDict, total=False): + """The license allocation price to add to the plan.""" + + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + license_allocations: Required[Iterable[ReplacePriceLicenseAllocationPriceLicenseAllocation]] + """License allocations to associate with this price. + + Each entry defines a per-license credit pool granted each cadence. Requires + license_type_id or license_type_configuration to be set. + """ + + model_type: Required[Literal["unit"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + unit_config: Required[UnitConfig] + """Configuration for unit pricing""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[ReplacePriceLicenseAllocationPriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class ReplacePricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfigFilter(TypedDict, total=False): """Configuration for a single property filter""" @@ -1306,6 +1675,137 @@ class ReplacePricePriceNewPlanCumulativeGroupedAllocationPrice(TypedDict, total= """ +class ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue(TypedDict, total=False): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +ReplacePricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class ReplacePricePriceNewPlanDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + daily_credit_allowance_config: Required[ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[ReplacePricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class ReplacePricePriceNewPlanPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -1540,6 +2040,7 @@ class ReplacePricePriceNewPlanEventOutputPrice(TypedDict, total=False): NewPlanScalableMatrixWithTieredPricingPrice, NewPlanCumulativeGroupedBulkPrice, ReplacePricePriceNewPlanCumulativeGroupedAllocationPrice, + ReplacePricePriceNewPlanDailyCreditAllowancePrice, NewPlanMinimumCompositePrice, ReplacePricePriceNewPlanPercentCompositePrice, ReplacePricePriceNewPlanEventOutputPrice, @@ -1553,6 +2054,9 @@ class ReplacePrice(TypedDict, total=False): allocation_price: Optional[NewAllocationPrice] """The allocation price to add to the plan.""" + license_allocation_price: Optional[ReplacePriceLicenseAllocationPrice] + """The license allocation price to add to the plan.""" + plan_phase_order: Optional[int] """The phase to replace this price from.""" diff --git a/src/orb/types/beta_create_plan_version_params.py b/src/orb/types/beta_create_plan_version_params.py index 813cbba0..a45fe163 100644 --- a/src/orb/types/beta_create_plan_version_params.py +++ b/src/orb/types/beta_create_plan_version_params.py @@ -5,8 +5,10 @@ from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from .._types import SequenceNotStr from .shared_params.new_maximum import NewMaximum from .shared_params.new_minimum import NewMinimum +from .shared_params.unit_config import UnitConfig from .shared_params.new_usage_discount import NewUsageDiscount from .shared_params.new_amount_discount import NewAmountDiscount from .shared_params.new_plan_bulk_price import NewPlanBulkPrice @@ -48,6 +50,9 @@ "AddAdjustment", "AddAdjustmentAdjustment", "AddPrice", + "AddPriceLicenseAllocationPrice", + "AddPriceLicenseAllocationPriceLicenseAllocation", + "AddPriceLicenseAllocationPriceConversionRateConfig", "AddPricePrice", "AddPricePriceNewPlanBulkWithFiltersPrice", "AddPricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfig", @@ -64,6 +69,10 @@ "AddPricePriceNewPlanCumulativeGroupedAllocationPrice", "AddPricePriceNewPlanCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "AddPricePriceNewPlanCumulativeGroupedAllocationPriceConversionRateConfig", + "AddPricePriceNewPlanDailyCreditAllowancePrice", + "AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "AddPricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig", "AddPricePriceNewPlanPercentCompositePrice", "AddPricePriceNewPlanPercentCompositePricePercentConfig", "AddPricePriceNewPlanPercentCompositePriceConversionRateConfig", @@ -75,6 +84,9 @@ "ReplaceAdjustment", "ReplaceAdjustmentAdjustment", "ReplacePrice", + "ReplacePriceLicenseAllocationPrice", + "ReplacePriceLicenseAllocationPriceLicenseAllocation", + "ReplacePriceLicenseAllocationPriceConversionRateConfig", "ReplacePricePrice", "ReplacePricePriceNewPlanBulkWithFiltersPrice", "ReplacePricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfig", @@ -91,6 +103,10 @@ "ReplacePricePriceNewPlanCumulativeGroupedAllocationPrice", "ReplacePricePriceNewPlanCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "ReplacePricePriceNewPlanCumulativeGroupedAllocationPriceConversionRateConfig", + "ReplacePricePriceNewPlanDailyCreditAllowancePrice", + "ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "ReplacePricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig", "ReplacePricePriceNewPlanPercentCompositePrice", "ReplacePricePriceNewPlanPercentCompositePricePercentConfig", "ReplacePricePriceNewPlanPercentCompositePriceConversionRateConfig", @@ -139,6 +155,115 @@ class AddAdjustment(TypedDict, total=False): """The phase to add this adjustment to.""" +class AddPriceLicenseAllocationPriceLicenseAllocation(TypedDict, total=False): + amount: Required[str] + """The amount of credits granted per active license per cadence.""" + + currency: Required[str] + """The currency of the license allocation.""" + + write_off_overage: Optional[bool] + """When True, overage beyond the allocation is written off.""" + + +AddPriceLicenseAllocationPriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class AddPriceLicenseAllocationPrice(TypedDict, total=False): + """The license allocation price to add to the plan.""" + + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + license_allocations: Required[Iterable[AddPriceLicenseAllocationPriceLicenseAllocation]] + """License allocations to associate with this price. + + Each entry defines a per-license credit pool granted each cadence. Requires + license_type_id or license_type_configuration to be set. + """ + + model_type: Required[Literal["unit"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + unit_config: Required[UnitConfig] + """Configuration for unit pricing""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[AddPriceLicenseAllocationPriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class AddPricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfigFilter(TypedDict, total=False): """Configuration for a single property filter""" @@ -581,6 +706,137 @@ class AddPricePriceNewPlanCumulativeGroupedAllocationPrice(TypedDict, total=Fals """ +class AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue(TypedDict, total=False): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +AddPricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class AddPricePriceNewPlanDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + daily_credit_allowance_config: Required[AddPricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[AddPricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class AddPricePriceNewPlanPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -815,6 +1071,7 @@ class AddPricePriceNewPlanEventOutputPrice(TypedDict, total=False): NewPlanScalableMatrixWithTieredPricingPrice, NewPlanCumulativeGroupedBulkPrice, AddPricePriceNewPlanCumulativeGroupedAllocationPrice, + AddPricePriceNewPlanDailyCreditAllowancePrice, NewPlanMinimumCompositePrice, AddPricePriceNewPlanPercentCompositePrice, AddPricePriceNewPlanEventOutputPrice, @@ -825,6 +1082,9 @@ class AddPrice(TypedDict, total=False): allocation_price: Optional[NewAllocationPrice] """The allocation price to add to the plan.""" + license_allocation_price: Optional[AddPriceLicenseAllocationPrice] + """The license allocation price to add to the plan.""" + plan_phase_order: Optional[int] """The phase to add this price to.""" @@ -864,6 +1124,115 @@ class ReplaceAdjustment(TypedDict, total=False): """The phase to replace this adjustment from.""" +class ReplacePriceLicenseAllocationPriceLicenseAllocation(TypedDict, total=False): + amount: Required[str] + """The amount of credits granted per active license per cadence.""" + + currency: Required[str] + """The currency of the license allocation.""" + + write_off_overage: Optional[bool] + """When True, overage beyond the allocation is written off.""" + + +ReplacePriceLicenseAllocationPriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class ReplacePriceLicenseAllocationPrice(TypedDict, total=False): + """The license allocation price to add to the plan.""" + + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + license_allocations: Required[Iterable[ReplacePriceLicenseAllocationPriceLicenseAllocation]] + """License allocations to associate with this price. + + Each entry defines a per-license credit pool granted each cadence. Requires + license_type_id or license_type_configuration to be set. + """ + + model_type: Required[Literal["unit"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + unit_config: Required[UnitConfig] + """Configuration for unit pricing""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[ReplacePriceLicenseAllocationPriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class ReplacePricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfigFilter(TypedDict, total=False): """Configuration for a single property filter""" @@ -1306,6 +1675,137 @@ class ReplacePricePriceNewPlanCumulativeGroupedAllocationPrice(TypedDict, total= """ +class ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue(TypedDict, total=False): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +ReplacePricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class ReplacePricePriceNewPlanDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + daily_credit_allowance_config: Required[ReplacePricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[ReplacePricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class ReplacePricePriceNewPlanPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -1540,6 +2040,7 @@ class ReplacePricePriceNewPlanEventOutputPrice(TypedDict, total=False): NewPlanScalableMatrixWithTieredPricingPrice, NewPlanCumulativeGroupedBulkPrice, ReplacePricePriceNewPlanCumulativeGroupedAllocationPrice, + ReplacePricePriceNewPlanDailyCreditAllowancePrice, NewPlanMinimumCompositePrice, ReplacePricePriceNewPlanPercentCompositePrice, ReplacePricePriceNewPlanEventOutputPrice, @@ -1553,6 +2054,9 @@ class ReplacePrice(TypedDict, total=False): allocation_price: Optional[NewAllocationPrice] """The allocation price to add to the plan.""" + license_allocation_price: Optional[ReplacePriceLicenseAllocationPrice] + """The license allocation price to add to the plan.""" + plan_phase_order: Optional[int] """The phase to replace this price from.""" diff --git a/src/orb/types/billable_metric.py b/src/orb/types/billable_metric.py index e618c404..b3b6a158 100644 --- a/src/orb/types/billable_metric.py +++ b/src/orb/types/billable_metric.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import Dict, Optional +from typing import Dict, List, Optional from typing_extensions import Literal from .item import Item @@ -38,3 +38,5 @@ class BillableMetric(BaseModel): name: str status: Literal["active", "draft", "archived"] + + parameter_definitions: Optional[List[Dict[str, object]]] = None diff --git a/src/orb/types/customer.py b/src/orb/types/customer.py index 4eccf4a8..c89b5f54 100644 --- a/src/orb/types/customer.py +++ b/src/orb/types/customer.py @@ -31,7 +31,7 @@ class Hierarchy(BaseModel): class AccountingSyncConfigurationAccountingProvider(BaseModel): external_provider_id: Optional[str] = None - provider_type: Literal["quickbooks", "netsuite"] + provider_type: Literal["quickbooks", "netsuite", "netsuite_ampersand"] class AccountingSyncConfiguration(BaseModel): @@ -44,6 +44,13 @@ class PaymentConfigurationPaymentProvider(BaseModel): provider_type: Literal["stripe"] """The payment provider to configure.""" + default_shared_payment_token: Optional[str] = None + """ + The ID of a shared payment token granted by an agent to use as the default + payment instrument for this customer. When set, auto-collection will use this + token instead of the customer's default payment method. + """ + excluded_payment_method_types: Optional[List[str]] = None """List of Stripe payment method types to exclude for this customer. @@ -139,7 +146,9 @@ class Customer(BaseModel): name: str """The full name of the customer""" - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] = None + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] = None """This is used for creating charges or invoices in an external system via Orb. When not in test mode, the connection must first be configured in the Orb diff --git a/src/orb/types/customer_create_params.py b/src/orb/types/customer_create_params.py index 7d8e6155..4e3aafe2 100644 --- a/src/orb/types/customer_create_params.py +++ b/src/orb/types/customer_create_params.py @@ -95,7 +95,9 @@ class CustomerCreateParams(TypedDict, total=False): a supported payment provider such as Stripe. """ - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] """This is used for creating charges or invoices in an external system via Orb. When not in test mode, the connection must first be configured in the Orb @@ -275,6 +277,13 @@ class PaymentConfigurationPaymentProvider(TypedDict, total=False): provider_type: Required[Literal["stripe"]] """The payment provider to configure.""" + default_shared_payment_token: Optional[str] + """ + The ID of a shared payment token granted by an agent to use as the default + payment instrument for this customer. When set, auto-collection will use this + token instead of the customer's default payment method. + """ + excluded_payment_method_types: SequenceNotStr[str] """List of Stripe payment method types to exclude for this customer. diff --git a/src/orb/types/customer_update_by_external_id_params.py b/src/orb/types/customer_update_by_external_id_params.py index 3d7a832e..92327800 100644 --- a/src/orb/types/customer_update_by_external_id_params.py +++ b/src/orb/types/customer_update_by_external_id_params.py @@ -57,7 +57,9 @@ class CustomerUpdateByExternalIDParams(TypedDict, total=False): currency: Optional[str] """An ISO 4217 currency string used for the customer's invoices and balance. - If not set at creation time, will be set at subscription creation time. + This can only be set if the customer does not already have a currency + configured. If not set at creation or update time, it will be set at + subscription creation time. """ email: Optional[str] @@ -93,7 +95,9 @@ class CustomerUpdateByExternalIDParams(TypedDict, total=False): a supported payment provider such as Stripe. """ - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] """This is used for creating charges or invoices in an external system via Orb. When not in test mode: @@ -270,6 +274,13 @@ class PaymentConfigurationPaymentProvider(TypedDict, total=False): provider_type: Required[Literal["stripe"]] """The payment provider to configure.""" + default_shared_payment_token: Optional[str] + """ + The ID of a shared payment token granted by an agent to use as the default + payment instrument for this customer. When set, auto-collection will use this + token instead of the customer's default payment method. + """ + excluded_payment_method_types: SequenceNotStr[str] """List of Stripe payment method types to exclude for this customer. diff --git a/src/orb/types/customer_update_params.py b/src/orb/types/customer_update_params.py index 13673f40..050d126d 100644 --- a/src/orb/types/customer_update_params.py +++ b/src/orb/types/customer_update_params.py @@ -57,7 +57,9 @@ class CustomerUpdateParams(TypedDict, total=False): currency: Optional[str] """An ISO 4217 currency string used for the customer's invoices and balance. - If not set at creation time, will be set at subscription creation time. + This can only be set if the customer does not already have a currency + configured. If not set at creation or update time, it will be set at + subscription creation time. """ email: Optional[str] @@ -93,7 +95,9 @@ class CustomerUpdateParams(TypedDict, total=False): a supported payment provider such as Stripe. """ - payment_provider: Optional[Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite"]] + payment_provider: Optional[ + Literal["quickbooks", "bill.com", "stripe_charge", "stripe_invoice", "netsuite", "netsuite_ampersand"] + ] """This is used for creating charges or invoices in an external system via Orb. When not in test mode: @@ -270,6 +274,13 @@ class PaymentConfigurationPaymentProvider(TypedDict, total=False): provider_type: Required[Literal["stripe"]] """The payment provider to configure.""" + default_shared_payment_token: Optional[str] + """ + The ID of a shared payment token granted by an agent to use as the default + payment instrument for this customer. When set, auto-collection will use this + token instead of the customer's default payment method. + """ + excluded_payment_method_types: SequenceNotStr[str] """List of Stripe payment method types to exclude for this customer. diff --git a/src/orb/types/customers/credits/decrement_ledger_entry.py b/src/orb/types/customers/credits/decrement_ledger_entry.py index 9373f061..3f0896c2 100644 --- a/src/orb/types/customers/credits/decrement_ledger_entry.py +++ b/src/orb/types/customers/credits/decrement_ledger_entry.py @@ -45,6 +45,10 @@ class DecrementLedgerEntry(BaseModel): starting_balance: float event_id: Optional[str] = None + """This field is deprecated and will always be null. + + Decrements are not associated with individual events. + """ invoice_id: Optional[str] = None diff --git a/src/orb/types/customers/credits/ledger_create_entry_by_external_id_params.py b/src/orb/types/customers/credits/ledger_create_entry_by_external_id_params.py index 598e681b..37c7f2b5 100644 --- a/src/orb/types/customers/credits/ledger_create_entry_by_external_id_params.py +++ b/src/orb/types/customers/credits/ledger_create_entry_by_external_id_params.py @@ -125,6 +125,9 @@ class AddIncrementCreditLedgerEntryRequestParamsInvoiceSettings(TypedDict, total If not provided, a default 'Credits' item will be used. """ + mark_as_paid: bool + """If true, the new credits purchase invoice will be marked as paid.""" + memo: Optional[str] """An optional memo to display on the invoice.""" @@ -134,8 +137,8 @@ class AddIncrementCreditLedgerEntryRequestParamsInvoiceSettings(TypedDict, total Due date is calculated based on the invoice or issuance date, depending on the account's configured due date calculation method. A value of '0' here represents that the invoice is due on issue, whereas a value of '30' represents that the - customer has 30 days to pay the invoice. Do not set this field if you want to - set a custom due date. + customer has 30 days to pay the invoice. You must set either `net_terms` or + `custom_due_date`, but not both. """ require_successful_payment: bool @@ -181,9 +184,9 @@ class AddExpirationChangeCreditLedgerEntryRequestParams(TypedDict, total=False): target_expiry_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] """ - A future date (specified in YYYY-MM-DD format) used for expiration change, - denoting when credits transferred (as part of a partial block expiration) should - expire. + A date (specified in YYYY-MM-DD format) used for expiration change, denoting + when credits transferred (as part of a partial block expiration) should expire. + This date must be on or after the effective date of the credit block. """ amount: Optional[float] diff --git a/src/orb/types/customers/credits/ledger_create_entry_params.py b/src/orb/types/customers/credits/ledger_create_entry_params.py index b0b47f75..f52c8ad2 100644 --- a/src/orb/types/customers/credits/ledger_create_entry_params.py +++ b/src/orb/types/customers/credits/ledger_create_entry_params.py @@ -125,6 +125,9 @@ class AddIncrementCreditLedgerEntryRequestParamsInvoiceSettings(TypedDict, total If not provided, a default 'Credits' item will be used. """ + mark_as_paid: bool + """If true, the new credits purchase invoice will be marked as paid.""" + memo: Optional[str] """An optional memo to display on the invoice.""" @@ -134,8 +137,8 @@ class AddIncrementCreditLedgerEntryRequestParamsInvoiceSettings(TypedDict, total Due date is calculated based on the invoice or issuance date, depending on the account's configured due date calculation method. A value of '0' here represents that the invoice is due on issue, whereas a value of '30' represents that the - customer has 30 days to pay the invoice. Do not set this field if you want to - set a custom due date. + customer has 30 days to pay the invoice. You must set either `net_terms` or + `custom_due_date`, but not both. """ require_successful_payment: bool @@ -181,9 +184,9 @@ class AddExpirationChangeCreditLedgerEntryRequestParams(TypedDict, total=False): target_expiry_date: Required[Annotated[Union[str, date], PropertyInfo(format="iso8601")]] """ - A future date (specified in YYYY-MM-DD format) used for expiration change, - denoting when credits transferred (as part of a partial block expiration) should - expire. + A date (specified in YYYY-MM-DD format) used for expiration change, denoting + when credits transferred (as part of a partial block expiration) should expire. + This date must be on or after the effective date of the credit block. """ amount: Optional[float] diff --git a/src/orb/types/invoice_create_params.py b/src/orb/types/invoice_create_params.py index 92ae0229..650d26c0 100644 --- a/src/orb/types/invoice_create_params.py +++ b/src/orb/types/invoice_create_params.py @@ -29,6 +29,13 @@ class InvoiceCreateParams(TypedDict, total=False): line_items: Required[Iterable[LineItem]] + auto_collection: Optional[bool] + """ + Determines whether this invoice will automatically attempt to charge a saved + payment method, if any. If not specified, the invoice inherits the customer's + auto_collection setting. + """ + customer_id: Optional[str] """The id of the `Customer` to create this invoice for. diff --git a/src/orb/types/invoice_pay_params.py b/src/orb/types/invoice_pay_params.py new file mode 100644 index 00000000..e7a883d8 --- /dev/null +++ b/src/orb/types/invoice_pay_params.py @@ -0,0 +1,12 @@ +# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +from __future__ import annotations + +from typing_extensions import Required, TypedDict + +__all__ = ["InvoicePayParams"] + + +class InvoicePayParams(TypedDict, total=False): + shared_payment_token_id: Required[str] + """The ID of a shared payment token granted by an agent to use for this payment.""" diff --git a/src/orb/types/invoice_update_params.py b/src/orb/types/invoice_update_params.py index b097d6f8..30367bce 100644 --- a/src/orb/types/invoice_update_params.py +++ b/src/orb/types/invoice_update_params.py @@ -12,6 +12,13 @@ class InvoiceUpdateParams(TypedDict, total=False): + auto_collection: Optional[bool] + """ + Determines whether this invoice will automatically attempt to charge a saved + payment method, if any. Can only be modified on draft invoices. If not + specified, the invoice's existing setting is unchanged. + """ + due_date: Annotated[Union[Union[str, date], Union[str, datetime], None], PropertyInfo(format="iso8601")] """An optional custom due date for the invoice. diff --git a/src/orb/types/item.py b/src/orb/types/item.py index c25cd993..6f4fcb32 100644 --- a/src/orb/types/item.py +++ b/src/orb/types/item.py @@ -15,7 +15,7 @@ class ExternalConnection(BaseModel): """ external_connection_name: Literal[ - "stripe", "quickbooks", "bill.com", "netsuite", "taxjar", "avalara", "anrok", "numeral" + "stripe", "quickbooks", "bill.com", "netsuite", "taxjar", "avalara", "anrok", "numeral", "stripe_tax" ] """The name of the external system this item is connected to.""" diff --git a/src/orb/types/item_update_params.py b/src/orb/types/item_update_params.py index d9504846..939b406c 100644 --- a/src/orb/types/item_update_params.py +++ b/src/orb/types/item_update_params.py @@ -27,7 +27,7 @@ class ExternalConnection(TypedDict, total=False): """ external_connection_name: Required[ - Literal["stripe", "quickbooks", "bill.com", "netsuite", "taxjar", "avalara", "anrok", "numeral"] + Literal["stripe", "quickbooks", "bill.com", "netsuite", "taxjar", "avalara", "anrok", "numeral", "stripe_tax"] ] """The name of the external system this item is connected to.""" diff --git a/src/orb/types/new_subscription_scalable_matrix_with_unit_pricing_price_param.py b/src/orb/types/new_subscription_scalable_matrix_with_unit_pricing_price_param.py index c0ea7c96..683bf6db 100644 --- a/src/orb/types/new_subscription_scalable_matrix_with_unit_pricing_price_param.py +++ b/src/orb/types/new_subscription_scalable_matrix_with_unit_pricing_price_param.py @@ -40,6 +40,9 @@ class ScalableMatrixWithUnitPricingConfig(TypedDict, total=False): unit_price: Required[str] """The final unit price to rate against the output of the matrix""" + grouping_key: Optional[str] + """The property used to group this price""" + prorate: Optional[bool] """If true, the unit price will be prorated to the billing period""" diff --git a/src/orb/types/plan_create_params.py b/src/orb/types/plan_create_params.py index ff20826c..feea923a 100644 --- a/src/orb/types/plan_create_params.py +++ b/src/orb/types/plan_create_params.py @@ -5,8 +5,10 @@ from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from .._types import SequenceNotStr from .shared_params.new_maximum import NewMaximum from .shared_params.new_minimum import NewMinimum +from .shared_params.unit_config import UnitConfig from .shared_params.new_usage_discount import NewUsageDiscount from .shared_params.new_amount_discount import NewAmountDiscount from .shared_params.new_plan_bulk_price import NewPlanBulkPrice @@ -46,6 +48,9 @@ __all__ = [ "PlanCreateParams", "Price", + "PriceLicenseAllocationPrice", + "PriceLicenseAllocationPriceLicenseAllocation", + "PriceLicenseAllocationPriceConversionRateConfig", "PricePrice", "PricePriceNewPlanBulkWithFiltersPrice", "PricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfig", @@ -62,6 +67,10 @@ "PricePriceNewPlanCumulativeGroupedAllocationPrice", "PricePriceNewPlanCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "PricePriceNewPlanCumulativeGroupedAllocationPriceConversionRateConfig", + "PricePriceNewPlanDailyCreditAllowancePrice", + "PricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "PricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "PricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig", "PricePriceNewPlanPercentCompositePrice", "PricePriceNewPlanPercentCompositePricePercentConfig", "PricePriceNewPlanPercentCompositePriceConversionRateConfig", @@ -100,6 +109,9 @@ class PlanCreateParams(TypedDict, total=False): Free-form text which is available on the invoice PDF and the Orb invoice portal. """ + description: Optional[str] + """An optional user-defined description of the plan.""" + external_plan_id: Optional[str] metadata: Optional[Dict[str, Optional[str]]] @@ -129,6 +141,113 @@ class PlanCreateParams(TypedDict, total=False): """ +class PriceLicenseAllocationPriceLicenseAllocation(TypedDict, total=False): + amount: Required[str] + """The amount of credits granted per active license per cadence.""" + + currency: Required[str] + """The currency of the license allocation.""" + + write_off_overage: Optional[bool] + """When True, overage beyond the allocation is written off.""" + + +PriceLicenseAllocationPriceConversionRateConfig: TypeAlias = Union[UnitConversionRateConfig, TieredConversionRateConfig] + + +class PriceLicenseAllocationPrice(TypedDict, total=False): + """The license allocation price to add to the plan.""" + + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + license_allocations: Required[Iterable[PriceLicenseAllocationPriceLicenseAllocation]] + """License allocations to associate with this price. + + Each entry defines a per-license credit pool granted each cadence. Requires + license_type_id or license_type_configuration to be set. + """ + + model_type: Required[Literal["unit"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + unit_config: Required[UnitConfig] + """Configuration for unit pricing""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[PriceLicenseAllocationPriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class PricePriceNewPlanBulkWithFiltersPriceBulkWithFiltersConfigFilter(TypedDict, total=False): """Configuration for a single property filter""" @@ -571,6 +690,135 @@ class PricePriceNewPlanCumulativeGroupedAllocationPrice(TypedDict, total=False): """ +class PricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue(TypedDict, total=False): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class PricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[Iterable[PricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue]] + """Per-dimension credit rates""" + + +PricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class PricePriceNewPlanDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + daily_credit_allowance_config: Required[PricePriceNewPlanDailyCreditAllowancePriceDailyCreditAllowanceConfig] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[PricePriceNewPlanDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class PricePriceNewPlanPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -805,6 +1053,7 @@ class PricePriceNewPlanEventOutputPrice(TypedDict, total=False): NewPlanScalableMatrixWithTieredPricingPrice, NewPlanCumulativeGroupedBulkPrice, PricePriceNewPlanCumulativeGroupedAllocationPrice, + PricePriceNewPlanDailyCreditAllowancePrice, NewPlanMinimumCompositePrice, PricePriceNewPlanPercentCompositePrice, PricePriceNewPlanEventOutputPrice, @@ -815,6 +1064,9 @@ class Price(TypedDict, total=False): allocation_price: Optional[NewAllocationPrice] """The allocation price to add to the plan.""" + license_allocation_price: Optional[PriceLicenseAllocationPrice] + """The license allocation price to add to the plan.""" + plan_phase_order: Optional[int] """The phase to add this price to.""" diff --git a/src/orb/types/plan_update_params.py b/src/orb/types/plan_update_params.py index 5fc717f0..c535603d 100644 --- a/src/orb/types/plan_update_params.py +++ b/src/orb/types/plan_update_params.py @@ -9,6 +9,9 @@ class PlanUpdateParams(TypedDict, total=False): + description: Optional[str] + """An optional user-defined description of the plan.""" + external_plan_id: Optional[str] """ An optional user-defined ID for this plan resource, used throughout the system diff --git a/src/orb/types/plans/external_plan_id_update_params.py b/src/orb/types/plans/external_plan_id_update_params.py index d7017899..7f58c79b 100644 --- a/src/orb/types/plans/external_plan_id_update_params.py +++ b/src/orb/types/plans/external_plan_id_update_params.py @@ -9,6 +9,9 @@ class ExternalPlanIDUpdateParams(TypedDict, total=False): + description: Optional[str] + """An optional user-defined description of the plan.""" + external_plan_id: Optional[str] """ An optional user-defined ID for this plan resource, used throughout the system diff --git a/src/orb/types/price_create_params.py b/src/orb/types/price_create_params.py index e114775b..35962507 100644 --- a/src/orb/types/price_create_params.py +++ b/src/orb/types/price_create_params.py @@ -5,6 +5,7 @@ from typing import Dict, Union, Iterable, Optional from typing_extensions import Literal, Required, TypeAlias, TypedDict +from .._types import SequenceNotStr from .shared_params.bulk_config import BulkConfig from .shared_params.unit_config import UnitConfig from .shared_params.matrix_config import MatrixConfig @@ -114,6 +115,10 @@ "NewFloatingCumulativeGroupedAllocationPrice", "NewFloatingCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "NewFloatingCumulativeGroupedAllocationPriceConversionRateConfig", + "NewFloatingDailyCreditAllowancePrice", + "NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "NewFloatingDailyCreditAllowancePriceConversionRateConfig", "NewFloatingMinimumCompositePrice", "NewFloatingMinimumCompositePriceMinimumCompositeConfig", "NewFloatingMinimumCompositePriceConversionRateConfig", @@ -2516,6 +2521,9 @@ class NewFloatingScalableMatrixWithUnitPricingPriceScalableMatrixWithUnitPricing unit_price: Required[str] """The final unit price to rate against the output of the matrix""" + grouping_key: Optional[str] + """The property used to group this price""" + prorate: Optional[bool] """If true, the unit price will be prorated to the billing period""" @@ -2849,6 +2857,126 @@ class NewFloatingCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConf ] +class NewFloatingDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + currency: Required[str] + """An ISO 4217 currency string for which this price is billed in.""" + + daily_credit_allowance_config: Required[NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[NewFloatingDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + +class NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue(TypedDict, total=False): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[Iterable[NewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue]] + """Per-dimension credit rates""" + + +NewFloatingDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + class NewFloatingMinimumCompositePrice(TypedDict, total=False): cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] """The cadence to bill for this price on.""" @@ -3152,6 +3280,7 @@ class NewFloatingEventOutputPriceEventOutputConfig(TypedDict, total=False): NewFloatingScalableMatrixWithTieredPricingPrice, NewFloatingCumulativeGroupedBulkPrice, NewFloatingCumulativeGroupedAllocationPrice, + NewFloatingDailyCreditAllowancePrice, NewFloatingMinimumCompositePrice, NewFloatingPercentCompositePrice, NewFloatingEventOutputPrice, diff --git a/src/orb/types/price_evaluate_multiple_params.py b/src/orb/types/price_evaluate_multiple_params.py index 90cd75d4..8abdd727 100644 --- a/src/orb/types/price_evaluate_multiple_params.py +++ b/src/orb/types/price_evaluate_multiple_params.py @@ -58,6 +58,10 @@ "PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPrice", "PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPriceConversionRateConfig", + "PriceEvaluationPriceNewFloatingDailyCreditAllowancePrice", + "PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceConversionRateConfig", "PriceEvaluationPriceNewFloatingPercentCompositePrice", "PriceEvaluationPriceNewFloatingPercentCompositePricePercentConfig", "PriceEvaluationPriceNewFloatingPercentCompositePriceConversionRateConfig", @@ -398,6 +402,132 @@ class PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPrice(TypedDict, """ +class PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue( + TypedDict, total=False +): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class PriceEvaluationPriceNewFloatingDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + currency: Required[str] + """An ISO 4217 currency string for which this price is billed in.""" + + daily_credit_allowance_config: Required[ + PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig + ] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + class PriceEvaluationPriceNewFloatingPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -614,6 +744,7 @@ class PriceEvaluationPriceNewFloatingEventOutputPrice(TypedDict, total=False): NewFloatingScalableMatrixWithTieredPricingPrice, NewFloatingCumulativeGroupedBulkPrice, PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPrice, + PriceEvaluationPriceNewFloatingDailyCreditAllowancePrice, NewFloatingMinimumCompositePrice, PriceEvaluationPriceNewFloatingPercentCompositePrice, PriceEvaluationPriceNewFloatingEventOutputPrice, @@ -638,6 +769,13 @@ class PriceEvaluation(TypedDict, total=False): to group the underlying billable metric """ + metric_parameter_overrides: Optional[Dict[str, object]] + """Optional overrides for parameterized billable metric parameters. + + If the metric has parameter definitions and no overrides are provided, defaults + will be used. + """ + price: Optional[PriceEvaluationPrice] """New floating price request body params.""" diff --git a/src/orb/types/price_evaluate_params.py b/src/orb/types/price_evaluate_params.py index fa8d6b66..4e508f11 100644 --- a/src/orb/types/price_evaluate_params.py +++ b/src/orb/types/price_evaluate_params.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Union, Optional +from typing import Dict, Union, Optional from datetime import datetime from typing_extensions import Required, Annotated, TypedDict @@ -38,3 +38,10 @@ class PriceEvaluateParams(TypedDict, total=False): [computed properties](/extensibility/advanced-metrics#computed-properties)) used to group the underlying billable metric """ + + metric_parameter_overrides: Optional[Dict[str, object]] + """Optional overrides for parameterized billable metric parameters. + + If the metric has parameter definitions and no overrides are provided, defaults + will be used. + """ diff --git a/src/orb/types/price_evaluate_preview_events_params.py b/src/orb/types/price_evaluate_preview_events_params.py index ec487155..b1e52a0f 100644 --- a/src/orb/types/price_evaluate_preview_events_params.py +++ b/src/orb/types/price_evaluate_preview_events_params.py @@ -59,6 +59,10 @@ "PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPrice", "PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPriceConversionRateConfig", + "PriceEvaluationPriceNewFloatingDailyCreditAllowancePrice", + "PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceConversionRateConfig", "PriceEvaluationPriceNewFloatingPercentCompositePrice", "PriceEvaluationPriceNewFloatingPercentCompositePricePercentConfig", "PriceEvaluationPriceNewFloatingPercentCompositePriceConversionRateConfig", @@ -430,6 +434,132 @@ class PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPrice(TypedDict, """ +class PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue( + TypedDict, total=False +): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class PriceEvaluationPriceNewFloatingDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + currency: Required[str] + """An ISO 4217 currency string for which this price is billed in.""" + + daily_credit_allowance_config: Required[ + PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig + ] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[PriceEvaluationPriceNewFloatingDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + class PriceEvaluationPriceNewFloatingPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -646,6 +776,7 @@ class PriceEvaluationPriceNewFloatingEventOutputPrice(TypedDict, total=False): NewFloatingScalableMatrixWithTieredPricingPrice, NewFloatingCumulativeGroupedBulkPrice, PriceEvaluationPriceNewFloatingCumulativeGroupedAllocationPrice, + PriceEvaluationPriceNewFloatingDailyCreditAllowancePrice, NewFloatingMinimumCompositePrice, PriceEvaluationPriceNewFloatingPercentCompositePrice, PriceEvaluationPriceNewFloatingEventOutputPrice, @@ -670,6 +801,13 @@ class PriceEvaluation(TypedDict, total=False): to group the underlying billable metric """ + metric_parameter_overrides: Optional[Dict[str, object]] + """Optional overrides for parameterized billable metric parameters. + + If the metric has parameter definitions and no overrides are provided, defaults + will be used. + """ + price: Optional[PriceEvaluationPrice] """New floating price request body params.""" diff --git a/src/orb/types/shared/new_allocation_price.py b/src/orb/types/shared/new_allocation_price.py index afaf82c6..7f6c1355 100644 --- a/src/orb/types/shared/new_allocation_price.py +++ b/src/orb/types/shared/new_allocation_price.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Dict, List, Optional from typing_extensions import Literal from ..._models import BaseModel @@ -57,6 +57,13 @@ class NewAllocationPrice(BaseModel): license_type_id: Optional[str] = None """The license type ID to associate the price with license allocation.""" + metadata: Optional[Dict[str, Optional[str]]] = None + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + per_unit_cost_basis: Optional[str] = None """The (per-unit) cost basis of each created block. diff --git a/src/orb/types/shared/new_floating_scalable_matrix_with_unit_pricing_price.py b/src/orb/types/shared/new_floating_scalable_matrix_with_unit_pricing_price.py index afeb9a28..e1cc30a2 100644 --- a/src/orb/types/shared/new_floating_scalable_matrix_with_unit_pricing_price.py +++ b/src/orb/types/shared/new_floating_scalable_matrix_with_unit_pricing_price.py @@ -42,6 +42,9 @@ class ScalableMatrixWithUnitPricingConfig(BaseModel): unit_price: str """The final unit price to rate against the output of the matrix""" + grouping_key: Optional[str] = None + """The property used to group this price""" + prorate: Optional[bool] = None """If true, the unit price will be prorated to the billing period""" diff --git a/src/orb/types/shared/new_plan_scalable_matrix_with_unit_pricing_price.py b/src/orb/types/shared/new_plan_scalable_matrix_with_unit_pricing_price.py index 9927a55e..5ed45a08 100644 --- a/src/orb/types/shared/new_plan_scalable_matrix_with_unit_pricing_price.py +++ b/src/orb/types/shared/new_plan_scalable_matrix_with_unit_pricing_price.py @@ -42,6 +42,9 @@ class ScalableMatrixWithUnitPricingConfig(BaseModel): unit_price: str """The final unit price to rate against the output of the matrix""" + grouping_key: Optional[str] = None + """The property used to group this price""" + prorate: Optional[bool] = None """If true, the unit price will be prorated to the billing period""" diff --git a/src/orb/types/shared/price.py b/src/orb/types/shared/price.py index 68a9a5a8..07a0e001 100644 --- a/src/orb/types/shared/price.py +++ b/src/orb/types/shared/price.py @@ -179,6 +179,12 @@ "CumulativeGroupedAllocationPriceConversionRateConfig", "CumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "CumulativeGroupedAllocationPriceLicenseType", + "DailyCreditAllowancePrice", + "DailyCreditAllowancePriceCompositePriceFilter", + "DailyCreditAllowancePriceConversionRateConfig", + "DailyCreditAllowancePriceDailyCreditAllowanceConfig", + "DailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "DailyCreditAllowancePriceLicenseType", "MinimumCompositePrice", "MinimumCompositePriceCompositePriceFilter", "MinimumCompositePriceConversionRateConfig", @@ -261,6 +267,8 @@ class UnitPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -377,6 +385,8 @@ class TieredPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -496,6 +506,8 @@ class BulkPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -642,6 +654,8 @@ class BulkWithFiltersPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -755,6 +769,8 @@ class PackagePrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -871,6 +887,8 @@ class MatrixPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -1009,6 +1027,8 @@ class ThresholdTotalAmountPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -1148,6 +1168,8 @@ class TieredPackagePrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -1291,6 +1313,8 @@ class TieredWithMinimumPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -1432,6 +1456,8 @@ class GroupedTieredPrice(BaseModel): grouped_tiered_config: GroupedTieredPriceGroupedTieredConfig """Configuration for grouped_tiered pricing""" + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -1567,6 +1593,8 @@ class TieredPackageWithMinimumPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -1693,6 +1721,8 @@ class PackageWithAllocationPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -1819,6 +1849,8 @@ class UnitWithPercentPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -1935,6 +1967,8 @@ class MatrixWithAllocationPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -2071,6 +2105,8 @@ class TieredWithProrationPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -2194,6 +2230,8 @@ class UnitWithProrationPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -2326,6 +2364,8 @@ class GroupedAllocationPrice(BaseModel): grouped_allocation_config: GroupedAllocationPriceGroupedAllocationConfig """Configuration for grouped_allocation pricing""" + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -2459,6 +2499,8 @@ class BulkWithProrationPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -2588,6 +2630,8 @@ class GroupedWithProratedMinimumPrice(BaseModel): grouped_with_prorated_minimum_config: GroupedWithProratedMinimumPriceGroupedWithProratedMinimumConfig """Configuration for grouped_with_prorated_minimum pricing""" + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -2749,6 +2793,8 @@ class GroupedWithMeteredMinimumPrice(BaseModel): grouped_with_metered_minimum_config: GroupedWithMeteredMinimumPriceGroupedWithMeteredMinimumConfig """Configuration for grouped_with_metered_minimum pricing""" + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -2881,6 +2927,8 @@ class GroupedWithMinMaxThresholdsPrice(BaseModel): grouped_with_min_max_thresholds_config: GroupedWithMinMaxThresholdsPriceGroupedWithMinMaxThresholdsConfig """Configuration for grouped_with_min_max_thresholds pricing""" + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -3017,6 +3065,8 @@ class MatrixWithDisplayNamePrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -3160,6 +3210,8 @@ class GroupedTieredPackagePrice(BaseModel): grouped_tiered_package_config: GroupedTieredPackagePriceGroupedTieredPackageConfig """Configuration for grouped_tiered_package pricing""" + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -3296,6 +3348,8 @@ class MaxGroupTieredPackagePrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -3388,6 +3442,9 @@ class ScalableMatrixWithUnitPricingPriceScalableMatrixWithUnitPricingConfig(Base unit_price: str """The final unit price to rate against the output of the matrix""" + grouping_key: Optional[str] = None + """The property used to group this price""" + prorate: Optional[bool] = None """If true, the unit price will be prorated to the billing period""" @@ -3443,6 +3500,8 @@ class ScalableMatrixWithUnitPricingPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -3594,6 +3653,8 @@ class ScalableMatrixWithTieredPricingPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -3737,6 +3798,8 @@ class CumulativeGroupedBulkPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -3869,6 +3932,8 @@ class CumulativeGroupedAllocationPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -3918,6 +3983,164 @@ class CumulativeGroupedAllocationPrice(BaseModel): """ +class DailyCreditAllowancePriceCompositePriceFilter(BaseModel): + field: Literal["price_id", "item_id", "price_type", "currency", "pricing_unit_id"] + """The property of the price to filter on.""" + + operator: Literal["includes", "excludes"] + """Should prices that match the filter be included or excluded.""" + + values: List[str] + """The IDs or values that match this filter.""" + + +DailyCreditAllowancePriceConversionRateConfig: TypeAlias = Annotated[ + Union[UnitConversionRateConfig, TieredConversionRateConfig], PropertyInfo(discriminator="conversion_rate_type") +] + + +class DailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue(BaseModel): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: List[Optional[str]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: str + """Credits charged per unit of usage matching the specified dimension_values""" + + +class DailyCreditAllowancePriceDailyCreditAllowanceConfig(BaseModel): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: str + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: str + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: List[Optional[str]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: str + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: List[DailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + """Per-dimension credit rates""" + + +class DailyCreditAllowancePriceLicenseType(BaseModel): + """ + The LicenseType resource represents a type of license that can be assigned to users. + License types are used during billing by grouping metrics on the configured grouping key. + """ + + id: str + """The Orb-assigned unique identifier for the license type.""" + + grouping_key: str + """The key used for grouping licenses of this type. + + This is typically a user identifier field. + """ + + name: str + """The name of the license type.""" + + +class DailyCreditAllowancePrice(BaseModel): + id: str + + billable_metric: Optional[BillableMetricTiny] = None + + billing_cycle_configuration: BillingCycleConfiguration + + billing_mode: Literal["in_advance", "in_arrear"] + + cadence: Literal["one_time", "monthly", "quarterly", "semi_annual", "annual", "custom"] + + composite_price_filters: Optional[List[DailyCreditAllowancePriceCompositePriceFilter]] = None + + conversion_rate: Optional[float] = None + + conversion_rate_config: Optional[DailyCreditAllowancePriceConversionRateConfig] = None + + created_at: datetime + + credit_allocation: Optional[Allocation] = None + + currency: str + + daily_credit_allowance_config: DailyCreditAllowancePriceDailyCreditAllowanceConfig + """Configuration for daily_credit_allowance pricing""" + + discount: Optional[Discount] = None + + external_price_id: Optional[str] = None + + fixed_price_quantity: Optional[float] = None + + invoice_grouping_key: Optional[str] = None + + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None + + item: ItemSlim + """ + A minimal representation of an Item containing only the essential identifying + information. + """ + + maximum: Optional[Maximum] = None + + maximum_amount: Optional[str] = None + + metadata: Dict[str, str] + """User specified key-value pairs for the resource. + + If not present, this defaults to an empty dictionary. Individual keys can be + removed by setting the value to `null`, and the entire metadata mapping can be + cleared by setting `metadata` to `null`. + """ + + minimum: Optional[Minimum] = None + + minimum_amount: Optional[str] = None + + price_model_type: Literal["daily_credit_allowance"] = FieldInfo(alias="model_type") + """The pricing model type""" + + name: str + + plan_phase_order: Optional[int] = None + + price_type: Literal["usage_price", "fixed_price", "composite_price"] + + replaces_price_id: Optional[str] = None + """The price id this price replaces. + + This price will take the place of the replaced price in plan version migrations. + """ + + dimensional_price_configuration: Optional[DimensionalPriceConfiguration] = None + + license_type: Optional[DailyCreditAllowancePriceLicenseType] = None + """ + The LicenseType resource represents a type of license that can be assigned to + users. License types are used during billing by grouping metrics on the + configured grouping key. + """ + + class MinimumCompositePriceCompositePriceFilter(BaseModel): field: Literal["price_id", "item_id", "price_type", "currency", "pricing_unit_id"] """The property of the price to filter on.""" @@ -3992,6 +4215,8 @@ class MinimumCompositePrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -4115,6 +4340,8 @@ class PercentCompositePrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -4254,6 +4481,8 @@ class EventOutputPrice(BaseModel): fixed_price_quantity: Optional[float] = None + invoice_grouping_key: Optional[str] = None + invoicing_cycle_configuration: Optional[BillingCycleConfiguration] = None item: ItemSlim @@ -4333,6 +4562,7 @@ class EventOutputPrice(BaseModel): ScalableMatrixWithTieredPricingPrice, CumulativeGroupedBulkPrice, CumulativeGroupedAllocationPrice, + DailyCreditAllowancePrice, MinimumCompositePrice, PercentCompositePrice, EventOutputPrice, diff --git a/src/orb/types/shared/price_interval.py b/src/orb/types/shared/price_interval.py index 6240931b..03fa50be 100644 --- a/src/orb/types/shared/price_interval.py +++ b/src/orb/types/shared/price_interval.py @@ -1,6 +1,6 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. -from typing import List, Optional +from typing import Dict, List, Optional from datetime import datetime from .price import Price @@ -83,3 +83,9 @@ class PriceInterval(BaseModel): A list of customer IDs whose usage events will be aggregated and billed under this price interval. """ + + metric_parameter_overrides: Optional[Dict[str, object]] = None + """Override values for parameterized billable metric variables. + + Keys are parameter names, values are the override values. + """ diff --git a/src/orb/types/shared_params/new_allocation_price.py b/src/orb/types/shared_params/new_allocation_price.py index 22331d9e..54500bea 100644 --- a/src/orb/types/shared_params/new_allocation_price.py +++ b/src/orb/types/shared_params/new_allocation_price.py @@ -2,7 +2,7 @@ from __future__ import annotations -from typing import Iterable, Optional +from typing import Dict, Iterable, Optional from typing_extensions import Literal, Required, TypedDict from ..._types import SequenceNotStr @@ -59,6 +59,13 @@ class NewAllocationPrice(TypedDict, total=False): license_type_id: Optional[str] """The license type ID to associate the price with license allocation.""" + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + per_unit_cost_basis: str """The (per-unit) cost basis of each created block. diff --git a/src/orb/types/shared_params/new_floating_scalable_matrix_with_unit_pricing_price.py b/src/orb/types/shared_params/new_floating_scalable_matrix_with_unit_pricing_price.py index 374f11e1..009b8569 100644 --- a/src/orb/types/shared_params/new_floating_scalable_matrix_with_unit_pricing_price.py +++ b/src/orb/types/shared_params/new_floating_scalable_matrix_with_unit_pricing_price.py @@ -40,6 +40,9 @@ class ScalableMatrixWithUnitPricingConfig(TypedDict, total=False): unit_price: Required[str] """The final unit price to rate against the output of the matrix""" + grouping_key: Optional[str] + """The property used to group this price""" + prorate: Optional[bool] """If true, the unit price will be prorated to the billing period""" diff --git a/src/orb/types/shared_params/new_plan_scalable_matrix_with_unit_pricing_price.py b/src/orb/types/shared_params/new_plan_scalable_matrix_with_unit_pricing_price.py index f9bee34c..d2085ec1 100644 --- a/src/orb/types/shared_params/new_plan_scalable_matrix_with_unit_pricing_price.py +++ b/src/orb/types/shared_params/new_plan_scalable_matrix_with_unit_pricing_price.py @@ -40,6 +40,9 @@ class ScalableMatrixWithUnitPricingConfig(TypedDict, total=False): unit_price: Required[str] """The final unit price to rate against the output of the matrix""" + grouping_key: Optional[str] + """The property used to group this price""" + prorate: Optional[bool] """If true, the unit price will be prorated to the billing period""" diff --git a/src/orb/types/subscription_create_params.py b/src/orb/types/subscription_create_params.py index 0f454571..228ed977 100644 --- a/src/orb/types/subscription_create_params.py +++ b/src/orb/types/subscription_create_params.py @@ -75,6 +75,10 @@ "AddPricePriceNewSubscriptionCumulativeGroupedAllocationPrice", "AddPricePriceNewSubscriptionCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "AddPricePriceNewSubscriptionCumulativeGroupedAllocationPriceConversionRateConfig", + "AddPricePriceNewSubscriptionDailyCreditAllowancePrice", + "AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "AddPricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig", "AddPricePriceNewSubscriptionPercentCompositePrice", "AddPricePriceNewSubscriptionPercentCompositePricePercentConfig", "AddPricePriceNewSubscriptionPercentCompositePriceConversionRateConfig", @@ -102,6 +106,10 @@ "ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPrice", "ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPriceConversionRateConfig", + "ReplacePricePriceNewSubscriptionDailyCreditAllowancePrice", + "ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig", "ReplacePricePriceNewSubscriptionPercentCompositePrice", "ReplacePricePriceNewSubscriptionPercentCompositePricePercentConfig", "ReplacePricePriceNewSubscriptionPercentCompositePriceConversionRateConfig", @@ -756,6 +764,141 @@ class AddPricePriceNewSubscriptionCumulativeGroupedAllocationPrice(TypedDict, to """ +class AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue( + TypedDict, total=False +): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +AddPricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class AddPricePriceNewSubscriptionDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + daily_credit_allowance_config: Required[ + AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig + ] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[AddPricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class AddPricePriceNewSubscriptionPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -990,6 +1133,7 @@ class AddPricePriceNewSubscriptionEventOutputPrice(TypedDict, total=False): NewSubscriptionScalableMatrixWithTieredPricingPriceParam, NewSubscriptionCumulativeGroupedBulkPriceParam, AddPricePriceNewSubscriptionCumulativeGroupedAllocationPrice, + AddPricePriceNewSubscriptionDailyCreditAllowancePrice, NewSubscriptionMinimumCompositePriceParam, AddPricePriceNewSubscriptionPercentCompositePrice, AddPricePriceNewSubscriptionEventOutputPrice, @@ -1022,6 +1166,12 @@ class AddPrice(TypedDict, total=False): The subscription's maximum amount for this price. """ + metric_parameter_overrides: Optional[Dict[str, object]] + """Override values for parameterized billable metric variables. + + Keys are parameter names, values are the override values. + """ + minimum_amount: Optional[str] """[DEPRECATED] Use add_adjustments instead. @@ -1523,6 +1673,141 @@ class ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPrice(TypedDict """ +class ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue( + TypedDict, total=False +): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class ReplacePricePriceNewSubscriptionDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + daily_credit_allowance_config: Required[ + ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig + ] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class ReplacePricePriceNewSubscriptionPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -1757,6 +2042,7 @@ class ReplacePricePriceNewSubscriptionEventOutputPrice(TypedDict, total=False): NewSubscriptionScalableMatrixWithTieredPricingPriceParam, NewSubscriptionCumulativeGroupedBulkPriceParam, ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPrice, + ReplacePricePriceNewSubscriptionDailyCreditAllowancePrice, NewSubscriptionMinimumCompositePriceParam, ReplacePricePriceNewSubscriptionPercentCompositePrice, ReplacePricePriceNewSubscriptionEventOutputPrice, @@ -1788,6 +2074,12 @@ class ReplacePrice(TypedDict, total=False): The subscription's maximum amount for the replacement price. """ + metric_parameter_overrides: Optional[Dict[str, object]] + """Override values for parameterized billable metric variables. + + Keys are parameter names, values are the override values. + """ + minimum_amount: Optional[str] """[DEPRECATED] Use add_adjustments instead. diff --git a/src/orb/types/subscription_price_intervals_params.py b/src/orb/types/subscription_price_intervals_params.py index eba9360a..35405a4e 100644 --- a/src/orb/types/subscription_price_intervals_params.py +++ b/src/orb/types/subscription_price_intervals_params.py @@ -70,6 +70,10 @@ "AddPriceNewFloatingCumulativeGroupedAllocationPrice", "AddPriceNewFloatingCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "AddPriceNewFloatingCumulativeGroupedAllocationPriceConversionRateConfig", + "AddPriceNewFloatingDailyCreditAllowancePrice", + "AddPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "AddPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "AddPriceNewFloatingDailyCreditAllowancePriceConversionRateConfig", "AddPriceNewFloatingPercentCompositePrice", "AddPriceNewFloatingPercentCompositePricePercentConfig", "AddPriceNewFloatingPercentCompositePriceConversionRateConfig", @@ -459,6 +463,126 @@ class AddPriceNewFloatingCumulativeGroupedAllocationPrice(TypedDict, total=False """ +class AddPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue(TypedDict, total=False): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class AddPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[Iterable[AddPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue]] + """Per-dimension credit rates""" + + +AddPriceNewFloatingDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class AddPriceNewFloatingDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + currency: Required[str] + """An ISO 4217 currency string for which this price is billed in.""" + + daily_credit_allowance_config: Required[AddPriceNewFloatingDailyCreditAllowancePriceDailyCreditAllowanceConfig] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[AddPriceNewFloatingDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + class AddPriceNewFloatingPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -675,6 +799,7 @@ class AddPriceNewFloatingEventOutputPrice(TypedDict, total=False): NewFloatingScalableMatrixWithTieredPricingPrice, NewFloatingCumulativeGroupedBulkPrice, AddPriceNewFloatingCumulativeGroupedAllocationPrice, + AddPriceNewFloatingDailyCreditAllowancePrice, NewFloatingMinimumCompositePrice, AddPriceNewFloatingPercentCompositePrice, AddPriceNewFloatingEventOutputPrice, @@ -729,6 +854,12 @@ class Add(TypedDict, total=False): billing period. """ + metric_parameter_overrides: Optional[Dict[str, object]] + """Override values for parameterized billable metric variables. + + Keys are parameter names, values are the override values (number or string). + """ + minimum_amount: Optional[float] """ The minimum amount that will be billed for this price interval for a given @@ -836,6 +967,12 @@ class Edit(TypedDict, total=False): on the price interval. """ + metric_parameter_overrides: Optional[Dict[str, object]] + """Override values for parameterized billable metric variables. + + Keys are parameter names, values are the override values (number or string). + """ + start_date: Annotated[Union[Union[str, datetime], BillingCycleRelativeDate], PropertyInfo(format="iso8601")] """The updated start date of this price interval. diff --git a/src/orb/types/subscription_schedule_plan_change_params.py b/src/orb/types/subscription_schedule_plan_change_params.py index e2208e29..b9001c82 100644 --- a/src/orb/types/subscription_schedule_plan_change_params.py +++ b/src/orb/types/subscription_schedule_plan_change_params.py @@ -75,6 +75,10 @@ "AddPricePriceNewSubscriptionCumulativeGroupedAllocationPrice", "AddPricePriceNewSubscriptionCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "AddPricePriceNewSubscriptionCumulativeGroupedAllocationPriceConversionRateConfig", + "AddPricePriceNewSubscriptionDailyCreditAllowancePrice", + "AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "AddPricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig", "AddPricePriceNewSubscriptionPercentCompositePrice", "AddPricePriceNewSubscriptionPercentCompositePricePercentConfig", "AddPricePriceNewSubscriptionPercentCompositePriceConversionRateConfig", @@ -102,6 +106,10 @@ "ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPrice", "ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPriceCumulativeGroupedAllocationConfig", "ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPriceConversionRateConfig", + "ReplacePricePriceNewSubscriptionDailyCreditAllowancePrice", + "ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig", + "ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue", + "ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig", "ReplacePricePriceNewSubscriptionPercentCompositePrice", "ReplacePricePriceNewSubscriptionPercentCompositePricePercentConfig", "ReplacePricePriceNewSubscriptionPercentCompositePriceConversionRateConfig", @@ -744,6 +752,141 @@ class AddPricePriceNewSubscriptionCumulativeGroupedAllocationPrice(TypedDict, to """ +class AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue( + TypedDict, total=False +): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +AddPricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class AddPricePriceNewSubscriptionDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + daily_credit_allowance_config: Required[ + AddPricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig + ] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[AddPricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class AddPricePriceNewSubscriptionPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -978,6 +1121,7 @@ class AddPricePriceNewSubscriptionEventOutputPrice(TypedDict, total=False): NewSubscriptionScalableMatrixWithTieredPricingPriceParam, NewSubscriptionCumulativeGroupedBulkPriceParam, AddPricePriceNewSubscriptionCumulativeGroupedAllocationPrice, + AddPricePriceNewSubscriptionDailyCreditAllowancePrice, NewSubscriptionMinimumCompositePriceParam, AddPricePriceNewSubscriptionPercentCompositePrice, AddPricePriceNewSubscriptionEventOutputPrice, @@ -1010,6 +1154,12 @@ class AddPrice(TypedDict, total=False): The subscription's maximum amount for this price. """ + metric_parameter_overrides: Optional[Dict[str, object]] + """Override values for parameterized billable metric variables. + + Keys are parameter names, values are the override values. + """ + minimum_amount: Optional[str] """[DEPRECATED] Use add_adjustments instead. @@ -1511,6 +1661,141 @@ class ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPrice(TypedDict """ +class ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue( + TypedDict, total=False +): + """Per-dimension credit price for the daily credit allowance model.""" + + dimension_values: Required[SequenceNotStr[Optional[str]]] + """One or two matrix keys to filter usage to this value by. + + For example, ["model"] could be used to apply a different credit rate to each AI + model. + """ + + unit_amount: Required[str] + """Credits charged per unit of usage matching the specified dimension_values""" + + +class ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig(TypedDict, total=False): + """Configuration for daily_credit_allowance pricing""" + + daily_allowance: Required[str] + """Credits granted per day. Lose-it-or-use-it; does not roll over.""" + + default_unit_amount: Required[str] + """ + Default per-unit credit rate for any usage not bucketed into a specified + matrix_value + """ + + dimensions: Required[SequenceNotStr[Optional[str]]] + """One or two event property values to evaluate matrix groups by""" + + event_day_property: Required[str] + """Event property whose value identifies the day bucket the event belongs to (e.g. + + 'event_day' set to an ISO date string in the customer's timezone). The allowance + resets per distinct value of this property. + """ + + matrix_values: Required[ + Iterable[ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfigMatrixValue] + ] + """Per-dimension credit rates""" + + +ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig: TypeAlias = Union[ + UnitConversionRateConfig, TieredConversionRateConfig +] + + +class ReplacePricePriceNewSubscriptionDailyCreditAllowancePrice(TypedDict, total=False): + cadence: Required[Literal["annual", "semi_annual", "monthly", "quarterly", "one_time", "custom"]] + """The cadence to bill for this price on.""" + + daily_credit_allowance_config: Required[ + ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceDailyCreditAllowanceConfig + ] + """Configuration for daily_credit_allowance pricing""" + + item_id: Required[str] + """The id of the item the price will be associated with.""" + + model_type: Required[Literal["daily_credit_allowance"]] + """The pricing model type""" + + name: Required[str] + """The name of the price.""" + + billable_metric_id: Optional[str] + """The id of the billable metric for the price. + + Only needed if the price is usage-based. + """ + + billed_in_advance: Optional[bool] + """ + If the Price represents a fixed cost, the price will be billed in-advance if + this is true, and in-arrears if this is false. + """ + + billing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """ + For custom cadence: specifies the duration of the billing period in days or + months. + """ + + conversion_rate: Optional[float] + """The per unit conversion rate of the price currency to the invoicing currency.""" + + conversion_rate_config: Optional[ReplacePricePriceNewSubscriptionDailyCreditAllowancePriceConversionRateConfig] + """The configuration for the rate of the price currency to the invoicing currency.""" + + currency: Optional[str] + """ + An ISO 4217 currency string, or custom pricing unit identifier, in which this + price is billed. + """ + + dimensional_price_configuration: Optional[NewDimensionalPriceConfiguration] + """For dimensional price: specifies a price group and dimension values""" + + external_price_id: Optional[str] + """An alias for the price.""" + + fixed_price_quantity: Optional[float] + """ + If the Price represents a fixed cost, this represents the quantity of units + applied. + """ + + invoice_grouping_key: Optional[str] + """The property used to group this price on an invoice""" + + invoicing_cycle_configuration: Optional[NewBillingCycleConfiguration] + """Within each billing cycle, specifies the cadence at which invoices are produced. + + If unspecified, a single invoice is produced per billing cycle. + """ + + license_type_id: Optional[str] + """The ID of the license type to associate with this price.""" + + metadata: Optional[Dict[str, Optional[str]]] + """User-specified key/value pairs for the resource. + + Individual keys can be removed by setting the value to `null`, and the entire + metadata mapping can be cleared by setting `metadata` to `null`. + """ + + reference_id: Optional[str] + """ + A transient ID that can be used to reference this price when adding adjustments + in the same API call. + """ + + class ReplacePricePriceNewSubscriptionPercentCompositePricePercentConfig(TypedDict, total=False): """Configuration for percent pricing""" @@ -1745,6 +2030,7 @@ class ReplacePricePriceNewSubscriptionEventOutputPrice(TypedDict, total=False): NewSubscriptionScalableMatrixWithTieredPricingPriceParam, NewSubscriptionCumulativeGroupedBulkPriceParam, ReplacePricePriceNewSubscriptionCumulativeGroupedAllocationPrice, + ReplacePricePriceNewSubscriptionDailyCreditAllowancePrice, NewSubscriptionMinimumCompositePriceParam, ReplacePricePriceNewSubscriptionPercentCompositePrice, ReplacePricePriceNewSubscriptionEventOutputPrice, @@ -1776,6 +2062,12 @@ class ReplacePrice(TypedDict, total=False): The subscription's maximum amount for the replacement price. """ + metric_parameter_overrides: Optional[Dict[str, object]] + """Override values for parameterized billable metric variables. + + Keys are parameter names, values are the override values. + """ + minimum_amount: Optional[str] """[DEPRECATED] Use add_adjustments instead. diff --git a/tests/api_resources/beta/test_external_plan_id.py b/tests/api_resources/beta/test_external_plan_id.py index 13c78e4b..15b702e7 100644 --- a/tests/api_resources/beta/test_external_plan_id.py +++ b/tests/api_resources/beta/test_external_plan_id.py @@ -72,8 +72,53 @@ def test_method_create_plan_version_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", @@ -170,8 +215,53 @@ def test_method_create_plan_version_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", @@ -401,8 +491,53 @@ async def test_method_create_plan_version_with_all_params(self, async_client: As ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", @@ -499,8 +634,53 @@ async def test_method_create_plan_version_with_all_params(self, async_client: As ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", diff --git a/tests/api_resources/customers/credits/test_ledger.py b/tests/api_resources/customers/credits/test_ledger.py index f3cf3ee6..d056bec6 100644 --- a/tests/api_resources/customers/credits/test_ledger.py +++ b/tests/api_resources/customers/credits/test_ledger.py @@ -110,6 +110,7 @@ def test_method_create_entry_with_all_params_overload_1(self, client: Orb) -> No "custom_due_date": parse_date("2019-12-27"), "invoice_date": parse_date("2019-12-27"), "item_id": "item_id", + "mark_as_paid": True, "memo": "memo", "net_terms": 0, "require_successful_payment": True, @@ -433,6 +434,7 @@ def test_method_create_entry_by_external_id_with_all_params_overload_1(self, cli "custom_due_date": parse_date("2019-12-27"), "invoice_date": parse_date("2019-12-27"), "item_id": "item_id", + "mark_as_paid": True, "memo": "memo", "net_terms": 0, "require_successful_payment": True, @@ -872,6 +874,7 @@ async def test_method_create_entry_with_all_params_overload_1(self, async_client "custom_due_date": parse_date("2019-12-27"), "invoice_date": parse_date("2019-12-27"), "item_id": "item_id", + "mark_as_paid": True, "memo": "memo", "net_terms": 0, "require_successful_payment": True, @@ -1195,6 +1198,7 @@ async def test_method_create_entry_by_external_id_with_all_params_overload_1(sel "custom_due_date": parse_date("2019-12-27"), "invoice_date": parse_date("2019-12-27"), "item_id": "item_id", + "mark_as_paid": True, "memo": "memo", "net_terms": 0, "require_successful_payment": True, diff --git a/tests/api_resources/plans/test_external_plan_id.py b/tests/api_resources/plans/test_external_plan_id.py index 3c77aa9c..64838190 100644 --- a/tests/api_resources/plans/test_external_plan_id.py +++ b/tests/api_resources/plans/test_external_plan_id.py @@ -28,6 +28,7 @@ def test_method_update(self, client: Orb) -> None: def test_method_update_with_all_params(self, client: Orb) -> None: external_plan_id = client.plans.external_plan_id.update( other_external_plan_id="external_plan_id", + description="description", external_plan_id="external_plan_id", metadata={"foo": "string"}, ) @@ -121,6 +122,7 @@ async def test_method_update(self, async_client: AsyncOrb) -> None: async def test_method_update_with_all_params(self, async_client: AsyncOrb) -> None: external_plan_id = await async_client.plans.external_plan_id.update( other_external_plan_id="external_plan_id", + description="description", external_plan_id="external_plan_id", metadata={"foo": "string"}, ) diff --git a/tests/api_resources/test_alerts.py b/tests/api_resources/test_alerts.py index bcae7a6b..b43e00ba 100644 --- a/tests/api_resources/test_alerts.py +++ b/tests/api_resources/test_alerts.py @@ -274,7 +274,22 @@ def test_method_create_for_subscription_with_all_params(self, client: Orb) -> No subscription_id="subscription_id", thresholds=[{"value": 0}], type="usage_exceeded", + grouping_keys=["string"], metric_id="metric_id", + price_filters=[ + { + "field": "price_id", + "operator": "includes", + "values": ["string"], + } + ], + pricing_unit_id="pricing_unit_id", + threshold_overrides=[ + { + "group_values": ["string"], + "thresholds": [{"value": 0}], + } + ], ) assert_matches_type(Alert, alert, path=["response"]) @@ -670,7 +685,22 @@ async def test_method_create_for_subscription_with_all_params(self, async_client subscription_id="subscription_id", thresholds=[{"value": 0}], type="usage_exceeded", + grouping_keys=["string"], metric_id="metric_id", + price_filters=[ + { + "field": "price_id", + "operator": "includes", + "values": ["string"], + } + ], + pricing_unit_id="pricing_unit_id", + threshold_overrides=[ + { + "group_values": ["string"], + "thresholds": [{"value": 0}], + } + ], ) assert_matches_type(Alert, alert, path=["response"]) diff --git a/tests/api_resources/test_beta.py b/tests/api_resources/test_beta.py index e391b9b2..d2f31794 100644 --- a/tests/api_resources/test_beta.py +++ b/tests/api_resources/test_beta.py @@ -72,8 +72,53 @@ def test_method_create_plan_version_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", @@ -170,8 +215,53 @@ def test_method_create_plan_version_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", @@ -401,8 +491,53 @@ async def test_method_create_plan_version_with_all_params(self, async_client: As ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", @@ -499,8 +634,53 @@ async def test_method_create_plan_version_with_all_params(self, async_client: As ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", diff --git a/tests/api_resources/test_customers.py b/tests/api_resources/test_customers.py index 99212e34..2fd979de 100644 --- a/tests/api_resources/test_customers.py +++ b/tests/api_resources/test_customers.py @@ -66,6 +66,7 @@ def test_method_create_with_all_params(self, client: Orb) -> None: "payment_providers": [ { "provider_type": "stripe", + "default_shared_payment_token": "default_shared_payment_token", "excluded_payment_method_types": ["string"], } ] @@ -167,6 +168,7 @@ def test_method_update_with_all_params(self, client: Orb) -> None: "payment_providers": [ { "provider_type": "stripe", + "default_shared_payment_token": "default_shared_payment_token", "excluded_payment_method_types": ["string"], } ] @@ -499,6 +501,7 @@ def test_method_update_by_external_id_with_all_params(self, client: Orb) -> None "payment_providers": [ { "provider_type": "stripe", + "default_shared_payment_token": "default_shared_payment_token", "excluded_payment_method_types": ["string"], } ] @@ -610,6 +613,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No "payment_providers": [ { "provider_type": "stripe", + "default_shared_payment_token": "default_shared_payment_token", "excluded_payment_method_types": ["string"], } ] @@ -711,6 +715,7 @@ async def test_method_update_with_all_params(self, async_client: AsyncOrb) -> No "payment_providers": [ { "provider_type": "stripe", + "default_shared_payment_token": "default_shared_payment_token", "excluded_payment_method_types": ["string"], } ] @@ -1055,6 +1060,7 @@ async def test_method_update_by_external_id_with_all_params(self, async_client: "payment_providers": [ { "provider_type": "stripe", + "default_shared_payment_token": "default_shared_payment_token", "excluded_payment_method_types": ["string"], } ] diff --git a/tests/api_resources/test_invoices.py b/tests/api_resources/test_invoices.py index 316aefc3..8ae434a3 100644 --- a/tests/api_resources/test_invoices.py +++ b/tests/api_resources/test_invoices.py @@ -62,6 +62,7 @@ def test_method_create_with_all_params(self, client: Orb) -> None: }, } ], + auto_collection=True, customer_id="4khy3nwzktxv7", discount={ "discount_type": "percentage", @@ -144,6 +145,7 @@ def test_method_update(self, client: Orb) -> None: def test_method_update_with_all_params(self, client: Orb) -> None: invoice = client.invoices.update( invoice_id="invoice_id", + auto_collection=True, due_date=parse_date("2023-09-22"), invoice_date=parse_date("2023-09-22"), metadata={"foo": "string"}, @@ -546,14 +548,16 @@ def test_path_params_mark_paid(self, client: Orb) -> None: @parametrize def test_method_pay(self, client: Orb) -> None: invoice = client.invoices.pay( - "invoice_id", + invoice_id="invoice_id", + shared_payment_token_id="shared_payment_token_id", ) assert_matches_type(Invoice, invoice, path=["response"]) @parametrize def test_raw_response_pay(self, client: Orb) -> None: response = client.invoices.with_raw_response.pay( - "invoice_id", + invoice_id="invoice_id", + shared_payment_token_id="shared_payment_token_id", ) assert response.is_closed is True @@ -564,7 +568,8 @@ def test_raw_response_pay(self, client: Orb) -> None: @parametrize def test_streaming_response_pay(self, client: Orb) -> None: with client.invoices.with_streaming_response.pay( - "invoice_id", + invoice_id="invoice_id", + shared_payment_token_id="shared_payment_token_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -578,7 +583,8 @@ def test_streaming_response_pay(self, client: Orb) -> None: def test_path_params_pay(self, client: Orb) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `invoice_id` but received ''"): client.invoices.with_raw_response.pay( - "", + invoice_id="", + shared_payment_token_id="shared_payment_token_id", ) @parametrize @@ -663,6 +669,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No }, } ], + auto_collection=True, customer_id="4khy3nwzktxv7", discount={ "discount_type": "percentage", @@ -745,6 +752,7 @@ async def test_method_update(self, async_client: AsyncOrb) -> None: async def test_method_update_with_all_params(self, async_client: AsyncOrb) -> None: invoice = await async_client.invoices.update( invoice_id="invoice_id", + auto_collection=True, due_date=parse_date("2023-09-22"), invoice_date=parse_date("2023-09-22"), metadata={"foo": "string"}, @@ -1147,14 +1155,16 @@ async def test_path_params_mark_paid(self, async_client: AsyncOrb) -> None: @parametrize async def test_method_pay(self, async_client: AsyncOrb) -> None: invoice = await async_client.invoices.pay( - "invoice_id", + invoice_id="invoice_id", + shared_payment_token_id="shared_payment_token_id", ) assert_matches_type(Invoice, invoice, path=["response"]) @parametrize async def test_raw_response_pay(self, async_client: AsyncOrb) -> None: response = await async_client.invoices.with_raw_response.pay( - "invoice_id", + invoice_id="invoice_id", + shared_payment_token_id="shared_payment_token_id", ) assert response.is_closed is True @@ -1165,7 +1175,8 @@ async def test_raw_response_pay(self, async_client: AsyncOrb) -> None: @parametrize async def test_streaming_response_pay(self, async_client: AsyncOrb) -> None: async with async_client.invoices.with_streaming_response.pay( - "invoice_id", + invoice_id="invoice_id", + shared_payment_token_id="shared_payment_token_id", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -1179,7 +1190,8 @@ async def test_streaming_response_pay(self, async_client: AsyncOrb) -> None: async def test_path_params_pay(self, async_client: AsyncOrb) -> None: with pytest.raises(ValueError, match=r"Expected a non-empty value for `invoice_id` but received ''"): await async_client.invoices.with_raw_response.pay( - "", + invoice_id="", + shared_payment_token_id="shared_payment_token_id", ) @parametrize diff --git a/tests/api_resources/test_license_types.py b/tests/api_resources/test_license_types.py index 18c708d2..804081fe 100644 --- a/tests/api_resources/test_license_types.py +++ b/tests/api_resources/test_license_types.py @@ -25,16 +25,16 @@ class TestLicenseTypes: @parametrize def test_method_create(self, client: Orb) -> None: license_type = client.license_types.create( - grouping_key="grouping_key", - name="name", + grouping_key="x", + name="x", ) assert_matches_type(LicenseTypeCreateResponse, license_type, path=["response"]) @parametrize def test_raw_response_create(self, client: Orb) -> None: response = client.license_types.with_raw_response.create( - grouping_key="grouping_key", - name="name", + grouping_key="x", + name="x", ) assert response.is_closed is True @@ -45,8 +45,8 @@ def test_raw_response_create(self, client: Orb) -> None: @parametrize def test_streaming_response_create(self, client: Orb) -> None: with client.license_types.with_streaming_response.create( - grouping_key="grouping_key", - name="name", + grouping_key="x", + name="x", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" @@ -136,16 +136,16 @@ class TestAsyncLicenseTypes: @parametrize async def test_method_create(self, async_client: AsyncOrb) -> None: license_type = await async_client.license_types.create( - grouping_key="grouping_key", - name="name", + grouping_key="x", + name="x", ) assert_matches_type(LicenseTypeCreateResponse, license_type, path=["response"]) @parametrize async def test_raw_response_create(self, async_client: AsyncOrb) -> None: response = await async_client.license_types.with_raw_response.create( - grouping_key="grouping_key", - name="name", + grouping_key="x", + name="x", ) assert response.is_closed is True @@ -156,8 +156,8 @@ async def test_raw_response_create(self, async_client: AsyncOrb) -> None: @parametrize async def test_streaming_response_create(self, async_client: AsyncOrb) -> None: async with async_client.license_types.with_streaming_response.create( - grouping_key="grouping_key", - name="name", + grouping_key="x", + name="x", ) as response: assert not response.is_closed assert response.http_request.headers.get("X-Stainless-Lang") == "python" diff --git a/tests/api_resources/test_plans.py b/tests/api_resources/test_plans.py index 24286962..b8867eb5 100644 --- a/tests/api_resources/test_plans.py +++ b/tests/api_resources/test_plans.py @@ -53,8 +53,53 @@ def test_method_create_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", @@ -118,6 +163,7 @@ def test_method_create_with_all_params(self, client: Orb) -> None: } ], default_invoice_memo="default_invoice_memo", + description="description", external_plan_id="external_plan_id", metadata={"foo": "string"}, net_terms=0, @@ -172,6 +218,7 @@ def test_method_update(self, client: Orb) -> None: def test_method_update_with_all_params(self, client: Orb) -> None: plan = client.plans.update( plan_id="plan_id", + description="description", external_plan_id="external_plan_id", metadata={"foo": "string"}, ) @@ -324,8 +371,53 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, + "license_allocation_price": { + "cadence": "annual", + "item_id": "item_id", + "license_allocations": [ + { + "amount": "amount", + "currency": "currency", + "write_off_overage": True, + } + ], + "model_type": "unit", + "name": "Annual fee", + "unit_config": { + "unit_amount": "unit_amount", + "prorated": True, + }, + "billable_metric_id": "billable_metric_id", + "billed_in_advance": True, + "billing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "conversion_rate": 0, + "conversion_rate_config": { + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + "currency": "currency", + "dimensional_price_configuration": { + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + "external_price_id": "external_price_id", + "fixed_price_quantity": 0, + "invoice_grouping_key": "x", + "invoicing_cycle_configuration": { + "duration": 0, + "duration_unit": "day", + }, + "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, + "reference_id": "reference_id", + }, "plan_phase_order": 0, "price": { "cadence": "annual", @@ -389,6 +481,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No } ], default_invoice_memo="default_invoice_memo", + description="description", external_plan_id="external_plan_id", metadata={"foo": "string"}, net_terms=0, @@ -443,6 +536,7 @@ async def test_method_update(self, async_client: AsyncOrb) -> None: async def test_method_update_with_all_params(self, async_client: AsyncOrb) -> None: plan = await async_client.plans.update( plan_id="plan_id", + description="description", external_plan_id="external_plan_id", metadata={"foo": "string"}, ) diff --git a/tests/api_resources/test_prices.py b/tests/api_resources/test_prices.py index 68a1c59b..17549d55 100644 --- a/tests/api_resources/test_prices.py +++ b/tests/api_resources/test_prices.py @@ -2802,6 +2802,7 @@ def test_method_create_with_all_params_overload_25(self, client: Orb) -> None: } ], "unit_price": "unit_price", + "grouping_key": "x", "prorate": True, "second_dimension": "second_dimension", }, @@ -3266,6 +3267,133 @@ def test_streaming_response_create_overload_28(self, client: Orb) -> None: @parametrize def test_method_create_overload_29(self, client: Orb) -> None: + price = client.prices.create( + cadence="annual", + currency="currency", + daily_credit_allowance_config={ + "daily_allowance": "daily_allowance", + "default_unit_amount": "default_unit_amount", + "dimensions": ["string"], + "event_day_property": "x", + "matrix_values": [ + { + "dimension_values": ["string"], + "unit_amount": "unit_amount", + } + ], + }, + item_id="item_id", + model_type="daily_credit_allowance", + name="Annual fee", + ) + assert_matches_type(Price, price, path=["response"]) + + @parametrize + def test_method_create_with_all_params_overload_29(self, client: Orb) -> None: + price = client.prices.create( + cadence="annual", + currency="currency", + daily_credit_allowance_config={ + "daily_allowance": "daily_allowance", + "default_unit_amount": "default_unit_amount", + "dimensions": ["string"], + "event_day_property": "x", + "matrix_values": [ + { + "dimension_values": ["string"], + "unit_amount": "unit_amount", + } + ], + }, + item_id="item_id", + model_type="daily_credit_allowance", + name="Annual fee", + billable_metric_id="billable_metric_id", + billed_in_advance=True, + billing_cycle_configuration={ + "duration": 0, + "duration_unit": "day", + }, + conversion_rate=0, + conversion_rate_config={ + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + dimensional_price_configuration={ + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + external_price_id="external_price_id", + fixed_price_quantity=0, + invoice_grouping_key="x", + invoicing_cycle_configuration={ + "duration": 0, + "duration_unit": "day", + }, + license_type_id="license_type_id", + metadata={"foo": "string"}, + ) + assert_matches_type(Price, price, path=["response"]) + + @parametrize + def test_raw_response_create_overload_29(self, client: Orb) -> None: + response = client.prices.with_raw_response.create( + cadence="annual", + currency="currency", + daily_credit_allowance_config={ + "daily_allowance": "daily_allowance", + "default_unit_amount": "default_unit_amount", + "dimensions": ["string"], + "event_day_property": "x", + "matrix_values": [ + { + "dimension_values": ["string"], + "unit_amount": "unit_amount", + } + ], + }, + item_id="item_id", + model_type="daily_credit_allowance", + name="Annual fee", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + price = response.parse() + assert_matches_type(Price, price, path=["response"]) + + @parametrize + def test_streaming_response_create_overload_29(self, client: Orb) -> None: + with client.prices.with_streaming_response.create( + cadence="annual", + currency="currency", + daily_credit_allowance_config={ + "daily_allowance": "daily_allowance", + "default_unit_amount": "default_unit_amount", + "dimensions": ["string"], + "event_day_property": "x", + "matrix_values": [ + { + "dimension_values": ["string"], + "unit_amount": "unit_amount", + } + ], + }, + item_id="item_id", + model_type="daily_credit_allowance", + name="Annual fee", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + price = response.parse() + assert_matches_type(Price, price, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + def test_method_create_overload_30(self, client: Orb) -> None: price = client.prices.create( cadence="annual", currency="currency", @@ -3277,7 +3405,7 @@ def test_method_create_overload_29(self, client: Orb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - def test_method_create_with_all_params_overload_29(self, client: Orb) -> None: + def test_method_create_with_all_params_overload_30(self, client: Orb) -> None: price = client.prices.create( cadence="annual", currency="currency", @@ -3317,7 +3445,7 @@ def test_method_create_with_all_params_overload_29(self, client: Orb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - def test_raw_response_create_overload_29(self, client: Orb) -> None: + def test_raw_response_create_overload_30(self, client: Orb) -> None: response = client.prices.with_raw_response.create( cadence="annual", currency="currency", @@ -3333,7 +3461,7 @@ def test_raw_response_create_overload_29(self, client: Orb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - def test_streaming_response_create_overload_29(self, client: Orb) -> None: + def test_streaming_response_create_overload_30(self, client: Orb) -> None: with client.prices.with_streaming_response.create( cadence="annual", currency="currency", @@ -3351,7 +3479,7 @@ def test_streaming_response_create_overload_29(self, client: Orb) -> None: assert cast(Any, response.is_closed) is True @parametrize - def test_method_create_overload_30(self, client: Orb) -> None: + def test_method_create_overload_31(self, client: Orb) -> None: price = client.prices.create( cadence="annual", currency="currency", @@ -3363,7 +3491,7 @@ def test_method_create_overload_30(self, client: Orb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - def test_method_create_with_all_params_overload_30(self, client: Orb) -> None: + def test_method_create_with_all_params_overload_31(self, client: Orb) -> None: price = client.prices.create( cadence="annual", currency="currency", @@ -3400,7 +3528,7 @@ def test_method_create_with_all_params_overload_30(self, client: Orb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - def test_raw_response_create_overload_30(self, client: Orb) -> None: + def test_raw_response_create_overload_31(self, client: Orb) -> None: response = client.prices.with_raw_response.create( cadence="annual", currency="currency", @@ -3416,7 +3544,7 @@ def test_raw_response_create_overload_30(self, client: Orb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - def test_streaming_response_create_overload_30(self, client: Orb) -> None: + def test_streaming_response_create_overload_31(self, client: Orb) -> None: with client.prices.with_streaming_response.create( cadence="annual", currency="currency", @@ -3434,7 +3562,7 @@ def test_streaming_response_create_overload_30(self, client: Orb) -> None: assert cast(Any, response.is_closed) is True @parametrize - def test_method_create_overload_31(self, client: Orb) -> None: + def test_method_create_overload_32(self, client: Orb) -> None: price = client.prices.create( cadence="annual", currency="currency", @@ -3446,7 +3574,7 @@ def test_method_create_overload_31(self, client: Orb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - def test_method_create_with_all_params_overload_31(self, client: Orb) -> None: + def test_method_create_with_all_params_overload_32(self, client: Orb) -> None: price = client.prices.create( cadence="annual", currency="currency", @@ -3487,7 +3615,7 @@ def test_method_create_with_all_params_overload_31(self, client: Orb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - def test_raw_response_create_overload_31(self, client: Orb) -> None: + def test_raw_response_create_overload_32(self, client: Orb) -> None: response = client.prices.with_raw_response.create( cadence="annual", currency="currency", @@ -3503,7 +3631,7 @@ def test_raw_response_create_overload_31(self, client: Orb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - def test_streaming_response_create_overload_31(self, client: Orb) -> None: + def test_streaming_response_create_overload_32(self, client: Orb) -> None: with client.prices.with_streaming_response.create( cadence="annual", currency="currency", @@ -3618,6 +3746,7 @@ def test_method_evaluate_with_all_params(self, client: Orb) -> None: external_customer_id="external_customer_id", filter="my_numeric_property > 100 AND my_other_property = 'bar'", grouping_keys=["case when my_event_type = 'foo' then true else false end"], + metric_parameter_overrides={"foo": "bar"}, ) assert_matches_type(PriceEvaluateResponse, price, path=["response"]) @@ -3678,6 +3807,7 @@ def test_method_evaluate_multiple_with_all_params(self, client: Orb) -> None: "external_price_id": "external_price_id", "filter": "my_numeric_property > 100 AND my_other_property = 'bar'", "grouping_keys": ["case when my_event_type = 'foo' then true else false end"], + "metric_parameter_overrides": {"foo": "bar"}, "price": { "cadence": "annual", "currency": "currency", @@ -3775,6 +3905,7 @@ def test_method_evaluate_preview_events_with_all_params(self, client: Orb) -> No "external_price_id": "external_price_id", "filter": "my_numeric_property > 100 AND my_other_property = 'bar'", "grouping_keys": ["case when my_event_type = 'foo' then true else false end"], + "metric_parameter_overrides": {"foo": "bar"}, "price": { "cadence": "annual", "currency": "currency", @@ -6665,6 +6796,7 @@ async def test_method_create_with_all_params_overload_25(self, async_client: Asy } ], "unit_price": "unit_price", + "grouping_key": "x", "prorate": True, "second_dimension": "second_dimension", }, @@ -7129,6 +7261,133 @@ async def test_streaming_response_create_overload_28(self, async_client: AsyncOr @parametrize async def test_method_create_overload_29(self, async_client: AsyncOrb) -> None: + price = await async_client.prices.create( + cadence="annual", + currency="currency", + daily_credit_allowance_config={ + "daily_allowance": "daily_allowance", + "default_unit_amount": "default_unit_amount", + "dimensions": ["string"], + "event_day_property": "x", + "matrix_values": [ + { + "dimension_values": ["string"], + "unit_amount": "unit_amount", + } + ], + }, + item_id="item_id", + model_type="daily_credit_allowance", + name="Annual fee", + ) + assert_matches_type(Price, price, path=["response"]) + + @parametrize + async def test_method_create_with_all_params_overload_29(self, async_client: AsyncOrb) -> None: + price = await async_client.prices.create( + cadence="annual", + currency="currency", + daily_credit_allowance_config={ + "daily_allowance": "daily_allowance", + "default_unit_amount": "default_unit_amount", + "dimensions": ["string"], + "event_day_property": "x", + "matrix_values": [ + { + "dimension_values": ["string"], + "unit_amount": "unit_amount", + } + ], + }, + item_id="item_id", + model_type="daily_credit_allowance", + name="Annual fee", + billable_metric_id="billable_metric_id", + billed_in_advance=True, + billing_cycle_configuration={ + "duration": 0, + "duration_unit": "day", + }, + conversion_rate=0, + conversion_rate_config={ + "conversion_rate_type": "unit", + "unit_config": {"unit_amount": "unit_amount"}, + }, + dimensional_price_configuration={ + "dimension_values": ["string"], + "dimensional_price_group_id": "dimensional_price_group_id", + "external_dimensional_price_group_id": "external_dimensional_price_group_id", + }, + external_price_id="external_price_id", + fixed_price_quantity=0, + invoice_grouping_key="x", + invoicing_cycle_configuration={ + "duration": 0, + "duration_unit": "day", + }, + license_type_id="license_type_id", + metadata={"foo": "string"}, + ) + assert_matches_type(Price, price, path=["response"]) + + @parametrize + async def test_raw_response_create_overload_29(self, async_client: AsyncOrb) -> None: + response = await async_client.prices.with_raw_response.create( + cadence="annual", + currency="currency", + daily_credit_allowance_config={ + "daily_allowance": "daily_allowance", + "default_unit_amount": "default_unit_amount", + "dimensions": ["string"], + "event_day_property": "x", + "matrix_values": [ + { + "dimension_values": ["string"], + "unit_amount": "unit_amount", + } + ], + }, + item_id="item_id", + model_type="daily_credit_allowance", + name="Annual fee", + ) + + assert response.is_closed is True + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + price = response.parse() + assert_matches_type(Price, price, path=["response"]) + + @parametrize + async def test_streaming_response_create_overload_29(self, async_client: AsyncOrb) -> None: + async with async_client.prices.with_streaming_response.create( + cadence="annual", + currency="currency", + daily_credit_allowance_config={ + "daily_allowance": "daily_allowance", + "default_unit_amount": "default_unit_amount", + "dimensions": ["string"], + "event_day_property": "x", + "matrix_values": [ + { + "dimension_values": ["string"], + "unit_amount": "unit_amount", + } + ], + }, + item_id="item_id", + model_type="daily_credit_allowance", + name="Annual fee", + ) as response: + assert not response.is_closed + assert response.http_request.headers.get("X-Stainless-Lang") == "python" + + price = await response.parse() + assert_matches_type(Price, price, path=["response"]) + + assert cast(Any, response.is_closed) is True + + @parametrize + async def test_method_create_overload_30(self, async_client: AsyncOrb) -> None: price = await async_client.prices.create( cadence="annual", currency="currency", @@ -7140,7 +7399,7 @@ async def test_method_create_overload_29(self, async_client: AsyncOrb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - async def test_method_create_with_all_params_overload_29(self, async_client: AsyncOrb) -> None: + async def test_method_create_with_all_params_overload_30(self, async_client: AsyncOrb) -> None: price = await async_client.prices.create( cadence="annual", currency="currency", @@ -7180,7 +7439,7 @@ async def test_method_create_with_all_params_overload_29(self, async_client: Asy assert_matches_type(Price, price, path=["response"]) @parametrize - async def test_raw_response_create_overload_29(self, async_client: AsyncOrb) -> None: + async def test_raw_response_create_overload_30(self, async_client: AsyncOrb) -> None: response = await async_client.prices.with_raw_response.create( cadence="annual", currency="currency", @@ -7196,7 +7455,7 @@ async def test_raw_response_create_overload_29(self, async_client: AsyncOrb) -> assert_matches_type(Price, price, path=["response"]) @parametrize - async def test_streaming_response_create_overload_29(self, async_client: AsyncOrb) -> None: + async def test_streaming_response_create_overload_30(self, async_client: AsyncOrb) -> None: async with async_client.prices.with_streaming_response.create( cadence="annual", currency="currency", @@ -7214,7 +7473,7 @@ async def test_streaming_response_create_overload_29(self, async_client: AsyncOr assert cast(Any, response.is_closed) is True @parametrize - async def test_method_create_overload_30(self, async_client: AsyncOrb) -> None: + async def test_method_create_overload_31(self, async_client: AsyncOrb) -> None: price = await async_client.prices.create( cadence="annual", currency="currency", @@ -7226,7 +7485,7 @@ async def test_method_create_overload_30(self, async_client: AsyncOrb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - async def test_method_create_with_all_params_overload_30(self, async_client: AsyncOrb) -> None: + async def test_method_create_with_all_params_overload_31(self, async_client: AsyncOrb) -> None: price = await async_client.prices.create( cadence="annual", currency="currency", @@ -7263,7 +7522,7 @@ async def test_method_create_with_all_params_overload_30(self, async_client: Asy assert_matches_type(Price, price, path=["response"]) @parametrize - async def test_raw_response_create_overload_30(self, async_client: AsyncOrb) -> None: + async def test_raw_response_create_overload_31(self, async_client: AsyncOrb) -> None: response = await async_client.prices.with_raw_response.create( cadence="annual", currency="currency", @@ -7279,7 +7538,7 @@ async def test_raw_response_create_overload_30(self, async_client: AsyncOrb) -> assert_matches_type(Price, price, path=["response"]) @parametrize - async def test_streaming_response_create_overload_30(self, async_client: AsyncOrb) -> None: + async def test_streaming_response_create_overload_31(self, async_client: AsyncOrb) -> None: async with async_client.prices.with_streaming_response.create( cadence="annual", currency="currency", @@ -7297,7 +7556,7 @@ async def test_streaming_response_create_overload_30(self, async_client: AsyncOr assert cast(Any, response.is_closed) is True @parametrize - async def test_method_create_overload_31(self, async_client: AsyncOrb) -> None: + async def test_method_create_overload_32(self, async_client: AsyncOrb) -> None: price = await async_client.prices.create( cadence="annual", currency="currency", @@ -7309,7 +7568,7 @@ async def test_method_create_overload_31(self, async_client: AsyncOrb) -> None: assert_matches_type(Price, price, path=["response"]) @parametrize - async def test_method_create_with_all_params_overload_31(self, async_client: AsyncOrb) -> None: + async def test_method_create_with_all_params_overload_32(self, async_client: AsyncOrb) -> None: price = await async_client.prices.create( cadence="annual", currency="currency", @@ -7350,7 +7609,7 @@ async def test_method_create_with_all_params_overload_31(self, async_client: Asy assert_matches_type(Price, price, path=["response"]) @parametrize - async def test_raw_response_create_overload_31(self, async_client: AsyncOrb) -> None: + async def test_raw_response_create_overload_32(self, async_client: AsyncOrb) -> None: response = await async_client.prices.with_raw_response.create( cadence="annual", currency="currency", @@ -7366,7 +7625,7 @@ async def test_raw_response_create_overload_31(self, async_client: AsyncOrb) -> assert_matches_type(Price, price, path=["response"]) @parametrize - async def test_streaming_response_create_overload_31(self, async_client: AsyncOrb) -> None: + async def test_streaming_response_create_overload_32(self, async_client: AsyncOrb) -> None: async with async_client.prices.with_streaming_response.create( cadence="annual", currency="currency", @@ -7481,6 +7740,7 @@ async def test_method_evaluate_with_all_params(self, async_client: AsyncOrb) -> external_customer_id="external_customer_id", filter="my_numeric_property > 100 AND my_other_property = 'bar'", grouping_keys=["case when my_event_type = 'foo' then true else false end"], + metric_parameter_overrides={"foo": "bar"}, ) assert_matches_type(PriceEvaluateResponse, price, path=["response"]) @@ -7541,6 +7801,7 @@ async def test_method_evaluate_multiple_with_all_params(self, async_client: Asyn "external_price_id": "external_price_id", "filter": "my_numeric_property > 100 AND my_other_property = 'bar'", "grouping_keys": ["case when my_event_type = 'foo' then true else false end"], + "metric_parameter_overrides": {"foo": "bar"}, "price": { "cadence": "annual", "currency": "currency", @@ -7638,6 +7899,7 @@ async def test_method_evaluate_preview_events_with_all_params(self, async_client "external_price_id": "external_price_id", "filter": "my_numeric_property > 100 AND my_other_property = 'bar'", "grouping_keys": ["case when my_event_type = 'foo' then true else false end"], + "metric_parameter_overrides": {"foo": "bar"}, "price": { "cadence": "annual", "currency": "currency", diff --git a/tests/api_resources/test_subscriptions.py b/tests/api_resources/test_subscriptions.py index 2078ccf8..849aad5c 100644 --- a/tests/api_resources/test_subscriptions.py +++ b/tests/api_resources/test_subscriptions.py @@ -77,6 +77,7 @@ def test_method_create_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "discounts": [ @@ -90,6 +91,7 @@ def test_method_create_with_all_params(self, client: Orb) -> None: "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), "external_price_id": "external_price_id", "maximum_amount": "1.23", + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": "1.23", "plan_phase_order": 0, "price": { @@ -211,6 +213,7 @@ def test_method_create_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "discounts": [ @@ -224,6 +227,7 @@ def test_method_create_with_all_params(self, client: Orb) -> None: "external_price_id": "external_price_id", "fixed_price_quantity": 2, "maximum_amount": "1.23", + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": "1.23", "price": { "cadence": "annual", @@ -648,7 +652,7 @@ def test_method_price_intervals_with_all_params(self, client: Orb) -> None: subscription_id="subscription_id", add=[ { - "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "start_date": "start_of_term", "allocation_price": { "amount": "10.00", "cadence": "monthly", @@ -667,6 +671,7 @@ def test_method_price_intervals_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "can_defer_billing": True, @@ -676,7 +681,7 @@ def test_method_price_intervals_with_all_params(self, client: Orb) -> None: "discount_type": "amount", } ], - "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "end_date": "start_of_term", "external_price_id": "external_price_id", "filter": "my_property > 100 AND my_other_property = 'bar'", "fixed_fee_quantity_transitions": [ @@ -686,6 +691,7 @@ def test_method_price_intervals_with_all_params(self, client: Orb) -> None: } ], "maximum_amount": 0, + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": 0, "price": { "cadence": "annual", @@ -729,7 +735,7 @@ def test_method_price_intervals_with_all_params(self, client: Orb) -> None: ], add_adjustments=[ { - "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "start_date": "start_of_term", "adjustment": { "adjustment_type": "percentage_discount", "percentage_discount": 0, @@ -748,7 +754,7 @@ def test_method_price_intervals_with_all_params(self, client: Orb) -> None: "price_type": "usage", }, "adjustment_id": "h74gfhdjvn7ujokd", - "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "end_date": "start_of_term", } ], allow_invoice_credit_or_void=True, @@ -758,7 +764,7 @@ def test_method_price_intervals_with_all_params(self, client: Orb) -> None: "price_interval_id": "sdfs6wdjvn7ujokd", "billing_cycle_day": 0, "can_defer_billing": True, - "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "end_date": "start_of_term", "filter": "my_property > 100 AND my_other_property = 'bar'", "fixed_fee_quantity_transitions": [ { @@ -766,15 +772,16 @@ def test_method_price_intervals_with_all_params(self, client: Orb) -> None: "quantity": 5, } ], - "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "metric_parameter_overrides": {"foo": "bar"}, + "start_date": "start_of_term", "usage_customer_ids": ["string"], } ], edit_adjustments=[ { "adjustment_interval_id": "sdfs6wdjvn7ujokd", - "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), - "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "end_date": "start_of_term", + "start_date": "start_of_term", } ], ) @@ -925,6 +932,7 @@ def test_method_schedule_plan_change_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "discounts": [ @@ -938,6 +946,7 @@ def test_method_schedule_plan_change_with_all_params(self, client: Orb) -> None: "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), "external_price_id": "external_price_id", "maximum_amount": "1.23", + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": "1.23", "plan_phase_order": 0, "price": { @@ -1052,6 +1061,7 @@ def test_method_schedule_plan_change_with_all_params(self, client: Orb) -> None: ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "discounts": [ @@ -1065,6 +1075,7 @@ def test_method_schedule_plan_change_with_all_params(self, client: Orb) -> None: "external_price_id": "external_price_id", "fixed_price_quantity": 2, "maximum_amount": "1.23", + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": "1.23", "price": { "cadence": "annual", @@ -1477,6 +1488,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "discounts": [ @@ -1490,6 +1502,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), "external_price_id": "external_price_id", "maximum_amount": "1.23", + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": "1.23", "plan_phase_order": 0, "price": { @@ -1611,6 +1624,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "discounts": [ @@ -1624,6 +1638,7 @@ async def test_method_create_with_all_params(self, async_client: AsyncOrb) -> No "external_price_id": "external_price_id", "fixed_price_quantity": 2, "maximum_amount": "1.23", + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": "1.23", "price": { "cadence": "annual", @@ -2048,7 +2063,7 @@ async def test_method_price_intervals_with_all_params(self, async_client: AsyncO subscription_id="subscription_id", add=[ { - "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "start_date": "start_of_term", "allocation_price": { "amount": "10.00", "cadence": "monthly", @@ -2067,6 +2082,7 @@ async def test_method_price_intervals_with_all_params(self, async_client: AsyncO ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "can_defer_billing": True, @@ -2076,7 +2092,7 @@ async def test_method_price_intervals_with_all_params(self, async_client: AsyncO "discount_type": "amount", } ], - "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "end_date": "start_of_term", "external_price_id": "external_price_id", "filter": "my_property > 100 AND my_other_property = 'bar'", "fixed_fee_quantity_transitions": [ @@ -2086,6 +2102,7 @@ async def test_method_price_intervals_with_all_params(self, async_client: AsyncO } ], "maximum_amount": 0, + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": 0, "price": { "cadence": "annual", @@ -2129,7 +2146,7 @@ async def test_method_price_intervals_with_all_params(self, async_client: AsyncO ], add_adjustments=[ { - "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "start_date": "start_of_term", "adjustment": { "adjustment_type": "percentage_discount", "percentage_discount": 0, @@ -2148,7 +2165,7 @@ async def test_method_price_intervals_with_all_params(self, async_client: AsyncO "price_type": "usage", }, "adjustment_id": "h74gfhdjvn7ujokd", - "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "end_date": "start_of_term", } ], allow_invoice_credit_or_void=True, @@ -2158,7 +2175,7 @@ async def test_method_price_intervals_with_all_params(self, async_client: AsyncO "price_interval_id": "sdfs6wdjvn7ujokd", "billing_cycle_day": 0, "can_defer_billing": True, - "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "end_date": "start_of_term", "filter": "my_property > 100 AND my_other_property = 'bar'", "fixed_fee_quantity_transitions": [ { @@ -2166,15 +2183,16 @@ async def test_method_price_intervals_with_all_params(self, async_client: AsyncO "quantity": 5, } ], - "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "metric_parameter_overrides": {"foo": "bar"}, + "start_date": "start_of_term", "usage_customer_ids": ["string"], } ], edit_adjustments=[ { "adjustment_interval_id": "sdfs6wdjvn7ujokd", - "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), - "start_date": parse_datetime("2019-12-27T18:11:19.117Z"), + "end_date": "start_of_term", + "start_date": "start_of_term", } ], ) @@ -2325,6 +2343,7 @@ async def test_method_schedule_plan_change_with_all_params(self, async_client: A ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "discounts": [ @@ -2338,6 +2357,7 @@ async def test_method_schedule_plan_change_with_all_params(self, async_client: A "end_date": parse_datetime("2019-12-27T18:11:19.117Z"), "external_price_id": "external_price_id", "maximum_amount": "1.23", + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": "1.23", "plan_phase_order": 0, "price": { @@ -2452,6 +2472,7 @@ async def test_method_schedule_plan_change_with_all_params(self, async_client: A ], "item_id": "item_id", "license_type_id": "license_type_id", + "metadata": {"foo": "string"}, "per_unit_cost_basis": "per_unit_cost_basis", }, "discounts": [ @@ -2465,6 +2486,7 @@ async def test_method_schedule_plan_change_with_all_params(self, async_client: A "external_price_id": "external_price_id", "fixed_price_quantity": 2, "maximum_amount": "1.23", + "metric_parameter_overrides": {"foo": "bar"}, "minimum_amount": "1.23", "price": { "cadence": "annual", diff --git a/tests/test_client.py b/tests/test_client.py index 32ed4d23..d6290454 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -419,6 +419,30 @@ def test_default_query_option(self) -> None: client.close() + def test_hardcoded_query_params_in_url(self, client: Orb) -> None: + request = client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Orb) -> None: request = client._build_request( FinalRequestOptions( @@ -993,6 +1017,14 @@ def retry_handler(_request: httpx.Request) -> httpx.Response: def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + # Delete in case our environment has any proxy env vars set + monkeypatch.delenv("HTTP_PROXY", raising=False) + monkeypatch.delenv("ALL_PROXY", raising=False) + monkeypatch.delenv("NO_PROXY", raising=False) + monkeypatch.delenv("http_proxy", raising=False) + monkeypatch.delenv("https_proxy", raising=False) + monkeypatch.delenv("all_proxy", raising=False) + monkeypatch.delenv("no_proxy", raising=False) client = DefaultHttpxClient() @@ -1358,6 +1390,30 @@ async def test_default_query_option(self) -> None: await client.close() + async def test_hardcoded_query_params_in_url(self, async_client: AsyncOrb) -> None: + request = async_client._build_request(FinalRequestOptions(method="get", url="/foo?beta=true")) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/foo?beta=true", + params={"limit": "10", "page": "abc"}, + ) + ) + url = httpx.URL(request.url) + assert dict(url.params) == {"beta": "true", "limit": "10", "page": "abc"} + + request = async_client._build_request( + FinalRequestOptions( + method="get", + url="/files/a%2Fb?beta=true", + params={"limit": "10"}, + ) + ) + assert request.url.raw_path == b"/files/a%2Fb?beta=true&limit=10" + def test_request_extra_json(self, client: Orb) -> None: request = client._build_request( FinalRequestOptions( @@ -1947,6 +2003,14 @@ async def test_get_platform(self) -> None: async def test_proxy_environment_variables(self, monkeypatch: pytest.MonkeyPatch) -> None: # Test that the proxy environment variables are set correctly monkeypatch.setenv("HTTPS_PROXY", "https://example.org") + # Delete in case our environment has any proxy env vars set + monkeypatch.delenv("HTTP_PROXY", raising=False) + monkeypatch.delenv("ALL_PROXY", raising=False) + monkeypatch.delenv("NO_PROXY", raising=False) + monkeypatch.delenv("http_proxy", raising=False) + monkeypatch.delenv("https_proxy", raising=False) + monkeypatch.delenv("all_proxy", raising=False) + monkeypatch.delenv("no_proxy", raising=False) client = DefaultAsyncHttpxClient() diff --git a/tests/test_deepcopy.py b/tests/test_deepcopy.py deleted file mode 100644 index c05f70c0..00000000 --- a/tests/test_deepcopy.py +++ /dev/null @@ -1,58 +0,0 @@ -from orb._utils import deepcopy_minimal - - -def assert_different_identities(obj1: object, obj2: object) -> None: - assert obj1 == obj2 - assert id(obj1) != id(obj2) - - -def test_simple_dict() -> None: - obj1 = {"foo": "bar"} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - - -def test_nested_dict() -> None: - obj1 = {"foo": {"bar": True}} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1["foo"], obj2["foo"]) - - -def test_complex_nested_dict() -> None: - obj1 = {"foo": {"bar": [{"hello": "world"}]}} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1["foo"], obj2["foo"]) - assert_different_identities(obj1["foo"]["bar"], obj2["foo"]["bar"]) - assert_different_identities(obj1["foo"]["bar"][0], obj2["foo"]["bar"][0]) - - -def test_simple_list() -> None: - obj1 = ["a", "b", "c"] - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - - -def test_nested_list() -> None: - obj1 = ["a", [1, 2, 3]] - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert_different_identities(obj1[1], obj2[1]) - - -class MyObject: ... - - -def test_ignores_other_types() -> None: - # custom classes - my_obj = MyObject() - obj1 = {"foo": my_obj} - obj2 = deepcopy_minimal(obj1) - assert_different_identities(obj1, obj2) - assert obj1["foo"] is my_obj - - # tuples - obj3 = ("a", "b") - obj4 = deepcopy_minimal(obj3) - assert obj3 is obj4 diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index 722b1a6c..180faff0 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -4,7 +4,7 @@ import pytest -from orb._types import FileTypes +from orb._types import FileTypes, ArrayFormat from orb._utils import extract_files @@ -35,6 +35,12 @@ def test_multiple_files() -> None: assert query == {"documents": [{}, {}]} +def test_top_level_file_array() -> None: + query = {"files": [b"file one", b"file two"], "title": "hello"} + assert extract_files(query, paths=[["files", ""]]) == [("files[]", b"file one"), ("files[]", b"file two")] + assert query == {"title": "hello"} + + @pytest.mark.parametrize( "query,paths,expected", [ @@ -62,3 +68,24 @@ def test_ignores_incorrect_paths( expected: list[tuple[str, FileTypes]], ) -> None: assert extract_files(query, paths=paths) == expected + + +@pytest.mark.parametrize( + "array_format,expected_top_level,expected_nested", + [ + ("brackets", [("files[]", b"a"), ("files[]", b"b")], [("items[][file]", b"a"), ("items[][file]", b"b")]), + ("repeat", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("comma", [("files", b"a"), ("files", b"b")], [("items[file]", b"a"), ("items[file]", b"b")]), + ("indices", [("files[0]", b"a"), ("files[1]", b"b")], [("items[0][file]", b"a"), ("items[1][file]", b"b")]), + ], +) +def test_array_format_controls_file_field_names( + array_format: ArrayFormat, + expected_top_level: list[tuple[str, FileTypes]], + expected_nested: list[tuple[str, FileTypes]], +) -> None: + top_level = {"files": [b"a", b"b"]} + assert extract_files(top_level, paths=[["files", ""]], array_format=array_format) == expected_top_level + + nested = {"items": [{"file": b"a"}, {"file": b"b"}]} + assert extract_files(nested, paths=[["items", "", "file"]], array_format=array_format) == expected_nested diff --git a/tests/test_files.py b/tests/test_files.py index b84f4e26..20e2990c 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -4,7 +4,8 @@ import pytest from dirty_equals import IsDict, IsList, IsBytes, IsTuple -from orb._files import to_httpx_files, async_to_httpx_files +from orb._files import to_httpx_files, deepcopy_with_paths, async_to_httpx_files +from orb._utils import extract_files readme_path = Path(__file__).parent.parent.joinpath("README.md") @@ -49,3 +50,99 @@ def test_string_not_allowed() -> None: "file": "foo", # type: ignore } ) + + +def assert_different_identities(obj1: object, obj2: object) -> None: + assert obj1 == obj2 + assert obj1 is not obj2 + + +class TestDeepcopyWithPaths: + def test_copies_top_level_dict(self) -> None: + original = {"file": b"data", "other": "value"} + result = deepcopy_with_paths(original, [["file"]]) + assert_different_identities(result, original) + + def test_file_value_is_same_reference(self) -> None: + file_bytes = b"contents" + original = {"file": file_bytes} + result = deepcopy_with_paths(original, [["file"]]) + assert_different_identities(result, original) + assert result["file"] is file_bytes + + def test_list_popped_wholesale(self) -> None: + files = [b"f1", b"f2"] + original = {"files": files, "title": "t"} + result = deepcopy_with_paths(original, [["files", ""]]) + assert_different_identities(result, original) + result_files = result["files"] + assert isinstance(result_files, list) + assert_different_identities(result_files, files) + + def test_nested_array_path_copies_list_and_elements(self) -> None: + elem1 = {"file": b"f1", "extra": 1} + elem2 = {"file": b"f2", "extra": 2} + original = {"items": [elem1, elem2]} + result = deepcopy_with_paths(original, [["items", "", "file"]]) + assert_different_identities(result, original) + result_items = result["items"] + assert isinstance(result_items, list) + assert_different_identities(result_items, original["items"]) + assert_different_identities(result_items[0], elem1) + assert_different_identities(result_items[1], elem2) + + def test_empty_paths_returns_same_object(self) -> None: + original = {"foo": "bar"} + result = deepcopy_with_paths(original, []) + assert result is original + + def test_multiple_paths(self) -> None: + f1 = b"file1" + f2 = b"file2" + original = {"a": f1, "b": f2, "c": "unchanged"} + result = deepcopy_with_paths(original, [["a"], ["b"]]) + assert_different_identities(result, original) + assert result["a"] is f1 + assert result["b"] is f2 + assert result["c"] is original["c"] + + def test_extract_files_does_not_mutate_original_top_level(self) -> None: + file_bytes = b"contents" + original = {"file": file_bytes, "other": "value"} + + copied = deepcopy_with_paths(original, [["file"]]) + extracted = extract_files(copied, paths=[["file"]]) + + assert extracted == [("file", file_bytes)] + assert original == {"file": file_bytes, "other": "value"} + assert copied == {"other": "value"} + + def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: + file1 = b"f1" + file2 = b"f2" + original = { + "items": [ + {"file": file1, "extra": 1}, + {"file": file2, "extra": 2}, + ], + "title": "example", + } + + copied = deepcopy_with_paths(original, [["items", "", "file"]]) + extracted = extract_files(copied, paths=[["items", "", "file"]]) + + assert [entry for _, entry in extracted] == [file1, file2] + assert original == { + "items": [ + {"file": file1, "extra": 1}, + {"file": file2, "extra": 2}, + ], + "title": "example", + } + assert copied == { + "items": [ + {"extra": 1}, + {"extra": 2}, + ], + "title": "example", + } diff --git a/tests/test_utils/test_path.py b/tests/test_utils/test_path.py new file mode 100644 index 00000000..a6b990b5 --- /dev/null +++ b/tests/test_utils/test_path.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from typing import Any + +import pytest + +from orb._utils._path import path_template + + +@pytest.mark.parametrize( + "template, kwargs, expected", + [ + ("/v1/{id}", dict(id="abc"), "/v1/abc"), + ("/v1/{a}/{b}", dict(a="x", b="y"), "/v1/x/y"), + ("/v1/{a}{b}/path/{c}?val={d}#{e}", dict(a="x", b="y", c="z", d="u", e="v"), "/v1/xy/path/z?val=u#v"), + ("/{w}/{w}", dict(w="echo"), "/echo/echo"), + ("/v1/static", {}, "/v1/static"), + ("", {}, ""), + ("/v1/?q={n}&count=10", dict(n=42), "/v1/?q=42&count=10"), + ("/v1/{v}", dict(v=None), "/v1/null"), + ("/v1/{v}", dict(v=True), "/v1/true"), + ("/v1/{v}", dict(v=False), "/v1/false"), + ("/v1/{v}", dict(v=".hidden"), "/v1/.hidden"), # dot prefix ok + ("/v1/{v}", dict(v="file.txt"), "/v1/file.txt"), # dot in middle ok + ("/v1/{v}", dict(v="..."), "/v1/..."), # triple dot ok + ("/v1/{a}{b}", dict(a=".", b="txt"), "/v1/.txt"), # dot var combining with adjacent to be ok + ("/items?q={v}#{f}", dict(v=".", f=".."), "/items?q=.#.."), # dots in query/fragment are fine + ( + "/v1/{a}?query={b}", + dict(a="../../other/endpoint", b="a&bad=true"), + "/v1/..%2F..%2Fother%2Fendpoint?query=a%26bad%3Dtrue", + ), + ("/v1/{val}", dict(val="a/b/c"), "/v1/a%2Fb%2Fc"), + ("/v1/{val}", dict(val="a/b/c?query=value"), "/v1/a%2Fb%2Fc%3Fquery=value"), + ("/v1/{val}", dict(val="a/b/c?query=value&bad=true"), "/v1/a%2Fb%2Fc%3Fquery=value&bad=true"), + ("/v1/{val}", dict(val="%20"), "/v1/%2520"), # escapes escape sequences in input + # Query: slash and ? are safe, # is not + ("/items?q={v}", dict(v="a/b"), "/items?q=a/b"), + ("/items?q={v}", dict(v="a?b"), "/items?q=a?b"), + ("/items?q={v}", dict(v="a#b"), "/items?q=a%23b"), + ("/items?q={v}", dict(v="a b"), "/items?q=a%20b"), + # Fragment: slash and ? are safe + ("/docs#{v}", dict(v="a/b"), "/docs#a/b"), + ("/docs#{v}", dict(v="a?b"), "/docs#a?b"), + # Path: slash, ? and # are all encoded + ("/v1/{v}", dict(v="a/b"), "/v1/a%2Fb"), + ("/v1/{v}", dict(v="a?b"), "/v1/a%3Fb"), + ("/v1/{v}", dict(v="a#b"), "/v1/a%23b"), + # same var encoded differently by component + ( + "/v1/{v}?q={v}#{v}", + dict(v="a/b?c#d"), + "/v1/a%2Fb%3Fc%23d?q=a/b?c%23d#a/b?c%23d", + ), + ("/v1/{val}", dict(val="x?admin=true"), "/v1/x%3Fadmin=true"), # query injection + ("/v1/{val}", dict(val="x#admin"), "/v1/x%23admin"), # fragment injection + ], +) +def test_interpolation(template: str, kwargs: dict[str, Any], expected: str) -> None: + assert path_template(template, **kwargs) == expected + + +def test_missing_kwarg_raises_key_error() -> None: + with pytest.raises(KeyError, match="org_id"): + path_template("/v1/{org_id}") + + +@pytest.mark.parametrize( + "template, kwargs", + [ + ("{a}/path", dict(a=".")), + ("{a}/path", dict(a="..")), + ("/v1/{a}", dict(a=".")), + ("/v1/{a}", dict(a="..")), + ("/v1/{a}/path", dict(a=".")), + ("/v1/{a}/path", dict(a="..")), + ("/v1/{a}{b}", dict(a=".", b=".")), # adjacent vars → ".." + ("/v1/{a}.", dict(a=".")), # var + static → ".." + ("/v1/{a}{b}", dict(a="", b=".")), # empty + dot → "." + ("/v1/%2e/{x}", dict(x="ok")), # encoded dot in static text + ("/v1/%2e./{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/.%2E/{x}", dict(x="ok")), # mixed encoded ".." in static + ("/v1/{v}?q=1", dict(v="..")), + ("/v1/{v}#frag", dict(v="..")), + ], +) +def test_dot_segment_rejected(template: str, kwargs: dict[str, Any]) -> None: + with pytest.raises(ValueError, match="dot-segment"): + path_template(template, **kwargs)