Skip to content

Commit 1d26beb

Browse files
authored
Optimize asyncio examples (#1049)
1 parent a5c0161 commit 1d26beb

File tree

6 files changed

+269
-36
lines changed

6 files changed

+269
-36
lines changed

examples/asyncio/async_add_campaigns.py

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -24,58 +24,53 @@
2424

2525
from google.ads.googleads.client import GoogleAdsClient
2626
from google.ads.googleads.errors import GoogleAdsException
27-
from google.ads.googleads.v22.services.services.campaign_budget_service import (
28-
CampaignBudgetServiceClient,
27+
from google.ads.googleads.v22.resources.types.campaign import Campaign
28+
from google.ads.googleads.v22.resources.types.campaign_budget import (
29+
CampaignBudget,
2930
)
3031
from google.ads.googleads.v22.services.types.campaign_budget_service import (
3132
CampaignBudgetOperation,
32-
MutateCampaignBudgetsResponse,
33-
)
34-
from google.ads.googleads.v22.services.services.campaign_service import (
35-
CampaignServiceClient,
3633
)
3734
from google.ads.googleads.v22.services.types.campaign_service import (
3835
CampaignOperation,
39-
MutateCampaignsResponse,
4036
)
41-
from google.ads.googleads.v22.resources.types.campaign_budget import (
42-
CampaignBudget,
37+
from google.ads.googleads.v22.services.types.google_ads_service import (
38+
MutateGoogleAdsResponse,
39+
MutateOperation,
4340
)
44-
from google.ads.googleads.v22.resources.types.campaign import Campaign
4541

4642

4743
_DATE_FORMAT: str = "%Y%m%d"
4844

4945

5046
async def main(client: GoogleAdsClient, customer_id: str) -> None:
51-
campaign_budget_service: CampaignBudgetServiceClient = client.get_service(
52-
"CampaignBudgetService", is_async=True
53-
)
54-
campaign_service: CampaignServiceClient = client.get_service(
55-
"CampaignService", is_async=True
56-
)
47+
# Gets the GoogleAdsService client.
48+
googleads_service = client.get_service("GoogleAdsService", is_async=True)
49+
50+
# We are creating both the budget and the campaign in the same request, so
51+
# we need to use a temporary resource name for the budget to reference it
52+
# in the campaign.
53+
# Temporary resource names must be negative integers formatted as strings.
54+
# https://developers.google.com/google-ads/api/docs/batch-processing/temporary-ids
55+
budget_resource_name: str = f"customers/{customer_id}/campaignBudgets/-1"
56+
57+
mutate_operations: List[MutateOperation] = []
5758

5859
# Create a budget, which can be shared by multiple campaigns.
5960
campaign_budget_operation: CampaignBudgetOperation = client.get_type(
6061
"CampaignBudgetOperation"
6162
)
6263
campaign_budget: CampaignBudget = campaign_budget_operation.create
64+
campaign_budget.resource_name = budget_resource_name
6365
campaign_budget.name = f"Interplanetary Budget {uuid.uuid4()}"
6466
campaign_budget.delivery_method = (
6567
client.enums.BudgetDeliveryMethodEnum.STANDARD
6668
)
6769
campaign_budget.amount_micros = 500000
6870

69-
# Add budget.
70-
budget_operations: List[CampaignBudgetOperation] = [
71-
campaign_budget_operation
72-
]
73-
campaign_budget_response: MutateCampaignBudgetsResponse = (
74-
await campaign_budget_service.mutate_campaign_budgets(
75-
customer_id=customer_id,
76-
operations=budget_operations,
77-
)
78-
)
71+
mutate_operation_budget: MutateOperation = client.get_type("MutateOperation")
72+
mutate_operation_budget.campaign_budget_operation = campaign_budget_operation
73+
mutate_operations.append(mutate_operation_budget)
7974

8075
# Create campaign.
8176
campaign_operation: CampaignOperation = client.get_type("CampaignOperation")
@@ -92,7 +87,8 @@ async def main(client: GoogleAdsClient, customer_id: str) -> None:
9287

9388
# Set the bidding strategy and budget.
9489
campaign.manual_cpc = client.get_type("ManualCpc")
95-
campaign.campaign_budget = campaign_budget_response.results[0].resource_name
90+
# Reference the budget created in the same request.
91+
campaign.campaign_budget = budget_resource_name
9692

9793
# Set the campaign network options.
9894
campaign.network_settings.target_google_search = True
@@ -121,12 +117,21 @@ async def main(client: GoogleAdsClient, customer_id: str) -> None:
121117
campaign.end_date = datetime.date.strftime(end_time, _DATE_FORMAT)
122118
# [END add_campaigns_1]
123119

124-
# Add the campaign.
125-
campaign_operations: List[CampaignOperation] = [campaign_operation]
126-
campaign_response: MutateCampaignsResponse = await campaign_service.mutate_campaigns(
127-
customer_id=customer_id, operations=campaign_operations
120+
mutate_operation_campaign: MutateOperation = client.get_type(
121+
"MutateOperation"
122+
)
123+
mutate_operation_campaign.campaign_operation = campaign_operation
124+
mutate_operations.append(mutate_operation_campaign)
125+
126+
# Issue a mutate request to add the budget and campaign.
127+
response: MutateGoogleAdsResponse = await googleads_service.mutate(
128+
customer_id=customer_id, mutate_operations=mutate_operations
128129
)
129-
print(f"Created campaign {campaign_response.results[0].resource_name}.")
130+
131+
# Check the result for the campaign (index 1 in the operations list).
132+
# The response returns results in the same order as operations.
133+
campaign_result = response.mutate_operation_responses[1].campaign_result
134+
print(f"Created campaign {campaign_result.resource_name}.")
130135

131136

132137
if __name__ == "__main__":

examples/asyncio/async_search.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ async def main(client: GoogleAdsClient, customer_id: str) -> None:
4040
campaign.id,
4141
campaign.name
4242
FROM campaign
43-
ORDER BY campaign.id
44-
LIMIT 10"""
43+
ORDER BY campaign.id"""
4544

4645
# Issues a search request using streaming.
4746
stream = await ga_service.search(

examples/asyncio/async_search_stream.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,7 @@ async def main(client: GoogleAdsClient, customer_id: str) -> None:
4040
campaign.id,
4141
campaign.name
4242
FROM campaign
43-
ORDER BY campaign.id
44-
LIMIT 10"""
43+
ORDER BY campaign.id"""
4544

4645
# Issues a search request using streaming.
4746
stream = await ga_service.search_stream(
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
import unittest
2+
import sys
3+
from unittest.mock import MagicMock, AsyncMock, patch
4+
5+
# Mocking modules before import because the environment seems to lack dependencies
6+
mock_google = MagicMock()
7+
sys.modules["google"] = mock_google
8+
sys.modules["google.ads"] = mock_google
9+
sys.modules["google.ads.googleads"] = mock_google
10+
sys.modules["google.ads.googleads.client"] = mock_google
11+
sys.modules["google.ads.googleads.errors"] = mock_google
12+
sys.modules["google.ads.googleads.v22"] = mock_google
13+
sys.modules["google.ads.googleads.v22.resources"] = mock_google
14+
sys.modules["google.ads.googleads.v22.resources.types"] = mock_google
15+
sys.modules["google.ads.googleads.v22.resources.types.campaign"] = mock_google
16+
sys.modules["google.ads.googleads.v22.resources.types.campaign_budget"] = mock_google
17+
sys.modules["google.ads.googleads.v22.services"] = mock_google
18+
sys.modules["google.ads.googleads.v22.services.services"] = mock_google
19+
sys.modules["google.ads.googleads.v22.services.services.campaign_budget_service"] = mock_google
20+
sys.modules["google.ads.googleads.v22.services.services.campaign_service"] = mock_google
21+
sys.modules["google.ads.googleads.v22.services.types"] = mock_google
22+
sys.modules["google.ads.googleads.v22.services.types.campaign_budget_service"] = mock_google
23+
sys.modules["google.ads.googleads.v22.services.types.campaign_service"] = mock_google
24+
sys.modules["google.ads.googleads.v22.services.types.google_ads_service"] = mock_google
25+
26+
from examples.asyncio import async_add_campaigns
27+
28+
29+
class TestAsyncAddCampaigns(unittest.IsolatedAsyncioTestCase):
30+
async def test_main(self):
31+
# Setup Mocks
32+
mock_client_instance = MagicMock() # Mock the client instance directly
33+
mock_googleads_service = AsyncMock()
34+
mock_client_instance.get_service.return_value = mock_googleads_service
35+
36+
mock_budget_op = MagicMock(name="BudgetOp")
37+
mock_campaign_op = MagicMock(name="CampaignOp")
38+
mock_mutate_op_budget = MagicMock(name="MutateOpBudget")
39+
mock_mutate_op_campaign = MagicMock(name="MutateOpCampaign")
40+
mock_manual_cpc = MagicMock(name="ManualCpc")
41+
42+
# side_effect for get_type calls:
43+
# 1. CampaignBudgetOperation
44+
# 2. MutateOperation (for budget)
45+
# 3. CampaignOperation
46+
# 4. ManualCpc
47+
# 5. MutateOperation (for campaign)
48+
mock_client_instance.get_type.side_effect = [
49+
mock_budget_op,
50+
mock_mutate_op_budget,
51+
mock_campaign_op,
52+
mock_manual_cpc,
53+
mock_mutate_op_campaign,
54+
]
55+
56+
# Setup inner objects
57+
mock_budget = mock_budget_op.create
58+
mock_campaign = mock_campaign_op.create
59+
60+
# Setup response
61+
mock_response = MagicMock()
62+
mock_campaign_result = MagicMock()
63+
mock_campaign_result.resource_name = "customers/123/campaigns/456"
64+
65+
# response.mutate_operation_responses[1].campaign_result
66+
mock_response.mutate_operation_responses = [
67+
MagicMock(), # budget result
68+
MagicMock(campaign_result=mock_campaign_result) # campaign result
69+
]
70+
71+
mock_googleads_service.mutate.return_value = mock_response
72+
73+
customer_id = "1234567890"
74+
75+
await async_add_campaigns.main(mock_client_instance, customer_id)
76+
77+
# Verification
78+
79+
# Check if service was retrieved correctly
80+
mock_client_instance.get_service.assert_called_with("GoogleAdsService", is_async=True)
81+
82+
# Verify Budget Resource Name
83+
expected_budget_resource_name = f"customers/{customer_id}/campaignBudgets/-1"
84+
self.assertEqual(mock_budget.resource_name, expected_budget_resource_name)
85+
86+
# Verify Campaign references Budget
87+
self.assertEqual(mock_campaign.campaign_budget, expected_budget_resource_name)
88+
89+
# Verify MutateOperations were constructed
90+
self.assertEqual(mock_mutate_op_budget.campaign_budget_operation, mock_budget_op)
91+
self.assertEqual(mock_mutate_op_campaign.campaign_operation, mock_campaign_op)
92+
93+
# Verify GoogleAdsService.mutate called with correct operations
94+
mock_googleads_service.mutate.assert_called_once()
95+
call_args = mock_googleads_service.mutate.call_args
96+
self.assertEqual(call_args.kwargs["customer_id"], customer_id)
97+
98+
operations = call_args.kwargs["mutate_operations"]
99+
self.assertEqual(len(operations), 2)
100+
self.assertEqual(operations[0], mock_mutate_op_budget)
101+
self.assertEqual(operations[1], mock_mutate_op_campaign)
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import unittest
2+
import sys
3+
from unittest.mock import MagicMock, AsyncMock, patch
4+
5+
# Mocking modules before import because the environment seems to lack dependencies
6+
mock_google = MagicMock()
7+
sys.modules["google"] = mock_google
8+
sys.modules["google.ads"] = mock_google
9+
sys.modules["google.ads.googleads"] = mock_google
10+
sys.modules["google.ads.googleads.client"] = mock_google
11+
sys.modules["google.ads.googleads.errors"] = mock_google
12+
sys.modules["google.ads.googleads.v22"] = mock_google
13+
sys.modules["google.ads.googleads.v22.services"] = mock_google
14+
sys.modules["google.ads.googleads.v22.services.services"] = mock_google
15+
sys.modules["google.ads.googleads.v22.services.services.google_ads_service"] = mock_google
16+
sys.modules["google.ads.googleads.v22.services.types"] = mock_google
17+
sys.modules["google.ads.googleads.v22.services.types.google_ads_service"] = mock_google
18+
19+
# Import module under test AFTER mocking
20+
from examples.asyncio import async_search_stream
21+
22+
23+
class TestAsyncSearchStream(unittest.IsolatedAsyncioTestCase):
24+
async def test_main(self):
25+
# Setup Mocks
26+
mock_client_instance = MagicMock()
27+
mock_googleads_service = AsyncMock()
28+
mock_client_instance.get_service.return_value = mock_googleads_service
29+
30+
# Mock the stream response
31+
# search_stream returns an async iterator of batches
32+
33+
# Create a mock batch
34+
mock_row = MagicMock()
35+
mock_row.campaign.id = 123
36+
mock_row.campaign.name = "Test Campaign"
37+
38+
mock_batch = MagicMock()
39+
mock_batch.results = [mock_row]
40+
41+
# Async iterator setup
42+
async def async_gen():
43+
yield mock_batch
44+
45+
mock_googleads_service.search_stream.return_value = async_gen()
46+
47+
customer_id = "1234567890"
48+
49+
# Execute
50+
await async_search_stream.main(mock_client_instance, customer_id)
51+
52+
# Verification
53+
54+
# Check if service was retrieved correctly
55+
mock_client_instance.get_service.assert_called_with("GoogleAdsService", is_async=True)
56+
57+
# Verify search_stream called
58+
mock_googleads_service.search_stream.assert_called_once()
59+
call_args = mock_googleads_service.search_stream.call_args
60+
self.assertEqual(call_args.kwargs["customer_id"], customer_id)
61+
62+
# Verify Query does NOT contain LIMIT 10
63+
query = call_args.kwargs["query"]
64+
self.assertNotIn("LIMIT 10", query)
65+
self.assertIn("SELECT", query)
66+
self.assertIn("FROM campaign", query)
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
import unittest
2+
import sys
3+
from unittest.mock import MagicMock, AsyncMock, patch
4+
5+
# Mocking modules before import because the environment seems to lack dependencies
6+
mock_google = MagicMock()
7+
sys.modules["google"] = mock_google
8+
sys.modules["google.ads"] = mock_google
9+
sys.modules["google.ads.googleads"] = mock_google
10+
sys.modules["google.ads.googleads.client"] = mock_google
11+
sys.modules["google.ads.googleads.errors"] = mock_google
12+
sys.modules["google.ads.googleads.v22"] = mock_google
13+
sys.modules["google.ads.googleads.v22.services"] = mock_google
14+
sys.modules["google.ads.googleads.v22.services.services"] = mock_google
15+
sys.modules["google.ads.googleads.v22.services.services.google_ads_service"] = mock_google
16+
sys.modules["google.ads.googleads.v22.services.types"] = mock_google
17+
sys.modules["google.ads.googleads.v22.services.types.google_ads_service"] = mock_google
18+
19+
# Import module under test AFTER mocking
20+
from examples.asyncio import async_search
21+
22+
23+
class TestAsyncSearch(unittest.IsolatedAsyncioTestCase):
24+
async def test_main(self):
25+
# Setup Mocks
26+
mock_client_instance = MagicMock()
27+
mock_googleads_service = AsyncMock()
28+
mock_client_instance.get_service.return_value = mock_googleads_service
29+
30+
# Mock the search response (AsyncPager)
31+
# search returns an object that is an async iterator
32+
33+
# Create a mock row
34+
mock_row = MagicMock()
35+
mock_row.campaign.id = 123
36+
mock_row.campaign.name = "Test Campaign"
37+
38+
# Async iterator setup for the pager
39+
async def async_gen():
40+
yield mock_row
41+
42+
mock_googleads_service.search.return_value = async_gen()
43+
44+
customer_id = "1234567890"
45+
46+
# Execute
47+
await async_search.main(mock_client_instance, customer_id)
48+
49+
# Verification
50+
51+
# Check if service was retrieved correctly
52+
mock_client_instance.get_service.assert_called_with("GoogleAdsService", is_async=True)
53+
54+
# Verify search called
55+
mock_googleads_service.search.assert_called_once()
56+
call_args = mock_googleads_service.search.call_args
57+
self.assertEqual(call_args.kwargs["customer_id"], customer_id)
58+
59+
# Verify Query does NOT contain LIMIT 10
60+
query = call_args.kwargs["query"]
61+
self.assertNotIn("LIMIT 10", query)
62+
self.assertIn("SELECT", query)
63+
self.assertIn("FROM campaign", query)

0 commit comments

Comments
 (0)