Skip to content

Commit 71d7020

Browse files
Merge pull request #15 from google-marketing-solutions/dev
Dev
2 parents 9e3c046 + 84c7c3b commit 71d7020

42 files changed

Lines changed: 3713 additions & 462 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

py/common/google_ads_client.py

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
"""The Google Ads client."""
1616

17-
from typing import Any, Optional, Union
17+
from typing import Any, Literal, Optional, Union
1818
from common import api_utils
1919

2020
GOOGLE_ADS_API_BASE_URL = 'https://googleads.googleapis.com'
@@ -270,3 +270,83 @@ def get_active_keywords_for_account(self, customer_id: str) -> list[Any]:
270270
api_version=self.api_version, customer_id=customer_id
271271
)
272272
return api_utils.send_api_request(url, payload, self._get_http_header())
273+
274+
def get_extensions_for_campaigns(
275+
self,
276+
customer_id: str,
277+
campaign_ids: Optional[list[Union[int, str]]] = None,
278+
) -> list[Any]:
279+
"""Gets extension assets for campaigns and ad groups.
280+
281+
Gets existing structured snippets, callouts and sitelink extensions.
282+
283+
Args:
284+
customer_id: The Google Ads customer id.
285+
campaign_ids: A list of Google Ads campaign ids.
286+
287+
Returns:
288+
The API response object containing a list of extensions.
289+
"""
290+
results = []
291+
for level in ['ad_group', 'campaign']:
292+
results.extend(
293+
self._get_extensions_for_campaigns_by_level(
294+
customer_id, level, campaign_ids
295+
)
296+
)
297+
return results
298+
299+
def _get_extensions_for_campaigns_by_level(
300+
self,
301+
customer_id: str,
302+
level: Literal['ad_group', 'campaign'],
303+
campaign_ids: Optional[list[Union[int, str]]] = None,
304+
) -> list[Any]:
305+
"""Gets extension assets for ad groups or campaigns.
306+
307+
Gets existing structured snippets, callouts and sitelink extensions.
308+
309+
Args:
310+
customer_id: The Google Ads account id.
311+
level: The level to get extensions for - ad_group or campaign.
312+
campaign_ids: A list of Google Ads campaign ids.
313+
314+
Returns:
315+
The API response object containing a list of extensions.
316+
"""
317+
ad_group_name_col = ', ad_group.name' if level == 'ad_group' else ''
318+
campaign_col = (
319+
'ad_group.campaign' if level == 'ad_group' else 'campaign.resource_name'
320+
)
321+
query = f"""
322+
SELECT
323+
campaign.name,
324+
{campaign_col},
325+
asset.type,
326+
asset.structured_snippet_asset.header,
327+
asset.structured_snippet_asset.values,
328+
asset.callout_asset.callout_text,
329+
asset.sitelink_asset.description1,
330+
asset.sitelink_asset.description2,
331+
asset.sitelink_asset.link_text,
332+
asset.final_urls,
333+
{level}_asset.status{ad_group_name_col}
334+
FROM
335+
{level}_asset
336+
WHERE
337+
{level}_asset.field_type IN (
338+
'STRUCTURED_SNIPPET', 'SITELINK', 'CALLOUT')
339+
"""
340+
if campaign_ids:
341+
campaign_ids_str = ', '.join(
342+
[
343+
f"'customers/{customer_id}/campaigns/{elem}'"
344+
for elem in campaign_ids
345+
]
346+
)
347+
query += f' AND {campaign_col} IN ({campaign_ids_str})'
348+
payload = {'query': ' '.join(query.split())}
349+
url = SEARCH_URL.format(
350+
api_version=self.api_version, customer_id=customer_id
351+
)
352+
return api_utils.send_api_request(url, payload, self._get_http_header())

py/common/google_ads_client_test.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,94 @@ def test_not_success_http_code_in_response_raises_error(self, mock_requests):
361361
with self.assertRaises(requests.exceptions.HTTPError):
362362
self.client.get_accounts(_FAKE_CUSTOMER_ID)
363363

364+
@parameterized.named_parameters(
365+
{
366+
'testcase_name': 'without_campaign_ids',
367+
'params': {
368+
'customer_id': 1234567890,
369+
'campaign_ids': None,
370+
},
371+
'expected_query_part_ad_group': '',
372+
'expected_query_part_campaign': '',
373+
},
374+
{
375+
'testcase_name': 'with_campaign_ids',
376+
'params': {
377+
'customer_id': 1234567890,
378+
'campaign_ids': [1],
379+
},
380+
'expected_query_part_ad_group': (
381+
" AND ad_group.campaign IN ('customers/1234567890/campaigns/1')"
382+
),
383+
'expected_query_part_campaign': (
384+
' AND campaign.resource_name IN'
385+
" ('customers/1234567890/campaigns/1')"
386+
),
387+
},
388+
)
389+
@requests_mock.Mocker()
390+
def test_get_extensions_for_campaigns(
391+
self,
392+
mock_requests,
393+
params,
394+
expected_query_part_ad_group,
395+
expected_query_part_campaign,
396+
):
397+
mock_response = _FAKE_RESPONSE
398+
mock_requests.post(
399+
requests_mock.ANY, json=mock_response, headers=_FAKE_HEADERS
400+
)
401+
query_ad_group = """
402+
SELECT
403+
campaign.name,
404+
ad_group.campaign,
405+
asset.type,
406+
asset.structured_snippet_asset.header,
407+
asset.structured_snippet_asset.values,
408+
asset.callout_asset.callout_text,
409+
asset.sitelink_asset.description1,
410+
asset.sitelink_asset.description2,
411+
asset.sitelink_asset.link_text,
412+
asset.final_urls,
413+
ad_group_asset.status, ad_group.name
414+
FROM
415+
ad_group_asset
416+
WHERE
417+
ad_group_asset.field_type IN (
418+
'STRUCTURED_SNIPPET', 'SITELINK', 'CALLOUT')
419+
""" + expected_query_part_ad_group
420+
query_campaign = """
421+
SELECT
422+
campaign.name,
423+
campaign.resource_name,
424+
asset.type,
425+
asset.structured_snippet_asset.header,
426+
asset.structured_snippet_asset.values,
427+
asset.callout_asset.callout_text,
428+
asset.sitelink_asset.description1,
429+
asset.sitelink_asset.description2,
430+
asset.sitelink_asset.link_text,
431+
asset.final_urls,
432+
campaign_asset.status
433+
FROM
434+
campaign_asset
435+
WHERE
436+
campaign_asset.field_type IN (
437+
'STRUCTURED_SNIPPET', 'SITELINK', 'CALLOUT')
438+
""" + expected_query_part_campaign
439+
expected_request_ad_group = _expected_request_from_query(query_ad_group)
440+
expected_request_campaign = _expected_request_from_query(query_campaign)
441+
self.client.get_extensions_for_campaigns(**params)
442+
443+
self.assertEqual(
444+
_request_record_asdict(mock_requests.request_history[0]),
445+
expected_request_ad_group,
446+
)
447+
self.assertEqual(
448+
_request_record_asdict(mock_requests.request_history[1]),
449+
expected_request_campaign,
450+
)
451+
364452

365453
if __name__ == '__main__':
366454
absltest.main()

0 commit comments

Comments
 (0)