-
Notifications
You must be signed in to change notification settings - Fork 4.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
✨Source Youtube Analytics - Migrate Python CDK to Low-code CDK to Manifest-only #42838
Open
topefolorunso
wants to merge
40
commits into
master
Choose a base branch
from
tope/migrate-youtube-analytics-lowcode
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+1,944
−3,012
Open
Changes from all commits
Commits
Show all changes
40 commits
Select commit
Hold shift + click to select a range
1fae640
migrate to low code
topefolorunso 1e5a8ca
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso 3dbdad9
manifest only
topefolorunso dacbff0
Revert "manifest only"
topefolorunso db30c0b
fix
topefolorunso a985697
fix poetry
topefolorunso d592a48
format
topefolorunso b0b5405
chore: auto-fix lint and format issues
octavia-squidington-iii 085ba73
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso 3f7cf05
Merge branch 'tope/migrate-youtube-analytics-lowcode' of https://gith…
topefolorunso b0a733f
migrations
topefolorunso 5c72152
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso ac61641
fix base image
topefolorunso 1c93e22
Merge branch 'master' into tope/migrate-youtube-analytics-lowcode
natikgadzhi 8c3879a
Merge branch 'master' into tope/migrate-youtube-analytics-lowcode
topefolorunso 315473a
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso c6c35d8
Merge branch 'master' into tope/migrate-youtube-analytics-lowcode
topefolorunso e6dba16
Merge branch 'master' into tope/migrate-youtube-analytics-lowcode
topefolorunso e1516f9
Update youtube-analytics.md
topefolorunso 2be8540
Merge branch 'master' into tope/migrate-youtube-analytics-lowcode
topefolorunso 6f84d4c
Merge branch 'master' into tope/migrate-youtube-analytics-lowcode
ChristoGrab a60716c
Merge branch 'tope/migrate-youtube-analytics-lowcode' of https://gith…
topefolorunso 6b8bfc2
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso 793d51c
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso 9f3b3f8
Merge branch 'tope/migrate-youtube-analytics-lowcode' of https://gith…
topefolorunso 7813557
implement async retriever
topefolorunso fb06a3c
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso 31e535a
chore: auto-fix lint and format issues
octavia-squidington-iii 5a76ca9
fix creation requester
topefolorunso 8e6397a
implement error handler
topefolorunso 0f1477d
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso 055ac38
Merge branch 'tope/migrate-youtube-analytics-lowcode' of https://gith…
topefolorunso 210ee70
chore: auto-fix lint and format issues
octavia-squidington-iii 3580f58
migrate to manifest only
topefolorunso cc5db2c
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso febd592
Merge branch 'tope/migrate-youtube-analytics-lowcode' of https://gith…
topefolorunso 223ffd2
chore: auto-fix lint and format issues
octavia-squidington-iii 21402a9
fix manfest
topefolorunso 99fc5b9
refactor manifest
topefolorunso 9cd5b4b
Merge branch 'master' of https://github.com/airbytehq/airbyte into to…
topefolorunso File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
82 changes: 28 additions & 54 deletions
82
airbyte-integrations/connectors/source-youtube-analytics/README.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
148 changes: 148 additions & 0 deletions
148
airbyte-integrations/connectors/source-youtube-analytics/components.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,148 @@ | ||
# | ||
# Copyright (c) 2023 Airbyte, Inc., all rights reserved. | ||
# | ||
|
||
from dataclasses import dataclass | ||
from typing import Any, Mapping, Optional, Union | ||
|
||
import requests | ||
|
||
from airbyte_cdk.sources.declarative.requesters.error_handlers import DefaultErrorHandler | ||
from airbyte_cdk.sources.declarative.requesters.http_requester import HttpRequester | ||
from airbyte_cdk.sources.declarative.requesters.request_options.interpolated_request_options_provider import ( | ||
InterpolatedRequestOptionsProvider, | ||
RequestInput, | ||
) | ||
from airbyte_cdk.sources.streams.http.error_handlers import ErrorResolution, ResponseAction | ||
|
||
|
||
@dataclass | ||
class CreationRequester(HttpRequester): | ||
request_body_json: Optional[RequestInput] = None | ||
|
||
def __post_init__(self, parameters: Mapping[str, Any]) -> None: | ||
self.request_options_provider = InterpolatedRequestOptionsProvider( | ||
request_body_json=self.request_body_json, | ||
config=self.config, | ||
parameters=parameters or {}, | ||
) | ||
super().__post_init__(parameters) | ||
|
||
def send_request(self, **kwargs): | ||
request, jobs_response = self._http_client.send_request( | ||
http_method="GET", | ||
url=self._join_url(self.get_url_base(), "jobs"), | ||
request_kwargs={"stream": self.stream_response}, | ||
) | ||
jobs_list = jobs_response.json().get("jobs", []) | ||
stream_name = self.name.split(" - ")[-1] | ||
if stream_name in [job["reportTypeId"] for job in jobs_list]: | ||
return jobs_response | ||
else: | ||
return super().send_request(**kwargs) | ||
|
||
|
||
@dataclass | ||
class PollingRequester(HttpRequester): | ||
def send_request(self, **kwargs): | ||
jobs_response = super().send_request(**kwargs) | ||
jobs_list = jobs_response.json().get("jobs", []) | ||
job_resource = [job for job in jobs_list if job["reportTypeId"] == self.name][0] | ||
job_id = job_resource["id"] | ||
|
||
request, reports_response = self._http_client.send_request( | ||
http_method=self.get_method().value, | ||
url=self._join_url(self.get_url_base(), f"jobs/{job_id}/reports"), | ||
request_kwargs={"stream": self.stream_response}, | ||
params={"startTimeAtOrAfter": self.start_time} if self.start_time else {}, | ||
) | ||
return reports_response | ||
|
||
|
||
@dataclass | ||
class YoutubeAnalyticsErrorHandler(DefaultErrorHandler): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It is better to describe error handler in the manifest instead have a custom component. |
||
def daily_quota_exceeded(self, response: requests.Response): | ||
"""Response example: | ||
{ | ||
"error": { | ||
"code": 429, | ||
"message": "Quota exceeded for quota metric 'Free requests' and limit 'Free requests per minute' of service 'youtubereporting.googleapis.com' for consumer 'project_number:863188056127'.", | ||
"status": "RESOURCE_EXHAUSTED", | ||
"details": [ | ||
{ | ||
"reason": "RATE_LIMIT_EXCEEDED", | ||
"metadata": { | ||
"consumer": "projects/863188056127", | ||
"quota_limit": "FreeQuotaRequestsPerMinutePerProject", | ||
"quota_limit_value": "60", | ||
"quota_metric": "youtubereporting.googleapis.com/free_quota_requests", | ||
"service": "youtubereporting.googleapis.com", | ||
} | ||
}, | ||
] | ||
} | ||
} | ||
|
||
:param response: | ||
:return: | ||
""" | ||
details = response.json().get("error", {}).get("details", []) | ||
for detail in details: | ||
if detail.get("reason") == "RATE_LIMIT_EXCEEDED": | ||
if detail.get("metadata", {}).get("quota_limit") == "FreeQuotaRequestsPerDayPerProject": | ||
return True, f"Exceeded daily quota: {detail.get('metadata', {}).get('quota_limit_value')} reqs/day" | ||
break | ||
return False, "" | ||
|
||
def should_retry(self, response: requests.Response): | ||
""" | ||
Override to set different conditions for backoff based on the response from the server. | ||
|
||
By default, back off on the following HTTP response statuses: | ||
- 500s to handle transient server errors | ||
- 429 (Too Many Requests) indicating rate limiting: | ||
Different behavior in case of 'RATE_LIMIT_EXCEEDED': | ||
|
||
Requests Per Minute: | ||
"message": "Quota exceeded for quota metric 'Free requests' and limit 'Free requests per minute' of service 'youtubereporting.googleapis.com' for consumer 'project_number:863188056127'." | ||
"quota_limit": "FreeQuotaRequestsPerMinutePerProject", | ||
"quota_limit_value": "60", | ||
|
||
--> use increased retry_factor (30 seconds) | ||
|
||
Requests Per Day: | ||
"message": "Quota exceeded for quota metric 'Free requests' and limit 'Free requests per day' of service 'youtubereporting.googleapis.com' for consumer 'project_number:863188056127" | ||
"quota_limit": "FreeQuotaRequestsPerDayPerProject | ||
"quota_limit_value": "20000", | ||
|
||
--> just throw an error, next scan is reasonable to start only in 1 day. | ||
""" | ||
if 500 <= response.status_code < 600: | ||
return True, "" | ||
|
||
if response.status_code == 429 and not self.daily_quota_exceeded(response): | ||
return True, "" | ||
|
||
return False, "" | ||
|
||
def interpret_response(self, response_or_exception: Optional[Union[requests.Response, Exception]]) -> ErrorResolution: | ||
""" | ||
Interprets responses and exceptions, providing custom error resolutions for specific exceptions. | ||
""" | ||
if_retry, error_message = self.should_retry(response_or_exception) | ||
if if_retry: | ||
return ErrorResolution( | ||
response_action=ResponseAction.RETRY, | ||
error_message=error_message, | ||
) | ||
return super().interpret_response(response_or_exception) | ||
|
||
def backoff_time( | ||
self, | ||
response_or_exception: Optional[Union[requests.Response, requests.RequestException]], | ||
attempt_count: int = 0, | ||
) -> Optional[float]: | ||
""" | ||
Default FreeQuotaRequestsPerMinutePerProject is 60 reqs/min, so reasonable delay is 30 seconds | ||
""" | ||
return 30 |
9 changes: 0 additions & 9 deletions
9
airbyte-integrations/connectors/source-youtube-analytics/main.py
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For this behavior you could use
AsyncRetirever
to avoid custom component.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried implementing the async retriever here @lazebnyi . This custom component is only a component of the async retriever to the best of my understanding. I will watch the loom and be back with questions 🤞🏼
https://github.com/airbytehq/airbyte/pull/42838/files#diff-cb52a8ae8fa55be3587d13879abff80da4c14577466259c5592a2701f3c84735R428-R469