Skip to content

Commit fdcded3

Browse files
authored
feat(low-code): Add API Budget (#314)
1 parent cb5a921 commit fdcded3

File tree

9 files changed

+876
-48
lines changed

9 files changed

+876
-48
lines changed

airbyte_cdk/sources/declarative/declarative_component_schema.yaml

Lines changed: 167 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ properties:
4040
"$ref": "#/definitions/Spec"
4141
concurrency_level:
4242
"$ref": "#/definitions/ConcurrencyLevel"
43+
api_budget:
44+
"$ref": "#/definitions/HTTPAPIBudget"
4345
metadata:
4446
type: object
4547
description: For internal Airbyte use only - DO NOT modify manually. Used by consumers of declarative manifests for storing related metadata.
@@ -794,7 +796,7 @@ definitions:
794796
description: This option is used to adjust the upper and lower boundaries of each datetime window to beginning and end of the provided target period (day, week, month)
795797
type: object
796798
required:
797-
- target
799+
- target
798800
properties:
799801
target:
800802
title: Target
@@ -1365,6 +1367,170 @@ definitions:
13651367
$parameters:
13661368
type: object
13671369
additional_properties: true
1370+
HTTPAPIBudget:
1371+
title: HTTP API Budget
1372+
description: >
1373+
Defines how many requests can be made to the API in a given time frame. `HTTPAPIBudget` extracts the remaining
1374+
call count and the reset time from HTTP response headers using the header names provided by
1375+
`ratelimit_remaining_header` and `ratelimit_reset_header`. Only requests using `HttpRequester`
1376+
are rate-limited; custom components that bypass `HttpRequester` are not covered by this budget.
1377+
type: object
1378+
required:
1379+
- type
1380+
- policies
1381+
properties:
1382+
type:
1383+
type: string
1384+
enum: [HTTPAPIBudget]
1385+
policies:
1386+
title: Policies
1387+
description: List of call rate policies that define how many calls are allowed.
1388+
type: array
1389+
items:
1390+
anyOf:
1391+
- "$ref": "#/definitions/FixedWindowCallRatePolicy"
1392+
- "$ref": "#/definitions/MovingWindowCallRatePolicy"
1393+
- "$ref": "#/definitions/UnlimitedCallRatePolicy"
1394+
ratelimit_reset_header:
1395+
title: Rate Limit Reset Header
1396+
description: The HTTP response header name that indicates when the rate limit resets.
1397+
type: string
1398+
default: "ratelimit-reset"
1399+
ratelimit_remaining_header:
1400+
title: Rate Limit Remaining Header
1401+
description: The HTTP response header name that indicates the number of remaining allowed calls.
1402+
type: string
1403+
default: "ratelimit-remaining"
1404+
status_codes_for_ratelimit_hit:
1405+
title: Status Codes for Rate Limit Hit
1406+
description: List of HTTP status codes that indicate a rate limit has been hit.
1407+
type: array
1408+
items:
1409+
type: integer
1410+
default: [429]
1411+
additionalProperties: true
1412+
FixedWindowCallRatePolicy:
1413+
title: Fixed Window Call Rate Policy
1414+
description: A policy that allows a fixed number of calls within a specific time window.
1415+
type: object
1416+
required:
1417+
- type
1418+
- period
1419+
- call_limit
1420+
- matchers
1421+
properties:
1422+
type:
1423+
type: string
1424+
enum: [FixedWindowCallRatePolicy]
1425+
period:
1426+
title: Period
1427+
description: The time interval for the rate limit window.
1428+
type: string
1429+
call_limit:
1430+
title: Call Limit
1431+
description: The maximum number of calls allowed within the period.
1432+
type: integer
1433+
matchers:
1434+
title: Matchers
1435+
description: List of matchers that define which requests this policy applies to.
1436+
type: array
1437+
items:
1438+
"$ref": "#/definitions/HttpRequestRegexMatcher"
1439+
additionalProperties: true
1440+
MovingWindowCallRatePolicy:
1441+
title: Moving Window Call Rate Policy
1442+
description: A policy that allows a fixed number of calls within a moving time window.
1443+
type: object
1444+
required:
1445+
- type
1446+
- rates
1447+
- matchers
1448+
properties:
1449+
type:
1450+
type: string
1451+
enum: [MovingWindowCallRatePolicy]
1452+
rates:
1453+
title: Rates
1454+
description: List of rates that define the call limits for different time intervals.
1455+
type: array
1456+
items:
1457+
"$ref": "#/definitions/Rate"
1458+
matchers:
1459+
title: Matchers
1460+
description: List of matchers that define which requests this policy applies to.
1461+
type: array
1462+
items:
1463+
"$ref": "#/definitions/HttpRequestRegexMatcher"
1464+
additionalProperties: true
1465+
UnlimitedCallRatePolicy:
1466+
title: Unlimited Call Rate Policy
1467+
description: A policy that allows unlimited calls for specific requests.
1468+
type: object
1469+
required:
1470+
- type
1471+
- matchers
1472+
properties:
1473+
type:
1474+
type: string
1475+
enum: [UnlimitedCallRatePolicy]
1476+
matchers:
1477+
title: Matchers
1478+
description: List of matchers that define which requests this policy applies to.
1479+
type: array
1480+
items:
1481+
"$ref": "#/definitions/HttpRequestRegexMatcher"
1482+
additionalProperties: true
1483+
Rate:
1484+
title: Rate
1485+
description: Defines a rate limit with a specific number of calls allowed within a time interval.
1486+
type: object
1487+
required:
1488+
- limit
1489+
- interval
1490+
properties:
1491+
limit:
1492+
title: Limit
1493+
description: The maximum number of calls allowed within the interval.
1494+
type: integer
1495+
interval:
1496+
title: Interval
1497+
description: The time interval for the rate limit.
1498+
type: string
1499+
examples:
1500+
- "PT1H"
1501+
- "P1D"
1502+
additionalProperties: true
1503+
HttpRequestRegexMatcher:
1504+
title: HTTP Request Matcher
1505+
description: >
1506+
Matches HTTP requests based on method, base URL, URL path pattern, query parameters, and headers.
1507+
Use `url_base` to specify the scheme and host (without trailing slash) and
1508+
`url_path_pattern` to apply a regex to the request path.
1509+
type: object
1510+
properties:
1511+
method:
1512+
title: Method
1513+
description: The HTTP method to match (e.g., GET, POST).
1514+
type: string
1515+
url_base:
1516+
title: URL Base
1517+
description: The base URL (scheme and host, e.g. "https://api.example.com") to match.
1518+
type: string
1519+
url_path_pattern:
1520+
title: URL Path Pattern
1521+
description: A regular expression pattern to match the URL path.
1522+
type: string
1523+
params:
1524+
title: Parameters
1525+
description: The query parameters to match.
1526+
type: object
1527+
additionalProperties: true
1528+
headers:
1529+
title: Headers
1530+
description: The headers to match.
1531+
type: object
1532+
additionalProperties: true
1533+
additionalProperties: true
13681534
DefaultErrorHandler:
13691535
title: Default Error Handler
13701536
description: Component defining how to handle errors. Default behavior includes only retrying server errors (HTTP 5XX) and too many requests (HTTP 429) with an exponential backoff.

airbyte_cdk/sources/declarative/manifest_declarative_source.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,10 @@ def streams(self, config: Mapping[str, Any]) -> List[Stream]:
137137
self._source_config, config
138138
)
139139

