|
142 | 142 | from airbyte_cdk.sources.declarative.transformations import AddFields, RemoveFields |
143 | 143 | from airbyte_cdk.sources.declarative.transformations.add_fields import AddedFieldDefinition |
144 | 144 | from airbyte_cdk.sources.declarative.yaml_declarative_source import YamlDeclarativeSource |
145 | | -from airbyte_cdk.sources.streams.call_rate import MovingWindowCallRatePolicy |
146 | 145 | from airbyte_cdk.sources.streams.concurrent.clamping import ( |
147 | 146 | ClampingEndProvider, |
148 | 147 | DayClampingStrategy, |
@@ -3685,161 +3684,3 @@ def test_create_async_retriever(): |
3685 | 3684 | assert isinstance(selector, RecordSelector) |
3686 | 3685 | assert isinstance(extractor, DpathExtractor) |
3687 | 3686 | assert extractor.field_path == ["data"] |
3688 | | - |
3689 | | - |
3690 | | -def test_api_budget(): |
3691 | | - manifest = { |
3692 | | - "type": "DeclarativeSource", |
3693 | | - "api_budget": { |
3694 | | - "type": "HTTPAPIBudget", |
3695 | | - "ratelimit_reset_header": "X-RateLimit-Reset", |
3696 | | - "ratelimit_remaining_header": "X-RateLimit-Remaining", |
3697 | | - "status_codes_for_ratelimit_hit": [429, 503], |
3698 | | - "policies": [ |
3699 | | - { |
3700 | | - "type": "MovingWindowCallRatePolicy", |
3701 | | - "rates": [ |
3702 | | - { |
3703 | | - "type": "Rate", |
3704 | | - "limit": 3, |
3705 | | - "interval": "PT0.1S", # 0.1 seconds |
3706 | | - } |
3707 | | - ], |
3708 | | - "matchers": [ |
3709 | | - { |
3710 | | - "type": "HttpRequestRegexMatcher", |
3711 | | - "method": "GET", |
3712 | | - "url_base": "https://api.sendgrid.com", |
3713 | | - "url_path_pattern": "/v3/marketing/lists", |
3714 | | - } |
3715 | | - ], |
3716 | | - } |
3717 | | - ], |
3718 | | - }, |
3719 | | - "my_requester": { |
3720 | | - "type": "HttpRequester", |
3721 | | - "path": "/v3/marketing/lists", |
3722 | | - "url_base": "https://api.sendgrid.com", |
3723 | | - "http_method": "GET", |
3724 | | - "authenticator": { |
3725 | | - "type": "BasicHttpAuthenticator", |
3726 | | - "username": "admin", |
3727 | | - "password": "{{ config['password'] }}", |
3728 | | - }, |
3729 | | - }, |
3730 | | - } |
3731 | | - |
3732 | | - config = { |
3733 | | - "password": "verysecrettoken", |
3734 | | - } |
3735 | | - |
3736 | | - factory = ModelToComponentFactory() |
3737 | | - if "api_budget" in manifest: |
3738 | | - factory.set_api_budget(manifest["api_budget"], config) |
3739 | | - |
3740 | | - from airbyte_cdk.sources.declarative.models.declarative_component_schema import ( |
3741 | | - HttpRequester as HttpRequesterModel, |
3742 | | - ) |
3743 | | - |
3744 | | - requester_definition = manifest["my_requester"] |
3745 | | - assert requester_definition["type"] == "HttpRequester" |
3746 | | - |
3747 | | - http_requester = factory.create_component( |
3748 | | - model_type=HttpRequesterModel, |
3749 | | - component_definition=requester_definition, |
3750 | | - config=config, |
3751 | | - name="lists_stream", |
3752 | | - decoder=None, |
3753 | | - ) |
3754 | | - |
3755 | | - assert http_requester.api_budget is not None |
3756 | | - assert http_requester.api_budget.ratelimit_reset_header == "X-RateLimit-Reset" |
3757 | | - assert http_requester.api_budget.status_codes_for_ratelimit_hit == [429, 503] |
3758 | | - assert len(http_requester.api_budget.policies) == 1 |
3759 | | - |
3760 | | - # The single policy is a MovingWindowCallRatePolicy |
3761 | | - policy = http_requester.api_budget.policies[0] |
3762 | | - assert isinstance(policy, MovingWindowCallRatePolicy) |
3763 | | - assert policy._bucket.rates[0].limit == 3 |
3764 | | - # The 0.1s from 'PT0.1S' is stored in ms by PyRateLimiter internally |
3765 | | - # but here just check that the limit and interval exist |
3766 | | - assert policy._bucket.rates[0].interval == 100 # 100 ms |
3767 | | - |
3768 | | - |
3769 | | -def test_api_budget_fixed_window_policy(): |
3770 | | - manifest = { |
3771 | | - "type": "DeclarativeSource", |
3772 | | - # Root-level api_budget referencing a FixedWindowCallRatePolicy |
3773 | | - "api_budget": { |
3774 | | - "type": "APIBudget", |
3775 | | - "maximum_attempts_to_acquire": 9999, |
3776 | | - "policies": [ |
3777 | | - { |
3778 | | - "type": "FixedWindowCallRatePolicy", |
3779 | | - "next_reset_ts": "2025-01-01T00:00:00Z", |
3780 | | - "period": "PT1M", # 1 minute |
3781 | | - "call_limit": 10, |
3782 | | - "matchers": [ |
3783 | | - { |
3784 | | - "type": "HttpRequestRegexMatcher", |
3785 | | - "method": "GET", |
3786 | | - "url_base": "https://example.org", |
3787 | | - "url_path_pattern": "/v2/data", |
3788 | | - } |
3789 | | - ], |
3790 | | - } |
3791 | | - ], |
3792 | | - }, |
3793 | | - # We'll define a single HttpRequester that references that base |
3794 | | - "my_requester": { |
3795 | | - "type": "HttpRequester", |
3796 | | - "path": "/v2/data", |
3797 | | - "url_base": "https://example.org", |
3798 | | - "http_method": "GET", |
3799 | | - "authenticator": {"type": "NoAuth"}, |
3800 | | - }, |
3801 | | - } |
3802 | | - |
3803 | | - config = {} |
3804 | | - |
3805 | | - factory = ModelToComponentFactory() |
3806 | | - if "api_budget" in manifest: |
3807 | | - factory.set_api_budget(manifest["api_budget"], config) |
3808 | | - |
3809 | | - from airbyte_cdk.sources.declarative.models.declarative_component_schema import ( |
3810 | | - HttpRequester as HttpRequesterModel, |
3811 | | - ) |
3812 | | - |
3813 | | - requester_definition = manifest["my_requester"] |
3814 | | - assert requester_definition["type"] == "HttpRequester" |
3815 | | - http_requester = factory.create_component( |
3816 | | - model_type=HttpRequesterModel, |
3817 | | - component_definition=requester_definition, |
3818 | | - config=config, |
3819 | | - name="my_stream", |
3820 | | - decoder=None, |
3821 | | - ) |
3822 | | - |
3823 | | - assert http_requester.api_budget is not None |
3824 | | - assert http_requester.api_budget.maximum_attempts_to_acquire == 9999 |
3825 | | - assert len(http_requester.api_budget.policies) == 1 |
3826 | | - |
3827 | | - from airbyte_cdk.sources.streams.call_rate import FixedWindowCallRatePolicy |
3828 | | - |
3829 | | - policy = http_requester.api_budget.policies[0] |
3830 | | - assert isinstance(policy, FixedWindowCallRatePolicy) |
3831 | | - assert policy._call_limit == 10 |
3832 | | - # The period is "PT1M" => 60 seconds |
3833 | | - assert policy._offset.total_seconds() == 60 |
3834 | | - |
3835 | | - expected_reset_dt = datetime(2025, 1, 1, 0, 0, 0, tzinfo=timezone.utc) |
3836 | | - assert policy._next_reset_ts == expected_reset_dt |
3837 | | - |
3838 | | - assert len(policy._matchers) == 1 |
3839 | | - matcher = policy._matchers[0] |
3840 | | - from airbyte_cdk.sources.streams.call_rate import HttpRequestRegexMatcher |
3841 | | - |
3842 | | - assert isinstance(matcher, HttpRequestRegexMatcher) |
3843 | | - assert matcher._method == "GET" |
3844 | | - assert matcher._url_base == "https://example.org" |
3845 | | - assert matcher._url_path_pattern.pattern == "/v2/data" |
0 commit comments