Skip to content

Commit 5f721ac

Browse files
author
Stainless Bot
committed
feat(api): price evaluation endpoint generally available (#240)
1 parent 3dddf85 commit 5f721ac

File tree

9 files changed

+397
-41
lines changed

9 files changed

+397
-41
lines changed

api.md

+3-16
Original file line numberDiff line numberDiff line change
@@ -289,13 +289,14 @@ Methods:
289289
Types:
290290

291291
```python
292-
from orb.types import Price
292+
from orb.types import EvaluatePriceGroup, Price, PriceEvaluateResponse
293293
```
294294

295295
Methods:
296296

297297
- <code title="post /prices">client.prices.<a href="./src/orb/resources/prices/prices.py">create</a>(\*\*<a href="src/orb/types/price_create_params.py">params</a>) -> <a href="./src/orb/types/price.py">Price</a></code>
298298
- <code title="get /prices">client.prices.<a href="./src/orb/resources/prices/prices.py">list</a>(\*\*<a href="src/orb/types/price_list_params.py">params</a>) -> <a href="./src/orb/types/price.py">SyncPage[Price]</a></code>
299+
- <code title="post /prices/{price_id}/evaluate">client.prices.<a href="./src/orb/resources/prices/prices.py">evaluate</a>(price_id, \*\*<a href="src/orb/types/price_evaluate_params.py">params</a>) -> <a href="./src/orb/types/price_evaluate_response.py">PriceEvaluateResponse</a></code>
299300
- <code title="get /prices/{price_id}">client.prices.<a href="./src/orb/resources/prices/prices.py">fetch</a>(price_id) -> <a href="./src/orb/types/price.py">Price</a></code>
300301

301302
## ExternalPriceID
@@ -341,18 +342,4 @@ Methods:
341342
Methods:
342343

343344
- <code>client.webhooks.<a href="./src/orb/resources/webhooks.py">unwrap</a>(\*args) -> object</code>
344-
- <code>client.webhooks.<a href="./src/orb/resources/webhooks.py">verify_signature</a>(\*args) -> None</code>
345-
346-
# Beta
347-
348-
## Price
349-
350-
Types:
351-
352-
```python
353-
from orb.types.beta import EvaluatePriceGroup, PriceEvaluateResponse
354-
```
355-
356-
Methods:
357-
358-
- <code title="post /prices/{price_id}/evaluate">client.beta.price.<a href="./src/orb/resources/beta/price.py">evaluate</a>(price_id, \*\*<a href="src/orb/types/beta/price_evaluate_params.py">params</a>) -> <a href="./src/orb/types/beta/price_evaluate_response.py">PriceEvaluateResponse</a></code>
345+
- <code>client.webhooks.<a href="./src/orb/resources/webhooks.py">verify_signature</a>(\*args) -> None</code>

src/orb/_client.py

-9
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ class Orb(SyncAPIClient):
6060
prices: resources.Prices
6161
subscriptions: resources.Subscriptions
6262
webhooks: resources.Webhooks
63-
beta: resources.Beta
6463
with_raw_response: OrbWithRawResponse
6564
with_streaming_response: OrbWithStreamedResponse
6665

@@ -141,8 +140,6 @@ def __init__(
141140
self.prices = resources.Prices(self)
142141
self.subscriptions = resources.Subscriptions(self)
143142
self.webhooks = resources.Webhooks(self)
144-
self.beta = resources.Beta(self)
145-
self.with_raw_response = OrbWithRawResponse(self)
146143
self.with_streaming_response = OrbWithStreamedResponse(self)
147144

148145
@property
@@ -314,7 +311,6 @@ class AsyncOrb(AsyncAPIClient):
314311
prices: resources.AsyncPrices
315312
subscriptions: resources.AsyncSubscriptions
316313
webhooks: resources.AsyncWebhooks
317-
beta: resources.AsyncBeta
318314
with_raw_response: AsyncOrbWithRawResponse
319315
with_streaming_response: AsyncOrbWithStreamedResponse
320316

@@ -395,7 +391,6 @@ def __init__(
395391
self.prices = resources.AsyncPrices(self)
396392
self.subscriptions = resources.AsyncSubscriptions(self)
397393
self.webhooks = resources.AsyncWebhooks(self)
398-
self.beta = resources.AsyncBeta(self)
399394
self.with_raw_response = AsyncOrbWithRawResponse(self)
400395
self.with_streaming_response = AsyncOrbWithStreamedResponse(self)
401396

@@ -568,7 +563,6 @@ def __init__(self, client: Orb) -> None:
568563
self.plans = resources.PlansWithRawResponse(client.plans)
569564
self.prices = resources.PricesWithRawResponse(client.prices)
570565
self.subscriptions = resources.SubscriptionsWithRawResponse(client.subscriptions)
571-
self.beta = resources.BetaWithRawResponse(client.beta)
572566

573567

574568
class AsyncOrbWithRawResponse:
@@ -585,7 +579,6 @@ def __init__(self, client: AsyncOrb) -> None:
585579
self.plans = resources.AsyncPlansWithRawResponse(client.plans)
586580
self.prices = resources.AsyncPricesWithRawResponse(client.prices)
587581
self.subscriptions = resources.AsyncSubscriptionsWithRawResponse(client.subscriptions)
588-
self.beta = resources.AsyncBetaWithRawResponse(client.beta)
589582

590583

591584
class OrbWithStreamedResponse:
@@ -602,7 +595,6 @@ def __init__(self, client: Orb) -> None:
602595
self.plans = resources.PlansWithStreamingResponse(client.plans)
603596
self.prices = resources.PricesWithStreamingResponse(client.prices)
604597
self.subscriptions = resources.SubscriptionsWithStreamingResponse(client.subscriptions)
605-
self.beta = resources.BetaWithStreamingResponse(client.beta)
606598

607599

608600
class AsyncOrbWithStreamedResponse:
@@ -619,7 +611,6 @@ def __init__(self, client: AsyncOrb) -> None:
619611
self.plans = resources.AsyncPlansWithStreamingResponse(client.plans)
620612
self.prices = resources.AsyncPricesWithStreamingResponse(client.prices)
621613
self.subscriptions = resources.AsyncSubscriptionsWithStreamingResponse(client.subscriptions)
622-
self.beta = resources.AsyncBetaWithStreamingResponse(client.beta)
623614

624615

625616
Client = Orb

src/orb/resources/__init__.py

-14
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,5 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

3-
from .beta import (
4-
Beta,
5-
AsyncBeta,
6-
BetaWithRawResponse,
7-
AsyncBetaWithRawResponse,
8-
BetaWithStreamingResponse,
9-
AsyncBetaWithStreamingResponse,
10-
)
113
from .items import (
124
Items,
135
AsyncItems,
@@ -182,12 +174,6 @@
182174
"AsyncSubscriptionsWithRawResponse",
183175
"SubscriptionsWithStreamingResponse",
184176
"AsyncSubscriptionsWithStreamingResponse",
185-
"Beta",
186-
"AsyncBeta",
187-
"BetaWithRawResponse",
188-
"AsyncBetaWithRawResponse",
189-
"BetaWithStreamingResponse",
190-
"AsyncBetaWithStreamingResponse",
191177
"Webhooks",
192178
"AsyncWebhooks",
193179
]

src/orb/resources/prices/prices.py

+202-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,14 @@
22

33
from __future__ import annotations
44

5-
from typing import Any, Dict, Optional, cast, overload
5+
from typing import Any, Dict, List, Union, Optional, cast, overload
6+
from datetime import datetime
67
from typing_extensions import Literal
78

89
import httpx
910

1011
from ... import _legacy_response
11-
from ...types import price_list_params, price_create_params
12+
from ...types import price_list_params, price_create_params, price_evaluate_params
1213
from ..._types import NOT_GIVEN, Body, Query, Headers, NotGiven
1314
from ..._utils import (
1415
required_args,
@@ -32,6 +33,7 @@
3233
ExternalPriceIDWithStreamingResponse,
3334
AsyncExternalPriceIDWithStreamingResponse,
3435
)
36+
from ...types.price_evaluate_response import PriceEvaluateResponse
3537

3638
__all__ = ["Prices", "AsyncPrices"]
3739

@@ -1335,6 +1337,99 @@ def list(
13351337
model=cast(Any, Price), # Union types cannot be passed in as arguments in the type system
13361338
)
13371339

1340+
def evaluate(
1341+
self,
1342+
price_id: str,
1343+
*,
1344+
timeframe_end: Union[str, datetime],
1345+
timeframe_start: Union[str, datetime],
1346+
customer_id: Optional[str] | NotGiven = NOT_GIVEN,
1347+
external_customer_id: Optional[str] | NotGiven = NOT_GIVEN,
1348+
filter: Optional[str] | NotGiven = NOT_GIVEN,
1349+
grouping_keys: List[str] | NotGiven = NOT_GIVEN,
1350+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
1351+
# The extra values given here take precedence over values defined on the client or passed to this method.
1352+
extra_headers: Headers | None = None,
1353+
extra_query: Query | None = None,
1354+
extra_body: Body | None = None,
1355+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
1356+
idempotency_key: str | None = None,
1357+
) -> PriceEvaluateResponse:
1358+
"""
1359+
This endpoint is used to evaluate the output of a price for a given customer and
1360+
time range. It enables filtering and grouping the output using
1361+
[computed properties](../guides/extensibility/advanced-metrics#computed-properties),
1362+
supporting the following workflows:
1363+
1364+
1. Showing detailed usage and costs to the end customer.
1365+
2. Auditing subtotals on invoice line items.
1366+
1367+
For these workflows, the expressiveness of computed properties in both the
1368+
filters and grouping is critical. For example, if you'd like to show your
1369+
customer their usage grouped by hour and another property, you can do so with
1370+
the following `grouping_keys`:
1371+
`["hour_floor_timestamp_millis(timestamp_millis)", "my_property"]`. If you'd
1372+
like to examine a customer's usage for a specific property value, you can do so
1373+
with the following `filter`:
1374+
`my_property = 'foo' AND my_other_property = 'bar'`.
1375+
1376+
By default, the start of the time range must be no more than 100 days ago and
1377+
the length of the results must be no greater than 1000. Note that this is a POST
1378+
endpoint rather than a GET endpoint because it employs a JSON body rather than
1379+
query parameters.
1380+
1381+
Args:
1382+
timeframe_end: The exclusive upper bound for event timestamps
1383+
1384+
timeframe_start: The inclusive lower bound for event timestamps
1385+
1386+
customer_id: The ID of the customer to which this evaluation is scoped.
1387+
1388+
external_customer_id: The external customer ID of the customer to which this evaluation is scoped.
1389+
1390+
filter: A boolean
1391+
[computed property](../guides/extensibility/advanced-metrics#computed-properties)
1392+
used to filter the underlying billable metric
1393+
1394+
grouping_keys: Properties (or
1395+
[computed properties](../guides/extensibility/advanced-metrics#computed-properties))
1396+
used to group the underlying billable metric
1397+
1398+
extra_headers: Send extra headers
1399+
1400+
extra_query: Add additional query parameters to the request
1401+
1402+
extra_body: Add additional JSON properties to the request
1403+
1404+
timeout: Override the client-level default timeout for this request, in seconds
1405+
1406+
idempotency_key: Specify a custom idempotency key for this request
1407+
"""
1408+
if not price_id:
1409+
raise ValueError(f"Expected a non-empty value for `price_id` but received {price_id!r}")
1410+
return self._post(
1411+
f"/prices/{price_id}/evaluate",
1412+
body=maybe_transform(
1413+
{
1414+
"timeframe_end": timeframe_end,
1415+
"timeframe_start": timeframe_start,
1416+
"customer_id": customer_id,
1417+
"external_customer_id": external_customer_id,
1418+
"filter": filter,
1419+
"grouping_keys": grouping_keys,
1420+
},
1421+
price_evaluate_params.PriceEvaluateParams,
1422+
),
1423+
options=make_request_options(
1424+
extra_headers=extra_headers,
1425+
extra_query=extra_query,
1426+
extra_body=extra_body,
1427+
timeout=timeout,
1428+
idempotency_key=idempotency_key,
1429+
),
1430+
cast_to=PriceEvaluateResponse,
1431+
)
1432+
13381433
def fetch(
13391434
self,
13401435
price_id: str,
@@ -2671,6 +2766,99 @@ def list(
26712766
model=cast(Any, Price), # Union types cannot be passed in as arguments in the type system
26722767
)
26732768

2769+
async def evaluate(
2770+
self,
2771+
price_id: str,
2772+
*,
2773+
timeframe_end: Union[str, datetime],
2774+
timeframe_start: Union[str, datetime],
2775+
customer_id: Optional[str] | NotGiven = NOT_GIVEN,
2776+
external_customer_id: Optional[str] | NotGiven = NOT_GIVEN,
2777+
filter: Optional[str] | NotGiven = NOT_GIVEN,
2778+
grouping_keys: List[str] | NotGiven = NOT_GIVEN,
2779+
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
2780+
# The extra values given here take precedence over values defined on the client or passed to this method.
2781+
extra_headers: Headers | None = None,
2782+
extra_query: Query | None = None,
2783+
extra_body: Body | None = None,
2784+
timeout: float | httpx.Timeout | None | NotGiven = NOT_GIVEN,
2785+
idempotency_key: str | None = None,
2786+
) -> PriceEvaluateResponse:
2787+
"""
2788+
This endpoint is used to evaluate the output of a price for a given customer and
2789+
time range. It enables filtering and grouping the output using
2790+
[computed properties](../guides/extensibility/advanced-metrics#computed-properties),
2791+
supporting the following workflows:
2792+
2793+
1. Showing detailed usage and costs to the end customer.
2794+
2. Auditing subtotals on invoice line items.
2795+
2796+
For these workflows, the expressiveness of computed properties in both the
2797+
filters and grouping is critical. For example, if you'd like to show your
2798+
customer their usage grouped by hour and another property, you can do so with
2799+
the following `grouping_keys`:
2800+
`["hour_floor_timestamp_millis(timestamp_millis)", "my_property"]`. If you'd
2801+
like to examine a customer's usage for a specific property value, you can do so
2802+
with the following `filter`:
2803+
`my_property = 'foo' AND my_other_property = 'bar'`.
2804+
2805+
By default, the start of the time range must be no more than 100 days ago and
2806+
the length of the results must be no greater than 1000. Note that this is a POST
2807+
endpoint rather than a GET endpoint because it employs a JSON body rather than
2808+
query parameters.
2809+
2810+
Args:
2811+
timeframe_end: The exclusive upper bound for event timestamps
2812+
2813+
timeframe_start: The inclusive lower bound for event timestamps
2814+
2815+
customer_id: The ID of the customer to which this evaluation is scoped.
2816+
2817+
external_customer_id: The external customer ID of the customer to which this evaluation is scoped.
2818+
2819+
filter: A boolean
2820+
[computed property](../guides/extensibility/advanced-metrics#computed-properties)
2821+
used to filter the underlying billable metric
2822+
2823+
grouping_keys: Properties (or
2824+
[computed properties](../guides/extensibility/advanced-metrics#computed-properties))
2825+
used to group the underlying billable metric
2826+
2827+
extra_headers: Send extra headers
2828+
2829+
extra_query: Add additional query parameters to the request
2830+
2831+
extra_body: Add additional JSON properties to the request
2832+
2833+
timeout: Override the client-level default timeout for this request, in seconds
2834+
2835+
idempotency_key: Specify a custom idempotency key for this request
2836+
"""
2837+
if not price_id:
2838+
raise ValueError(f"Expected a non-empty value for `price_id` but received {price_id!r}")
2839+
return await self._post(
2840+
f"/prices/{price_id}/evaluate",
2841+
body=await async_maybe_transform(
2842+
{
2843+
"timeframe_end": timeframe_end,
2844+
"timeframe_start": timeframe_start,
2845+
"customer_id": customer_id,
2846+
"external_customer_id": external_customer_id,
2847+
"filter": filter,
2848+
"grouping_keys": grouping_keys,
2849+
},
2850+
price_evaluate_params.PriceEvaluateParams,
2851+
),
2852+
options=make_request_options(
2853+
extra_headers=extra_headers,
2854+
extra_query=extra_query,
2855+
extra_body=extra_body,
2856+
timeout=timeout,
2857+
idempotency_key=idempotency_key,
2858+
),
2859+
cast_to=PriceEvaluateResponse,
2860+
)
2861+
26742862
async def fetch(
26752863
self,
26762864
price_id: str,
@@ -2718,6 +2906,9 @@ def __init__(self, prices: Prices) -> None:
27182906
self.list = _legacy_response.to_raw_response_wrapper(
27192907
prices.list,
27202908
)
2909+
self.evaluate = _legacy_response.to_raw_response_wrapper(
2910+
prices.evaluate,
2911+
)
27212912
self.fetch = _legacy_response.to_raw_response_wrapper(
27222913
prices.fetch,
27232914
)
@@ -2737,6 +2928,9 @@ def __init__(self, prices: AsyncPrices) -> None:
27372928
self.list = _legacy_response.async_to_raw_response_wrapper(
27382929
prices.list,
27392930
)
2931+
self.evaluate = _legacy_response.async_to_raw_response_wrapper(
2932+
prices.evaluate,
2933+
)
27402934
self.fetch = _legacy_response.async_to_raw_response_wrapper(
27412935
prices.fetch,
27422936
)
@@ -2756,6 +2950,9 @@ def __init__(self, prices: Prices) -> None:
27562950
self.list = to_streamed_response_wrapper(
27572951
prices.list,
27582952
)
2953+
self.evaluate = to_streamed_response_wrapper(
2954+
prices.evaluate,
2955+
)
27592956
self.fetch = to_streamed_response_wrapper(
27602957
prices.fetch,
27612958
)
@@ -2775,6 +2972,9 @@ def __init__(self, prices: AsyncPrices) -> None:
27752972
self.list = async_to_streamed_response_wrapper(
27762973
prices.list,
27772974
)
2975+
self.evaluate = async_to_streamed_response_wrapper(
2976+
prices.evaluate,
2977+
)
27782978
self.fetch = async_to_streamed_response_wrapper(
27792979
prices.fetch,
27802980
)

0 commit comments

Comments
 (0)