140+
api_budget_model = self._source_config.get("api_budget")
141+
if api_budget_model:
142+
self._constructor.set_api_budget(api_budget_model, config)
143+
140144
source_streams = [
141145
self._constructor.create_component(
142146
DeclarativeStreamModel,

airbyte_cdk/sources/declarative/models/declarative_component_schema.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -642,6 +642,48 @@ class OAuthAuthenticator(BaseModel):
642642
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
643643

644644

645+
class Rate(BaseModel):
646+
class Config:
647+
extra = Extra.allow
648+
649+
limit: int = Field(
650+
...,
651+
description="The maximum number of calls allowed within the interval.",
652+
title="Limit",
653+
)
654+
interval: str = Field(
655+
...,
656+
description="The time interval for the rate limit.",
657+
examples=["PT1H", "P1D"],
658+
title="Interval",
659+
)
660+
661+
662+
class HttpRequestRegexMatcher(BaseModel):
663+
class Config:
664+
extra = Extra.allow
665+
666+
method: Optional[str] = Field(
667+
None, description="The HTTP method to match (e.g., GET, POST).", title="Method"
668+
)
669+
url_base: Optional[str] = Field(
670+
None,
671+
description='The base URL (scheme and host, e.g. "https://api.example.com") to match.',
672+
title="URL Base",
673+
)
674+
url_path_pattern: Optional[str] = Field(
675+
None,
676+
description="A regular expression pattern to match the URL path.",
677+
title="URL Path Pattern",
678+
)
679+
params: Optional[Dict[str, Any]] = Field(
680+
None, description="The query parameters to match.", title="Parameters"
681+
)
682+
headers: Optional[Dict[str, Any]] = Field(
683+
None, description="The headers to match.", title="Headers"
684+
)
685+
686+
645687
class DpathExtractor(BaseModel):
646688
type: Literal["DpathExtractor"]
647689
field_path: List[str] = Field(
@@ -1565,6 +1607,55 @@ class DatetimeBasedCursor(BaseModel):
15651607
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
15661608

15671609

1610+
class FixedWindowCallRatePolicy(BaseModel):
1611+
class Config:
1612+
extra = Extra.allow
1613+
1614+
type: Literal["FixedWindowCallRatePolicy"]
1615+
period: str = Field(
1616+
..., description="The time interval for the rate limit window.", title="Period"
1617+
)
1618+
call_limit: int = Field(
1619+
...,
1620+
description="The maximum number of calls allowed within the period.",
1621+
title="Call Limit",
1622+
)
1623+
matchers: List[HttpRequestRegexMatcher] = Field(
1624+
...,
1625+
description="List of matchers that define which requests this policy applies to.",
1626+
title="Matchers",
1627+
)
1628+
1629+
1630+
class MovingWindowCallRatePolicy(BaseModel):
1631+
class Config:
1632+
extra = Extra.allow
1633+
1634+
type: Literal["MovingWindowCallRatePolicy"]
1635+
rates: List[Rate] = Field(
1636+
...,
1637+
description="List of rates that define the call limits for different time intervals.",
1638+
title="Rates",
1639+
)
1640+
matchers: List[HttpRequestRegexMatcher] = Field(
1641+
...,
1642+
description="List of matchers that define which requests this policy applies to.",
1643+
title="Matchers",
1644+
)
1645+
1646+
1647+
class UnlimitedCallRatePolicy(BaseModel):
1648+
class Config:
1649+
extra = Extra.allow
1650+
1651+
type: Literal["UnlimitedCallRatePolicy"]
1652+
matchers: List[HttpRequestRegexMatcher] = Field(
1653+
...,
1654+
description="List of matchers that define which requests this policy applies to.",
1655+
title="Matchers",
1656+
)
1657+
1658+
15681659
class DefaultErrorHandler(BaseModel):
15691660
type: Literal["DefaultErrorHandler"]
15701661
backoff_strategies: Optional[
@@ -1696,6 +1787,39 @@ class CompositeErrorHandler(BaseModel):
16961787
parameters: Optional[Dict[str, Any]] = Field(None, alias="$parameters")
16971788

16981789

1790+
class HTTPAPIBudget(BaseModel):
1791+
class Config:
1792+
extra = Extra.allow
1793+
1794+
type: Literal["HTTPAPIBudget"]
1795+
policies: List[
1796+
Union[
1797+
FixedWindowCallRatePolicy,
1798+
MovingWindowCallRatePolicy,
1799+
UnlimitedCallRatePolicy,
1800+
]
1801+
] = Field(
1802+
...,
1803+
description="List of call rate policies that define how many calls are allowed.",
1804+
title="Policies",
1805+
)
1806+
ratelimit_reset_header: Optional[str] = Field(
1807+
"ratelimit-reset",
1808+
description="The HTTP response header name that indicates when the rate limit resets.",
1809+
title="Rate Limit Reset Header",
1810+
)
1811+
ratelimit_remaining_header: Optional[str] = Field(
1812+
"ratelimit-remaining",
1813+
description="The HTTP response header name that indicates the number of remaining allowed calls.",
1814+
title="Rate Limit Remaining Header",
1815+
)
1816+
status_codes_for_ratelimit_hit: Optional[List[int]] = Field(
1817+
[429],
1818+
description="List of HTTP status codes that indicate a rate limit has been hit.",
1819+
title="Status Codes for Rate Limit Hit",
1820+
)
1821+
1822+
16991823
class ZipfileDecoder(BaseModel):
17001824
class Config:
17011825
extra = Extra.allow
@@ -1724,6 +1848,7 @@ class Config:
17241848
definitions: Optional[Dict[str, Any]] = None
17251849
spec: Optional[Spec] = None
17261850
concurrency_level: Optional[ConcurrencyLevel] = None
1851+
api_budget: Optional[HTTPAPIBudget] = None
17271852
metadata: Optional[Dict[str, Any]] = Field(
17281853
None,
17291854
description="For internal Airbyte use only - DO NOT modify manually. Used by consumers of declarative manifests for storing related metadata.",
@@ -1750,6 +1875,7 @@ class Config:
17501875
definitions: Optional[Dict[str, Any]] = None
17511876
spec: Optional[Spec] = None
17521877
concurrency_level: Optional[ConcurrencyLevel] = None
1878+
api_budget: Optional[HTTPAPIBudget] = None
17531879
metadata: Optional[Dict[str, Any]] = Field(
17541880
None,
17551881
description="For internal Airbyte use only - DO NOT modify manually. Used by consumers of declarative manifests for storing related metadata.",

0 commit comments

Comments
 (0)