From d1effd76e6d5a03fad1a5d8419ca8b52816b39e9 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 17:35:17 +0000 Subject: [PATCH 01/31] fix(pydantic): do not pass `by_alias` unless set --- src/m3ter/_compat.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/m3ter/_compat.py b/src/m3ter/_compat.py index 786ff42..e6690a4 100644 --- a/src/m3ter/_compat.py +++ b/src/m3ter/_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]", From f4e0bf765ad9b9ac52fd1b20961502b0ffb4c5ab Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 19:04:14 +0000 Subject: [PATCH 02/31] fix(deps): bump minimum typing-extensions version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 7fad8c4..39c3bf3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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", From 8b957b0f1ae1fa8319ef24fc46b53a02f1c1a7e5 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:52:28 +0000 Subject: [PATCH 03/31] chore(internal): tweak CI branches --- .github/workflows/ci.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b5898d..d070711 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/**' From 5e988949139bf65480de9f9d2c698fee6e075439 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 15:38:48 +0000 Subject: [PATCH 04/31] fix: sanitize endpoint path params --- src/m3ter/_utils/__init__.py | 1 + src/m3ter/_utils/_path.py | 127 +++++++++++++++++ src/m3ter/resources/account_plans.py | 22 +-- src/m3ter/resources/accounts.py | 34 ++--- src/m3ter/resources/aggregations.py | 22 +-- src/m3ter/resources/balances/balances.py | 22 +-- .../resources/balances/charge_schedules.py | 80 +++++++++-- .../balances/transaction_schedules.py | 80 +++++++++-- src/m3ter/resources/balances/transactions.py | 30 +++- src/m3ter/resources/bill_config.py | 10 +- src/m3ter/resources/bill_jobs.py | 22 +-- src/m3ter/resources/bills/bills.py | 34 ++--- .../resources/bills/credit_line_items.py | 34 +++-- src/m3ter/resources/bills/debit_line_items.py | 34 +++-- src/m3ter/resources/bills/line_items.py | 14 +- src/m3ter/resources/charges.py | 22 +-- src/m3ter/resources/commitments.py | 26 ++-- src/m3ter/resources/compound_aggregations.py | 22 +-- src/m3ter/resources/contracts.py | 26 ++-- src/m3ter/resources/counter_adjustments.py | 22 +-- src/m3ter/resources/counter_pricings.py | 22 +-- src/m3ter/resources/counters.py | 22 +-- src/m3ter/resources/credit_reasons.py | 22 +-- src/m3ter/resources/currencies.py | 22 +-- src/m3ter/resources/custom_fields.py | 10 +- .../resources/data_exports/data_exports.py | 6 +- .../resources/data_exports/destinations.py | 22 +-- src/m3ter/resources/data_exports/jobs.py | 18 ++- src/m3ter/resources/data_exports/schedules.py | 22 +-- src/m3ter/resources/debit_reasons.py | 22 +-- src/m3ter/resources/events.py | 18 +-- src/m3ter/resources/external_mappings.py | 52 +++++-- .../resources/integration_configurations.py | 38 +++-- .../lookup_table_revision_data.py | 134 +++++++++++++++--- .../lookup_table_revision_data_jobs.py | 62 ++++++-- .../lookup_tables/lookup_table_revisions.py | 82 +++++++++-- .../resources/lookup_tables/lookup_tables.py | 22 +-- src/m3ter/resources/meters.py | 22 +-- .../resources/notification_configurations.py | 22 +-- src/m3ter/resources/organization_config.py | 10 +- src/m3ter/resources/permission_policies.py | 118 +++++++++++---- src/m3ter/resources/plan_group_links.py | 22 +-- src/m3ter/resources/plan_groups.py | 22 +-- src/m3ter/resources/plan_templates.py | 22 +-- src/m3ter/resources/plans.py | 22 +-- src/m3ter/resources/pricings.py | 22 +-- src/m3ter/resources/products.py | 22 +-- src/m3ter/resources/resource_groups.py | 78 +++++++--- .../scheduled_event_configurations.py | 22 +-- .../statements/statement_definitions.py | 22 +-- .../resources/statements/statement_jobs.py | 22 +-- src/m3ter/resources/statements/statements.py | 13 +- src/m3ter/resources/transaction_types.py | 22 +-- .../usage/file_uploads/file_uploads.py | 6 +- .../resources/usage/file_uploads/jobs.py | 14 +- src/m3ter/resources/usage/usage.py | 20 ++- src/m3ter/resources/users/invitations.py | 14 +- src/m3ter/resources/users/users.py | 30 ++-- src/m3ter/resources/webhooks.py | 26 ++-- tests/test_utils/test_path.py | 89 ++++++++++++ 60 files changed, 1332 insertions(+), 630 deletions(-) create mode 100644 src/m3ter/_utils/_path.py create mode 100644 tests/test_utils/test_path.py diff --git a/src/m3ter/_utils/__init__.py b/src/m3ter/_utils/__init__.py index dc64e29..10cb66d 100644 --- a/src/m3ter/_utils/__init__.py +++ b/src/m3ter/_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 ( diff --git a/src/m3ter/_utils/_path.py b/src/m3ter/_utils/_path.py new file mode 100644 index 0000000..4d6e1e4 --- /dev/null +++ b/src/m3ter/_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/m3ter/resources/account_plans.py b/src/m3ter/resources/account_plans.py index 06aafe2..b554a4a 100644 --- a/src/m3ter/resources/account_plans.py +++ b/src/m3ter/resources/account_plans.py @@ -10,7 +10,7 @@ from ..types import account_plan_list_params, account_plan_create_params, account_plan_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 ( @@ -194,7 +194,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/accountplans", + path_template("/organizations/{org_id}/accountplans", org_id=org_id), body=maybe_transform( { "account_id": account_id, @@ -249,7 +249,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/accountplans/{id}", + path_template("/organizations/{org_id}/accountplans/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -384,7 +384,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/accountplans/{id}", + path_template("/organizations/{org_id}/accountplans/{id}", org_id=org_id, id=id), body=maybe_transform( { "account_id": account_id, @@ -497,7 +497,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/accountplans", + path_template("/organizations/{org_id}/accountplans", org_id=org_id), page=SyncCursor[AccountPlanResponse], options=make_request_options( extra_headers=extra_headers, @@ -556,7 +556,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/accountplans/{id}", + path_template("/organizations/{org_id}/accountplans/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -732,7 +732,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/accountplans", + path_template("/organizations/{org_id}/accountplans", org_id=org_id), body=await async_maybe_transform( { "account_id": account_id, @@ -787,7 +787,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/accountplans/{id}", + path_template("/organizations/{org_id}/accountplans/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -922,7 +922,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/accountplans/{id}", + path_template("/organizations/{org_id}/accountplans/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "account_id": account_id, @@ -1035,7 +1035,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/accountplans", + path_template("/organizations/{org_id}/accountplans", org_id=org_id), page=AsyncCursor[AccountPlanResponse], options=make_request_options( extra_headers=extra_headers, @@ -1094,7 +1094,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/accountplans/{id}", + path_template("/organizations/{org_id}/accountplans/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/accounts.py b/src/m3ter/resources/accounts.py index 275e73a..7a0d1e0 100644 --- a/src/m3ter/resources/accounts.py +++ b/src/m3ter/resources/accounts.py @@ -17,7 +17,7 @@ account_end_date_billing_entities_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 ( @@ -224,7 +224,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/accounts", + path_template("/organizations/{org_id}/accounts", org_id=org_id), body=maybe_transform( { "code": code, @@ -281,7 +281,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/accounts/{id}", + path_template("/organizations/{org_id}/accounts/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -448,7 +448,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/accounts/{id}", + path_template("/organizations/{org_id}/accounts/{id}", org_id=org_id, id=id), body=maybe_transform( { "code": code, @@ -515,7 +515,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/accounts", + path_template("/organizations/{org_id}/accounts", org_id=org_id), page=SyncCursor[AccountResponse], options=make_request_options( extra_headers=extra_headers, @@ -568,7 +568,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/accounts/{id}", + path_template("/organizations/{org_id}/accounts/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -626,7 +626,7 @@ def end_date_billing_entities( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/accounts/{id}/enddatebillingentities", + path_template("/organizations/{org_id}/accounts/{id}/enddatebillingentities", org_id=org_id, id=id), body=maybe_transform( { "billing_entities": billing_entities, @@ -674,7 +674,7 @@ def list_children( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get_api_list( - f"/organizations/{org_id}/accounts/{id}/children", + path_template("/organizations/{org_id}/accounts/{id}/children", org_id=org_id, id=id), page=SyncCursor[AccountResponse], options=make_request_options( extra_headers=extra_headers, @@ -765,7 +765,7 @@ def search( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/accounts/search", + path_template("/organizations/{org_id}/accounts/search", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -975,7 +975,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/accounts", + path_template("/organizations/{org_id}/accounts", org_id=org_id), body=await async_maybe_transform( { "code": code, @@ -1032,7 +1032,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/accounts/{id}", + path_template("/organizations/{org_id}/accounts/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1199,7 +1199,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/accounts/{id}", + path_template("/organizations/{org_id}/accounts/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "code": code, @@ -1266,7 +1266,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/accounts", + path_template("/organizations/{org_id}/accounts", org_id=org_id), page=AsyncCursor[AccountResponse], options=make_request_options( extra_headers=extra_headers, @@ -1319,7 +1319,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/accounts/{id}", + path_template("/organizations/{org_id}/accounts/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1377,7 +1377,7 @@ async def end_date_billing_entities( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/accounts/{id}/enddatebillingentities", + path_template("/organizations/{org_id}/accounts/{id}/enddatebillingentities", org_id=org_id, id=id), body=await async_maybe_transform( { "billing_entities": billing_entities, @@ -1425,7 +1425,7 @@ def list_children( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get_api_list( - f"/organizations/{org_id}/accounts/{id}/children", + path_template("/organizations/{org_id}/accounts/{id}/children", org_id=org_id, id=id), page=AsyncCursor[AccountResponse], options=make_request_options( extra_headers=extra_headers, @@ -1516,7 +1516,7 @@ async def search( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/accounts/search", + path_template("/organizations/{org_id}/accounts/search", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/m3ter/resources/aggregations.py b/src/m3ter/resources/aggregations.py index 641f891..dc4872e 100644 --- a/src/m3ter/resources/aggregations.py +++ b/src/m3ter/resources/aggregations.py @@ -9,7 +9,7 @@ from ..types import aggregation_list_params, aggregation_create_params, aggregation_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 ( @@ -220,7 +220,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/aggregations", + path_template("/organizations/{org_id}/aggregations", org_id=org_id), body=maybe_transform( { "aggregation": aggregation, @@ -278,7 +278,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/aggregations/{id}", + path_template("/organizations/{org_id}/aggregations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -457,7 +457,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/aggregations/{id}", + path_template("/organizations/{org_id}/aggregations/{id}", org_id=org_id, id=id), body=maybe_transform( { "aggregation": aggregation, @@ -529,7 +529,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/aggregations", + path_template("/organizations/{org_id}/aggregations", org_id=org_id), page=SyncCursor[AggregationResponse], options=make_request_options( extra_headers=extra_headers, @@ -581,7 +581,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/aggregations/{id}", + path_template("/organizations/{org_id}/aggregations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -784,7 +784,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/aggregations", + path_template("/organizations/{org_id}/aggregations", org_id=org_id), body=await async_maybe_transform( { "aggregation": aggregation, @@ -842,7 +842,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/aggregations/{id}", + path_template("/organizations/{org_id}/aggregations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1021,7 +1021,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/aggregations/{id}", + path_template("/organizations/{org_id}/aggregations/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "aggregation": aggregation, @@ -1093,7 +1093,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/aggregations", + path_template("/organizations/{org_id}/aggregations", org_id=org_id), page=AsyncCursor[AggregationResponse], options=make_request_options( extra_headers=extra_headers, @@ -1145,7 +1145,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/aggregations/{id}", + path_template("/organizations/{org_id}/aggregations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/balances/balances.py b/src/m3ter/resources/balances/balances.py index e5158d4..7935946 100644 --- a/src/m3ter/resources/balances/balances.py +++ b/src/m3ter/resources/balances/balances.py @@ -10,7 +10,7 @@ from ...types import balance_list_params, balance_create_params, balance_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 ( @@ -312,7 +312,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/balances", + path_template("/organizations/{org_id}/balances", org_id=org_id), body=maybe_transform( { "account_id": account_id, @@ -377,7 +377,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/balances/{id}", + path_template("/organizations/{org_id}/balances/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -554,7 +554,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/balances/{id}", + path_template("/organizations/{org_id}/balances/{id}", org_id=org_id, id=id), body=maybe_transform( { "account_id": account_id, @@ -650,7 +650,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/balances", + path_template("/organizations/{org_id}/balances", org_id=org_id), page=SyncCursor[Balance], options=make_request_options( extra_headers=extra_headers, @@ -707,7 +707,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/balances/{id}", + path_template("/organizations/{org_id}/balances/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -977,7 +977,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/balances", + path_template("/organizations/{org_id}/balances", org_id=org_id), body=await async_maybe_transform( { "account_id": account_id, @@ -1042,7 +1042,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/balances/{id}", + path_template("/organizations/{org_id}/balances/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1219,7 +1219,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/balances/{id}", + path_template("/organizations/{org_id}/balances/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "account_id": account_id, @@ -1315,7 +1315,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/balances", + path_template("/organizations/{org_id}/balances", org_id=org_id), page=AsyncCursor[Balance], options=make_request_options( extra_headers=extra_headers, @@ -1372,7 +1372,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/balances/{id}", + path_template("/organizations/{org_id}/balances/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/balances/charge_schedules.py b/src/m3ter/resources/balances/charge_schedules.py index a26d246..55c22d1 100644 --- a/src/m3ter/resources/balances/charge_schedules.py +++ b/src/m3ter/resources/balances/charge_schedules.py @@ -9,7 +9,7 @@ import httpx 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 ( @@ -169,7 +169,11 @@ def create( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._post( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules", + org_id=org_id, + balance_id=balance_id, + ), body=maybe_transform( { "bill_frequency": bill_frequency, @@ -230,7 +234,12 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -348,7 +357,12 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), body=maybe_transform( { "bill_frequency": bill_frequency, @@ -413,7 +427,11 @@ def list( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._get_api_list( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules", + org_id=org_id, + balance_id=balance_id, + ), page=SyncCursor[ChargeScheduleListResponse], options=make_request_options( extra_headers=extra_headers, @@ -466,7 +484,12 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -590,7 +613,11 @@ def preview( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._post( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/preview", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/preview", + org_id=org_id, + balance_id=balance_id, + ), body=maybe_transform( { "bill_frequency": bill_frequency, @@ -761,7 +788,11 @@ async def create( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return await self._post( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules", + org_id=org_id, + balance_id=balance_id, + ), body=await async_maybe_transform( { "bill_frequency": bill_frequency, @@ -822,7 +853,12 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -940,7 +976,12 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), body=await async_maybe_transform( { "bill_frequency": bill_frequency, @@ -1005,7 +1046,11 @@ def list( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._get_api_list( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules", + org_id=org_id, + balance_id=balance_id, + ), page=AsyncCursor[ChargeScheduleListResponse], options=make_request_options( extra_headers=extra_headers, @@ -1058,7 +1103,12 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1182,7 +1232,11 @@ async def preview( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return await self._post( - f"/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/preview", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancechargeschedules/preview", + org_id=org_id, + balance_id=balance_id, + ), body=await async_maybe_transform( { "bill_frequency": bill_frequency, diff --git a/src/m3ter/resources/balances/transaction_schedules.py b/src/m3ter/resources/balances/transaction_schedules.py index 9fe6636..1059335 100644 --- a/src/m3ter/resources/balances/transaction_schedules.py +++ b/src/m3ter/resources/balances/transaction_schedules.py @@ -9,7 +9,7 @@ import httpx 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 ( @@ -154,7 +154,11 @@ def create( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._post( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules", + org_id=org_id, + balance_id=balance_id, + ), body=maybe_transform( { "amount": amount, @@ -213,7 +217,12 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -321,7 +330,12 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), body=maybe_transform( { "amount": amount, @@ -384,7 +398,11 @@ def list( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._get_api_list( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules", + org_id=org_id, + balance_id=balance_id, + ), page=SyncCursor[ScheduleResponse], options=make_request_options( extra_headers=extra_headers, @@ -437,7 +455,12 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -550,7 +573,11 @@ def preview( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._post( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/preview", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/preview", + org_id=org_id, + balance_id=balance_id, + ), body=maybe_transform( { "amount": amount, @@ -709,7 +736,11 @@ async def create( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return await self._post( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules", + org_id=org_id, + balance_id=balance_id, + ), body=await async_maybe_transform( { "amount": amount, @@ -768,7 +799,12 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -876,7 +912,12 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), body=await async_maybe_transform( { "amount": amount, @@ -939,7 +980,11 @@ def list( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._get_api_list( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules", + org_id=org_id, + balance_id=balance_id, + ), page=AsyncCursor[ScheduleResponse], options=make_request_options( extra_headers=extra_headers, @@ -992,7 +1037,12 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/{id}", + org_id=org_id, + balance_id=balance_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1105,7 +1155,11 @@ async def preview( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return await self._post( - f"/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/preview", + path_template( + "/organizations/{org_id}/balances/{balance_id}/balancetransactionschedules/preview", + org_id=org_id, + balance_id=balance_id, + ), body=await async_maybe_transform( { "amount": amount, diff --git a/src/m3ter/resources/balances/transactions.py b/src/m3ter/resources/balances/transactions.py index 292a131..27fea58 100644 --- a/src/m3ter/resources/balances/transactions.py +++ b/src/m3ter/resources/balances/transactions.py @@ -9,7 +9,7 @@ import httpx 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 ( @@ -156,7 +156,9 @@ def create( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._post( - f"/organizations/{org_id}/balances/{balance_id}/transactions", + path_template( + "/organizations/{org_id}/balances/{balance_id}/transactions", org_id=org_id, balance_id=balance_id + ), body=maybe_transform( { "amount": amount, @@ -223,7 +225,9 @@ def list( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._get_api_list( - f"/organizations/{org_id}/balances/{balance_id}/transactions", + path_template( + "/organizations/{org_id}/balances/{balance_id}/transactions", org_id=org_id, balance_id=balance_id + ), page=SyncCursor[TransactionResponse], options=make_request_options( extra_headers=extra_headers, @@ -297,7 +301,11 @@ def summary( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._get( - f"/organizations/{org_id}/balances/{balance_id}/transactions/summary", + path_template( + "/organizations/{org_id}/balances/{balance_id}/transactions/summary", + org_id=org_id, + balance_id=balance_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -434,7 +442,9 @@ async def create( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return await self._post( - f"/organizations/{org_id}/balances/{balance_id}/transactions", + path_template( + "/organizations/{org_id}/balances/{balance_id}/transactions", org_id=org_id, balance_id=balance_id + ), body=await async_maybe_transform( { "amount": amount, @@ -501,7 +511,9 @@ def list( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return self._get_api_list( - f"/organizations/{org_id}/balances/{balance_id}/transactions", + path_template( + "/organizations/{org_id}/balances/{balance_id}/transactions", org_id=org_id, balance_id=balance_id + ), page=AsyncCursor[TransactionResponse], options=make_request_options( extra_headers=extra_headers, @@ -575,7 +587,11 @@ async def summary( if not balance_id: raise ValueError(f"Expected a non-empty value for `balance_id` but received {balance_id!r}") return await self._get( - f"/organizations/{org_id}/balances/{balance_id}/transactions/summary", + path_template( + "/organizations/{org_id}/balances/{balance_id}/transactions/summary", + org_id=org_id, + balance_id=balance_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/bill_config.py b/src/m3ter/resources/bill_config.py index 8d9c4c0..c3efb90 100644 --- a/src/m3ter/resources/bill_config.py +++ b/src/m3ter/resources/bill_config.py @@ -9,7 +9,7 @@ from ..types import bill_config_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 ( @@ -82,7 +82,7 @@ def retrieve( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/billconfig", + path_template("/organizations/{org_id}/billconfig", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -135,7 +135,7 @@ def update( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._put( - f"/organizations/{org_id}/billconfig", + path_template("/organizations/{org_id}/billconfig", org_id=org_id), body=maybe_transform( { "bill_lock_date": bill_lock_date, @@ -208,7 +208,7 @@ async def retrieve( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/billconfig", + path_template("/organizations/{org_id}/billconfig", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -261,7 +261,7 @@ async def update( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._put( - f"/organizations/{org_id}/billconfig", + path_template("/organizations/{org_id}/billconfig", org_id=org_id), body=await async_maybe_transform( { "bill_lock_date": bill_lock_date, diff --git a/src/m3ter/resources/bill_jobs.py b/src/m3ter/resources/bill_jobs.py index c28010a..ee8e2c5 100644 --- a/src/m3ter/resources/bill_jobs.py +++ b/src/m3ter/resources/bill_jobs.py @@ -10,7 +10,7 @@ from ..types import bill_job_list_params, bill_job_create_params, bill_job_recalculate_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 ( @@ -214,7 +214,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/billjobs", + path_template("/organizations/{org_id}/billjobs", org_id=org_id), body=maybe_transform( { "account_ids": account_ids, @@ -272,7 +272,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/billjobs/{id}", + path_template("/organizations/{org_id}/billjobs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -338,7 +338,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/billjobs", + path_template("/organizations/{org_id}/billjobs", org_id=org_id), page=SyncCursor[BillJobResponse], options=make_request_options( extra_headers=extra_headers, @@ -393,7 +393,7 @@ def cancel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/organizations/{org_id}/billjobs/{id}/cancel", + path_template("/organizations/{org_id}/billjobs/{id}/cancel", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -457,7 +457,7 @@ def recalculate( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/billjobs/recalculate", + path_template("/organizations/{org_id}/billjobs/recalculate", org_id=org_id), body=maybe_transform( { "bill_ids": bill_ids, @@ -659,7 +659,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/billjobs", + path_template("/organizations/{org_id}/billjobs", org_id=org_id), body=await async_maybe_transform( { "account_ids": account_ids, @@ -717,7 +717,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/billjobs/{id}", + path_template("/organizations/{org_id}/billjobs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -783,7 +783,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/billjobs", + path_template("/organizations/{org_id}/billjobs", org_id=org_id), page=AsyncCursor[BillJobResponse], options=make_request_options( extra_headers=extra_headers, @@ -838,7 +838,7 @@ async def cancel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/organizations/{org_id}/billjobs/{id}/cancel", + path_template("/organizations/{org_id}/billjobs/{id}/cancel", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -902,7 +902,7 @@ async def recalculate( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/billjobs/recalculate", + path_template("/organizations/{org_id}/billjobs/recalculate", org_id=org_id), body=await async_maybe_transform( { "bill_ids": bill_ids, diff --git a/src/m3ter/resources/bills/bills.py b/src/m3ter/resources/bills/bills.py index b3c4fe3..2fd0187 100644 --- a/src/m3ter/resources/bills/bills.py +++ b/src/m3ter/resources/bills/bills.py @@ -16,7 +16,7 @@ bill_latest_by_account_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 .line_items import ( LineItemsResource, @@ -148,7 +148,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/bills/{id}", + path_template("/organizations/{org_id}/bills/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -243,7 +243,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/bills", + path_template("/organizations/{org_id}/bills", org_id=org_id), page=SyncCursor[BillResponse], options=make_request_options( extra_headers=extra_headers, @@ -312,7 +312,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/bills/{id}", + path_template("/organizations/{org_id}/bills/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -377,7 +377,7 @@ def approve( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/bills/approve", + path_template("/organizations/{org_id}/bills/approve", org_id=org_id), body=maybe_transform({"bill_ids": bill_ids}, bill_approve_params.BillApproveParams), options=make_request_options( extra_headers=extra_headers, @@ -434,7 +434,7 @@ def latest_by_account( if not account_id: raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") return self._get( - f"/organizations/{org_id}/bills/latest/{account_id}", + path_template("/organizations/{org_id}/bills/latest/{account_id}", org_id=org_id, account_id=account_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -485,7 +485,7 @@ def lock( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/bills/{id}/lock", + path_template("/organizations/{org_id}/bills/{id}/lock", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -565,7 +565,7 @@ def search( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/bills/search", + path_template("/organizations/{org_id}/bills/search", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -624,7 +624,7 @@ def update_status( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/bills/{id}/status", + path_template("/organizations/{org_id}/bills/{id}/status", org_id=org_id, id=id), body=maybe_transform({"status": status}, bill_update_status_params.BillUpdateStatusParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -723,7 +723,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/bills/{id}", + path_template("/organizations/{org_id}/bills/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -818,7 +818,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/bills", + path_template("/organizations/{org_id}/bills", org_id=org_id), page=AsyncCursor[BillResponse], options=make_request_options( extra_headers=extra_headers, @@ -887,7 +887,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/bills/{id}", + path_template("/organizations/{org_id}/bills/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -952,7 +952,7 @@ async def approve( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/bills/approve", + path_template("/organizations/{org_id}/bills/approve", org_id=org_id), body=await async_maybe_transform({"bill_ids": bill_ids}, bill_approve_params.BillApproveParams), options=make_request_options( extra_headers=extra_headers, @@ -1009,7 +1009,7 @@ async def latest_by_account( if not account_id: raise ValueError(f"Expected a non-empty value for `account_id` but received {account_id!r}") return await self._get( - f"/organizations/{org_id}/bills/latest/{account_id}", + path_template("/organizations/{org_id}/bills/latest/{account_id}", org_id=org_id, account_id=account_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1060,7 +1060,7 @@ async def lock( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/bills/{id}/lock", + path_template("/organizations/{org_id}/bills/{id}/lock", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1140,7 +1140,7 @@ async def search( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/bills/search", + path_template("/organizations/{org_id}/bills/search", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1199,7 +1199,7 @@ async def update_status( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/bills/{id}/status", + path_template("/organizations/{org_id}/bills/{id}/status", org_id=org_id, id=id), body=await async_maybe_transform({"status": status}, bill_update_status_params.BillUpdateStatusParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/m3ter/resources/bills/credit_line_items.py b/src/m3ter/resources/bills/credit_line_items.py index 6d79612..1318476 100644 --- a/src/m3ter/resources/bills/credit_line_items.py +++ b/src/m3ter/resources/bills/credit_line_items.py @@ -9,7 +9,7 @@ import httpx 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 ( @@ -156,7 +156,7 @@ def create( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return self._post( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/creditlineitems", org_id=org_id, bill_id=bill_id), body=maybe_transform( { "accounting_product_id": accounting_product_id, @@ -215,7 +215,9 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -325,7 +327,9 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), body=maybe_transform( { "accounting_product_id": accounting_product_id, @@ -387,7 +391,7 @@ def list( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return self._get_api_list( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/creditlineitems", org_id=org_id, bill_id=bill_id), page=SyncCursor[CreditLineItemResponse], options=make_request_options( extra_headers=extra_headers, @@ -439,7 +443,9 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -577,7 +583,7 @@ async def create( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return await self._post( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/creditlineitems", org_id=org_id, bill_id=bill_id), body=await async_maybe_transform( { "accounting_product_id": accounting_product_id, @@ -636,7 +642,9 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -746,7 +754,9 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), body=await async_maybe_transform( { "accounting_product_id": accounting_product_id, @@ -808,7 +818,7 @@ def list( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return self._get_api_list( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/creditlineitems", org_id=org_id, bill_id=bill_id), page=AsyncCursor[CreditLineItemResponse], options=make_request_options( extra_headers=extra_headers, @@ -860,7 +870,9 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/creditlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/bills/debit_line_items.py b/src/m3ter/resources/bills/debit_line_items.py index 6b762de..f0b3710 100644 --- a/src/m3ter/resources/bills/debit_line_items.py +++ b/src/m3ter/resources/bills/debit_line_items.py @@ -9,7 +9,7 @@ import httpx 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 ( @@ -155,7 +155,7 @@ def create( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return self._post( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/debitlineitems", org_id=org_id, bill_id=bill_id), body=maybe_transform( { "accounting_product_id": accounting_product_id, @@ -214,7 +214,9 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -324,7 +326,9 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), body=maybe_transform( { "accounting_product_id": accounting_product_id, @@ -386,7 +390,7 @@ def list( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return self._get_api_list( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/debitlineitems", org_id=org_id, bill_id=bill_id), page=SyncCursor[DebitLineItemResponse], options=make_request_options( extra_headers=extra_headers, @@ -438,7 +442,9 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -575,7 +581,7 @@ async def create( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return await self._post( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/debitlineitems", org_id=org_id, bill_id=bill_id), body=await async_maybe_transform( { "accounting_product_id": accounting_product_id, @@ -634,7 +640,9 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -744,7 +752,9 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), body=await async_maybe_transform( { "accounting_product_id": accounting_product_id, @@ -806,7 +816,7 @@ def list( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return self._get_api_list( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/debitlineitems", org_id=org_id, bill_id=bill_id), page=AsyncCursor[DebitLineItemResponse], options=make_request_options( extra_headers=extra_headers, @@ -858,7 +868,9 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/debitlineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/bills/line_items.py b/src/m3ter/resources/bills/line_items.py index 2585fd2..4a8b39b 100644 --- a/src/m3ter/resources/bills/line_items.py +++ b/src/m3ter/resources/bills/line_items.py @@ -5,7 +5,7 @@ import httpx 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 ( @@ -88,7 +88,9 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/bills/{bill_id}/lineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/lineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -145,7 +147,7 @@ def list( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return self._get_api_list( - f"/organizations/{org_id}/bills/{bill_id}/lineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/lineitems", org_id=org_id, bill_id=bill_id), page=SyncCursor[LineItemResponse], options=make_request_options( extra_headers=extra_headers, @@ -231,7 +233,9 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/bills/{bill_id}/lineitems/{id}", + path_template( + "/organizations/{org_id}/bills/{bill_id}/lineitems/{id}", org_id=org_id, bill_id=bill_id, id=id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -290,7 +294,7 @@ def list( if not bill_id: raise ValueError(f"Expected a non-empty value for `bill_id` but received {bill_id!r}") return self._get_api_list( - f"/organizations/{org_id}/bills/{bill_id}/lineitems", + path_template("/organizations/{org_id}/bills/{bill_id}/lineitems", org_id=org_id, bill_id=bill_id), page=AsyncCursor[LineItemResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/m3ter/resources/charges.py b/src/m3ter/resources/charges.py index 7c18df4..95b8c67 100644 --- a/src/m3ter/resources/charges.py +++ b/src/m3ter/resources/charges.py @@ -10,7 +10,7 @@ from ..types import charge_list_params, charge_create_params, charge_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 ( @@ -176,7 +176,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/charges", + path_template("/organizations/{org_id}/charges", org_id=org_id), body=maybe_transform( { "account_id": account_id, @@ -237,7 +237,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/charges/{id}", + path_template("/organizations/{org_id}/charges/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -351,7 +351,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/charges/{id}", + path_template("/organizations/{org_id}/charges/{id}", org_id=org_id, id=id), body=maybe_transform( { "account_id": account_id, @@ -433,7 +433,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/charges", + path_template("/organizations/{org_id}/charges", org_id=org_id), page=SyncCursor[ChargeListResponse], options=make_request_options( extra_headers=extra_headers, @@ -488,7 +488,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/charges/{id}", + path_template("/organizations/{org_id}/charges/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -642,7 +642,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/charges", + path_template("/organizations/{org_id}/charges", org_id=org_id), body=await async_maybe_transform( { "account_id": account_id, @@ -703,7 +703,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/charges/{id}", + path_template("/organizations/{org_id}/charges/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -817,7 +817,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/charges/{id}", + path_template("/organizations/{org_id}/charges/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "account_id": account_id, @@ -899,7 +899,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/charges", + path_template("/organizations/{org_id}/charges", org_id=org_id), page=AsyncCursor[ChargeListResponse], options=make_request_options( extra_headers=extra_headers, @@ -954,7 +954,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/charges/{id}", + path_template("/organizations/{org_id}/charges/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/commitments.py b/src/m3ter/resources/commitments.py index 2806938..8e0c552 100644 --- a/src/m3ter/resources/commitments.py +++ b/src/m3ter/resources/commitments.py @@ -10,7 +10,7 @@ from ..types import commitment_list_params, commitment_create_params, commitment_search_params, commitment_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 ( @@ -305,7 +305,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/commitments", + path_template("/organizations/{org_id}/commitments", org_id=org_id), body=maybe_transform( { "account_id": account_id, @@ -378,7 +378,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/commitments/{id}", + path_template("/organizations/{org_id}/commitments/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -610,7 +610,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/commitments/{id}", + path_template("/organizations/{org_id}/commitments/{id}", org_id=org_id, id=id), body=maybe_transform( { "account_id": account_id, @@ -712,7 +712,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/commitments", + path_template("/organizations/{org_id}/commitments", org_id=org_id), page=SyncCursor[CommitmentResponse], options=make_request_options( extra_headers=extra_headers, @@ -772,7 +772,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/commitments/{id}", + path_template("/organizations/{org_id}/commitments/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -853,7 +853,7 @@ def search( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/commitments/search", + path_template("/organizations/{org_id}/commitments/search", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -1152,7 +1152,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/commitments", + path_template("/organizations/{org_id}/commitments", org_id=org_id), body=await async_maybe_transform( { "account_id": account_id, @@ -1225,7 +1225,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/commitments/{id}", + path_template("/organizations/{org_id}/commitments/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1457,7 +1457,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/commitments/{id}", + path_template("/organizations/{org_id}/commitments/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "account_id": account_id, @@ -1559,7 +1559,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/commitments", + path_template("/organizations/{org_id}/commitments", org_id=org_id), page=AsyncCursor[CommitmentResponse], options=make_request_options( extra_headers=extra_headers, @@ -1619,7 +1619,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/commitments/{id}", + path_template("/organizations/{org_id}/commitments/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1700,7 +1700,7 @@ async def search( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/commitments/search", + path_template("/organizations/{org_id}/commitments/search", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/m3ter/resources/compound_aggregations.py b/src/m3ter/resources/compound_aggregations.py index 8e92270..7ac490b 100644 --- a/src/m3ter/resources/compound_aggregations.py +++ b/src/m3ter/resources/compound_aggregations.py @@ -13,7 +13,7 @@ compound_aggregation_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 ( @@ -189,7 +189,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/compoundaggregations", + path_template("/organizations/{org_id}/compoundaggregations", org_id=org_id), body=maybe_transform( { "calculation": calculation, @@ -246,7 +246,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/compoundaggregations/{id}", + path_template("/organizations/{org_id}/compoundaggregations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -380,7 +380,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/compoundaggregations/{id}", + path_template("/organizations/{org_id}/compoundaggregations/{id}", org_id=org_id, id=id), body=maybe_transform( { "calculation": calculation, @@ -456,7 +456,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/compoundaggregations", + path_template("/organizations/{org_id}/compoundaggregations", org_id=org_id), page=SyncCursor[CompoundAggregationResponse], options=make_request_options( extra_headers=extra_headers, @@ -513,7 +513,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/compoundaggregations/{id}", + path_template("/organizations/{org_id}/compoundaggregations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -680,7 +680,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/compoundaggregations", + path_template("/organizations/{org_id}/compoundaggregations", org_id=org_id), body=await async_maybe_transform( { "calculation": calculation, @@ -737,7 +737,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/compoundaggregations/{id}", + path_template("/organizations/{org_id}/compoundaggregations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -871,7 +871,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/compoundaggregations/{id}", + path_template("/organizations/{org_id}/compoundaggregations/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "calculation": calculation, @@ -947,7 +947,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/compoundaggregations", + path_template("/organizations/{org_id}/compoundaggregations", org_id=org_id), page=AsyncCursor[CompoundAggregationResponse], options=make_request_options( extra_headers=extra_headers, @@ -1004,7 +1004,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/compoundaggregations/{id}", + path_template("/organizations/{org_id}/compoundaggregations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/contracts.py b/src/m3ter/resources/contracts.py index 5eb1c28..a647d0d 100644 --- a/src/m3ter/resources/contracts.py +++ b/src/m3ter/resources/contracts.py @@ -15,7 +15,7 @@ contract_end_date_billing_entities_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 ( @@ -172,7 +172,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/contracts", + path_template("/organizations/{org_id}/contracts", org_id=org_id), body=maybe_transform( { "account_id": account_id, @@ -229,7 +229,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/contracts/{id}", + path_template("/organizations/{org_id}/contracts/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -353,7 +353,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/contracts/{id}", + path_template("/organizations/{org_id}/contracts/{id}", org_id=org_id, id=id), body=maybe_transform( { "account_id": account_id, @@ -423,7 +423,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/contracts", + path_template("/organizations/{org_id}/contracts", org_id=org_id), page=SyncCursor[ContractResponse], options=make_request_options( extra_headers=extra_headers, @@ -481,7 +481,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/contracts/{id}", + path_template("/organizations/{org_id}/contracts/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -546,7 +546,7 @@ def end_date_billing_entities( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/contracts/{id}/enddatebillingentities", + path_template("/organizations/{org_id}/contracts/{id}/enddatebillingentities", org_id=org_id, id=id), body=maybe_transform( { "billing_entities": billing_entities, @@ -702,7 +702,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/contracts", + path_template("/organizations/{org_id}/contracts", org_id=org_id), body=await async_maybe_transform( { "account_id": account_id, @@ -759,7 +759,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/contracts/{id}", + path_template("/organizations/{org_id}/contracts/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -883,7 +883,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/contracts/{id}", + path_template("/organizations/{org_id}/contracts/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "account_id": account_id, @@ -953,7 +953,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/contracts", + path_template("/organizations/{org_id}/contracts", org_id=org_id), page=AsyncCursor[ContractResponse], options=make_request_options( extra_headers=extra_headers, @@ -1011,7 +1011,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/contracts/{id}", + path_template("/organizations/{org_id}/contracts/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1076,7 +1076,7 @@ async def end_date_billing_entities( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/contracts/{id}/enddatebillingentities", + path_template("/organizations/{org_id}/contracts/{id}/enddatebillingentities", org_id=org_id, id=id), body=await async_maybe_transform( { "billing_entities": billing_entities, diff --git a/src/m3ter/resources/counter_adjustments.py b/src/m3ter/resources/counter_adjustments.py index 92bbb88..a30a17d 100644 --- a/src/m3ter/resources/counter_adjustments.py +++ b/src/m3ter/resources/counter_adjustments.py @@ -8,7 +8,7 @@ from ..types import counter_adjustment_list_params, counter_adjustment_create_params, counter_adjustment_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 ( @@ -126,7 +126,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/counteradjustments", + path_template("/organizations/{org_id}/counteradjustments", org_id=org_id), body=maybe_transform( { "account_id": account_id, @@ -175,7 +175,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/counteradjustments/{id}", + path_template("/organizations/{org_id}/counteradjustments/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -249,7 +249,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/counteradjustments/{id}", + path_template("/organizations/{org_id}/counteradjustments/{id}", org_id=org_id, id=id), body=maybe_transform( { "account_id": account_id, @@ -330,7 +330,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/counteradjustments", + path_template("/organizations/{org_id}/counteradjustments", org_id=org_id), page=SyncCursor[CounterAdjustmentResponse], options=make_request_options( extra_headers=extra_headers, @@ -387,7 +387,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/counteradjustments/{id}", + path_template("/organizations/{org_id}/counteradjustments/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -497,7 +497,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/counteradjustments", + path_template("/organizations/{org_id}/counteradjustments", org_id=org_id), body=await async_maybe_transform( { "account_id": account_id, @@ -546,7 +546,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/counteradjustments/{id}", + path_template("/organizations/{org_id}/counteradjustments/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -620,7 +620,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/counteradjustments/{id}", + path_template("/organizations/{org_id}/counteradjustments/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "account_id": account_id, @@ -701,7 +701,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/counteradjustments", + path_template("/organizations/{org_id}/counteradjustments", org_id=org_id), page=AsyncCursor[CounterAdjustmentResponse], options=make_request_options( extra_headers=extra_headers, @@ -758,7 +758,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/counteradjustments/{id}", + path_template("/organizations/{org_id}/counteradjustments/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/counter_pricings.py b/src/m3ter/resources/counter_pricings.py index 6e7fe8f..0b81361 100644 --- a/src/m3ter/resources/counter_pricings.py +++ b/src/m3ter/resources/counter_pricings.py @@ -9,7 +9,7 @@ from ..types import counter_pricing_list_params, counter_pricing_create_params, counter_pricing_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 ( @@ -187,7 +187,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/counterpricings", + path_template("/organizations/{org_id}/counterpricings", org_id=org_id), body=maybe_transform( { "counter_id": counter_id, @@ -245,7 +245,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/counterpricings/{id}", + path_template("/organizations/{org_id}/counterpricings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -388,7 +388,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/counterpricings/{id}", + path_template("/organizations/{org_id}/counterpricings/{id}", org_id=org_id, id=id), body=maybe_transform( { "counter_id": counter_id, @@ -462,7 +462,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/counterpricings", + path_template("/organizations/{org_id}/counterpricings", org_id=org_id), page=SyncCursor[CounterPricingResponse], options=make_request_options( extra_headers=extra_headers, @@ -515,7 +515,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/counterpricings/{id}", + path_template("/organizations/{org_id}/counterpricings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -684,7 +684,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/counterpricings", + path_template("/organizations/{org_id}/counterpricings", org_id=org_id), body=await async_maybe_transform( { "counter_id": counter_id, @@ -742,7 +742,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/counterpricings/{id}", + path_template("/organizations/{org_id}/counterpricings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -885,7 +885,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/counterpricings/{id}", + path_template("/organizations/{org_id}/counterpricings/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "counter_id": counter_id, @@ -959,7 +959,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/counterpricings", + path_template("/organizations/{org_id}/counterpricings", org_id=org_id), page=AsyncCursor[CounterPricingResponse], options=make_request_options( extra_headers=extra_headers, @@ -1012,7 +1012,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/counterpricings/{id}", + path_template("/organizations/{org_id}/counterpricings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/counters.py b/src/m3ter/resources/counters.py index 50eb877..dbe2dbd 100644 --- a/src/m3ter/resources/counters.py +++ b/src/m3ter/resources/counters.py @@ -6,7 +6,7 @@ from ..types import counter_list_params, counter_create_params, counter_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 ( @@ -105,7 +105,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/counters", + path_template("/organizations/{org_id}/counters", org_id=org_id), body=maybe_transform( { "name": name, @@ -153,7 +153,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/counters/{id}", + path_template("/organizations/{org_id}/counters/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -217,7 +217,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/counters/{id}", + path_template("/organizations/{org_id}/counters/{id}", org_id=org_id, id=id), body=maybe_transform( { "name": name, @@ -279,7 +279,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/counters", + path_template("/organizations/{org_id}/counters", org_id=org_id), page=SyncCursor[CounterResponse], options=make_request_options( extra_headers=extra_headers, @@ -331,7 +331,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/counters/{id}", + path_template("/organizations/{org_id}/counters/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -422,7 +422,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/counters", + path_template("/organizations/{org_id}/counters", org_id=org_id), body=await async_maybe_transform( { "name": name, @@ -470,7 +470,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/counters/{id}", + path_template("/organizations/{org_id}/counters/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -534,7 +534,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/counters/{id}", + path_template("/organizations/{org_id}/counters/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "name": name, @@ -596,7 +596,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/counters", + path_template("/organizations/{org_id}/counters", org_id=org_id), page=AsyncCursor[CounterResponse], options=make_request_options( extra_headers=extra_headers, @@ -648,7 +648,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/counters/{id}", + path_template("/organizations/{org_id}/counters/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/credit_reasons.py b/src/m3ter/resources/credit_reasons.py index 8efc3b5..51014bd 100644 --- a/src/m3ter/resources/credit_reasons.py +++ b/src/m3ter/resources/credit_reasons.py @@ -6,7 +6,7 @@ from ..types import credit_reason_list_params, credit_reason_create_params, credit_reason_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 ( @@ -104,7 +104,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/picklists/creditreasons", + path_template("/organizations/{org_id}/picklists/creditreasons", org_id=org_id), body=maybe_transform( { "name": name, @@ -151,7 +151,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/picklists/creditreasons/{id}", + path_template("/organizations/{org_id}/picklists/creditreasons/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -213,7 +213,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/picklists/creditreasons/{id}", + path_template("/organizations/{org_id}/picklists/creditreasons/{id}", org_id=org_id, id=id), body=maybe_transform( { "name": name, @@ -279,7 +279,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/picklists/creditreasons", + path_template("/organizations/{org_id}/picklists/creditreasons", org_id=org_id), page=SyncCursor[CreditReasonResponse], options=make_request_options( extra_headers=extra_headers, @@ -331,7 +331,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/picklists/creditreasons/{id}", + path_template("/organizations/{org_id}/picklists/creditreasons/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -421,7 +421,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/picklists/creditreasons", + path_template("/organizations/{org_id}/picklists/creditreasons", org_id=org_id), body=await async_maybe_transform( { "name": name, @@ -468,7 +468,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/picklists/creditreasons/{id}", + path_template("/organizations/{org_id}/picklists/creditreasons/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -530,7 +530,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/picklists/creditreasons/{id}", + path_template("/organizations/{org_id}/picklists/creditreasons/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "name": name, @@ -596,7 +596,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/picklists/creditreasons", + path_template("/organizations/{org_id}/picklists/creditreasons", org_id=org_id), page=AsyncCursor[CreditReasonResponse], options=make_request_options( extra_headers=extra_headers, @@ -648,7 +648,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/picklists/creditreasons/{id}", + path_template("/organizations/{org_id}/picklists/creditreasons/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/currencies.py b/src/m3ter/resources/currencies.py index 5585f44..b2a67f5 100644 --- a/src/m3ter/resources/currencies.py +++ b/src/m3ter/resources/currencies.py @@ -8,7 +8,7 @@ from ..types import currency_list_params, currency_create_params, currency_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 ( @@ -118,7 +118,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/picklists/currency", + path_template("/organizations/{org_id}/picklists/currency", org_id=org_id), body=maybe_transform( { "name": name, @@ -169,7 +169,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/picklists/currency/{id}", + path_template("/organizations/{org_id}/picklists/currency/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -241,7 +241,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/picklists/currency/{id}", + path_template("/organizations/{org_id}/picklists/currency/{id}", org_id=org_id, id=id), body=maybe_transform( { "name": name, @@ -313,7 +313,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/picklists/currency", + path_template("/organizations/{org_id}/picklists/currency", org_id=org_id), page=SyncCursor[CurrencyResponse], options=make_request_options( extra_headers=extra_headers, @@ -368,7 +368,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/picklists/currency/{id}", + path_template("/organizations/{org_id}/picklists/currency/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -470,7 +470,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/picklists/currency", + path_template("/organizations/{org_id}/picklists/currency", org_id=org_id), body=await async_maybe_transform( { "name": name, @@ -521,7 +521,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/picklists/currency/{id}", + path_template("/organizations/{org_id}/picklists/currency/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -593,7 +593,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/picklists/currency/{id}", + path_template("/organizations/{org_id}/picklists/currency/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "name": name, @@ -665,7 +665,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/picklists/currency", + path_template("/organizations/{org_id}/picklists/currency", org_id=org_id), page=AsyncCursor[CurrencyResponse], options=make_request_options( extra_headers=extra_headers, @@ -720,7 +720,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/picklists/currency/{id}", + path_template("/organizations/{org_id}/picklists/currency/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/custom_fields.py b/src/m3ter/resources/custom_fields.py index 6615e2f..ca12cd1 100644 --- a/src/m3ter/resources/custom_fields.py +++ b/src/m3ter/resources/custom_fields.py @@ -8,7 +8,7 @@ from ..types import custom_field_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 ( @@ -127,7 +127,7 @@ def retrieve( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/customfields", + path_template("/organizations/{org_id}/customfields", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -203,7 +203,7 @@ def update( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._put( - f"/organizations/{org_id}/customfields", + path_template("/organizations/{org_id}/customfields", org_id=org_id), body=maybe_transform( { "account": account, @@ -331,7 +331,7 @@ async def retrieve( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/customfields", + path_template("/organizations/{org_id}/customfields", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -407,7 +407,7 @@ async def update( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._put( - f"/organizations/{org_id}/customfields", + path_template("/organizations/{org_id}/customfields", org_id=org_id), body=await async_maybe_transform( { "account": account, diff --git a/src/m3ter/resources/data_exports/data_exports.py b/src/m3ter/resources/data_exports/data_exports.py index 7c56bd8..2c84ace 100644 --- a/src/m3ter/resources/data_exports/data_exports.py +++ b/src/m3ter/resources/data_exports/data_exports.py @@ -18,7 +18,7 @@ ) from ...types import data_export_create_adhoc_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 .schedules import ( SchedulesResource, @@ -396,7 +396,7 @@ def create_adhoc( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/dataexports/adhoc", + path_template("/organizations/{org_id}/dataexports/adhoc", org_id=org_id), body=maybe_transform( { "operational_data_types": operational_data_types, @@ -764,7 +764,7 @@ async def create_adhoc( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/dataexports/adhoc", + path_template("/organizations/{org_id}/dataexports/adhoc", org_id=org_id), body=await async_maybe_transform( { "operational_data_types": operational_data_types, diff --git a/src/m3ter/resources/data_exports/destinations.py b/src/m3ter/resources/data_exports/destinations.py index 98ed7ed..a59fa2c 100644 --- a/src/m3ter/resources/data_exports/destinations.py +++ b/src/m3ter/resources/data_exports/destinations.py @@ -8,7 +8,7 @@ import httpx 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 ( @@ -280,7 +280,7 @@ def create( return cast( DestinationCreateResponse, self._post( - f"/organizations/{org_id}/dataexports/destinations", + path_template("/organizations/{org_id}/dataexports/destinations", org_id=org_id), body=maybe_transform( { "bucket_name": bucket_name, @@ -338,7 +338,7 @@ def retrieve( return cast( DestinationRetrieveResponse, self._get( - f"/organizations/{org_id}/dataexports/destinations/{id}", + path_template("/organizations/{org_id}/dataexports/destinations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -570,7 +570,7 @@ def update( return cast( DestinationUpdateResponse, self._put( - f"/organizations/{org_id}/dataexports/destinations/{id}", + path_template("/organizations/{org_id}/dataexports/destinations/{id}", org_id=org_id, id=id), body=maybe_transform( { "bucket_name": bucket_name, @@ -634,7 +634,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/dataexports/destinations", + path_template("/organizations/{org_id}/dataexports/destinations", org_id=org_id), page=SyncCursor[DataExportDestinationResponse], options=make_request_options( extra_headers=extra_headers, @@ -690,7 +690,7 @@ def delete( return cast( DestinationDeleteResponse, self._delete( - f"/organizations/{org_id}/dataexports/destinations/{id}", + path_template("/organizations/{org_id}/dataexports/destinations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -952,7 +952,7 @@ async def create( return cast( DestinationCreateResponse, await self._post( - f"/organizations/{org_id}/dataexports/destinations", + path_template("/organizations/{org_id}/dataexports/destinations", org_id=org_id), body=await async_maybe_transform( { "bucket_name": bucket_name, @@ -1010,7 +1010,7 @@ async def retrieve( return cast( DestinationRetrieveResponse, await self._get( - f"/organizations/{org_id}/dataexports/destinations/{id}", + path_template("/organizations/{org_id}/dataexports/destinations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1242,7 +1242,7 @@ async def update( return cast( DestinationUpdateResponse, await self._put( - f"/organizations/{org_id}/dataexports/destinations/{id}", + path_template("/organizations/{org_id}/dataexports/destinations/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "bucket_name": bucket_name, @@ -1306,7 +1306,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/dataexports/destinations", + path_template("/organizations/{org_id}/dataexports/destinations", org_id=org_id), page=AsyncCursor[DataExportDestinationResponse], options=make_request_options( extra_headers=extra_headers, @@ -1362,7 +1362,7 @@ async def delete( return cast( DestinationDeleteResponse, await self._delete( - f"/organizations/{org_id}/dataexports/destinations/{id}", + path_template("/organizations/{org_id}/dataexports/destinations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/data_exports/jobs.py b/src/m3ter/resources/data_exports/jobs.py index 9e23ee0..c080e75 100644 --- a/src/m3ter/resources/data_exports/jobs.py +++ b/src/m3ter/resources/data_exports/jobs.py @@ -7,7 +7,7 @@ import httpx from ..._types import Body, Omit, Query, Headers, NotGiven, SequenceNotStr, 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 ( @@ -82,7 +82,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/dataexports/jobs/{id}", + path_template("/organizations/{org_id}/dataexports/jobs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -142,7 +142,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/dataexports/jobs", + path_template("/organizations/{org_id}/dataexports/jobs", org_id=org_id), page=SyncCursor[DataExportJobResponse], options=make_request_options( extra_headers=extra_headers, @@ -213,7 +213,9 @@ def get_download_url( if not job_id: raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") return self._get( - f"/organizations/{org_id}/dataexports/jobs/{job_id}/getdownloadurl", + path_template( + "/organizations/{org_id}/dataexports/jobs/{job_id}/getdownloadurl", org_id=org_id, job_id=job_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -278,7 +280,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/dataexports/jobs/{id}", + path_template("/organizations/{org_id}/dataexports/jobs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -338,7 +340,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/dataexports/jobs", + path_template("/organizations/{org_id}/dataexports/jobs", org_id=org_id), page=AsyncCursor[DataExportJobResponse], options=make_request_options( extra_headers=extra_headers, @@ -409,7 +411,9 @@ async def get_download_url( if not job_id: raise ValueError(f"Expected a non-empty value for `job_id` but received {job_id!r}") return await self._get( - f"/organizations/{org_id}/dataexports/jobs/{job_id}/getdownloadurl", + path_template( + "/organizations/{org_id}/dataexports/jobs/{job_id}/getdownloadurl", org_id=org_id, job_id=job_id + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/data_exports/schedules.py b/src/m3ter/resources/data_exports/schedules.py index 441951e..ee41968 100644 --- a/src/m3ter/resources/data_exports/schedules.py +++ b/src/m3ter/resources/data_exports/schedules.py @@ -8,7 +8,7 @@ import httpx 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 ( @@ -388,7 +388,7 @@ def create( return cast( ScheduleCreateResponse, self._post( - f"/organizations/{org_id}/dataexports/schedules", + path_template("/organizations/{org_id}/dataexports/schedules", org_id=org_id), body=maybe_transform( { "operational_data_types": operational_data_types, @@ -447,7 +447,7 @@ def retrieve( return cast( ScheduleRetrieveResponse, self._get( - f"/organizations/{org_id}/dataexports/schedules/{id}", + path_template("/organizations/{org_id}/dataexports/schedules/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -766,7 +766,7 @@ def update( return cast( ScheduleUpdateResponse, self._put( - f"/organizations/{org_id}/dataexports/schedules/{id}", + path_template("/organizations/{org_id}/dataexports/schedules/{id}", org_id=org_id, id=id), body=maybe_transform( { "operational_data_types": operational_data_types, @@ -832,7 +832,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/dataexports/schedules", + path_template("/organizations/{org_id}/dataexports/schedules", org_id=org_id), page=SyncCursor[ScheduleListResponse], options=make_request_options( extra_headers=extra_headers, @@ -886,7 +886,7 @@ def delete( return cast( ScheduleDeleteResponse, self._delete( - f"/organizations/{org_id}/dataexports/schedules/{id}", + path_template("/organizations/{org_id}/dataexports/schedules/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1255,7 +1255,7 @@ async def create( return cast( ScheduleCreateResponse, await self._post( - f"/organizations/{org_id}/dataexports/schedules", + path_template("/organizations/{org_id}/dataexports/schedules", org_id=org_id), body=await async_maybe_transform( { "operational_data_types": operational_data_types, @@ -1314,7 +1314,7 @@ async def retrieve( return cast( ScheduleRetrieveResponse, await self._get( - f"/organizations/{org_id}/dataexports/schedules/{id}", + path_template("/organizations/{org_id}/dataexports/schedules/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1633,7 +1633,7 @@ async def update( return cast( ScheduleUpdateResponse, await self._put( - f"/organizations/{org_id}/dataexports/schedules/{id}", + path_template("/organizations/{org_id}/dataexports/schedules/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "operational_data_types": operational_data_types, @@ -1699,7 +1699,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/dataexports/schedules", + path_template("/organizations/{org_id}/dataexports/schedules", org_id=org_id), page=AsyncCursor[ScheduleListResponse], options=make_request_options( extra_headers=extra_headers, @@ -1753,7 +1753,7 @@ async def delete( return cast( ScheduleDeleteResponse, await self._delete( - f"/organizations/{org_id}/dataexports/schedules/{id}", + path_template("/organizations/{org_id}/dataexports/schedules/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/debit_reasons.py b/src/m3ter/resources/debit_reasons.py index 6e286b3..e128d1f 100644 --- a/src/m3ter/resources/debit_reasons.py +++ b/src/m3ter/resources/debit_reasons.py @@ -6,7 +6,7 @@ from ..types import debit_reason_list_params, debit_reason_create_params, debit_reason_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 ( @@ -104,7 +104,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/picklists/debitreasons", + path_template("/organizations/{org_id}/picklists/debitreasons", org_id=org_id), body=maybe_transform( { "name": name, @@ -151,7 +151,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/picklists/debitreasons/{id}", + path_template("/organizations/{org_id}/picklists/debitreasons/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -213,7 +213,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/picklists/debitreasons/{id}", + path_template("/organizations/{org_id}/picklists/debitreasons/{id}", org_id=org_id, id=id), body=maybe_transform( { "name": name, @@ -279,7 +279,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/picklists/debitreasons", + path_template("/organizations/{org_id}/picklists/debitreasons", org_id=org_id), page=SyncCursor[DebitReasonResponse], options=make_request_options( extra_headers=extra_headers, @@ -331,7 +331,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/picklists/debitreasons/{id}", + path_template("/organizations/{org_id}/picklists/debitreasons/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -421,7 +421,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/picklists/debitreasons", + path_template("/organizations/{org_id}/picklists/debitreasons", org_id=org_id), body=await async_maybe_transform( { "name": name, @@ -468,7 +468,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/picklists/debitreasons/{id}", + path_template("/organizations/{org_id}/picklists/debitreasons/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -530,7 +530,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/picklists/debitreasons/{id}", + path_template("/organizations/{org_id}/picklists/debitreasons/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "name": name, @@ -596,7 +596,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/picklists/debitreasons", + path_template("/organizations/{org_id}/picklists/debitreasons", org_id=org_id), page=AsyncCursor[DebitReasonResponse], options=make_request_options( extra_headers=extra_headers, @@ -648,7 +648,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/picklists/debitreasons/{id}", + path_template("/organizations/{org_id}/picklists/debitreasons/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/events.py b/src/m3ter/resources/events.py index 3716f38..85425e8 100644 --- a/src/m3ter/resources/events.py +++ b/src/m3ter/resources/events.py @@ -8,7 +8,7 @@ from ..types import event_list_params, event_get_fields_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 ( @@ -137,7 +137,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/events/{id}", + path_template("/organizations/{org_id}/events/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -226,7 +226,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/events", + path_template("/organizations/{org_id}/events", org_id=org_id), page=SyncCursor[EventResponse], options=make_request_options( extra_headers=extra_headers, @@ -309,7 +309,7 @@ def get_fields( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/events/fields", + path_template("/organizations/{org_id}/events/fields", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -351,7 +351,7 @@ def get_types( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/events/types", + path_template("/organizations/{org_id}/events/types", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -470,7 +470,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/events/{id}", + path_template("/organizations/{org_id}/events/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -559,7 +559,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/events", + path_template("/organizations/{org_id}/events", org_id=org_id), page=AsyncCursor[EventResponse], options=make_request_options( extra_headers=extra_headers, @@ -642,7 +642,7 @@ async def get_fields( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/events/fields", + path_template("/organizations/{org_id}/events/fields", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -686,7 +686,7 @@ async def get_types( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/events/types", + path_template("/organizations/{org_id}/events/types", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/external_mappings.py b/src/m3ter/resources/external_mappings.py index a1f3541..135cc72 100644 --- a/src/m3ter/resources/external_mappings.py +++ b/src/m3ter/resources/external_mappings.py @@ -12,7 +12,7 @@ external_mapping_list_by_external_entity_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 ( @@ -119,7 +119,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/externalmappings", + path_template("/organizations/{org_id}/externalmappings", org_id=org_id), body=maybe_transform( { "external_id": external_id, @@ -172,7 +172,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/externalmappings/{id}", + path_template("/organizations/{org_id}/externalmappings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -245,7 +245,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/externalmappings/{id}", + path_template("/organizations/{org_id}/externalmappings/{id}", org_id=org_id, id=id), body=maybe_transform( { "external_id": external_id, @@ -317,7 +317,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/externalmappings", + path_template("/organizations/{org_id}/externalmappings", org_id=org_id), page=SyncCursor[ExternalMappingResponse], options=make_request_options( extra_headers=extra_headers, @@ -369,7 +369,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/externalmappings/{id}", + path_template("/organizations/{org_id}/externalmappings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -425,7 +425,13 @@ def list_by_external_entity( if not external_id: raise ValueError(f"Expected a non-empty value for `external_id` but received {external_id!r}") return self._get_api_list( - f"/organizations/{org_id}/externalmappings/externalid/{system}/{external_table}/{external_id}", + path_template( + "/organizations/{org_id}/externalmappings/externalid/{system}/{external_table}/{external_id}", + org_id=org_id, + system=system, + external_table=external_table, + external_id=external_id, + ), page=SyncCursor[ExternalMappingResponse], options=make_request_options( extra_headers=extra_headers, @@ -487,7 +493,12 @@ def list_by_m3ter_entity( if not m3ter_id: raise ValueError(f"Expected a non-empty value for `m3ter_id` but received {m3ter_id!r}") return self._get_api_list( - f"/organizations/{org_id}/externalmappings/external/{entity}/{m3ter_id}", + path_template( + "/organizations/{org_id}/externalmappings/external/{entity}/{m3ter_id}", + org_id=org_id, + entity=entity, + m3ter_id=m3ter_id, + ), page=SyncCursor[ExternalMappingResponse], options=make_request_options( extra_headers=extra_headers, @@ -597,7 +608,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/externalmappings", + path_template("/organizations/{org_id}/externalmappings", org_id=org_id), body=await async_maybe_transform( { "external_id": external_id, @@ -650,7 +661,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/externalmappings/{id}", + path_template("/organizations/{org_id}/externalmappings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -723,7 +734,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/externalmappings/{id}", + path_template("/organizations/{org_id}/externalmappings/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "external_id": external_id, @@ -795,7 +806,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/externalmappings", + path_template("/organizations/{org_id}/externalmappings", org_id=org_id), page=AsyncCursor[ExternalMappingResponse], options=make_request_options( extra_headers=extra_headers, @@ -847,7 +858,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/externalmappings/{id}", + path_template("/organizations/{org_id}/externalmappings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -903,7 +914,13 @@ def list_by_external_entity( if not external_id: raise ValueError(f"Expected a non-empty value for `external_id` but received {external_id!r}") return self._get_api_list( - f"/organizations/{org_id}/externalmappings/externalid/{system}/{external_table}/{external_id}", + path_template( + "/organizations/{org_id}/externalmappings/externalid/{system}/{external_table}/{external_id}", + org_id=org_id, + system=system, + external_table=external_table, + external_id=external_id, + ), page=AsyncCursor[ExternalMappingResponse], options=make_request_options( extra_headers=extra_headers, @@ -965,7 +982,12 @@ def list_by_m3ter_entity( if not m3ter_id: raise ValueError(f"Expected a non-empty value for `m3ter_id` but received {m3ter_id!r}") return self._get_api_list( - f"/organizations/{org_id}/externalmappings/external/{entity}/{m3ter_id}", + path_template( + "/organizations/{org_id}/externalmappings/external/{entity}/{m3ter_id}", + org_id=org_id, + entity=entity, + m3ter_id=m3ter_id, + ), page=AsyncCursor[ExternalMappingResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/m3ter/resources/integration_configurations.py b/src/m3ter/resources/integration_configurations.py index 534a043..7feca42 100644 --- a/src/m3ter/resources/integration_configurations.py +++ b/src/m3ter/resources/integration_configurations.py @@ -13,7 +13,7 @@ integration_configuration_get_by_entity_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 ( @@ -137,7 +137,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/integrationconfigs", + path_template("/organizations/{org_id}/integrationconfigs", org_id=org_id), body=maybe_transform( { "destination": destination, @@ -193,7 +193,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/integrationconfigs/{id}", + path_template("/organizations/{org_id}/integrationconfigs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -275,7 +275,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/integrationconfigs/{id}", + path_template("/organizations/{org_id}/integrationconfigs/{id}", org_id=org_id, id=id), body=maybe_transform( { "destination": destination, @@ -337,7 +337,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/integrationconfigs", + path_template("/organizations/{org_id}/integrationconfigs", org_id=org_id), page=SyncCursor[IntegrationConfigurationListResponse], options=make_request_options( extra_headers=extra_headers, @@ -391,7 +391,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/integrationconfigs/{id}", + path_template("/organizations/{org_id}/integrationconfigs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -430,7 +430,7 @@ def enable( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/organizations/{org_id}/integrationconfigs/{id}/enable", + path_template("/organizations/{org_id}/integrationconfigs/{id}/enable", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -483,7 +483,11 @@ def get_by_entity( if not entity_type: raise ValueError(f"Expected a non-empty value for `entity_type` but received {entity_type!r}") return self._get( - f"/organizations/{org_id}/integrationconfigs/entity/{entity_type}", + path_template( + "/organizations/{org_id}/integrationconfigs/entity/{entity_type}", + org_id=org_id, + entity_type=entity_type, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -607,7 +611,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/integrationconfigs", + path_template("/organizations/{org_id}/integrationconfigs", org_id=org_id), body=await async_maybe_transform( { "destination": destination, @@ -663,7 +667,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/integrationconfigs/{id}", + path_template("/organizations/{org_id}/integrationconfigs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -745,7 +749,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/integrationconfigs/{id}", + path_template("/organizations/{org_id}/integrationconfigs/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "destination": destination, @@ -807,7 +811,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/integrationconfigs", + path_template("/organizations/{org_id}/integrationconfigs", org_id=org_id), page=AsyncCursor[IntegrationConfigurationListResponse], options=make_request_options( extra_headers=extra_headers, @@ -861,7 +865,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/integrationconfigs/{id}", + path_template("/organizations/{org_id}/integrationconfigs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -900,7 +904,7 @@ async def enable( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/organizations/{org_id}/integrationconfigs/{id}/enable", + path_template("/organizations/{org_id}/integrationconfigs/{id}/enable", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -953,7 +957,11 @@ async def get_by_entity( if not entity_type: raise ValueError(f"Expected a non-empty value for `entity_type` but received {entity_type!r}") return await self._get( - f"/organizations/{org_id}/integrationconfigs/entity/{entity_type}", + path_template( + "/organizations/{org_id}/integrationconfigs/entity/{entity_type}", + org_id=org_id, + entity_type=entity_type, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/src/m3ter/resources/lookup_tables/lookup_table_revision_data/lookup_table_revision_data.py b/src/m3ter/resources/lookup_tables/lookup_table_revision_data/lookup_table_revision_data.py index 69e88ac..5a26c48 100644 --- a/src/m3ter/resources/lookup_tables/lookup_table_revision_data/lookup_table_revision_data.py +++ b/src/m3ter/resources/lookup_tables/lookup_table_revision_data/lookup_table_revision_data.py @@ -8,7 +8,7 @@ import httpx 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 ( @@ -157,7 +157,12 @@ def retrieve( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return self._get( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -222,7 +227,12 @@ def update( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return self._put( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=maybe_transform( { "items": items, @@ -279,7 +289,12 @@ def delete( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return self._delete( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -332,7 +347,12 @@ def archieve( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/archived", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/archived", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=maybe_transform( {"content_type": content_type}, lookup_table_revision_data_archieve_params.LookupTableRevisionDataArchieveParams, @@ -391,7 +411,12 @@ def copy( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/copy", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/copy", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=maybe_transform( {"revision_id": revision_id}, lookup_table_revision_data_copy_params.LookupTableRevisionDataCopyParams ), @@ -453,7 +478,13 @@ def delete_key( if not lookup_key: raise ValueError(f"Expected a non-empty value for `lookup_key` but received {lookup_key!r}") return self._delete( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + lookup_key=lookup_key, + ), body=maybe_transform( {"version": version}, lookup_table_revision_data_delete_key_params.LookupTableRevisionDataDeleteKeyParams, @@ -530,7 +561,12 @@ def generate_download_url( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/generateuploadurl", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/generateuploadurl", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=maybe_transform( { "content_length": content_length, @@ -595,7 +631,13 @@ def retrieve_key( if not lookup_key: raise ValueError(f"Expected a non-empty value for `lookup_key` but received {lookup_key!r}") return self._get( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + lookup_key=lookup_key, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -661,7 +703,13 @@ def update_key( if not lookup_key: raise ValueError(f"Expected a non-empty value for `lookup_key` but received {lookup_key!r}") return self._put( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + lookup_key=lookup_key, + ), body=maybe_transform( { "item": item, @@ -784,7 +832,12 @@ async def retrieve( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return await self._get( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -849,7 +902,12 @@ async def update( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return await self._put( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=await async_maybe_transform( { "items": items, @@ -906,7 +964,12 @@ async def delete( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return await self._delete( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -959,7 +1022,12 @@ async def archieve( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return await self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/archived", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/archived", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=await async_maybe_transform( {"content_type": content_type}, lookup_table_revision_data_archieve_params.LookupTableRevisionDataArchieveParams, @@ -1018,7 +1086,12 @@ async def copy( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return await self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/copy", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/copy", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=await async_maybe_transform( {"revision_id": revision_id}, lookup_table_revision_data_copy_params.LookupTableRevisionDataCopyParams ), @@ -1080,7 +1153,13 @@ async def delete_key( if not lookup_key: raise ValueError(f"Expected a non-empty value for `lookup_key` but received {lookup_key!r}") return await self._delete( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + lookup_key=lookup_key, + ), body=await async_maybe_transform( {"version": version}, lookup_table_revision_data_delete_key_params.LookupTableRevisionDataDeleteKeyParams, @@ -1157,7 +1236,12 @@ async def generate_download_url( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return await self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/generateuploadurl", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/generateuploadurl", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=await async_maybe_transform( { "content_length": content_length, @@ -1222,7 +1306,13 @@ async def retrieve_key( if not lookup_key: raise ValueError(f"Expected a non-empty value for `lookup_key` but received {lookup_key!r}") return await self._get( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + lookup_key=lookup_key, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1288,7 +1378,13 @@ async def update_key( if not lookup_key: raise ValueError(f"Expected a non-empty value for `lookup_key` but received {lookup_key!r}") return await self._put( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/{lookup_key}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + lookup_key=lookup_key, + ), body=await async_maybe_transform( { "item": item, diff --git a/src/m3ter/resources/lookup_tables/lookup_table_revision_data/lookup_table_revision_data_jobs.py b/src/m3ter/resources/lookup_tables/lookup_table_revision_data/lookup_table_revision_data_jobs.py index 84f1c68..ab1839c 100644 --- a/src/m3ter/resources/lookup_tables/lookup_table_revision_data/lookup_table_revision_data_jobs.py +++ b/src/m3ter/resources/lookup_tables/lookup_table_revision_data/lookup_table_revision_data_jobs.py @@ -7,7 +7,7 @@ import httpx 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 ( @@ -115,7 +115,13 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -191,7 +197,12 @@ def list( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return self._get_api_list( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), page=SyncCursor[LookupTableRevisionDataJobListResponse], options=make_request_options( extra_headers=extra_headers, @@ -252,7 +263,13 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -304,7 +321,12 @@ def download( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/download", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/download", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=maybe_transform( {"content_type": content_type}, lookup_table_revision_data_job_download_params.LookupTableRevisionDataJobDownloadParams, @@ -393,7 +415,13 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -469,7 +497,12 @@ def list( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return self._get_api_list( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), page=AsyncCursor[LookupTableRevisionDataJobListResponse], options=make_request_options( extra_headers=extra_headers, @@ -530,7 +563,13 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -582,7 +621,12 @@ async def download( f"Expected a non-empty value for `lookup_table_revision_id` but received {lookup_table_revision_id!r}" ) return await self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/download", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{lookup_table_revision_id}/data/jobs/download", + org_id=org_id, + lookup_table_id=lookup_table_id, + lookup_table_revision_id=lookup_table_revision_id, + ), body=await async_maybe_transform( {"content_type": content_type}, lookup_table_revision_data_job_download_params.LookupTableRevisionDataJobDownloadParams, diff --git a/src/m3ter/resources/lookup_tables/lookup_table_revisions.py b/src/m3ter/resources/lookup_tables/lookup_table_revisions.py index d23d993..e6642c4 100644 --- a/src/m3ter/resources/lookup_tables/lookup_table_revisions.py +++ b/src/m3ter/resources/lookup_tables/lookup_table_revisions.py @@ -9,7 +9,7 @@ import httpx 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 ( @@ -145,7 +145,11 @@ def create( if not lookup_table_id: raise ValueError(f"Expected a non-empty value for `lookup_table_id` but received {lookup_table_id!r}") return self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions", + org_id=org_id, + lookup_table_id=lookup_table_id, + ), body=maybe_transform( { "fields": fields, @@ -197,7 +201,12 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -280,7 +289,12 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + id=id, + ), body=maybe_transform( { "fields": fields, @@ -338,7 +352,11 @@ def list( if not lookup_table_id: raise ValueError(f"Expected a non-empty value for `lookup_table_id` but received {lookup_table_id!r}") return self._get_api_list( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions", + org_id=org_id, + lookup_table_id=lookup_table_id, + ), page=SyncCursor[LookupTableRevisionResponse], options=make_request_options( extra_headers=extra_headers, @@ -391,7 +409,12 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -438,7 +461,12 @@ def update_status( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}/status", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}/status", + org_id=org_id, + lookup_table_id=lookup_table_id, + id=id, + ), body=maybe_transform( { "status": status, @@ -567,7 +595,11 @@ async def create( if not lookup_table_id: raise ValueError(f"Expected a non-empty value for `lookup_table_id` but received {lookup_table_id!r}") return await self._post( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions", + org_id=org_id, + lookup_table_id=lookup_table_id, + ), body=await async_maybe_transform( { "fields": fields, @@ -619,7 +651,12 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -702,7 +739,12 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + id=id, + ), body=await async_maybe_transform( { "fields": fields, @@ -760,7 +802,11 @@ def list( if not lookup_table_id: raise ValueError(f"Expected a non-empty value for `lookup_table_id` but received {lookup_table_id!r}") return self._get_api_list( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions", + org_id=org_id, + lookup_table_id=lookup_table_id, + ), page=AsyncCursor[LookupTableRevisionResponse], options=make_request_options( extra_headers=extra_headers, @@ -813,7 +859,12 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}", + org_id=org_id, + lookup_table_id=lookup_table_id, + id=id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -860,7 +911,12 @@ async def update_status( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}/status", + path_template( + "/organizations/{org_id}/lookuptables/{lookup_table_id}/revisions/{id}/status", + org_id=org_id, + lookup_table_id=lookup_table_id, + id=id, + ), body=await async_maybe_transform( { "status": status, diff --git a/src/m3ter/resources/lookup_tables/lookup_tables.py b/src/m3ter/resources/lookup_tables/lookup_tables.py index 8b9a6fc..a8c9d7b 100644 --- a/src/m3ter/resources/lookup_tables/lookup_tables.py +++ b/src/m3ter/resources/lookup_tables/lookup_tables.py @@ -13,7 +13,7 @@ lookup_table_retrieve_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 ( @@ -164,7 +164,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/lookuptables", + path_template("/organizations/{org_id}/lookuptables", org_id=org_id), body=maybe_transform( { "code": code, @@ -216,7 +216,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/lookuptables/{id}", + path_template("/organizations/{org_id}/lookuptables/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -293,7 +293,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/lookuptables/{id}", + path_template("/organizations/{org_id}/lookuptables/{id}", org_id=org_id, id=id), body=maybe_transform( { "code": code, @@ -356,7 +356,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/lookuptables", + path_template("/organizations/{org_id}/lookuptables", org_id=org_id), page=SyncCursor[LookupTableResponse], options=make_request_options( extra_headers=extra_headers, @@ -407,7 +407,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/lookuptables/{id}", + path_template("/organizations/{org_id}/lookuptables/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -534,7 +534,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/lookuptables", + path_template("/organizations/{org_id}/lookuptables", org_id=org_id), body=await async_maybe_transform( { "code": code, @@ -586,7 +586,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/lookuptables/{id}", + path_template("/organizations/{org_id}/lookuptables/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -663,7 +663,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/lookuptables/{id}", + path_template("/organizations/{org_id}/lookuptables/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "code": code, @@ -726,7 +726,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/lookuptables", + path_template("/organizations/{org_id}/lookuptables", org_id=org_id), page=AsyncCursor[LookupTableResponse], options=make_request_options( extra_headers=extra_headers, @@ -777,7 +777,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/lookuptables/{id}", + path_template("/organizations/{org_id}/lookuptables/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/meters.py b/src/m3ter/resources/meters.py index 7919cd6..c4fdffc 100644 --- a/src/m3ter/resources/meters.py +++ b/src/m3ter/resources/meters.py @@ -8,7 +8,7 @@ from ..types import meter_list_params, meter_create_params, meter_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 ( @@ -178,7 +178,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/meters", + path_template("/organizations/{org_id}/meters", org_id=org_id), body=maybe_transform( { "code": code, @@ -229,7 +229,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/meters/{id}", + path_template("/organizations/{org_id}/meters/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -325,7 +325,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/meters/{id}", + path_template("/organizations/{org_id}/meters/{id}", org_id=org_id, id=id), body=maybe_transform( { "code": code, @@ -390,7 +390,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/meters", + path_template("/organizations/{org_id}/meters", org_id=org_id), page=SyncCursor[MeterResponse], options=make_request_options( extra_headers=extra_headers, @@ -442,7 +442,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/meters/{id}", + path_template("/organizations/{org_id}/meters/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -602,7 +602,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/meters", + path_template("/organizations/{org_id}/meters", org_id=org_id), body=await async_maybe_transform( { "code": code, @@ -653,7 +653,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/meters/{id}", + path_template("/organizations/{org_id}/meters/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -749,7 +749,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/meters/{id}", + path_template("/organizations/{org_id}/meters/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "code": code, @@ -814,7 +814,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/meters", + path_template("/organizations/{org_id}/meters", org_id=org_id), page=AsyncCursor[MeterResponse], options=make_request_options( extra_headers=extra_headers, @@ -866,7 +866,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/meters/{id}", + path_template("/organizations/{org_id}/meters/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/notification_configurations.py b/src/m3ter/resources/notification_configurations.py index 4da7e50..eb5cc5b 100644 --- a/src/m3ter/resources/notification_configurations.py +++ b/src/m3ter/resources/notification_configurations.py @@ -10,7 +10,7 @@ notification_configuration_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 ( @@ -149,7 +149,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/notifications/configurations", + path_template("/organizations/{org_id}/notifications/configurations", org_id=org_id), body=maybe_transform( { "code": code, @@ -204,7 +204,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/notifications/configurations/{id}", + path_template("/organizations/{org_id}/notifications/configurations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -306,7 +306,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/notifications/configurations/{id}", + path_template("/organizations/{org_id}/notifications/configurations/{id}", org_id=org_id, id=id), body=maybe_transform( { "code": code, @@ -379,7 +379,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/notifications/configurations", + path_template("/organizations/{org_id}/notifications/configurations", org_id=org_id), page=SyncCursor[NotificationConfigurationResponse], options=make_request_options( extra_headers=extra_headers, @@ -434,7 +434,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/notifications/configurations/{id}", + path_template("/organizations/{org_id}/notifications/configurations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -565,7 +565,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/notifications/configurations", + path_template("/organizations/{org_id}/notifications/configurations", org_id=org_id), body=await async_maybe_transform( { "code": code, @@ -620,7 +620,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/notifications/configurations/{id}", + path_template("/organizations/{org_id}/notifications/configurations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -722,7 +722,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/notifications/configurations/{id}", + path_template("/organizations/{org_id}/notifications/configurations/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "code": code, @@ -795,7 +795,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/notifications/configurations", + path_template("/organizations/{org_id}/notifications/configurations", org_id=org_id), page=AsyncCursor[NotificationConfigurationResponse], options=make_request_options( extra_headers=extra_headers, @@ -850,7 +850,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/notifications/configurations/{id}", + path_template("/organizations/{org_id}/notifications/configurations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/organization_config.py b/src/m3ter/resources/organization_config.py index 7791a6b..8010251 100644 --- a/src/m3ter/resources/organization_config.py +++ b/src/m3ter/resources/organization_config.py @@ -9,7 +9,7 @@ from ..types import organization_config_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 ( @@ -93,7 +93,7 @@ def retrieve( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/organizationconfig", + path_template("/organizations/{org_id}/organizationconfig", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -378,7 +378,7 @@ def update( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._put( - f"/organizations/{org_id}/organizationconfig", + path_template("/organizations/{org_id}/organizationconfig", org_id=org_id), body=maybe_transform( { "currency": currency, @@ -485,7 +485,7 @@ async def retrieve( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/organizationconfig", + path_template("/organizations/{org_id}/organizationconfig", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -770,7 +770,7 @@ async def update( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._put( - f"/organizations/{org_id}/organizationconfig", + path_template("/organizations/{org_id}/organizationconfig", org_id=org_id), body=await async_maybe_transform( { "currency": currency, diff --git a/src/m3ter/resources/permission_policies.py b/src/m3ter/resources/permission_policies.py index 3e7172c..f183cb6 100644 --- a/src/m3ter/resources/permission_policies.py +++ b/src/m3ter/resources/permission_policies.py @@ -19,7 +19,7 @@ permission_policy_remove_from_service_user_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 ( @@ -142,7 +142,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/permissionpolicies", + path_template("/organizations/{org_id}/permissionpolicies", org_id=org_id), body=maybe_transform( { "name": name, @@ -188,7 +188,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/permissionpolicies/{id}", + path_template("/organizations/{org_id}/permissionpolicies/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -268,7 +268,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/permissionpolicies/{id}", + path_template("/organizations/{org_id}/permissionpolicies/{id}", org_id=org_id, id=id), body=maybe_transform( { "name": name, @@ -317,7 +317,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/permissionpolicies", + path_template("/organizations/{org_id}/permissionpolicies", org_id=org_id), page=SyncCursor[PermissionPolicyResponse], options=make_request_options( extra_headers=extra_headers, @@ -366,7 +366,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/permissionpolicies/{id}", + path_template("/organizations/{org_id}/permissionpolicies/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -420,7 +420,11 @@ def add_to_service_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtoserviceuser", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtoserviceuser", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=maybe_transform( { "principal_id": principal_id, @@ -478,7 +482,11 @@ def add_to_support_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtosupportusers", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtosupportusers", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=maybe_transform( {"version": version}, permission_policy_add_to_support_user_params.PermissionPolicyAddToSupportUserParams, @@ -536,7 +544,11 @@ def add_to_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtouser", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtouser", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=maybe_transform( { "principal_id": principal_id, @@ -597,7 +609,11 @@ def add_to_user_group( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtousergroup", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtousergroup", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=maybe_transform( { "principal_id": principal_id, @@ -658,7 +674,11 @@ def remove_from_service_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromserviceuser", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromserviceuser", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=maybe_transform( { "principal_id": principal_id, @@ -705,7 +725,11 @@ def remove_from_support_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromsupportusers", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromsupportusers", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -759,7 +783,11 @@ def remove_from_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromuser", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromuser", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=maybe_transform( { "principal_id": principal_id, @@ -820,7 +848,11 @@ def remove_from_user_group( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromusergroup", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromusergroup", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=maybe_transform( { "principal_id": principal_id, @@ -933,7 +965,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/permissionpolicies", + path_template("/organizations/{org_id}/permissionpolicies", org_id=org_id), body=await async_maybe_transform( { "name": name, @@ -979,7 +1011,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/permissionpolicies/{id}", + path_template("/organizations/{org_id}/permissionpolicies/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1059,7 +1091,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/permissionpolicies/{id}", + path_template("/organizations/{org_id}/permissionpolicies/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "name": name, @@ -1108,7 +1140,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/permissionpolicies", + path_template("/organizations/{org_id}/permissionpolicies", org_id=org_id), page=AsyncCursor[PermissionPolicyResponse], options=make_request_options( extra_headers=extra_headers, @@ -1157,7 +1189,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/permissionpolicies/{id}", + path_template("/organizations/{org_id}/permissionpolicies/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1211,7 +1243,11 @@ async def add_to_service_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return await self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtoserviceuser", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtoserviceuser", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=await async_maybe_transform( { "principal_id": principal_id, @@ -1269,7 +1305,11 @@ async def add_to_support_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return await self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtosupportusers", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtosupportusers", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=await async_maybe_transform( {"version": version}, permission_policy_add_to_support_user_params.PermissionPolicyAddToSupportUserParams, @@ -1327,7 +1367,11 @@ async def add_to_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return await self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtouser", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtouser", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=await async_maybe_transform( { "principal_id": principal_id, @@ -1388,7 +1432,11 @@ async def add_to_user_group( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return await self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtousergroup", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/addtousergroup", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=await async_maybe_transform( { "principal_id": principal_id, @@ -1449,7 +1497,11 @@ async def remove_from_service_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return await self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromserviceuser", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromserviceuser", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=await async_maybe_transform( { "principal_id": principal_id, @@ -1496,7 +1548,11 @@ async def remove_from_support_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return await self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromsupportusers", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromsupportusers", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1550,7 +1606,11 @@ async def remove_from_user( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return await self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromuser", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromuser", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=await async_maybe_transform( { "principal_id": principal_id, @@ -1611,7 +1671,11 @@ async def remove_from_user_group( f"Expected a non-empty value for `permission_policy_id` but received {permission_policy_id!r}" ) return await self._post( - f"/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromusergroup", + path_template( + "/organizations/{org_id}/permissionpolicies/{permission_policy_id}/removefromusergroup", + org_id=org_id, + permission_policy_id=permission_policy_id, + ), body=await async_maybe_transform( { "principal_id": principal_id, diff --git a/src/m3ter/resources/plan_group_links.py b/src/m3ter/resources/plan_group_links.py index 60f77fb..41c38de 100644 --- a/src/m3ter/resources/plan_group_links.py +++ b/src/m3ter/resources/plan_group_links.py @@ -6,7 +6,7 @@ from ..types import plan_group_link_list_params, plan_group_link_create_params, plan_group_link_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 ( @@ -93,7 +93,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/plangrouplinks", + path_template("/organizations/{org_id}/plangrouplinks", org_id=org_id), body=maybe_transform( { "plan_group_id": plan_group_id, @@ -139,7 +139,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/plangrouplinks/{id}", + path_template("/organizations/{org_id}/plangrouplinks/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -194,7 +194,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/plangrouplinks/{id}", + path_template("/organizations/{org_id}/plangrouplinks/{id}", org_id=org_id, id=id), body=maybe_transform( { "plan_group_id": plan_group_id, @@ -252,7 +252,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/plangrouplinks", + path_template("/organizations/{org_id}/plangrouplinks", org_id=org_id), page=SyncCursor[PlanGroupLinkResponse], options=make_request_options( extra_headers=extra_headers, @@ -304,7 +304,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/plangrouplinks/{id}", + path_template("/organizations/{org_id}/plangrouplinks/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -383,7 +383,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/plangrouplinks", + path_template("/organizations/{org_id}/plangrouplinks", org_id=org_id), body=await async_maybe_transform( { "plan_group_id": plan_group_id, @@ -429,7 +429,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/plangrouplinks/{id}", + path_template("/organizations/{org_id}/plangrouplinks/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -484,7 +484,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/plangrouplinks/{id}", + path_template("/organizations/{org_id}/plangrouplinks/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "plan_group_id": plan_group_id, @@ -542,7 +542,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/plangrouplinks", + path_template("/organizations/{org_id}/plangrouplinks", org_id=org_id), page=AsyncCursor[PlanGroupLinkResponse], options=make_request_options( extra_headers=extra_headers, @@ -594,7 +594,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/plangrouplinks/{id}", + path_template("/organizations/{org_id}/plangrouplinks/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/plan_groups.py b/src/m3ter/resources/plan_groups.py index 5880939..9e1e8bf 100644 --- a/src/m3ter/resources/plan_groups.py +++ b/src/m3ter/resources/plan_groups.py @@ -8,7 +8,7 @@ from ..types import plan_group_list_params, plan_group_create_params, plan_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 ( @@ -152,7 +152,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/plangroups", + path_template("/organizations/{org_id}/plangroups", org_id=org_id), body=maybe_transform( { "currency": currency, @@ -212,7 +212,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/plangroups/{id}", + path_template("/organizations/{org_id}/plangroups/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -331,7 +331,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/plangroups/{id}", + path_template("/organizations/{org_id}/plangroups/{id}", org_id=org_id, id=id), body=maybe_transform( { "currency": currency, @@ -402,7 +402,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/plangroups", + path_template("/organizations/{org_id}/plangroups", org_id=org_id), page=SyncCursor[PlanGroupResponse], options=make_request_options( extra_headers=extra_headers, @@ -457,7 +457,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/plangroups/{id}", + path_template("/organizations/{org_id}/plangroups/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -593,7 +593,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/plangroups", + path_template("/organizations/{org_id}/plangroups", org_id=org_id), body=await async_maybe_transform( { "currency": currency, @@ -653,7 +653,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/plangroups/{id}", + path_template("/organizations/{org_id}/plangroups/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -772,7 +772,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/plangroups/{id}", + path_template("/organizations/{org_id}/plangroups/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "currency": currency, @@ -843,7 +843,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/plangroups", + path_template("/organizations/{org_id}/plangroups", org_id=org_id), page=AsyncCursor[PlanGroupResponse], options=make_request_options( extra_headers=extra_headers, @@ -898,7 +898,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/plangroups/{id}", + path_template("/organizations/{org_id}/plangroups/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/plan_templates.py b/src/m3ter/resources/plan_templates.py index 027e172..66b7d92 100644 --- a/src/m3ter/resources/plan_templates.py +++ b/src/m3ter/resources/plan_templates.py @@ -9,7 +9,7 @@ from ..types import plan_template_list_params, plan_template_create_params, plan_template_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 ( @@ -206,7 +206,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/plantemplates", + path_template("/organizations/{org_id}/plantemplates", org_id=org_id), body=maybe_transform( { "bill_frequency": bill_frequency, @@ -269,7 +269,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/plantemplates/{id}", + path_template("/organizations/{org_id}/plantemplates/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -437,7 +437,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/plantemplates/{id}", + path_template("/organizations/{org_id}/plantemplates/{id}", org_id=org_id, id=id), body=maybe_transform( { "bill_frequency": bill_frequency, @@ -512,7 +512,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/plantemplates", + path_template("/organizations/{org_id}/plantemplates", org_id=org_id), page=SyncCursor[PlanTemplateResponse], options=make_request_options( extra_headers=extra_headers, @@ -566,7 +566,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/plantemplates/{id}", + path_template("/organizations/{org_id}/plantemplates/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -755,7 +755,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/plantemplates", + path_template("/organizations/{org_id}/plantemplates", org_id=org_id), body=await async_maybe_transform( { "bill_frequency": bill_frequency, @@ -818,7 +818,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/plantemplates/{id}", + path_template("/organizations/{org_id}/plantemplates/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -986,7 +986,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/plantemplates/{id}", + path_template("/organizations/{org_id}/plantemplates/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "bill_frequency": bill_frequency, @@ -1061,7 +1061,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/plantemplates", + path_template("/organizations/{org_id}/plantemplates", org_id=org_id), page=AsyncCursor[PlanTemplateResponse], options=make_request_options( extra_headers=extra_headers, @@ -1115,7 +1115,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/plantemplates/{id}", + path_template("/organizations/{org_id}/plantemplates/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/plans.py b/src/m3ter/resources/plans.py index e0e55b4..527cf5e 100644 --- a/src/m3ter/resources/plans.py +++ b/src/m3ter/resources/plans.py @@ -8,7 +8,7 @@ from ..types import plan_list_params, plan_create_params, plan_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 ( @@ -183,7 +183,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/plans", + path_template("/organizations/{org_id}/plans", org_id=org_id), body=maybe_transform( { "code": code, @@ -242,7 +242,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/plans/{id}", + path_template("/organizations/{org_id}/plans/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -383,7 +383,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/plans/{id}", + path_template("/organizations/{org_id}/plans/{id}", org_id=org_id, id=id), body=maybe_transform( { "code": code, @@ -454,7 +454,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/plans", + path_template("/organizations/{org_id}/plans", org_id=org_id), page=SyncCursor[PlanResponse], options=make_request_options( extra_headers=extra_headers, @@ -506,7 +506,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/plans/{id}", + path_template("/organizations/{org_id}/plans/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -673,7 +673,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/plans", + path_template("/organizations/{org_id}/plans", org_id=org_id), body=await async_maybe_transform( { "code": code, @@ -732,7 +732,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/plans/{id}", + path_template("/organizations/{org_id}/plans/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -873,7 +873,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/plans/{id}", + path_template("/organizations/{org_id}/plans/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "code": code, @@ -944,7 +944,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/plans", + path_template("/organizations/{org_id}/plans", org_id=org_id), page=AsyncCursor[PlanResponse], options=make_request_options( extra_headers=extra_headers, @@ -996,7 +996,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/plans/{id}", + path_template("/organizations/{org_id}/plans/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/pricings.py b/src/m3ter/resources/pricings.py index 59fa992..7b88563 100644 --- a/src/m3ter/resources/pricings.py +++ b/src/m3ter/resources/pricings.py @@ -10,7 +10,7 @@ from ..types import pricing_list_params, pricing_create_params, pricing_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 ( @@ -228,7 +228,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/pricings", + path_template("/organizations/{org_id}/pricings", org_id=org_id), body=maybe_transform( { "pricing_bands": pricing_bands, @@ -290,7 +290,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/pricings/{id}", + path_template("/organizations/{org_id}/pricings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -474,7 +474,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/pricings/{id}", + path_template("/organizations/{org_id}/pricings/{id}", org_id=org_id, id=id), body=maybe_transform( { "pricing_bands": pricing_bands, @@ -555,7 +555,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/pricings", + path_template("/organizations/{org_id}/pricings", org_id=org_id), page=SyncCursor[PricingResponse], options=make_request_options( extra_headers=extra_headers, @@ -609,7 +609,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/pricings/{id}", + path_template("/organizations/{org_id}/pricings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -818,7 +818,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/pricings", + path_template("/organizations/{org_id}/pricings", org_id=org_id), body=await async_maybe_transform( { "pricing_bands": pricing_bands, @@ -880,7 +880,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/pricings/{id}", + path_template("/organizations/{org_id}/pricings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -1064,7 +1064,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/pricings/{id}", + path_template("/organizations/{org_id}/pricings/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "pricing_bands": pricing_bands, @@ -1145,7 +1145,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/pricings", + path_template("/organizations/{org_id}/pricings", org_id=org_id), page=AsyncCursor[PricingResponse], options=make_request_options( extra_headers=extra_headers, @@ -1199,7 +1199,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/pricings/{id}", + path_template("/organizations/{org_id}/pricings/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/products.py b/src/m3ter/resources/products.py index 221859d..5ccf896 100644 --- a/src/m3ter/resources/products.py +++ b/src/m3ter/resources/products.py @@ -8,7 +8,7 @@ from ..types import product_list_params, product_create_params, product_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 ( @@ -110,7 +110,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/products", + path_template("/organizations/{org_id}/products", org_id=org_id), body=maybe_transform( { "code": code, @@ -160,7 +160,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/products/{id}", + path_template("/organizations/{org_id}/products/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -237,7 +237,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/products/{id}", + path_template("/organizations/{org_id}/products/{id}", org_id=org_id, id=id), body=maybe_transform( { "code": code, @@ -295,7 +295,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/products", + path_template("/organizations/{org_id}/products", org_id=org_id), page=SyncCursor[ProductResponse], options=make_request_options( extra_headers=extra_headers, @@ -348,7 +348,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/products/{id}", + path_template("/organizations/{org_id}/products/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -442,7 +442,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/products", + path_template("/organizations/{org_id}/products", org_id=org_id), body=await async_maybe_transform( { "code": code, @@ -492,7 +492,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/products/{id}", + path_template("/organizations/{org_id}/products/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -569,7 +569,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/products/{id}", + path_template("/organizations/{org_id}/products/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "code": code, @@ -627,7 +627,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/products", + path_template("/organizations/{org_id}/products", org_id=org_id), page=AsyncCursor[ProductResponse], options=make_request_options( extra_headers=extra_headers, @@ -680,7 +680,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/products/{id}", + path_template("/organizations/{org_id}/products/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/resource_groups.py b/src/m3ter/resources/resource_groups.py index d494d04..277d8f1 100644 --- a/src/m3ter/resources/resource_groups.py +++ b/src/m3ter/resources/resource_groups.py @@ -16,7 +16,7 @@ resource_group_list_permissions_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 ( @@ -125,7 +125,7 @@ def create( if not type: raise ValueError(f"Expected a non-empty value for `type` but received {type!r}") return self._post( - f"/organizations/{org_id}/resourcegroups/{type}", + path_template("/organizations/{org_id}/resourcegroups/{type}", org_id=org_id, type=type), body=maybe_transform( { "name": name, @@ -173,7 +173,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/resourcegroups/{type}/{id}", + path_template("/organizations/{org_id}/resourcegroups/{type}/{id}", org_id=org_id, type=type, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -220,7 +220,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/resourcegroups/{type}/{id}", + path_template("/organizations/{org_id}/resourcegroups/{type}/{id}", org_id=org_id, type=type, id=id), body=maybe_transform( { "name": name, @@ -271,7 +271,7 @@ def list( if not type: raise ValueError(f"Expected a non-empty value for `type` but received {type!r}") return self._get_api_list( - f"/organizations/{org_id}/resourcegroups/{type}", + path_template("/organizations/{org_id}/resourcegroups/{type}", org_id=org_id, type=type), page=SyncCursor[ResourceGroupResponse], options=make_request_options( extra_headers=extra_headers, @@ -323,7 +323,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/resourcegroups/{type}/{id}", + path_template("/organizations/{org_id}/resourcegroups/{type}/{id}", org_id=org_id, type=type, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -387,7 +387,12 @@ def add_resource( if not resource_group_id: raise ValueError(f"Expected a non-empty value for `resource_group_id` but received {resource_group_id!r}") return self._post( - f"/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/addresource", + path_template( + "/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/addresource", + org_id=org_id, + type=type, + resource_group_id=resource_group_id, + ), body=maybe_transform( { "target_id": target_id, @@ -442,7 +447,12 @@ def list_contents( if not resource_group_id: raise ValueError(f"Expected a non-empty value for `resource_group_id` but received {resource_group_id!r}") return self._get_api_list( - f"/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/contents", + path_template( + "/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/contents", + org_id=org_id, + type=type, + resource_group_id=resource_group_id, + ), page=SyncCursor[ResourceGroupListContentsResponse], options=make_request_options( extra_headers=extra_headers, @@ -501,7 +511,12 @@ def list_permissions( if not resource_group_id: raise ValueError(f"Expected a non-empty value for `resource_group_id` but received {resource_group_id!r}") return self._get_api_list( - f"/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/permissions", + path_template( + "/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/permissions", + org_id=org_id, + type=type, + resource_group_id=resource_group_id, + ), page=SyncCursor[PermissionPolicyResponse], options=make_request_options( extra_headers=extra_headers, @@ -576,7 +591,12 @@ def remove_resource( if not resource_group_id: raise ValueError(f"Expected a non-empty value for `resource_group_id` but received {resource_group_id!r}") return self._post( - f"/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/removeresource", + path_template( + "/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/removeresource", + org_id=org_id, + type=type, + resource_group_id=resource_group_id, + ), body=maybe_transform( { "target_id": target_id, @@ -683,7 +703,7 @@ async def create( if not type: raise ValueError(f"Expected a non-empty value for `type` but received {type!r}") return await self._post( - f"/organizations/{org_id}/resourcegroups/{type}", + path_template("/organizations/{org_id}/resourcegroups/{type}", org_id=org_id, type=type), body=await async_maybe_transform( { "name": name, @@ -731,7 +751,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/resourcegroups/{type}/{id}", + path_template("/organizations/{org_id}/resourcegroups/{type}/{id}", org_id=org_id, type=type, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -778,7 +798,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/resourcegroups/{type}/{id}", + path_template("/organizations/{org_id}/resourcegroups/{type}/{id}", org_id=org_id, type=type, id=id), body=await async_maybe_transform( { "name": name, @@ -829,7 +849,7 @@ def list( if not type: raise ValueError(f"Expected a non-empty value for `type` but received {type!r}") return self._get_api_list( - f"/organizations/{org_id}/resourcegroups/{type}", + path_template("/organizations/{org_id}/resourcegroups/{type}", org_id=org_id, type=type), page=AsyncCursor[ResourceGroupResponse], options=make_request_options( extra_headers=extra_headers, @@ -881,7 +901,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/resourcegroups/{type}/{id}", + path_template("/organizations/{org_id}/resourcegroups/{type}/{id}", org_id=org_id, type=type, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -945,7 +965,12 @@ async def add_resource( if not resource_group_id: raise ValueError(f"Expected a non-empty value for `resource_group_id` but received {resource_group_id!r}") return await self._post( - f"/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/addresource", + path_template( + "/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/addresource", + org_id=org_id, + type=type, + resource_group_id=resource_group_id, + ), body=await async_maybe_transform( { "target_id": target_id, @@ -1000,7 +1025,12 @@ def list_contents( if not resource_group_id: raise ValueError(f"Expected a non-empty value for `resource_group_id` but received {resource_group_id!r}") return self._get_api_list( - f"/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/contents", + path_template( + "/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/contents", + org_id=org_id, + type=type, + resource_group_id=resource_group_id, + ), page=AsyncCursor[ResourceGroupListContentsResponse], options=make_request_options( extra_headers=extra_headers, @@ -1059,7 +1089,12 @@ def list_permissions( if not resource_group_id: raise ValueError(f"Expected a non-empty value for `resource_group_id` but received {resource_group_id!r}") return self._get_api_list( - f"/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/permissions", + path_template( + "/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/permissions", + org_id=org_id, + type=type, + resource_group_id=resource_group_id, + ), page=AsyncCursor[PermissionPolicyResponse], options=make_request_options( extra_headers=extra_headers, @@ -1134,7 +1169,12 @@ async def remove_resource( if not resource_group_id: raise ValueError(f"Expected a non-empty value for `resource_group_id` but received {resource_group_id!r}") return await self._post( - f"/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/removeresource", + path_template( + "/organizations/{org_id}/resourcegroups/{type}/{resource_group_id}/removeresource", + org_id=org_id, + type=type, + resource_group_id=resource_group_id, + ), body=await async_maybe_transform( { "target_id": target_id, diff --git a/src/m3ter/resources/scheduled_event_configurations.py b/src/m3ter/resources/scheduled_event_configurations.py index a66aeb4..b4c0c1f 100644 --- a/src/m3ter/resources/scheduled_event_configurations.py +++ b/src/m3ter/resources/scheduled_event_configurations.py @@ -10,7 +10,7 @@ scheduled_event_configuration_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 ( @@ -115,7 +115,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/scheduledevents/configurations", + path_template("/organizations/{org_id}/scheduledevents/configurations", org_id=org_id), body=maybe_transform( { "entity": entity, @@ -163,7 +163,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/scheduledevents/configurations/{id}", + path_template("/organizations/{org_id}/scheduledevents/configurations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -235,7 +235,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/scheduledevents/configurations/{id}", + path_template("/organizations/{org_id}/scheduledevents/configurations/{id}", org_id=org_id, id=id), body=maybe_transform( { "entity": entity, @@ -289,7 +289,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/scheduledevents/configurations", + path_template("/organizations/{org_id}/scheduledevents/configurations", org_id=org_id), page=SyncCursor[ScheduledEventConfigurationResponse], options=make_request_options( extra_headers=extra_headers, @@ -339,7 +339,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/scheduledevents/configurations/{id}", + path_template("/organizations/{org_id}/scheduledevents/configurations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -436,7 +436,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/scheduledevents/configurations", + path_template("/organizations/{org_id}/scheduledevents/configurations", org_id=org_id), body=await async_maybe_transform( { "entity": entity, @@ -484,7 +484,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/scheduledevents/configurations/{id}", + path_template("/organizations/{org_id}/scheduledevents/configurations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -556,7 +556,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/scheduledevents/configurations/{id}", + path_template("/organizations/{org_id}/scheduledevents/configurations/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "entity": entity, @@ -610,7 +610,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/scheduledevents/configurations", + path_template("/organizations/{org_id}/scheduledevents/configurations", org_id=org_id), page=AsyncCursor[ScheduledEventConfigurationResponse], options=make_request_options( extra_headers=extra_headers, @@ -660,7 +660,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/scheduledevents/configurations/{id}", + path_template("/organizations/{org_id}/scheduledevents/configurations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/statements/statement_definitions.py b/src/m3ter/resources/statements/statement_definitions.py index a2f9219..ddf4198 100644 --- a/src/m3ter/resources/statements/statement_definitions.py +++ b/src/m3ter/resources/statements/statement_definitions.py @@ -8,7 +8,7 @@ import httpx 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 ( @@ -122,7 +122,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/statementdefinitions", + path_template("/organizations/{org_id}/statementdefinitions", org_id=org_id), body=maybe_transform( { "aggregation_frequency": aggregation_frequency, @@ -176,7 +176,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/statementdefinitions/{id}", + path_template("/organizations/{org_id}/statementdefinitions/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -251,7 +251,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/statementdefinitions/{id}", + path_template("/organizations/{org_id}/statementdefinitions/{id}", org_id=org_id, id=id), body=maybe_transform( { "aggregation_frequency": aggregation_frequency, @@ -308,7 +308,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/statementdefinitions", + path_template("/organizations/{org_id}/statementdefinitions", org_id=org_id), page=SyncCursor[StatementDefinitionResponse], options=make_request_options( extra_headers=extra_headers, @@ -360,7 +360,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/statementdefinitions/{id}", + path_template("/organizations/{org_id}/statementdefinitions/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -461,7 +461,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/statementdefinitions", + path_template("/organizations/{org_id}/statementdefinitions", org_id=org_id), body=await async_maybe_transform( { "aggregation_frequency": aggregation_frequency, @@ -515,7 +515,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/statementdefinitions/{id}", + path_template("/organizations/{org_id}/statementdefinitions/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -590,7 +590,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/statementdefinitions/{id}", + path_template("/organizations/{org_id}/statementdefinitions/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "aggregation_frequency": aggregation_frequency, @@ -647,7 +647,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/statementdefinitions", + path_template("/organizations/{org_id}/statementdefinitions", org_id=org_id), page=AsyncCursor[StatementDefinitionResponse], options=make_request_options( extra_headers=extra_headers, @@ -699,7 +699,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/statementdefinitions/{id}", + path_template("/organizations/{org_id}/statementdefinitions/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/statements/statement_jobs.py b/src/m3ter/resources/statements/statement_jobs.py index 8034011..9bcdb64 100644 --- a/src/m3ter/resources/statements/statement_jobs.py +++ b/src/m3ter/resources/statements/statement_jobs.py @@ -5,7 +5,7 @@ import httpx 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 ( @@ -139,7 +139,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/statementjobs", + path_template("/organizations/{org_id}/statementjobs", org_id=org_id), body=maybe_transform( { "bill_id": bill_id, @@ -198,7 +198,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/statementjobs/{id}", + path_template("/organizations/{org_id}/statementjobs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -275,7 +275,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/statementjobs", + path_template("/organizations/{org_id}/statementjobs", org_id=org_id), page=SyncCursor[StatementJobResponse], options=make_request_options( extra_headers=extra_headers, @@ -331,7 +331,7 @@ def cancel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/organizations/{org_id}/statementjobs/{id}/cancel", + path_template("/organizations/{org_id}/statementjobs/{id}/cancel", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -424,7 +424,7 @@ def create_batch( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/statementjobs/batch", + path_template("/organizations/{org_id}/statementjobs/batch", org_id=org_id), body=maybe_transform( { "bill_ids": bill_ids, @@ -553,7 +553,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/statementjobs", + path_template("/organizations/{org_id}/statementjobs", org_id=org_id), body=await async_maybe_transform( { "bill_id": bill_id, @@ -612,7 +612,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/statementjobs/{id}", + path_template("/organizations/{org_id}/statementjobs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -689,7 +689,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/statementjobs", + path_template("/organizations/{org_id}/statementjobs", org_id=org_id), page=AsyncCursor[StatementJobResponse], options=make_request_options( extra_headers=extra_headers, @@ -745,7 +745,7 @@ async def cancel( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/organizations/{org_id}/statementjobs/{id}/cancel", + path_template("/organizations/{org_id}/statementjobs/{id}/cancel", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -838,7 +838,7 @@ async def create_batch( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/statementjobs/batch", + path_template("/organizations/{org_id}/statementjobs/batch", org_id=org_id), body=await async_maybe_transform( { "bill_ids": bill_ids, diff --git a/src/m3ter/resources/statements/statements.py b/src/m3ter/resources/statements/statements.py index c948f32..c881bc9 100644 --- a/src/m3ter/resources/statements/statements.py +++ b/src/m3ter/resources/statements/statements.py @@ -5,6 +5,7 @@ import httpx from ..._types import Body, Query, Headers, NotGiven, not_given +from ..._utils import path_template from ..._compat import cached_property from ..._resource import SyncAPIResource, AsyncAPIResource from ..._response import ( @@ -120,7 +121,7 @@ def create_csv( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._post( - f"/organizations/{org_id}/bills/{id}/statement/csv", + path_template("/organizations/{org_id}/bills/{id}/statement/csv", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -166,7 +167,7 @@ def get_csv( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/bills/{id}/statement/csv", + path_template("/organizations/{org_id}/bills/{id}/statement/csv", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -211,7 +212,7 @@ def get_json( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/bills/{id}/statement/json", + path_template("/organizations/{org_id}/bills/{id}/statement/json", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -304,7 +305,7 @@ async def create_csv( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._post( - f"/organizations/{org_id}/bills/{id}/statement/csv", + path_template("/organizations/{org_id}/bills/{id}/statement/csv", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -350,7 +351,7 @@ async def get_csv( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/bills/{id}/statement/csv", + path_template("/organizations/{org_id}/bills/{id}/statement/csv", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -395,7 +396,7 @@ async def get_json( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/bills/{id}/statement/json", + path_template("/organizations/{org_id}/bills/{id}/statement/json", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/transaction_types.py b/src/m3ter/resources/transaction_types.py index 3e025d1..7b6acf4 100644 --- a/src/m3ter/resources/transaction_types.py +++ b/src/m3ter/resources/transaction_types.py @@ -6,7 +6,7 @@ from ..types import transaction_type_list_params, transaction_type_create_params, transaction_type_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 ( @@ -104,7 +104,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/picklists/transactiontypes", + path_template("/organizations/{org_id}/picklists/transactiontypes", org_id=org_id), body=maybe_transform( { "name": name, @@ -152,7 +152,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/picklists/transactiontypes/{id}", + path_template("/organizations/{org_id}/picklists/transactiontypes/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -216,7 +216,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/picklists/transactiontypes/{id}", + path_template("/organizations/{org_id}/picklists/transactiontypes/{id}", org_id=org_id, id=id), body=maybe_transform( { "name": name, @@ -283,7 +283,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/picklists/transactiontypes", + path_template("/organizations/{org_id}/picklists/transactiontypes", org_id=org_id), page=SyncCursor[TransactionTypeResponse], options=make_request_options( extra_headers=extra_headers, @@ -335,7 +335,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/picklists/transactiontypes/{id}", + path_template("/organizations/{org_id}/picklists/transactiontypes/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -425,7 +425,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/picklists/transactiontypes", + path_template("/organizations/{org_id}/picklists/transactiontypes", org_id=org_id), body=await async_maybe_transform( { "name": name, @@ -473,7 +473,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/picklists/transactiontypes/{id}", + path_template("/organizations/{org_id}/picklists/transactiontypes/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -537,7 +537,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/picklists/transactiontypes/{id}", + path_template("/organizations/{org_id}/picklists/transactiontypes/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "name": name, @@ -604,7 +604,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/picklists/transactiontypes", + path_template("/organizations/{org_id}/picklists/transactiontypes", org_id=org_id), page=AsyncCursor[TransactionTypeResponse], options=make_request_options( extra_headers=extra_headers, @@ -656,7 +656,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/picklists/transactiontypes/{id}", + path_template("/organizations/{org_id}/picklists/transactiontypes/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/usage/file_uploads/file_uploads.py b/src/m3ter/resources/usage/file_uploads/file_uploads.py index 43d0e37..18f7220 100644 --- a/src/m3ter/resources/usage/file_uploads/file_uploads.py +++ b/src/m3ter/resources/usage/file_uploads/file_uploads.py @@ -15,7 +15,7 @@ AsyncJobsResourceWithStreamingResponse, ) from ...._types import Body, Query, Headers, NotGiven, 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 ( @@ -149,7 +149,7 @@ def generate_upload_url( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/fileuploads/measurements/generateUploadUrl", + path_template("/organizations/{org_id}/fileuploads/measurements/generateUploadUrl", org_id=org_id), body=maybe_transform( { "content_length": content_length, @@ -283,7 +283,7 @@ async def generate_upload_url( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/fileuploads/measurements/generateUploadUrl", + path_template("/organizations/{org_id}/fileuploads/measurements/generateUploadUrl", org_id=org_id), body=await async_maybe_transform( { "content_length": content_length, diff --git a/src/m3ter/resources/usage/file_uploads/jobs.py b/src/m3ter/resources/usage/file_uploads/jobs.py index 4ef56b0..f507b80 100644 --- a/src/m3ter/resources/usage/file_uploads/jobs.py +++ b/src/m3ter/resources/usage/file_uploads/jobs.py @@ -7,7 +7,7 @@ import httpx 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 ( @@ -96,7 +96,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/fileuploads/measurements/jobs/{id}", + path_template("/organizations/{org_id}/fileuploads/measurements/jobs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -156,7 +156,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/fileuploads/measurements/jobs", + path_template("/organizations/{org_id}/fileuploads/measurements/jobs", org_id=org_id), page=SyncCursor[FileUploadJobResponse], options=make_request_options( extra_headers=extra_headers, @@ -215,7 +215,7 @@ def get_original_download_url( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/fileuploads/measurements/jobs/{id}/original", + path_template("/organizations/{org_id}/fileuploads/measurements/jobs/{id}/original", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -294,7 +294,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/fileuploads/measurements/jobs/{id}", + path_template("/organizations/{org_id}/fileuploads/measurements/jobs/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -354,7 +354,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/fileuploads/measurements/jobs", + path_template("/organizations/{org_id}/fileuploads/measurements/jobs", org_id=org_id), page=AsyncCursor[FileUploadJobResponse], options=make_request_options( extra_headers=extra_headers, @@ -413,7 +413,7 @@ async def get_original_download_url( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/fileuploads/measurements/jobs/{id}/original", + path_template("/organizations/{org_id}/fileuploads/measurements/jobs/{id}/original", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/usage/usage.py b/src/m3ter/resources/usage/usage.py index ff91bb5..f94e4d9 100644 --- a/src/m3ter/resources/usage/usage.py +++ b/src/m3ter/resources/usage/usage.py @@ -9,7 +9,7 @@ from ...types import usage_query_params, usage_submit_params, usage_get_failed_ingest_download_url_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 ( @@ -148,7 +148,7 @@ def get_failed_ingest_download_url( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/measurements/failedIngest/getDownloadUrl", + path_template("/organizations/{org_id}/measurements/failedIngest/getDownloadUrl", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -264,7 +264,7 @@ def query( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/usage/query", + path_template("/organizations/{org_id}/usage/query", org_id=org_id), body=maybe_transform( { "account_ids": account_ids, @@ -353,9 +353,8 @@ def submit( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/measurements" - if self._client._base_url_overridden - else f"https://ingest.m3ter.com/organizations/{org_id}/measurements", + ("https://ingest.m3ter.com" if not self._client._base_url_overridden else "") + + path_template("/organizations/{org_id}/measurements", org_id=org_id), body=maybe_transform({"measurements": measurements}, usage_submit_params.UsageSubmitParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout @@ -545,7 +544,7 @@ async def get_failed_ingest_download_url( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/measurements/failedIngest/getDownloadUrl", + path_template("/organizations/{org_id}/measurements/failedIngest/getDownloadUrl", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -661,7 +660,7 @@ async def query( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/usage/query", + path_template("/organizations/{org_id}/usage/query", org_id=org_id), body=await async_maybe_transform( { "account_ids": account_ids, @@ -750,9 +749,8 @@ async def submit( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/measurements" - if self._client._base_url_overridden - else f"https://ingest.m3ter.com/organizations/{org_id}/measurements", + ("https://ingest.m3ter.com" if not self._client._base_url_overridden else "") + + path_template("/organizations/{org_id}/measurements", org_id=org_id), body=await async_maybe_transform({"measurements": measurements}, usage_submit_params.UsageSubmitParams), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout diff --git a/src/m3ter/resources/users/invitations.py b/src/m3ter/resources/users/invitations.py index 4abb900..47512ec 100644 --- a/src/m3ter/resources/users/invitations.py +++ b/src/m3ter/resources/users/invitations.py @@ -8,7 +8,7 @@ import httpx 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 ( @@ -123,7 +123,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/invitations", + path_template("/organizations/{org_id}/invitations", org_id=org_id), body=maybe_transform( { "email": email, @@ -175,7 +175,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/invitations/{id}", + path_template("/organizations/{org_id}/invitations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -216,7 +216,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/invitations", + path_template("/organizations/{org_id}/invitations", org_id=org_id), page=SyncCursor[InvitationResponse], options=make_request_options( extra_headers=extra_headers, @@ -333,7 +333,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/invitations", + path_template("/organizations/{org_id}/invitations", org_id=org_id), body=await async_maybe_transform( { "email": email, @@ -385,7 +385,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/invitations/{id}", + path_template("/organizations/{org_id}/invitations/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -426,7 +426,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/invitations", + path_template("/organizations/{org_id}/invitations", org_id=org_id), page=AsyncCursor[InvitationResponse], options=make_request_options( extra_headers=extra_headers, diff --git a/src/m3ter/resources/users/users.py b/src/m3ter/resources/users/users.py index 595808f..4e84704 100644 --- a/src/m3ter/resources/users/users.py +++ b/src/m3ter/resources/users/users.py @@ -9,7 +9,7 @@ from ...types import user_list_params, user_update_params, user_get_permissions_params, user_get_user_groups_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 ( @@ -110,7 +110,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/users/{id}", + path_template("/organizations/{org_id}/users/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -174,7 +174,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/users/{id}", + path_template("/organizations/{org_id}/users/{id}", org_id=org_id, id=id), body=maybe_transform( { "dt_end_access": dt_end_access, @@ -231,7 +231,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/users", + path_template("/organizations/{org_id}/users", org_id=org_id), page=SyncCursor[UserResponse], options=make_request_options( extra_headers=extra_headers, @@ -291,7 +291,7 @@ def get_permissions( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/users/{id}/permissions", + path_template("/organizations/{org_id}/users/{id}/permissions", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -370,7 +370,7 @@ def get_user_groups( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/users/{id}/usergroups", + path_template("/organizations/{org_id}/users/{id}/usergroups", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -415,7 +415,7 @@ def me( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get( - f"/organizations/{org_id}/users/me", + path_template("/organizations/{org_id}/users/me", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -454,7 +454,7 @@ def resend_password( raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return self._put( - f"/organizations/{org_id}/users/{id}/password/resend", + path_template("/organizations/{org_id}/users/{id}/password/resend", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -535,7 +535,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/users/{id}", + path_template("/organizations/{org_id}/users/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -599,7 +599,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/users/{id}", + path_template("/organizations/{org_id}/users/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "dt_end_access": dt_end_access, @@ -656,7 +656,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/users", + path_template("/organizations/{org_id}/users", org_id=org_id), page=AsyncCursor[UserResponse], options=make_request_options( extra_headers=extra_headers, @@ -716,7 +716,7 @@ async def get_permissions( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/users/{id}/permissions", + path_template("/organizations/{org_id}/users/{id}/permissions", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -795,7 +795,7 @@ async def get_user_groups( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/users/{id}/usergroups", + path_template("/organizations/{org_id}/users/{id}/usergroups", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -840,7 +840,7 @@ async def me( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._get( - f"/organizations/{org_id}/users/me", + path_template("/organizations/{org_id}/users/me", org_id=org_id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -879,7 +879,7 @@ async def resend_password( raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") extra_headers = {"Accept": "*/*", **(extra_headers or {})} return await self._put( - f"/organizations/{org_id}/users/{id}/password/resend", + path_template("/organizations/{org_id}/users/{id}/password/resend", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), diff --git a/src/m3ter/resources/webhooks.py b/src/m3ter/resources/webhooks.py index 141621b..5629ca2 100644 --- a/src/m3ter/resources/webhooks.py +++ b/src/m3ter/resources/webhooks.py @@ -11,7 +11,7 @@ webhook_set_active_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 ( @@ -122,7 +122,7 @@ def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._post( - f"/organizations/{org_id}/integrationdestinations/webhooks", + path_template("/organizations/{org_id}/integrationdestinations/webhooks", org_id=org_id), body=maybe_transform( { "credentials": credentials, @@ -172,7 +172,7 @@ def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._get( - f"/organizations/{org_id}/integrationdestinations/webhooks/{id}", + path_template("/organizations/{org_id}/integrationdestinations/webhooks/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -239,7 +239,7 @@ def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/integrationdestinations/webhooks/{id}", + path_template("/organizations/{org_id}/integrationdestinations/webhooks/{id}", org_id=org_id, id=id), body=maybe_transform( { "credentials": credentials, @@ -293,7 +293,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/integrationdestinations/webhooks", + path_template("/organizations/{org_id}/integrationdestinations/webhooks", org_id=org_id), page=SyncCursor[Webhook], options=make_request_options( extra_headers=extra_headers, @@ -343,7 +343,7 @@ def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._delete( - f"/organizations/{org_id}/integrationdestinations/webhooks/{id}", + path_template("/organizations/{org_id}/integrationdestinations/webhooks/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -388,7 +388,7 @@ def set_active( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return self._put( - f"/organizations/{org_id}/integrationdestinations/webhooks/{id}/active", + path_template("/organizations/{org_id}/integrationdestinations/webhooks/{id}/active", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, @@ -494,7 +494,7 @@ async def create( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return await self._post( - f"/organizations/{org_id}/integrationdestinations/webhooks", + path_template("/organizations/{org_id}/integrationdestinations/webhooks", org_id=org_id), body=await async_maybe_transform( { "credentials": credentials, @@ -544,7 +544,7 @@ async def retrieve( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._get( - f"/organizations/{org_id}/integrationdestinations/webhooks/{id}", + path_template("/organizations/{org_id}/integrationdestinations/webhooks/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -611,7 +611,7 @@ async def update( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/integrationdestinations/webhooks/{id}", + path_template("/organizations/{org_id}/integrationdestinations/webhooks/{id}", org_id=org_id, id=id), body=await async_maybe_transform( { "credentials": credentials, @@ -665,7 +665,7 @@ def list( if not org_id: raise ValueError(f"Expected a non-empty value for `org_id` but received {org_id!r}") return self._get_api_list( - f"/organizations/{org_id}/integrationdestinations/webhooks", + path_template("/organizations/{org_id}/integrationdestinations/webhooks", org_id=org_id), page=AsyncCursor[Webhook], options=make_request_options( extra_headers=extra_headers, @@ -715,7 +715,7 @@ async def delete( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._delete( - f"/organizations/{org_id}/integrationdestinations/webhooks/{id}", + path_template("/organizations/{org_id}/integrationdestinations/webhooks/{id}", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, extra_body=extra_body, timeout=timeout ), @@ -760,7 +760,7 @@ async def set_active( if not id: raise ValueError(f"Expected a non-empty value for `id` but received {id!r}") return await self._put( - f"/organizations/{org_id}/integrationdestinations/webhooks/{id}/active", + path_template("/organizations/{org_id}/integrationdestinations/webhooks/{id}/active", org_id=org_id, id=id), options=make_request_options( extra_headers=extra_headers, extra_query=extra_query, diff --git a/tests/test_utils/test_path.py b/tests/test_utils/test_path.py new file mode 100644 index 0000000..15c2363 --- /dev/null +++ b/tests/test_utils/test_path.py @@ -0,0 +1,89 @@ +from __future__ import annotations + +from typing import Any + +import pytest + +from m3ter._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) From 65a1e47911b86486a1f6b8bd5bc8ae5feb520d15 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 19 Mar 2026 19:03:27 +0000 Subject: [PATCH 05/31] refactor(tests): switch from prism to steady --- CONTRIBUTING.md | 2 +- scripts/mock | 26 +++++++++++++------------- scripts/test | 16 ++++++++-------- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a601330..7f324c7 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,7 +85,7 @@ $ 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 $ ./scripts/mock diff --git a/scripts/mock b/scripts/mock index bcf3b39..38201de 100755 --- a/scripts/mock +++ b/scripts/mock @@ -19,34 +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 # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism --version + npm exec --package=@stdy/cli@0.19.3 -- steady --version - npm exec --package=@stainless-api/prism-cli@5.15.0 -- prism mock "$URL" &> .prism.log & + npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" &> .stdy.log & - # Wait for server to come online (max 30s) + # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" attempts=0 - while ! grep -q "✖ fatal\|Prism is listening" ".prism.log" ; do + 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 Prism server to start" - cat .prism.log + 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.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index dbeda2d..2dfdc40 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.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-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 From d0ef76766537a414720512f10fb4373194cfaf44 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 17:52:51 +0000 Subject: [PATCH 06/31] chore(tests): bump steady to v0.19.4 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 38201de..e1c19e8 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.3 -- steady --version + npm exec --package=@stdy/cli@0.19.4 -- steady --version - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.3 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 2dfdc40..36fab0a 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.3 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 6d6fcb3631dbbbe79fdd7761ad9ed71be313b40b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 20 Mar 2026 21:39:11 +0000 Subject: [PATCH 07/31] chore(tests): bump steady to v0.19.5 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index e1c19e8..ab814d3 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.4 -- steady --version + npm exec --package=@stdy/cli@0.19.5 -- steady --version - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.4 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 36fab0a..d1c8e1a 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.4 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 82b49845738e8adde71e3e754f88a908d038a07e Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 12:41:02 +0000 Subject: [PATCH 08/31] chore(internal): update gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 95ceb18..3824f4c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ .prism.log +.stdy.log _dev __pycache__ From 3d0a67c9d8f2d14fc1597617cae17f049538bb3b Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 23 Mar 2026 18:02:25 +0000 Subject: [PATCH 09/31] chore(tests): bump steady to v0.19.6 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index ab814d3..b319bdf 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.5 -- steady --version + npm exec --package=@stdy/cli@0.19.6 -- steady --version - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.5 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index d1c8e1a..ab01948 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.5 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From edf08074926089e25f642074a80c5af93cdafc79 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:16:15 +0000 Subject: [PATCH 10/31] chore(ci): skip lint on metadata-only changes Note that we still want to run tests, as these depend on the metadata. --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d070711..6f33223 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: timeout-minutes: 10 name: lint runs-on: ${{ github.repository == 'stainless-sdks/m3ter-sdk-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 @@ -38,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: From 12eed8a0fa281e595cbe7d89e25ff45a4eb5268d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 24 Mar 2026 15:45:08 +0000 Subject: [PATCH 11/31] chore(tests): bump steady to v0.19.7 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index b319bdf..09eb49f 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.6 -- steady --version + npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & # Wait for server to come online via health endpoint (max 30s) echo -n "Waiting for server" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.6 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index ab01948..e46b9b5 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.6 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" echo exit 1 From 3cd118a9bc21161240103b20c337543971085869 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 19:25:48 +0000 Subject: [PATCH 12/31] feat(internal): implement indices array format for query and form serialization --- scripts/mock | 4 ++-- scripts/test | 2 +- src/m3ter/_qs.py | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 09eb49f..290e21b 100755 --- a/scripts/mock +++ b/scripts/mock @@ -24,7 +24,7 @@ if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout npm exec --package=@stdy/cli@0.19.7 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --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" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index e46b9b5..661f9bf 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-form-array-format=comma --validator-query-array-format=comma --validator-form-object-format=brackets --validator-query-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 diff --git a/src/m3ter/_qs.py b/src/m3ter/_qs.py index ada6fd3..de8c99b 100644 --- a/src/m3ter/_qs.py +++ b/src/m3ter/_qs.py @@ -101,7 +101,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 + "[]" From b1f62634a84d9c9caa18728cfe11feffdb406cfa Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 13:29:29 +0000 Subject: [PATCH 13/31] chore(tests): bump steady to v0.20.1 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 290e21b..15c2994 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.19.7 -- steady --version + npm exec --package=@stdy/cli@0.20.1 -- steady --version - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --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" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.19.7 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index 661f9bf..c8e2e9d 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.19.7 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From 2311bd8d4338a46ebef3d47110d078a84479d960 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 31 Mar 2026 19:19:51 +0000 Subject: [PATCH 14/31] chore(tests): bump steady to v0.20.2 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 15c2994..5cd7c15 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.1 -- steady --version + npm exec --package=@stdy/cli@0.20.2 -- steady --version - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --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" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index c8e2e9d..b8143aa 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.1 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" + echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From 9a0e3e1b22996f808cdb22d4c3758c447d2bbd01 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:10:53 +0000 Subject: [PATCH 15/31] fix(client): preserve hardcoded query params when merging with user params --- src/m3ter/_base_client.py | 4 ++++ tests/test_client.py | 48 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/src/m3ter/_base_client.py b/src/m3ter/_base_client.py index b35740a..27b8754 100644 --- a/src/m3ter/_base_client.py +++ b/src/m3ter/_base_client.py @@ -540,6 +540,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/tests/test_client.py b/tests/test_client.py index 59ac85d..6e1106c 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -552,6 +552,30 @@ def test_default_query_option(self) -> None: client.close() + def test_hardcoded_query_params_in_url(self, client: M3ter) -> 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: M3ter) -> None: request = client._build_request( FinalRequestOptions( @@ -1589,6 +1613,30 @@ async def test_default_query_option(self) -> None: await client.close() + async def test_hardcoded_query_params_in_url(self, async_client: AsyncM3ter) -> 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: M3ter) -> None: request = client._build_request( FinalRequestOptions( From 1b3db7bf3a779a3880b5c1dfef4b0a2d6b0eb8a3 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 14:56:12 +0000 Subject: [PATCH 16/31] fix: ensure file data are only sent as 1 parameter --- src/m3ter/_utils/_utils.py | 5 +++-- tests/test_extract_files.py | 9 +++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/m3ter/_utils/_utils.py b/src/m3ter/_utils/_utils.py index eec7f4a..63b8cd6 100644 --- a/src/m3ter/_utils/_utils.py +++ b/src/m3ter/_utils/_utils.py @@ -86,8 +86,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] diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index e4a1155..fe7a853 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -35,6 +35,15 @@ 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", [ From 3e53f729f671160b65b4694a35c3c42501701d91 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 17:36:13 +0000 Subject: [PATCH 17/31] perf(client): optimize file structure copying in multipart requests --- src/m3ter/_files.py | 56 ++++++++++++++++++-- src/m3ter/_utils/__init__.py | 1 - src/m3ter/_utils/_utils.py | 15 ------ tests/test_deepcopy.py | 58 --------------------- tests/test_files.py | 99 +++++++++++++++++++++++++++++++++++- 5 files changed, 151 insertions(+), 78 deletions(-) delete mode 100644 tests/test_deepcopy.py diff --git a/src/m3ter/_files.py b/src/m3ter/_files.py index cc14c14..0fdce17 100644 --- a/src/m3ter/_files.py +++ b/src/m3ter/_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]: @@ -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/m3ter/_utils/__init__.py b/src/m3ter/_utils/__init__.py index 10cb66d..1c090e5 100644 --- a/src/m3ter/_utils/__init__.py +++ b/src/m3ter/_utils/__init__.py @@ -24,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/m3ter/_utils/_utils.py b/src/m3ter/_utils/_utils.py index 63b8cd6..771859f 100644 --- a/src/m3ter/_utils/_utils.py +++ b/src/m3ter/_utils/_utils.py @@ -177,21 +177,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/tests/test_deepcopy.py b/tests/test_deepcopy.py deleted file mode 100644 index d0a2c74..0000000 --- a/tests/test_deepcopy.py +++ /dev/null @@ -1,58 +0,0 @@ -from m3ter._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_files.py b/tests/test_files.py index 890bfc7..a43b094 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 m3ter._files import to_httpx_files, async_to_httpx_files +from m3ter._files import to_httpx_files, deepcopy_with_paths, async_to_httpx_files +from m3ter._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 extracted == [("items[][file]", file1), ("items[][file]", file2)] + assert original == { + "items": [ + {"file": file1, "extra": 1}, + {"file": file2, "extra": 2}, + ], + "title": "example", + } + assert copied == { + "items": [ + {"extra": 1}, + {"extra": 2}, + ], + "title": "example", + } From afb0d6cc0f6b17f386ba162ffe5dbab68ccd10e2 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Apr 2026 19:52:13 +0000 Subject: [PATCH 18/31] chore(tests): bump steady to v0.22.1 --- scripts/mock | 6 +++--- scripts/test | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/scripts/mock b/scripts/mock index 5cd7c15..feebe5e 100755 --- a/scripts/mock +++ b/scripts/mock @@ -22,9 +22,9 @@ echo "==> Starting mock server with URL ${URL}" # Run steady mock on the given spec if [ "$1" == "--daemon" ]; then # Pre-install the package so the download doesn't eat into the startup timeout - npm exec --package=@stdy/cli@0.20.2 -- steady --version + npm exec --package=@stdy/cli@0.22.1 -- steady --version - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" &> .stdy.log & + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --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" @@ -48,5 +48,5 @@ if [ "$1" == "--daemon" ]; then echo else - npm exec --package=@stdy/cli@0.20.2 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" + npm exec --package=@stdy/cli@0.22.1 -- steady --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets "$URL" fi diff --git a/scripts/test b/scripts/test index b8143aa..19acc91 100755 --- a/scripts/test +++ b/scripts/test @@ -43,7 +43,7 @@ elif ! steady_is_running ; then echo -e "To run the server, pass in the path or url of your OpenAPI" echo -e "spec to the steady command:" echo - echo -e " \$ ${YELLOW}npm exec --package=@stdy/cli@0.20.2 -- steady path/to/your.openapi.yml --host 127.0.0.1 -p 4010 --validator-query-array-format=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${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=comma --validator-form-array-format=comma --validator-query-object-format=brackets --validator-form-object-format=brackets${NC}" echo exit 1 From e6be6268f7194886718d8aaf4cadf23762ea6bb4 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 22 Apr 2026 14:03:10 +0000 Subject: [PATCH 19/31] chore(internal): more robust bootstrap script --- scripts/bootstrap | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/bootstrap b/scripts/bootstrap index b430fee..fe8451e 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 From 56881765eb3db683d4b8f01ecae568d1cc8edd47 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 15:01:48 +0000 Subject: [PATCH 20/31] fix: use correct field name format for multipart file arrays --- src/m3ter/_qs.py | 8 ++----- src/m3ter/_types.py | 3 +++ src/m3ter/_utils/_utils.py | 42 ++++++++++++++++++++++++++++++------- tests/test_extract_files.py | 28 ++++++++++++++++++++----- tests/test_files.py | 2 +- 5 files changed, 63 insertions(+), 20 deletions(-) diff --git a/src/m3ter/_qs.py b/src/m3ter/_qs.py index de8c99b..4127c19 100644 --- a/src/m3ter/_qs.py +++ b/src/m3ter/_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 diff --git a/src/m3ter/_types.py b/src/m3ter/_types.py index 5eee961..9fa7c4c 100644 --- a/src/m3ter/_types.py +++ b/src/m3ter/_types.py @@ -47,6 +47,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/m3ter/_utils/_utils.py b/src/m3ter/_utils/_utils.py index 771859f..199cd23 100644 --- a/src/m3ter/_utils/_utils.py +++ b/src/m3ter/_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) @@ -106,6 +128,7 @@ def _extract_items( path, index=index, flattened_key=flattened_key, + array_format=array_format, ) elif is_list(obj): if key != "": @@ -117,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) ] ) diff --git a/tests/test_extract_files.py b/tests/test_extract_files.py index fe7a853..605a2bb 100644 --- a/tests/test_extract_files.py +++ b/tests/test_extract_files.py @@ -4,7 +4,7 @@ import pytest -from m3ter._types import FileTypes +from m3ter._types import FileTypes, ArrayFormat from m3ter._utils import extract_files @@ -37,10 +37,7 @@ def test_multiple_files() -> None: 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 extract_files(query, paths=[["files", ""]]) == [("files[]", b"file one"), ("files[]", b"file two")] assert query == {"title": "hello"} @@ -71,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 a43b094..a3d84e2 100644 --- a/tests/test_files.py +++ b/tests/test_files.py @@ -131,7 +131,7 @@ def test_extract_files_does_not_mutate_original_nested_array_path(self) -> None: copied = deepcopy_with_paths(original, [["items", "", "file"]]) extracted = extract_files(copied, paths=[["items", "", "file"]]) - assert extracted == [("items[][file]", file1), ("items[][file]", file2)] + assert [entry for _, entry in extracted] == [file1, file2] assert original == { "items": [ {"file": file1, "extra": 1}, From 7abe70c78c984efc97fdf2bdef561b3b4b121a01 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 27 Apr 2026 22:30:01 +0000 Subject: [PATCH 21/31] feat: support setting headers via env --- src/m3ter/_client.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/m3ter/_client.py b/src/m3ter/_client.py index 39c034d..1a372a1 100644 --- a/src/m3ter/_client.py +++ b/src/m3ter/_client.py @@ -22,7 +22,11 @@ RequestOptions, not_given, ) -from ._utils import is_given, get_async_library +from ._utils import ( + is_given, + is_mapping_t, + get_async_library, +) from ._compat import cached_property from ._models import FinalRequestOptions from ._version import __version__ @@ -212,6 +216,15 @@ def __init__( if base_url is None: base_url = f"https://api.m3ter.com" + custom_headers_env = os.environ.get("M3TER_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, @@ -1214,6 +1227,15 @@ def __init__( if base_url is None: base_url = f"https://api.m3ter.com" + custom_headers_env = os.environ.get("M3TER_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, From 6c33d19cbeaece55a81b03cfb56d3e0a341f4e91 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 29 Apr 2026 18:16:47 +0000 Subject: [PATCH 22/31] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index 4af5e39..ac05a80 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 286 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/rg%2Fm3ter-sdk-da8df9ce0e1464799f3b9feea8f957a3ca4c016fcba753ec71deb0ee2caf6536.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/rg/m3ter-sdk-da8df9ce0e1464799f3b9feea8f957a3ca4c016fcba753ec71deb0ee2caf6536.yml openapi_spec_hash: 3e75a21276e7cd06944188f512a7a4e3 config_hash: 10410ec7acfbc33ee5116775c6714440 From cf4f862065221f2a89f00efb8eb94cdf454d32d7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 16:23:34 +0000 Subject: [PATCH 23/31] codegen metadata --- .stats.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.stats.yml b/.stats.yml index ac05a80..01ec473 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 286 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/rg/m3ter-sdk-da8df9ce0e1464799f3b9feea8f957a3ca4c016fcba753ec71deb0ee2caf6536.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/rg/m3ter-sdk-0262e662baa79bf59551e012d323bf39693011afc2ab558a93e3a5425cc72a17.yml openapi_spec_hash: 3e75a21276e7cd06944188f512a7a4e3 config_hash: 10410ec7acfbc33ee5116775c6714440 From b94f0ed6582d97df147e15a60c94cb07c3e8ca2f Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Thu, 30 Apr 2026 20:59:11 +0000 Subject: [PATCH 24/31] chore(internal): reformat pyproject.toml --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 39c3bf3..5b22658 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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/m3ter/_files.py', '_dev/.*.py', 'tests/.*'] +exclude = ["src/m3ter/_files.py", "_dev/.*.py", "tests/.*"] strict_equality = true implicit_reexport = true From 1189baf65f73248e66dadb15c009efd8088893ed Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 8 May 2026 16:36:06 +0000 Subject: [PATCH 25/31] fix(client): add missing f-string prefix in file type error message --- src/m3ter/_files.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m3ter/_files.py b/src/m3ter/_files.py index 0fdce17..76da9e0 100644 --- a/src/m3ter/_files.py +++ b/src/m3ter/_files.py @@ -99,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 From 3bc5e2308d9c72883e96030e5772f1aa2b284fd7 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Mon, 11 May 2026 17:40:35 +0000 Subject: [PATCH 26/31] feat(internal/types): support eagerly validating pydantic iterators --- src/m3ter/_models.py | 80 ++++++++++++++++++++++++++++++++++++++++++++ tests/test_models.py | 60 +++++++++++++++++++++++++++++++-- 2 files changed, 137 insertions(+), 3 deletions(-) diff --git a/src/m3ter/_models.py b/src/m3ter/_models.py index 29070e0..8c5ab26 100644 --- a/src/m3ter/_models.py +++ b/src/m3ter/_models.py @@ -25,7 +25,9 @@ ClassVar, Protocol, Required, + Annotated, ParamSpec, + TypeAlias, TypedDict, TypeGuard, final, @@ -79,7 +81,15 @@ from ._constants import RAW_RESPONSE_HEADER if TYPE_CHECKING: + from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler + from pydantic_core import CoreSchema, core_schema from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema +else: + try: + from pydantic_core import CoreSchema, core_schema + except ImportError: + CoreSchema = None + core_schema = None __all__ = ["BaseModel", "GenericModel"] @@ -396,6 +406,76 @@ def model_dump_json( ) +class _EagerIterable(list[_T], Generic[_T]): + """ + Accepts any Iterable[T] input (including generators), consumes it + eagerly, and validates all items upfront. + + Validation preserves the original container type where possible + (e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON) + always emits a list — round-tripping through model_dump() will not + restore the original container type. + """ + + @classmethod + def __get_pydantic_core_schema__( + cls, + source_type: Any, + handler: GetCoreSchemaHandler, + ) -> CoreSchema: + (item_type,) = get_args(source_type) or (Any,) + item_schema: CoreSchema = handler.generate_schema(item_type) + list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema) + + return core_schema.no_info_wrap_validator_function( + cls._validate, + list_of_items_schema, + serialization=core_schema.plain_serializer_function_ser_schema( + cls._serialize, + info_arg=False, + ), + ) + + @staticmethod + def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any: + original_type: type[Any] = type(v) + + # Normalize to list so list_schema can validate each item + if isinstance(v, list): + items: list[_T] = v + else: + try: + items = list(v) + except TypeError as e: + raise TypeError("Value is not iterable") from e + + # Validate items against the inner schema + validated: list[_T] = handler(items) + + # Reconstruct original container type + if original_type is list: + return validated + # str(list) produces the list's repr, not a string built from items, + # so skip reconstruction for str and its subclasses. + if issubclass(original_type, str): + return validated + try: + return original_type(validated) + except (TypeError, ValueError): + # If the type cannot be reconstructed, just return the validated list + return validated + + @staticmethod + def _serialize(v: Iterable[_T]) -> list[_T]: + """Always serialize as a list so Pydantic's JSON encoder is happy.""" + if isinstance(v, list): + return v + return list(v) + + +EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable] + + def _construct_field(value: object, field: FieldInfo, key: str) -> object: if value is None: return field_get_default(field) diff --git a/tests/test_models.py b/tests/test_models.py index 615448f..7c0d650 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,7 +1,8 @@ import json -from typing import TYPE_CHECKING, Any, Dict, List, Union, Optional, cast +from typing import TYPE_CHECKING, Any, Dict, List, Union, Iterable, Optional, cast from datetime import datetime, timezone -from typing_extensions import Literal, Annotated, TypeAliasType +from collections import deque +from typing_extensions import Literal, Annotated, TypedDict, TypeAliasType import pytest import pydantic @@ -9,7 +10,7 @@ from m3ter._utils import PropertyInfo from m3ter._compat import PYDANTIC_V1, parse_obj, model_dump, model_json -from m3ter._models import DISCRIMINATOR_CACHE, BaseModel, construct_type +from m3ter._models import DISCRIMINATOR_CACHE, BaseModel, EagerIterable, construct_type class BasicModel(BaseModel): @@ -961,3 +962,56 @@ def __getattr__(self, attr: str) -> Item: ... assert model.a.prop == 1 assert isinstance(model.a, Item) assert model.other == "foo" + + +# NOTE: Workaround for Pydantic Iterable behavior. +# Iterable fields are replaced with a ValidatorIterator and may be consumed +# during serialization, which can cause subsequent dumps to return empty data. +# See: https://github.com/pydantic/pydantic/issues/9541 +@pytest.mark.parametrize( + "data, expected_validated", + [ + ([1, 2, 3], [1, 2, 3]), + ((1, 2, 3), (1, 2, 3)), + (set([1, 2, 3]), set([1, 2, 3])), + (iter([1, 2, 3]), [1, 2, 3]), + ([], []), + ((x for x in [1, 2, 3]), [1, 2, 3]), + (map(lambda x: x, [1, 2, 3]), [1, 2, 3]), + (frozenset([1, 2, 3]), frozenset([1, 2, 3])), + (deque([1, 2, 3]), deque([1, 2, 3])), + ], + ids=["list", "tuple", "set", "iterator", "empty", "generator", "map", "frozenset", "deque"], +) +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") +def test_iterable_construction(data: Iterable[int], expected_validated: Iterable[int]) -> None: + class TypeWithIterable(TypedDict): + items: EagerIterable[int] + + class Model(BaseModel): + data: TypeWithIterable + + m = Model.model_validate({"data": {"items": data}}) + assert m.data["items"] == expected_validated + + # Verify repeated dumps don't lose data (the original bug) + assert m.model_dump()["data"]["items"] == list(expected_validated) + assert m.model_dump()["data"]["items"] == list(expected_validated) + + +@pytest.mark.skipif(PYDANTIC_V1, reason="this is only supported in pydantic v2") +def test_iterable_construction_str_falls_back_to_list() -> None: + # str is iterable (over chars), but str(list_of_chars) produces the list's repr + # rather than reconstructing a string from items. We special-case str to fall + # back to list instead of attempting reconstruction. + class TypeWithIterable(TypedDict): + items: EagerIterable[str] + + class Model(BaseModel): + data: TypeWithIterable + + m = Model.model_validate({"data": {"items": "hello"}}) + + # falls back to list of chars rather than calling str(["h", "e", "l", "l", "o"]) + assert m.data["items"] == ["h", "e", "l", "l", "o"] + assert m.model_dump()["data"]["items"] == ["h", "e", "l", "l", "o"] From 17dcee8c13e0859e68ceef1b3ef4b609ae726c86 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Tue, 12 May 2026 19:08:12 +0000 Subject: [PATCH 27/31] ci: pin GitHub Actions to commit SHAs Pin all GitHub Actions referenced in generated workflows (both first-party `actions/*` and third-party) to immutable commit SHAs. Updating pinned actions is now a deliberate codegen-side bump rather than implicit on every workflow run. --- .github/workflows/ci.yml | 8 ++++---- .github/workflows/publish-pypi.yml | 2 +- .github/workflows/release-doctor.yml | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6f33223..8f2cbe4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/m3ter-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} 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 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | @@ -46,7 +46,7 @@ jobs: id-token: write runs-on: ${{ github.repository == 'stainless-sdks/m3ter-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | @@ -67,7 +67,7 @@ jobs: github.repository == 'stainless-sdks/m3ter-sdk-python' && !startsWith(github.ref, 'refs/heads/stl/') id: github-oidc - uses: actions/github-script@v8 + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0 with: script: core.setOutput('github_token', await core.getIDToken()); @@ -87,7 +87,7 @@ jobs: runs-on: ${{ github.repository == 'stainless-sdks/m3ter-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | diff --git a/.github/workflows/publish-pypi.yml b/.github/workflows/publish-pypi.yml index 9da16f4..7d17827 100644 --- a/.github/workflows/publish-pypi.yml +++ b/.github/workflows/publish-pypi.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Install Rye run: | diff --git a/.github/workflows/release-doctor.yml b/.github/workflows/release-doctor.yml index 727f050..b0b0dbd 100644 --- a/.github/workflows/release-doctor.yml +++ b/.github/workflows/release-doctor.yml @@ -12,7 +12,7 @@ jobs: if: github.repository == 'm3ter-com/m3ter-sdk-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next') steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - name: Check release environment run: | From 57c1e3c32584a63fbf443ad0ee9826cec6c57948 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 07:50:40 +0000 Subject: [PATCH 28/31] feat(api): switch to TypeScript SDK Revert the incorrect change that rolled back to using the Node SDK --- .stats.yml | 4 ++-- README.md | 9 --------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/.stats.yml b/.stats.yml index 01ec473..1f65d30 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 286 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/rg/m3ter-sdk-0262e662baa79bf59551e012d323bf39693011afc2ab558a93e3a5425cc72a17.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/rg/m3ter-sdk-b6704740a735035c0d02d5c52df2385c508c85cdb649a250c4515265c22dff5a.yml openapi_spec_hash: 3e75a21276e7cd06944188f512a7a4e3 -config_hash: 10410ec7acfbc33ee5116775c6714440 +config_hash: 49362aa74a21515a3e36e3743fdf1e55 diff --git a/README.md b/README.md index 9180b52..71b4b7c 100644 --- a/README.md +++ b/README.md @@ -9,15 +9,6 @@ and offers both synchronous and asynchronous clients powered by [httpx](https:// It is generated with [Stainless](https://www.stainless.com/). -## MCP Server - -Use the M3ter MCP Server to enable AI assistants to interact with this API, allowing them to explore endpoints, make test requests, and use documentation to help integrate this SDK into your application. - -[![Add to Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en-US/install-mcp?name=m3ter-sdk-mcp&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIm0zdGVyLXNkay1tY3AiXSwiZW52Ijp7Ik0zVEVSX0FQSV9LRVkiOiJNeSBBUEkgS2V5IiwiTTNURVJfQVBJX1NFQ1JFVCI6Ik15IEFQSSBTZWNyZXQiLCJNM1RFUl9BUElfVE9LRU4iOiJNeSBUb2tlbiIsIk0zVEVSX09SR19JRCI6Ik15IE9yZyBJRCJ9fQ) -[![Install in VS Code](https://img.shields.io/badge/_-Add_to_VS_Code-blue?style=for-the-badge&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIGZpbGw9Im5vbmUiIHZpZXdCb3g9IjAgMCA0MCA0MCI+PHBhdGggZmlsbD0iI0VFRSIgZmlsbC1ydWxlPSJldmVub2RkIiBkPSJNMzAuMjM1IDM5Ljg4NGEyLjQ5MSAyLjQ5MSAwIDAgMS0xLjc4MS0uNzNMMTIuNyAyNC43OGwtMy40NiAyLjYyNC0zLjQwNiAyLjU4MmExLjY2NSAxLjY2NSAwIDAgMS0xLjA4Mi4zMzggMS42NjQgMS42NjQgMCAwIDEtMS4wNDYtLjQzMWwtMi4yLTJhMS42NjYgMS42NjYgMCAwIDEgMC0yLjQ2M0w3LjQ1OCAyMCA0LjY3IDE3LjQ1MyAxLjUwNyAxNC41N2ExLjY2NSAxLjY2NSAwIDAgMSAwLTIuNDYzbDIuMi0yYTEuNjY1IDEuNjY1IDAgMCAxIDIuMTMtLjA5N2w2Ljg2MyA1LjIwOUwyOC40NTIuODQ0YTIuNDg4IDIuNDg4IDAgMCAxIDEuODQxLS43MjljLjM1MS4wMDkuNjk5LjA5MSAxLjAxOS4yNDVsOC4yMzYgMy45NjFhMi41IDIuNSAwIDAgMSAxLjQxNSAyLjI1M3YuMDk5LS4wNDVWMzMuMzd2LS4wNDUuMDk1YTIuNTAxIDIuNTAxIDAgMCAxLTEuNDE2IDIuMjU3bC04LjIzNSAzLjk2MWEyLjQ5MiAyLjQ5MiAwIDAgMS0xLjA3Ny4yNDZabS43MTYtMjguOTQ3LTExLjk0OCA5LjA2MiAxMS45NTIgOS4wNjUtLjAwNC0xOC4xMjdaIi8+PC9zdmc+)](https://vscode.stainless.com/mcp/%7B%22name%22%3A%22m3ter-sdk-mcp%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22m3ter-sdk-mcp%22%5D%2C%22env%22%3A%7B%22M3TER_API_KEY%22%3A%22My%20API%20Key%22%2C%22M3TER_API_SECRET%22%3A%22My%20API%20Secret%22%2C%22M3TER_API_TOKEN%22%3A%22My%20Token%22%2C%22M3TER_ORG_ID%22%3A%22My%20Org%20ID%22%7D%7D) - -> Note: You may need to set environment variables in your MCP client. - ## Documentation The REST API documentation can be found on [www.m3ter.com](https://www.m3ter.com). The full API of this library can be found in [api.md](api.md). From e5c705f9b3172ecb576c903d959257f39127155d Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 07:58:24 +0000 Subject: [PATCH 29/31] codegen metadata --- .stats.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.stats.yml b/.stats.yml index 1f65d30..ba1032d 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ configured_endpoints: 286 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/rg/m3ter-sdk-b6704740a735035c0d02d5c52df2385c508c85cdb649a250c4515265c22dff5a.yml +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/rg/m3ter-sdk-8ed00aff39e19053e35301d739d96ff2bafb352273d647c86073ca879a1dad3f.yml openapi_spec_hash: 3e75a21276e7cd06944188f512a7a4e3 -config_hash: 49362aa74a21515a3e36e3743fdf1e55 +config_hash: 5de7039a6202fb360a3970934cffb50e From 75e6694bd74dd1c17aa1b91831c30fd88721e91c Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:19:18 +0000 Subject: [PATCH 30/31] feat(stlc): configurable CI runner and private-production-repo support in workflow templates --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8f2cbe4..ceb048f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -18,7 +18,7 @@ jobs: lint: timeout-minutes: 10 name: lint - runs-on: ${{ github.repository == 'stainless-sdks/m3ter-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} 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@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -44,7 +44,7 @@ jobs: permissions: contents: read id-token: write - runs-on: ${{ github.repository == 'stainless-sdks/m3ter-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 @@ -84,7 +84,7 @@ jobs: test: timeout-minutes: 10 name: test - runs-on: ${{ github.repository == 'stainless-sdks/m3ter-sdk-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} + runs-on: ${{ startsWith(github.repository, 'stainless-sdks/') && 'depot-ubuntu-24.04' || 'ubuntu-latest' }} if: github.event_name == 'push' || github.event.pull_request.head.repo.fork steps: - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 From 4585f59c2d7b7e6fdd4291b50948cc2c31c0e505 Mon Sep 17 00:00:00 2001 From: "stainless-app[bot]" <142633134+stainless-app[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 17:19:55 +0000 Subject: [PATCH 31/31] release: 0.10.0 --- .release-please-manifest.json | 2 +- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++ pyproject.toml | 2 +- src/m3ter/_version.py | 2 +- 4 files changed, 52 insertions(+), 3 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 6d78745..091cfb1 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.9.0" + ".": "0.10.0" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 099e7e9..38fbd97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,54 @@ # Changelog +## 0.10.0 (2026-07-17) + +Full Changelog: [v0.9.0...v0.10.0](https://github.com/m3ter-com/m3ter-sdk-python/compare/v0.9.0...v0.10.0) + +### Features + +* **api:** switch to TypeScript SDK ([57c1e3c](https://github.com/m3ter-com/m3ter-sdk-python/commit/57c1e3c32584a63fbf443ad0ee9826cec6c57948)) +* **internal/types:** support eagerly validating pydantic iterators ([3bc5e23](https://github.com/m3ter-com/m3ter-sdk-python/commit/3bc5e2308d9c72883e96030e5772f1aa2b284fd7)) +* **internal:** implement indices array format for query and form serialization ([3cd118a](https://github.com/m3ter-com/m3ter-sdk-python/commit/3cd118a9bc21161240103b20c337543971085869)) +* **stlc:** configurable CI runner and private-production-repo support in workflow templates ([75e6694](https://github.com/m3ter-com/m3ter-sdk-python/commit/75e6694bd74dd1c17aa1b91831c30fd88721e91c)) +* support setting headers via env ([7abe70c](https://github.com/m3ter-com/m3ter-sdk-python/commit/7abe70c78c984efc97fdf2bdef561b3b4b121a01)) + + +### Bug Fixes + +* **client:** add missing f-string prefix in file type error message ([1189baf](https://github.com/m3ter-com/m3ter-sdk-python/commit/1189baf65f73248e66dadb15c009efd8088893ed)) +* **client:** preserve hardcoded query params when merging with user params ([9a0e3e1](https://github.com/m3ter-com/m3ter-sdk-python/commit/9a0e3e1b22996f808cdb22d4c3758c447d2bbd01)) +* **deps:** bump minimum typing-extensions version ([f4e0bf7](https://github.com/m3ter-com/m3ter-sdk-python/commit/f4e0bf765ad9b9ac52fd1b20961502b0ffb4c5ab)) +* ensure file data are only sent as 1 parameter ([1b3db7b](https://github.com/m3ter-com/m3ter-sdk-python/commit/1b3db7bf3a779a3880b5c1dfef4b0a2d6b0eb8a3)) +* **pydantic:** do not pass `by_alias` unless set ([d1effd7](https://github.com/m3ter-com/m3ter-sdk-python/commit/d1effd76e6d5a03fad1a5d8419ca8b52816b39e9)) +* sanitize endpoint path params ([5e98894](https://github.com/m3ter-com/m3ter-sdk-python/commit/5e988949139bf65480de9f9d2c698fee6e075439)) +* use correct field name format for multipart file arrays ([5688176](https://github.com/m3ter-com/m3ter-sdk-python/commit/56881765eb3db683d4b8f01ecae568d1cc8edd47)) + + +### Performance Improvements + +* **client:** optimize file structure copying in multipart requests ([3e53f72](https://github.com/m3ter-com/m3ter-sdk-python/commit/3e53f729f671160b65b4694a35c3c42501701d91)) + + +### Chores + +* **ci:** skip lint on metadata-only changes ([edf0807](https://github.com/m3ter-com/m3ter-sdk-python/commit/edf08074926089e25f642074a80c5af93cdafc79)) +* **internal:** more robust bootstrap script ([e6be626](https://github.com/m3ter-com/m3ter-sdk-python/commit/e6be6268f7194886718d8aaf4cadf23762ea6bb4)) +* **internal:** reformat pyproject.toml ([b94f0ed](https://github.com/m3ter-com/m3ter-sdk-python/commit/b94f0ed6582d97df147e15a60c94cb07c3e8ca2f)) +* **internal:** tweak CI branches ([8b957b0](https://github.com/m3ter-com/m3ter-sdk-python/commit/8b957b0f1ae1fa8319ef24fc46b53a02f1c1a7e5)) +* **internal:** update gitignore ([82b4984](https://github.com/m3ter-com/m3ter-sdk-python/commit/82b49845738e8adde71e3e754f88a908d038a07e)) +* **tests:** bump steady to v0.19.4 ([d0ef767](https://github.com/m3ter-com/m3ter-sdk-python/commit/d0ef76766537a414720512f10fb4373194cfaf44)) +* **tests:** bump steady to v0.19.5 ([6d6fcb3](https://github.com/m3ter-com/m3ter-sdk-python/commit/6d6fcb3631dbbbe79fdd7761ad9ed71be313b40b)) +* **tests:** bump steady to v0.19.6 ([3d0a67c](https://github.com/m3ter-com/m3ter-sdk-python/commit/3d0a67c9d8f2d14fc1597617cae17f049538bb3b)) +* **tests:** bump steady to v0.19.7 ([12eed8a](https://github.com/m3ter-com/m3ter-sdk-python/commit/12eed8a0fa281e595cbe7d89e25ff45a4eb5268d)) +* **tests:** bump steady to v0.20.1 ([b1f6263](https://github.com/m3ter-com/m3ter-sdk-python/commit/b1f62634a84d9c9caa18728cfe11feffdb406cfa)) +* **tests:** bump steady to v0.20.2 ([2311bd8](https://github.com/m3ter-com/m3ter-sdk-python/commit/2311bd8d4338a46ebef3d47110d078a84479d960)) +* **tests:** bump steady to v0.22.1 ([afb0d6c](https://github.com/m3ter-com/m3ter-sdk-python/commit/afb0d6cc0f6b17f386ba162ffe5dbab68ccd10e2)) + + +### Refactors + +* **tests:** switch from prism to steady ([65a1e47](https://github.com/m3ter-com/m3ter-sdk-python/commit/65a1e47911b86486a1f6b8bd5bc8ae5feb520d15)) + ## 0.9.0 (2026-03-07) Full Changelog: [v0.8.0...v0.9.0](https://github.com/m3ter-com/m3ter-sdk-python/compare/v0.8.0...v0.9.0) diff --git a/pyproject.toml b/pyproject.toml index 5b22658..f677bf6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "m3ter" -version = "0.9.0" +version = "0.10.0" description = "The official Python library for the m3ter API" dynamic = ["readme"] license = "Apache-2.0" diff --git a/src/m3ter/_version.py b/src/m3ter/_version.py index 1ca1465..5fb7059 100644 --- a/src/m3ter/_version.py +++ b/src/m3ter/_version.py @@ -1,4 +1,4 @@ # File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. __title__ = "m3ter" -__version__ = "0.9.0" # x-release-please-version +__version__ = "0.10.0" # x-release-please-version