Skip to content

Commit efdb2e4

Browse files
authored
🐛 Source Facebook Marketing: fix url parsing and add report that exposes conversions. (#5826)
1 parent 35b38a2 commit efdb2e4

File tree

5 files changed

+91
-11
lines changed

5 files changed

+91
-11
lines changed

airbyte-integrations/connectors/source-facebook-marketing/Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,5 +12,5 @@ RUN pip install .
1212
ENV AIRBYTE_ENTRYPOINT "python /airbyte/integration_code/main.py"
1313
ENTRYPOINT ["python", "/airbyte/integration_code/main.py"]
1414

15-
LABEL io.airbyte.version=0.2.14
15+
LABEL io.airbyte.version=0.2.15
1616
LABEL io.airbyte.name=airbyte/source-facebook-marketing

airbyte-integrations/connectors/source-facebook-marketing/integration_tests/abnormal_state.json

+4
Original file line numberDiff line numberDiff line change
@@ -38,5 +38,9 @@
3838
"ads_insights_region": {
3939
"date_start": "2021-07-25T13:34:26Z",
4040
"include_deleted": true
41+
},
42+
"ads_insights_action_types": {
43+
"date_start": "2021-07-25T13:34:26Z",
44+
"include_deleted": true
4145
}
4246
}

airbyte-integrations/connectors/source-facebook-marketing/source_facebook_marketing/source.py

+2
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
Ads,
3636
AdSets,
3737
AdsInsights,
38+
AdsInsightsActionType,
3839
AdsInsightsAgeAndGender,
3940
AdsInsightsCountry,
4041
AdsInsightsDma,
@@ -124,6 +125,7 @@ def streams(self, config: Mapping[str, Any]) -> List[Type[Stream]]:
124125
AdsInsightsRegion(**insights_args),
125126
AdsInsightsDma(**insights_args),
126127
AdsInsightsPlatformAndDevice(**insights_args),
128+
AdsInsightsActionType(**insights_args),
127129
]
128130

129131
def spec(self, *args, **kwargs) -> ConnectorSpecification:

airbyte-integrations/connectors/source-facebook-marketing/source_facebook_marketing/streams.py

+18-10
Original file line numberDiff line numberDiff line change
@@ -46,16 +46,19 @@
4646
backoff_policy = retry_pattern(backoff.expo, FacebookRequestError, max_tries=5, factor=5)
4747

4848

49-
def remove_params_from_url(url, params):
50-
parsed_url = urlparse.urlparse(url)
51-
res_query = []
52-
for q in parsed_url.query.split("&"):
53-
key, value = q.split("=")
54-
if key not in params:
55-
res_query.append(f"{key}={value}")
56-
57-
parse_result = parsed_url._replace(query="&".join(res_query))
58-
return urlparse.urlunparse(parse_result)
49+
def remove_params_from_url(url: str, params: [str]) -> str:
50+
"""
51+
Parses a URL and removes the query parameters specified in params
52+
:param url: URL
53+
:param params: list of query parameters
54+
:return: URL with params removed
55+
"""
56+
parsed = urlparse.urlparse(url)
57+
query = urlparse.parse_qs(parsed.query, keep_blank_values=True)
58+
filtered = dict((k, v) for k, v in query.items() if k not in params)
59+
return urlparse.urlunparse(
60+
[parsed.scheme, parsed.netloc, parsed.path, parsed.params, urlparse.urlencode(filtered, doseq=True), parsed.fragment]
61+
)
5962

6063

6164
class FBMarketingStream(Stream, ABC):
@@ -479,3 +482,8 @@ class AdsInsightsDma(AdsInsights):
479482
class AdsInsightsPlatformAndDevice(AdsInsights):
480483
breakdowns = ["publisher_platform", "platform_position", "impression_device"]
481484
action_breakdowns = ["action_type"] # FB Async Job fails for unknown reason if we set other breakdowns
485+
486+
487+
class AdsInsightsActionType(AdsInsights):
488+
breakdowns = []
489+
action_breakdowns = ["action_type"]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#
2+
# MIT License
3+
#
4+
# Copyright (c) 2020 Airbyte
5+
#
6+
# Permission is hereby granted, free of charge, to any person obtaining a copy
7+
# of this software and associated documentation files (the "Software"), to deal
8+
# in the Software without restriction, including without limitation the rights
9+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
# copies of the Software, and to permit persons to whom the Software is
11+
# furnished to do so, subject to the following conditions:
12+
#
13+
# The above copyright notice and this permission notice shall be included in all
14+
# copies or substantial portions of the Software.
15+
#
16+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
# SOFTWARE.
23+
#
24+
from source_facebook_marketing.streams import remove_params_from_url
25+
26+
27+
class TestUrlParsing:
28+
def test_empty_url(self):
29+
url = ""
30+
parsed_url = remove_params_from_url(url=url, params=[])
31+
assert parsed_url == url
32+
33+
def test_does_not_raise_exception_for_invalid_url(self):
34+
url = "abcd"
35+
parsed_url = remove_params_from_url(url=url, params=["test"])
36+
assert parsed_url == url
37+
38+
def test_escaped_characters(self):
39+
url = "https://google.com?test=123%23%24%25%2A&test2=456"
40+
parsed_url = remove_params_from_url(url=url, params=["test3"])
41+
assert parsed_url == url
42+
43+
def test_no_params_url(self):
44+
url = "https://google.com"
45+
parsed_url = remove_params_from_url(url=url, params=["test"])
46+
assert parsed_url == url
47+
48+
def test_no_params_arg(self):
49+
url = "https://google.com?"
50+
parsed_url = remove_params_from_url(url=url, params=["test"])
51+
assert parsed_url == "https://google.com"
52+
53+
def test_partially_empty_params(self):
54+
url = "https://google.com?test=122&&"
55+
parsed_url = remove_params_from_url(url=url, params=[])
56+
assert parsed_url == "https://google.com?test=122"
57+
58+
def test_no_matching_params(self):
59+
url = "https://google.com?test=123"
60+
parsed_url = remove_params_from_url(url=url, params=["test2"])
61+
assert parsed_url == url
62+
63+
def test_removes_params(self):
64+
url = "https://google.com?test=123&test2=456"
65+
parsed_url = remove_params_from_url(url=url, params=["test2"])
66+
assert parsed_url == "https://google.com?test=123"

0 commit comments

Comments
 (0)